--- old/README	Wed Oct  5 00:31:33 2016
+++ new/README	Wed Oct  5 00:31:33 2016
@@ -1,3 +1,91 @@
+Last illumos-joyent commit:  d21e83058c8edeb1becd9202380d088cb056f0c4
+
+This is an attempt to port over LX zones into OmniOS.  It's going to be a long,
+painful slog, and I don't mean the ZFS kind, either.  :)
+
+An IPS repo that matches public updates of the r151019 bloody repo can be
+found at http:/pkg.omniti.com/omnios/LX/.  ipkg and lipkg zones can boot,
+appear to be fully functional, but need more testing.  LX zones can be
+installed using ZFS send streams (gzipped or uncompressed) from Joyent's
+images, or from tarballs often used for Docker image setup.  The
+"kernel-version" attribute must be set by the administrator to match the
+installed image.  For example:
+
+	# zonecfg -z lx0 export
+	create -b
+	set zonepath=/zones/lx0
+	set brand=lx
+	set autoboot=false
+	set ip-type=exclusive
+	add net
+	set physical=lx0
+	end
+	add attr
+	set name=kernel-version
+	set type=string
+	set value=2.6.32
+	end
+	#
+
+This matches a CentOS 6.8 image from Joyent.  To install the image:
+
+     zoneadm -z lx0 install -s /root/path/to/centos68.gz
+
+A tarball install uses the -t flag instead:
+
+     zoneadm -z lx0 install -s /root/path/to/docker-ubuntu.tgz
+
+Effective September 1st, one can use SmartOS-style IP address and DNS
+configuration using zonecfg parameters.  Here's the above zone with a pre-
+configured set of networking parameters:
+
+	# zonecfg -z lx0 export
+	create -b
+	set zonepath=/zones/lx0
+	set brand=lx
+	set autoboot=false
+	set ip-type=exclusive
+	add net
+	set physical=lx0
+	add property (name=gateway,value="192.168.0.1")
+	add property (name=ips,value="192.168.0.69/24")
+	add property (name=primary,value="true")
+	end
+	add attr
+	set name=dns-domain
+	set type=string
+	set value=example.com
+	end
+	add attr
+	set name=resolvers
+	set type=string
+	set value=192.168.0.1
+	end
+	add attr
+	set name=kernel-version
+	set type=string
+	set value=2.6.32
+	end
+	#
+
+You'd obtain the .gz file from Joyent's image servers.
+
+LX zones boot, and if not configured with zonecfg(1M), one needs to use
+/native tools and/or scripts to configure networking on them.  (NOTE:  This
+follows the OmniOS adminstrative model for zones.)  LX zones need even more
+testing than ipkg/lipkg zones, but appear to at least boot and allow both
+"zlogin -C" console logins and "zlogin" direct logins.  Some distros do not
+present a console login on the first boot, and the zone may need to be
+rebooted
+
+I reserve the right to burn this repo to the ground and start fresh.
+
+This repo also has the "joyent" branch, which is a downstream of illumos-joyent.
+
+Thanks -- Dan
+
+===========
+
 illumos gate README - Sep 16, 2012.
 
 This is the illumos gate.  This is the illumos source tree.  It contains
--- old/exception_lists/check_rtime	Wed Oct  5 00:31:33 2016
+++ new/exception_lists/check_rtime	Wed Oct  5 00:31:33 2016
@@ -233,4 +233,10 @@
 # unneeded library.
 UNREF_OBJ	lib/(libdevinfo|libcfgadm)\.so\.1; .*\ of\ .*SUNW,Netra-CP2300/lib/libprtdiag_psr\.so\.1
 
+# The lx brand's VDSO object is a bit special. Its construction leads us
+# to end up having several symbols for shared objects all at the same
+# address of zero. We should root cause this further and either add an
+# explicit exception for this kind of thing or better, fix the issues
+# it's upset about.
+SKIP	^usr/lib/brand/MACH(lx)/lx_vdso\.so\.1$
 
--- old/exception_lists/interface_check	Wed Oct  5 00:31:33 2016
+++ new/exception_lists/interface_check	Wed Oct  5 00:31:33 2016
@@ -94,6 +94,7 @@
 NOVERDEF	^usr/lib/picl/plugins$
 NOVERDEF	^usr/sadm/admin/dhcpmgr/dhcpmgr\.so\.1$
 NOVERDEF	^usr/sadm/admin/printmgr/lib/libpmgr\.so\.1$
+NOVERDEF	^usr/MACH(lib)/lx_brand\.so\.1$
 
 
 # Objects that are allowed to deviate from our standard version
@@ -100,6 +101,8 @@
 # names.
 NONSTD_VERNAME	^MACH(lib)/libsqlite-sys\.so\.
 NONSTD_VERNAME	^usr/MACH(lib)/libtecla\.so\.1$
+NONSTD_VERNAME	^usr/lib/brand/lx/lx_vdso\.so\.1$
+NONSTD_VERNAME	^usr/lib/brand/lx/amd64/lx_vdso\.so\.1$
 
 # These libc variants have an SONAME of libc\.so\.1$
 NONSTD_VERNAME	^usr/MACH(lib)/libc/libc_hwcap[1-3]+\.so\.1$
--- old/exception_lists/packaging	Wed Oct  5 00:31:33 2016
+++ new/exception_lists/packaging	Wed Oct  5 00:31:33 2016
@@ -985,3 +985,10 @@
 # debugging program for libadutils
 #
 usr/bin/test-getdc
+
+#
+# LX Brand internals
+#
+
+usr/include/sys/inotify.h
+usr/include/sys/zfd.h
--- old/usr/src/Makefile.lint	Wed Oct  5 00:31:33 2016
+++ new/usr/src/Makefile.lint	Wed Oct  5 00:31:33 2016
@@ -484,6 +484,7 @@
 	cmd/biosdev \
 	cmd/rtc \
 	cmd/ucodeadm \
+	lib/brand/lx \
 	lib/cfgadm_plugins/sata \
 	lib/cfgadm_plugins/sbd \
 	lib/libfdisk
--- old/usr/src/Makefile.master.64	Wed Oct  5 00:31:33 2016
+++ new/usr/src/Makefile.master.64	Wed Oct  5 00:31:33 2016
@@ -21,6 +21,7 @@
 #
 # Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
 # Copyright 2014 Garrett D'Amore <garrett@damore.org>
+# Copyright (c) 2014 Joyent, Inc.  All rights reserved.
 #
 
 # rebind basic build macros to 64-bit versions
@@ -67,7 +68,7 @@
 REAL_CCC=	$(CW_CCC_CMD:sh)
 
 BUILD.SO=	$(CC) $(CFLAGS) -o $@ $(GSHARED) $(DYNFLAGS) \
-		$(PICS) $(EXTPICS) -L $(ROOTLIBDIR64) $(LDLIBS)
+		$(PICS) $(EXTPICS) $(USDT_PICS) -L $(ROOTLIBDIR64) $(LDLIBS)
 
 #
 # ld(1) requires the -64 option to create a 64-bit filter solely from a mapfile
--- old/usr/src/Targetdirs	Wed Oct  5 00:31:34 2016
+++ new/usr/src/Targetdirs	Wed Oct  5 00:31:34 2016
@@ -53,6 +53,8 @@
 	/boot/grub/bin		\
 	/platform/i86pc		\
 	/lib/libmvec		\
+	/usr/lib/brand/lx \
+	/usr/lib/brand/lx/amd64 \
 	/usr/lib/xen		\
 	/usr/lib/xen/bin
 
@@ -509,6 +511,9 @@
 	/usr/ucblib/32 \
 	/var/ld/32
 
+i386_SYM.DIRS64= \
+	/usr/lib/brand/lx/64
+
 sparc_SYM.DIRS64=
 
 SYM.DIRS64= \
@@ -624,6 +629,7 @@
 $(BUILD64) $(ROOT)/lib/secure/64:=	LINKDEST=$(MACH64)
 $(BUILD64) $(ROOT)/usr/lib/64:=		LINKDEST=$(MACH64)
 $(BUILD64) $(ROOT)/usr/lib/elfedit/64:=	LINKDEST=$(MACH64)
+$(BUILD64) $(ROOT)/usr/lib/brand/lx/64:=	LINKDEST=$(MACH64)
 $(BUILD64) $(ROOT)/usr/lib/brand/sn1/64:=	LINKDEST=$(MACH64)
 $(BUILD64) $(ROOT)/usr/lib/brand/solaris10/64:=	LINKDEST=$(MACH64)
 $(BUILD64) $(ROOT)/usr/lib/lwp/64:=	LINKDEST=$(MACH64)
--- old/usr/src/cmd/cmd-inet/etc/sock2path.d/system%2Fkernel	Wed Oct  5 00:31:34 2016
+++ new/usr/src/cmd/cmd-inet/etc/sock2path.d/system%2Fkernel	Wed Oct  5 00:31:34 2016
@@ -18,6 +18,7 @@
 # CDDL HEADER END
 #
 # Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2014, Joyent, Inc. All rights reserved.
 #
 # socket configuration information
 #
@@ -52,3 +53,6 @@
 	29	4	1	/dev/spdsock
 
 	31	1	0	trill
+
+	33	1	0	lx_netlink
+	33	4	0	lx_netlink
--- old/usr/src/cmd/cmd-inet/lib/ipmgmtd/Makefile	Wed Oct  5 00:31:34 2016
+++ new/usr/src/cmd/cmd-inet/lib/ipmgmtd/Makefile	Wed Oct  5 00:31:34 2016
@@ -19,6 +19,7 @@
 # CDDL HEADER END
 #
 # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2015 Joyent, Inc.
 #
 
 # Needed for ROOTFS_LIBDIR definition
@@ -25,7 +26,8 @@
 include		../../../../lib/Makefile.lib
 
 PROG=		ipmgmtd
-OBJS=		ipmgmt_main.o ipmgmt_door.o ipmgmt_persist.o ipmgmt_util.o
+OBJS=		ipmgmt_main.o ipmgmt_door.o ipmgmt_persist.o ipmgmt_util.o \
+		ipmgmt_path.o
 SRCS=		$(OBJS:.o=.c)
 SVCMETHOD=	net-ipmgmt
 MANIFEST=	network-ipmgmt.xml
--- old/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c	Wed Oct  5 00:31:34 2016
+++ new/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c	Wed Oct  5 00:31:34 2016
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2014, Joyent, Inc. All rights reserved.
  */
 
 /*
@@ -112,7 +113,9 @@
 		goto fail;
 	}
 
-	/* check for solaris.network.interface.config authorization */
+	/*
+	 * if not root, check for solaris.network.interface.config authorization
+	 */
 	if (infop->idi_set) {
 		uid_t		uid;
 		struct passwd	pwd;
@@ -124,24 +127,32 @@
 			goto fail;
 		}
 		uid = ucred_getruid(cred);
+		ucred_free(cred);
 		if ((int)uid < 0) {
 			err = errno;
 			ipmgmt_log(LOG_ERR, "Could not get user id.");
 			goto fail;
 		}
-		if (getpwuid_r(uid, &pwd, buf, sizeof (buf)) ==
-		    NULL) {
-			err = errno;
-			ipmgmt_log(LOG_ERR, "Could not get password entry.");
-			goto fail;
+
+		/*
+		 * Branded zones may have different auth, but root always
+		 * allowed.
+		 */
+		if (uid != 0) {
+			if (getpwuid_r(uid, &pwd, buf, sizeof (buf)) == NULL) {
+				err = errno;
+				ipmgmt_log(LOG_ERR,
+				    "Could not get password entry.");
+				goto fail;
+			}
+			if (chkauthattr(NETWORK_INTERFACE_CONFIG_AUTH,
+			    pwd.pw_name) != 1) {
+				err = EPERM;
+				ipmgmt_log(LOG_ERR,
+				    "Not authorized for operation.");
+				goto fail;
+			}
 		}
-		if (chkauthattr(NETWORK_INTERFACE_CONFIG_AUTH,
-		    pwd.pw_name) != 1) {
-			err = EPERM;
-			ipmgmt_log(LOG_ERR, "Not authorized for operation.");
-			goto fail;
-		}
-		ucred_free(cred);
 	}
 
 	/* individual handlers take care of calling door_return */
--- old/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_impl.h	Wed Oct  5 00:31:34 2016
+++ new/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_impl.h	Wed Oct  5 00:31:34 2016
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
  */
 
 #ifndef	_IPMGMT_IMPL_H
@@ -134,8 +135,6 @@
 #define	ADDROBJ_LOOKUPADD	0x00000004
 #define	ADDROBJ_SETLIFNUM	0x00000008
 
-/* Permanent data store for ipadm */
-#define	IPADM_DB_FILE		"/etc/ipadm/ipadm.conf"
 #define	IPADM_FILE_MODE		(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
 
 /*
@@ -145,21 +144,13 @@
  */
 #define	IPADM_DB_VERSION	1
 
-/*
- * A temporary file created in SMF volatile filesystem. This file captures the
- * in-memory copy of list `aobjmap' on disk. This is done to recover from
- * daemon reboot (using svcadm) or crashes.
- */
-#define	IPADM_TMPFS_DIR		"/etc/svc/volatile/ipadm"
-#define	ADDROBJ_MAPPING_DB_FILE	IPADM_TMPFS_DIR"/aobjmap.conf"
+typedef enum ipadm_path {
+	IPADM_PATH_TMPFS_DIR = 1,
+	IPADM_PATH_ADDROBJ_MAP_DB,
+	IPADM_PATH_DB,
+	IPADM_PATH_VOL_DB
+} ipadm_path_t;
 
-/*
- * A temporary copy of the ipadm configuration file might need
- * to be created if write requests are encountered during boottime
- * and the root filesystem is mounted read-only.
- */
-#define	IPADM_VOL_DB_FILE	IPADM_TMPFS_DIR"/ipadm.conf"
-
 /* SCF resources required to interact with svc.configd */
 typedef struct scf_resources {
 	scf_handle_t		*sr_handle;
@@ -188,6 +179,8 @@
 extern boolean_t	ipmgmt_needs_upgrade(scf_resources_t *);
 extern void		ipmgmt_update_dbver(scf_resources_t *);
 
+extern void		ipmgmt_path(ipadm_path_t, char *, size_t);
+
 #ifdef  __cplusplus
 }
 #endif
--- old/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_main.c	Wed Oct  5 00:31:34 2016
+++ new/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_main.c	Wed Oct  5 00:31:34 2016
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
  */
 
 /*
@@ -105,6 +106,7 @@
 	int		fd, err, scferr;
 	scf_resources_t	res;
 	boolean_t	upgrade = B_TRUE;
+	char		aobjpath[MAXPATHLEN];
 
 	/*
 	 * Check to see if we need to upgrade the data-store. We need to
@@ -134,11 +136,11 @@
 		ipmgmt_release_scf_resources(&res);
 
 	/* creates the address object data store, if it doesn't exist */
-	if ((fd = open(ADDROBJ_MAPPING_DB_FILE, O_CREAT|O_RDONLY,
-	    IPADM_FILE_MODE)) == -1) {
+	ipmgmt_path(IPADM_PATH_ADDROBJ_MAP_DB, aobjpath, sizeof (aobjpath));
+	if ((fd = open(aobjpath, O_CREAT|O_RDONLY, IPADM_FILE_MODE)) == -1) {
 		err = errno;
-		ipmgmt_log(LOG_ERR, "could not open %s: %s",
-		    ADDROBJ_MAPPING_DB_FILE, strerror(err));
+		ipmgmt_log(LOG_ERR, "could not open %s: %s", aobjpath,
+		    strerror(err));
 		return (err);
 	}
 	(void) close(fd);
@@ -152,8 +154,8 @@
 	 * representation of the mapping. That is, build `aobjmap' structure
 	 * from address object data store.
 	 */
-	if ((err = ipadm_rw_db(ipmgmt_aobjmap_init, NULL,
-	    ADDROBJ_MAPPING_DB_FILE, 0, IPADM_DB_READ)) != 0) {
+	if ((err = ipadm_rw_db(ipmgmt_aobjmap_init, NULL, aobjpath, 0,
+	    IPADM_DB_READ)) != 0) {
 		/* if there was nothing to initialize, it's fine */
 		if (err != ENOENT)
 			return (err);
@@ -165,17 +167,42 @@
 	return (err);
 }
 
+static const char *
+ipmgmt_door_path()
+{
+	static char door[MAXPATHLEN];
+	static boolean_t init_done = B_FALSE;
+
+	if (!init_done) {
+		const char *zroot = zone_get_nroot();
+
+		/*
+		 * If this is a branded zone, make sure we use the "/native"
+		 * prefix for the door path:
+		 */
+		(void) snprintf(door, sizeof (door), "%s%s", zroot != NULL ?
+		    zroot : "", IPMGMT_DOOR);
+
+		init_done = B_TRUE;
+	}
+
+	return (door);
+}
+
 static int
 ipmgmt_door_init()
 {
 	int fd;
 	int err;
+	const char *door = ipmgmt_door_path();
 
-	/* create the door file for ipmgmtd */
-	if ((fd = open(IPMGMT_DOOR, O_CREAT|O_RDONLY, IPADM_FILE_MODE)) == -1) {
+	/*
+	 * Create the door file for ipmgmtd.
+	 */
+	if ((fd = open(door, O_CREAT | O_RDONLY, IPADM_FILE_MODE)) == -1) {
 		err = errno;
-		ipmgmt_log(LOG_ERR, "could not open %s: %s",
-		    IPMGMT_DOOR, strerror(err));
+		ipmgmt_log(LOG_ERR, "could not open %s: %s", door,
+		    strerror(err));
 		return (err);
 	}
 	(void) close(fd);
@@ -186,15 +213,16 @@
 		ipmgmt_log(LOG_ERR, "failed to create door: %s", strerror(err));
 		return (err);
 	}
+
 	/*
 	 * fdetach first in case a previous daemon instance exited
 	 * ungracefully.
 	 */
-	(void) fdetach(IPMGMT_DOOR);
-	if (fattach(ipmgmt_door_fd, IPMGMT_DOOR) != 0) {
+	(void) fdetach(door);
+	if (fattach(ipmgmt_door_fd, door) != 0) {
 		err = errno;
-		ipmgmt_log(LOG_ERR, "failed to attach door to %s: %s",
-		    IPMGMT_DOOR, strerror(err));
+		ipmgmt_log(LOG_ERR, "failed to attach door to %s: %s", door,
+		    strerror(err));
 		goto fail;
 	}
 	return (0);
@@ -207,13 +235,15 @@
 static void
 ipmgmt_door_fini()
 {
+	const char *door = ipmgmt_door_path();
+
 	if (ipmgmt_door_fd == -1)
 		return;
 
-	(void) fdetach(IPMGMT_DOOR);
+	(void) fdetach(door);
 	if (door_revoke(ipmgmt_door_fd) == -1) {
 		ipmgmt_log(LOG_ERR, "failed to revoke access to door %s: %s",
-		    IPMGMT_DOOR, strerror(errno));
+		    door, strerror(errno));
 	}
 }
 
@@ -350,10 +380,14 @@
 {
 	struct stat	statbuf;
 	int		err;
+	char		tmpfsdir[MAXPATHLEN];
 
-	/* create the IPADM_TMPFS_DIR directory */
-	if (stat(IPADM_TMPFS_DIR, &statbuf) < 0) {
-		if (mkdir(IPADM_TMPFS_DIR, (mode_t)0755) < 0) {
+	/*
+	 * Create the volatile storage directory:
+	 */
+	ipmgmt_path(IPADM_PATH_TMPFS_DIR, tmpfsdir, sizeof (tmpfsdir));
+	if (stat(tmpfsdir, &statbuf) < 0) {
+		if (mkdir(tmpfsdir, (mode_t)0755) < 0) {
 			err = errno;
 			goto fail;
 		}
@@ -364,8 +398,8 @@
 		}
 	}
 
-	if ((chmod(IPADM_TMPFS_DIR, 0755) < 0) ||
-	    (chown(IPADM_TMPFS_DIR, UID_NETADM, GID_NETADM) < 0)) {
+	if ((chmod(tmpfsdir, 0755) < 0) ||
+	    (chown(tmpfsdir, UID_NETADM, GID_NETADM) < 0)) {
 		err = errno;
 		goto fail;
 	}
--- /dev/null	Wed Oct  5 00:31:34 2016
+++ new/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_path.c	Wed Oct  5 00:31:34 2016
@@ -0,0 +1,84 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+/*
+ * Lookup functions for various file paths used by ipmgmtd.  This mechanism
+ * primarily exists to account for a native root prefix when run within a
+ * branded zone (e.g. "/native").
+ */
+
+#include <stdio.h>
+#include <zone.h>
+#include "ipmgmt_impl.h"
+
+#define	IPADM_PERM_DIR		"/etc/ipadm"
+#define	IPADM_TMPFS_DIR		"/etc/svc/volatile/ipadm"
+
+typedef struct ipadm_path_ent {
+	ipadm_path_t ipe_id;
+	const char *ipe_path;
+} ipadm_path_ent_t;
+
+static ipadm_path_ent_t ipadm_paths[] = {
+	/*
+	 * A temporary directory created in the SMF volatile filesystem.
+	 */
+	{ IPADM_PATH_TMPFS_DIR,		IPADM_TMPFS_DIR },
+
+	/*
+	 * This file captures the in-memory copy of list `aobjmap' on disk.
+	 * This allows the system to recover in the event that the daemon
+	 * crashes or is restarted.
+	 */
+	{ IPADM_PATH_ADDROBJ_MAP_DB,	IPADM_TMPFS_DIR "/aobjmap.conf" },
+
+	/*
+	 * The permanent data store for ipadm.
+	 */
+	{ IPADM_PATH_DB,		IPADM_PERM_DIR "/ipadm.conf" },
+
+	/*
+	 * A temporary copy of the ipadm configuration created, if needed, to
+	 * service write requests early in boot.  This file is merged with the
+	 * permanent data store once it is available for writes.
+	 */
+	{ IPADM_PATH_VOL_DB,		IPADM_TMPFS_DIR "/ipadm.conf" },
+
+	{ 0,				NULL }
+};
+
+/*
+ * Load one of the paths used by ipadm into the provided string buffer.
+ * Prepends the native system prefix (e.g. "/native") if one is in effect,
+ * such as when running within a branded zone.
+ */
+void
+ipmgmt_path(ipadm_path_t ip, char *buf, size_t bufsz)
+{
+	int i;
+
+	for (i = 0; ipadm_paths[i].ipe_path != NULL; i++) {
+		if (ipadm_paths[i].ipe_id == ip) {
+			const char *zroot = zone_get_nroot();
+
+			(void) snprintf(buf, bufsz, "%s%s", zroot != NULL ?
+			    zroot : "", ipadm_paths[i].ipe_path);
+
+			return;
+		}
+	}
+
+	abort();
+}
--- old/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c	Wed Oct  5 00:31:34 2016
+++ new/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c	Wed Oct  5 00:31:34 2016
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
  * Copyright 2016 Argo Technologie SA.
  */
 
@@ -384,13 +385,18 @@
 ipmgmt_db_restore_thread(void *arg)
 {
 	int err;
+	char confpath[MAXPATHLEN];
+	char tmpconfpath[MAXPATHLEN];
 
+	ipmgmt_path(IPADM_PATH_DB, confpath, sizeof (confpath));
+	ipmgmt_path(IPADM_PATH_VOL_DB, tmpconfpath, sizeof (tmpconfpath));
+
 	for (;;) {
 		(void) sleep(5);
 		(void) pthread_rwlock_wrlock(&ipmgmt_dbconf_lock);
 		if (!ipmgmt_rdonly_root)
 			break;
-		err = ipmgmt_cpfile(IPADM_VOL_DB_FILE, IPADM_DB_FILE, B_FALSE);
+		err = ipmgmt_cpfile(tmpconfpath, confpath, B_FALSE);
 		if (err == 0) {
 			ipmgmt_rdonly_root = B_FALSE;
 			break;
@@ -422,7 +428,12 @@
 	mode_t		mode;
 	pthread_t	tid;
 	pthread_attr_t	attr;
+	char		confpath[MAXPATHLEN];
+	char		tmpconfpath[MAXPATHLEN];
 
+	ipmgmt_path(IPADM_PATH_DB, confpath, sizeof (confpath));
+	ipmgmt_path(IPADM_PATH_VOL_DB, tmpconfpath, sizeof (tmpconfpath));
+
 	writeop = (db_op != IPADM_DB_READ);
 	if (writeop) {
 		(void) pthread_rwlock_wrlock(&ipmgmt_dbconf_lock);
@@ -434,11 +445,10 @@
 
 	/*
 	 * Did a previous write attempt fail? If so, don't even try to
-	 * read/write to IPADM_DB_FILE.
+	 * read/write to the permanent configuration file.
 	 */
 	if (!ipmgmt_rdonly_root) {
-		err = ipadm_rw_db(db_walk_func, db_warg, IPADM_DB_FILE,
-		    mode, db_op);
+		err = ipadm_rw_db(db_walk_func, db_warg, confpath, mode, db_op);
 		if (err != EROFS)
 			goto done;
 	}
@@ -446,11 +456,11 @@
 	/*
 	 * If we haven't already copied the file to the volatile
 	 * file system, do so. This should only happen on a failed
-	 * writeop(i.e., we have acquired the write lock above).
+	 * writeop (i.e., we have acquired the write lock above).
 	 */
-	if (access(IPADM_VOL_DB_FILE, F_OK) != 0) {
+	if (access(tmpconfpath, F_OK) != 0) {
 		assert(writeop);
-		err = ipmgmt_cpfile(IPADM_DB_FILE, IPADM_VOL_DB_FILE, B_TRUE);
+		err = ipmgmt_cpfile(confpath, tmpconfpath, B_TRUE);
 		if (err != 0)
 			goto done;
 		(void) pthread_attr_init(&attr);
@@ -460,7 +470,7 @@
 		    NULL);
 		(void) pthread_attr_destroy(&attr);
 		if (err != 0) {
-			(void) unlink(IPADM_VOL_DB_FILE);
+			(void) unlink(tmpconfpath);
 			goto done;
 		}
 		ipmgmt_rdonly_root = B_TRUE;
@@ -469,7 +479,7 @@
 	/*
 	 * Read/write from the volatile copy.
 	 */
-	err = ipadm_rw_db(db_walk_func, db_warg, IPADM_VOL_DB_FILE,
+	err = ipadm_rw_db(db_walk_func, db_warg, tmpconfpath,
 	    mode, db_op);
 done:
 	(void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock);
@@ -1237,7 +1247,10 @@
 	int			err;
 	ipadm_dbwrite_cbarg_t	cb;
 	nvlist_t		*nvl = NULL;
+	char			aobjpath[MAXPATHLEN];
 
+	ipmgmt_path(IPADM_PATH_ADDROBJ_MAP_DB, aobjpath, sizeof (aobjpath));
+
 	if (op == IPADM_DB_WRITE) {
 		if ((err = i_ipmgmt_node2nvl(&nvl, nodep)) != 0)
 			return (err);
@@ -1247,14 +1260,14 @@
 		else
 			cb.dbw_flags = 0;
 
-		err = ipadm_rw_db(ipmgmt_update_aobjmap, &cb,
-		    ADDROBJ_MAPPING_DB_FILE, IPADM_FILE_MODE, IPADM_DB_WRITE);
+		err = ipadm_rw_db(ipmgmt_update_aobjmap, &cb, aobjpath,
+		    IPADM_FILE_MODE, IPADM_DB_WRITE);
 		nvlist_free(nvl);
 	} else {
 		assert(op == IPADM_DB_DELETE);
 
-		err = ipadm_rw_db(ipmgmt_delete_aobjmap, nodep,
-		    ADDROBJ_MAPPING_DB_FILE, IPADM_FILE_MODE, IPADM_DB_DELETE);
+		err = ipadm_rw_db(ipmgmt_delete_aobjmap, nodep, aobjpath,
+		    IPADM_FILE_MODE, IPADM_DB_DELETE);
 	}
 	return (err);
 }
--- old/usr/src/cmd/cmd-inet/sbin/dhcpagent/defaults.c	Wed Oct  5 00:31:35 2016
+++ new/usr/src/cmd/cmd-inet/sbin/dhcpagent/defaults.c	Wed Oct  5 00:31:35 2016
@@ -31,6 +31,7 @@
 #include <stdio.h>
 #include <sys/stat.h>
 #include <libnvpair.h>
+#include <zone.h>
 
 #include "common.h"
 #include "defaults.h"
@@ -62,7 +63,33 @@
 	{ "PARAM_IGNORE_LIST",	 NULL,	 0,   0   }
 };
 
+
 /*
+ * df_find_defaults(): builds the path to the default configuration file
+ *
+ *   input: void
+ *  output: void
+ */
+
+static const char *
+df_find_defaults(void)
+{
+	static char agent_defaults_path[MAXPATHLEN] = { 0 };
+	const char	*zroot = NULL;
+
+	if (agent_defaults_path[0] != '\0') {
+		return agent_defaults_path;
+	}
+
+	zroot = zone_get_nroot();
+
+	(void) snprintf(agent_defaults_path, MAXPATHLEN, "%s%s",
+	    zroot != NULL ?  zroot : "", DHCP_AGENT_DEFAULTS);
+
+	return agent_defaults_path;
+}
+
+/*
  * df_build_cache(): builds the defaults nvlist cache
  *
  *   input: void
@@ -72,6 +99,7 @@
 static nvlist_t *
 df_build_cache(void)
 {
+	const char	*agent_defaults_path = df_find_defaults();
 	char		entry[1024];
 	int		i;
 	char		*param, *pastv6, *value, *end;
@@ -79,7 +107,7 @@
 	nvlist_t 	*nvlist;
 	struct dhcp_default *defp;
 
-	if ((fp = fopen(DHCP_AGENT_DEFAULTS, "r")) == NULL)
+	if ((fp = fopen(agent_defaults_path, "r")) == NULL)
 		return (NULL);
 
 	if (nvlist_alloc(&nvlist, NV_UNIQUE_NAME, 0) != 0) {
@@ -159,6 +187,7 @@
 const char *
 df_get_string(const char *if_name, boolean_t isv6, uint_t param)
 {
+	const char		*agent_defaults_path = df_find_defaults();
 	char			*value;
 	char			paramstr[256];
 	char			name[256];
@@ -170,10 +199,11 @@
 	if (param >= (sizeof (defaults) / sizeof (*defaults)))
 		return (NULL);
 
-	if (stat(DHCP_AGENT_DEFAULTS, &statbuf) != 0) {
+
+	if (stat(agent_defaults_path, &statbuf) != 0) {
 		if (!df_unavail_msg) {
 			dhcpmsg(MSG_WARNING, "cannot access %s; using "
-			    "built-in defaults", DHCP_AGENT_DEFAULTS);
+			    "built-in defaults", agent_defaults_path);
 			df_unavail_msg = B_TRUE;
 		}
 		return (defaults[param].df_default);
--- old/usr/src/cmd/cmd-inet/usr.sbin/arp.c	Wed Oct  5 00:31:35 2016
+++ new/usr/src/cmd/cmd-inet/usr.sbin/arp.c	Wed Oct  5 00:31:35 2016
@@ -58,6 +58,7 @@
 #include <arpa/inet.h>
 #include <net/if_types.h>
 #include <net/if_dl.h>
+#include <zone.h>
 
 static int file(char *);
 static int set(int, char *[]);
@@ -118,11 +119,15 @@
 		 * is to let netstat, which prints it as part of
 		 * the MIB statistics, do it.
 		 */
-		(void) execl("/usr/bin/netstat", "netstat",
+		char netstat_path[MAXPATHLEN];
+		const char *zroot = zone_get_nroot();
+		(void) snprintf(netstat_path, sizeof (netstat_path), "%s%s", zroot != NULL ?
+		    zroot : "", "/usr/bin/netstat");
+		(void) execl(netstat_path, "netstat",
 		    (n_flag ? "-np" : "-p"),
 		    "-f", "inet", (char *)0);
-		(void) fprintf(stderr, "failed to exec netstat: %s\n",
-		    strerror(errno));
+		(void) fprintf(stderr, "failed to exec %s: %s\n",
+		    netstat_path, strerror(errno));
 		exit(1);
 
 	} else if (s_flag && (argsleft >= 2)) {
--- old/usr/src/cmd/cmd-inet/usr.sbin/ndp.c	Wed Oct  5 00:31:35 2016
+++ new/usr/src/cmd/cmd-inet/usr.sbin/ndp.c	Wed Oct  5 00:31:35 2016
@@ -40,6 +40,7 @@
 #include <inet/ip.h>
 #include <net/if_dl.h>
 #include <net/route.h>
+#include <zone.h>
 
 typedef	struct	sockaddr_in6	sin6_t;
 
@@ -95,7 +96,6 @@
 static	int	ndp_set_file(char *);
 
 static	char		*ndp_iface = NULL;
-static	char		*netstat_path = "/usr/bin/netstat";
 static	pid_t		ndp_pid;
 static	boolean_t	ndp_noresolve = B_FALSE; /* Don't lookup addresses */
 static	boolean_t	ndp_run = B_TRUE;
@@ -103,6 +103,7 @@
 #define	MAX_ATTEMPTS 5
 #define	MAX_OPTS 5
 #define	WORDSEPS " \t\r\n"
+#define	NETSTAT_PATH	"/usr/bin/netstat"
 
 /*
  * Macros borrowed from route(1M) for working with PF_ROUTE messages
@@ -767,6 +768,12 @@
 static void
 ndp_get_all(void)
 {
+	char netstat_path[MAXPATHLEN];
+	const char *zroot = zone_get_nroot();
+
+	(void) snprintf(netstat_path, sizeof (netstat_path), "%s%s", zroot != NULL ?
+	    zroot : "", NETSTAT_PATH);
+
 	(void) execl(netstat_path, "netstat",
 	    (ndp_noresolve ? "-np" : "-p"),
 	    "-f", "inet6", (char *)0);
--- old/usr/src/cmd/devfsadm/i386/Makefile	Wed Oct  5 00:31:35 2016
+++ new/usr/src/cmd/devfsadm/i386/Makefile	Wed Oct  5 00:31:35 2016
@@ -24,8 +24,11 @@
 
 LINK_OBJS_i386 = \
 	misc_link_i386.o \
+	lx_link_i386.o \
 	xen_link.o
 
+lx_link_i386.o lx_link_i386.po lx_link_i386.ln := CPPFLAGS += -I$(UTSBASE)/common/brand/lx
+
 xen_link.o xen_link.ln xen_link.po := CPPFLAGS += -I$(UTSBASE)/i86xpv
 
 include ../Makefile.com
--- /dev/null	Wed Oct  5 00:31:35 2016
+++ new/usr/src/cmd/devfsadm/i386/lx_link_i386.c	Wed Oct  5 00:31:35 2016
@@ -0,0 +1,81 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#include <devfsadm.h>
+#include <strings.h>
+#include <stdio.h>
+#include <sys/lx_ptm.h>
+#include <sys/lx_autofs.h>
+
+static int lx_ptm(di_minor_t minor, di_node_t node);
+static int lx_autofs(di_minor_t minor, di_node_t node);
+static int lx_systrace(di_minor_t minor, di_node_t node);
+
+static devfsadm_create_t lx_create_cbt[] = {
+	{ "pseudo", "ddi_pseudo", LX_PTM_DRV,
+	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, lx_ptm },
+	{ "pseudo", "ddi_pseudo", LX_AUTOFS_NAME,
+	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, lx_autofs },
+	{ "pseudo", "ddi_pseudo", "lx_systrace",
+	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, lx_systrace },
+};
+
+DEVFSADM_CREATE_INIT_V0(lx_create_cbt);
+
+static int
+lx_ptm(di_minor_t minor, di_node_t node)
+{
+	char *mname = di_minor_name(minor);
+
+	if (strcmp(LX_PTM_MINOR_NODE, mname) == 0)
+		(void) devfsadm_mklink("brand/lx/ptmx", node, minor, 0);
+
+	return (DEVFSADM_CONTINUE);
+}
+
+static int
+lx_autofs(di_minor_t minor, di_node_t node)
+{
+	char *mname = di_minor_name(minor);
+
+	if (strcmp(LX_AUTOFS_MINORNAME, mname) == 0)
+		(void) devfsadm_mklink("brand/lx/autofs", node, minor, 0);
+
+	return (DEVFSADM_CONTINUE);
+}
+
+static int
+lx_systrace(di_minor_t minor, di_node_t node)
+{
+	char *mname = di_minor_name(minor);
+	char path[MAXPATHLEN];
+
+	(void) snprintf(path, sizeof (path), "dtrace/provider/%s", mname);
+	(void) devfsadm_mklink(path, node, minor, 0);
+
+	return (DEVFSADM_CONTINUE);
+}
--- old/usr/src/cmd/devfsadm/misc_link.c	Wed Oct  5 00:31:35 2016
+++ new/usr/src/cmd/devfsadm/misc_link.c	Wed Oct  5 00:31:35 2016
@@ -32,6 +32,7 @@
 #include <limits.h>
 #include <sys/zone.h>
 #include <sys/zcons.h>
+#include <sys/zfd.h>
 #include <sys/cpuid_drv.h>
 
 static int display(di_minor_t minor, di_node_t node);
@@ -53,6 +54,7 @@
 static int tsalarm_create(di_minor_t minor, di_node_t node);
 static int ntwdt_create(di_minor_t minor, di_node_t node);
 static int zcons_create(di_minor_t minor, di_node_t node);
+static int zfd_create(di_minor_t minor, di_node_t node);
 static int cpuid(di_minor_t minor, di_node_t node);
 static int glvc(di_minor_t minor, di_node_t node);
 static int ses_callback(di_minor_t minor, di_node_t node);
@@ -114,6 +116,9 @@
 	    "(^nca$)|(^rds$)|(^sdp$)|(^ipnet$)|(^dlpistub$)|(^bpf$)",
 	    TYPE_EXACT | DRV_RE, ILEVEL_1, minor_name
 	},
+	{ "pseudo", "ddi_pseudo", "inotify",
+	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, minor_name
+	},
 	{ "pseudo", "ddi_pseudo", "ipd",
 	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, minor_name
 	},
@@ -180,6 +185,9 @@
 	{ "pseudo", "ddi_pseudo", "zcons",
 	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, zcons_create,
 	},
+	{ "pseudo", "ddi_pseudo", "zfd",
+	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, zfd_create,
+	},
 	{ "pseudo", "ddi_pseudo", CPUID_DRIVER_NAME,
 	    TYPE_EXACT | DRV_EXACT, ILEVEL_0, cpuid,
 	},
@@ -228,6 +236,9 @@
 		ZCONS_SLAVE_NAME ")$",
 	    RM_PRE | RM_HOT | RM_ALWAYS, ILEVEL_0, devfsadm_rm_all
 	},
+	{ "pseudo", "^zfd/" ZONENAME_REGEXP "/(master|slave)/[0-9]+$",
+	    RM_PRE | RM_HOT | RM_ALWAYS, ILEVEL_0, devfsadm_rm_all
+	},
 	{ "pseudo", "^" CPUID_SELF_NAME "$", RM_ALWAYS | RM_PRE | RM_HOT,
 	    ILEVEL_0, devfsadm_rm_all
 	},
@@ -672,6 +683,35 @@
 	    minor_str);
 	(void) devfsadm_mklink(path, node, minor, 0);
 
+	return (DEVFSADM_CONTINUE);
+}
+
+static int
+zfd_create(di_minor_t minor, di_node_t node)
+{
+	char	*minor_str;
+	char	*zonename;
+	int	*id;
+	char	path[MAXPATHLEN];
+
+	minor_str = di_minor_name(minor);
+
+	if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, "zfd_zname",
+	    &zonename) == -1)
+		return (DEVFSADM_CONTINUE);
+
+	if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "zfd_id", &id) == -1)
+		return (DEVFSADM_CONTINUE);
+
+	if (strncmp(minor_str, "slave", 5) == 0) {
+		(void) snprintf(path, sizeof (path), "zfd/%s/slave/%d",
+		    zonename, id[0]);
+	} else {
+		(void) snprintf(path, sizeof (path), "zfd/%s/master/%d",
+		    zonename, id[0]);
+	}
+	(void) devfsadm_mklink(path, node, minor, 0);
+
 	return (DEVFSADM_CONTINUE);
 }
 
--- old/usr/src/cmd/dlmgmtd/dlmgmt_db.c	Wed Oct  5 00:31:35 2016
+++ new/usr/src/cmd/dlmgmtd/dlmgmt_db.c	Wed Oct  5 00:31:36 2016
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015, Joyent Inc.
  */
 
 #include <assert.h>
@@ -43,6 +44,8 @@
 #include <libcontract.h>
 #include <libcontract_priv.h>
 #include <sys/contract/process.h>
+#include <sys/vnic.h>
+#include <zone.h>
 #include "dlmgmt_impl.h"
 
 typedef enum dlmgmt_db_op {
@@ -714,6 +717,7 @@
 	char			attr_name[MAXLINKATTRLEN];
 	size_t			attr_buf_len = 0;
 	void			*attr_buf = NULL;
+	boolean_t		rename;
 
 	curr = buf;
 	len = strlen(buf);
@@ -723,6 +727,7 @@
 		boolean_t	match = (c == '=' ||
 		    (c == ',' && !found_type) || c == ';');
 
+		rename = B_FALSE;
 		/*
 		 * Move to the next character if there is no match and
 		 * if we have not reached the last character.
@@ -768,6 +773,21 @@
 					goto parse_fail;
 				linkp->ll_media =
 				    (uint32_t)*(int64_t *)attr_buf;
+			} else if (strcmp(attr_name, "zone") == 0) {
+				if (read_str(curr, &attr_buf) == 0)
+					goto parse_fail;
+				linkp->ll_zoneid = getzoneidbyname(attr_buf);
+				if (linkp->ll_zoneid == -1) {
+					if (errno == EFAULT)
+						abort();
+					/*
+					 * If we can't find the zone, assign the
+					 * link to the GZ and mark it for being
+					 * renamed.
+					 */
+					linkp->ll_zoneid = 0;
+					rename = B_TRUE;
+				}
 			} else {
 				attr_buf_len = translators[type].read_func(curr,
 				    &attr_buf);
@@ -811,6 +831,16 @@
 
 			(void) snprintf(attr_name, MAXLINKATTRLEN, "%s", curr);
 		}
+
+		/*
+		 * The zone that this link belongs to has died, we are
+		 * reparenting it to the GZ and renaming it to avoid name
+		 * collisions.
+		 */
+		if (rename == B_TRUE) {
+			(void) snprintf(linkp->ll_link, MAXLINKNAMELEN,
+			    "SUNWorphan%u", (uint16_t)(gethrtime() / 1000));
+		}
 		curr = buf + i + 1;
 	}
 
@@ -1222,12 +1252,19 @@
 
 	ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s\t", linkp->ll_link);
 	if (!persist) {
+		char zname[ZONENAME_MAX];
 		/*
-		 * We store the linkid in the active database so that dlmgmtd
-		 * can recover in the event that it is restarted.
+		 * We store the linkid and the zone name in the active database
+		 * so that dlmgmtd can recover in the event that it is
+		 * restarted.
 		 */
 		u64 = linkp->ll_linkid;
 		ptr += write_uint64(ptr, BUFLEN(lim, ptr), "linkid", &u64);
+
+		if (getzonenamebyid(linkp->ll_zoneid, zname,
+		    sizeof (zname)) != -1) {
+			ptr += write_str(ptr, BUFLEN(lim, ptr), "zone", zname);
+		}
 	}
 	u64 = linkp->ll_class;
 	ptr += write_uint64(ptr, BUFLEN(lim, ptr), "class", &u64);
@@ -1382,6 +1419,42 @@
 }
 
 /*
+ * Attempt to mitigate one of the deadlocks in the dlmgmtd architecture.
+ *
+ * dlmgmt_db_init() calls dlmgmt_process_db_req() which eventually gets to
+ * dlmgmt_zfop() which tries to fork, enter the zone and read the file.
+ * Because of the upcall architecture of dlmgmtd this can lead to deadlock
+ * with the following scenario:
+ *    a) the thread preparing to fork will have acquired the malloc locks
+ *       then attempt to suspend every thread in preparation to fork.
+ *    b) all of the upcalls will be blocked in door_ucred() trying to malloc()
+ *       and get the credentials of their caller.
+ *    c) we can't suspend the in-kernel thread making the upcall.
+ *
+ * Thus, we cannot serve door requests because we're blocked in malloc()
+ * which fork() owns, but fork() is in turn blocked on the in-kernel thread
+ * making the door upcall.  This is a fundamental architectural problem with
+ * any server handling upcalls and also trying to fork().
+ *
+ * To minimize the chance of this deadlock occuring, we check ahead of time to
+ * see if the file we want to read actually exists in the zone (which it almost
+ * never does), so we don't need fork in that case (i.e. rarely to never).
+ */
+static boolean_t
+zone_file_exists(char *zoneroot, char *filename)
+{
+	struct stat	sb;
+	char		fname[MAXPATHLEN];
+
+	(void) snprintf(fname, sizeof (fname), "%s/%s", zoneroot, filename);
+
+	if (stat(fname, &sb) == -1)
+		return (B_FALSE);
+
+	return (B_TRUE);
+}
+
+/*
  * Initialize the datalink <link name, linkid> mapping and the link's
  * attributes list based on the configuration file /etc/dladm/datalink.conf
  * and the active configuration cache file
@@ -1388,25 +1461,37 @@
  * /etc/svc/volatile/dladm/datalink-management:default.cache.
  */
 int
-dlmgmt_db_init(zoneid_t zoneid)
+dlmgmt_db_init(zoneid_t zoneid, char *zoneroot)
 {
 	dlmgmt_db_req_t	*req;
 	int		err;
 	boolean_t	boot = B_FALSE;
+	char 		tdir[MAXPATHLEN];
+	char		*path = cachefile;
 
 	if ((req = dlmgmt_db_req_alloc(DLMGMT_DB_OP_READ, NULL,
 	    DATALINK_INVALID_LINKID, zoneid, DLMGMT_ACTIVE, &err)) == NULL)
 		return (err);
 
-	if ((err = dlmgmt_process_db_req(req)) != 0) {
-		/*
-		 * If we get back ENOENT, that means that the active
-		 * configuration file doesn't exist yet, and is not an error.
-		 * We'll create it down below after we've loaded the
-		 * persistent configuration.
-		 */
-		if (err != ENOENT)
-			goto done;
+	/* Handle running in a non-native branded zone (i.e. has /native) */
+	if (zone_file_exists(zoneroot, "/native" DLMGMT_TMPFS_DIR)) {
+		(void) snprintf(tdir, sizeof (tdir), "/native%s", cachefile);
+		path = tdir;
+	}
+
+	if (zone_file_exists(zoneroot, path)) {
+		if ((err = dlmgmt_process_db_req(req)) != 0) {
+			/*
+			 * If we get back ENOENT, that means that the active
+			 * configuration file doesn't exist yet, and is not an
+			 * error.  We'll create it down below after we've
+			 * loaded the persistent configuration.
+			 */
+			if (err != ENOENT)
+				goto done;
+			boot = B_TRUE;
+		}
+	} else {
 		boot = B_TRUE;
 	}
 
@@ -1442,6 +1527,11 @@
 
 /*
  * Remove all links in the given zoneid.
+ *
+ * We do this work in two different passes. In the first pass, we remove any
+ * entry that hasn't been loaned and mark every entry that has been loaned as
+ * something that is going to be tombstomed. In the second pass, we drop the
+ * table lock for every entry and remove the tombstombed entry for our zone.
  */
 void
 dlmgmt_db_fini(zoneid_t zoneid)
@@ -1451,9 +1541,65 @@
 	while (linkp != NULL) {
 		next_linkp = AVL_NEXT(&dlmgmt_name_avl, linkp);
 		if (linkp->ll_zoneid == zoneid) {
-			(void) dlmgmt_destroy_common(linkp,
-			    DLMGMT_ACTIVE | DLMGMT_PERSIST);
+			boolean_t onloan = linkp->ll_onloan;
+
+			/*
+			 * Cleanup any VNICs that were loaned to the zone
+			 * before the zone goes away and we can no longer
+			 * refer to the VNIC by the name/zoneid.
+			 */
+			if (onloan) {
+				(void) dlmgmt_delete_db_entry(linkp,
+				    DLMGMT_ACTIVE);
+				linkp->ll_tomb = B_TRUE;
+			} else {
+				(void) dlmgmt_destroy_common(linkp,
+				    DLMGMT_ACTIVE | DLMGMT_PERSIST);
+			}
 		}
 		linkp = next_linkp;
 	}
+
+again:
+	linkp = avl_first(&dlmgmt_name_avl);
+	while (linkp != NULL) {
+		vnic_ioc_delete_t ioc;
+
+		next_linkp = AVL_NEXT(&dlmgmt_name_avl, linkp);
+
+		if (linkp->ll_zoneid != zoneid) {
+			linkp = next_linkp;
+			continue;
+		}
+		ioc.vd_vnic_id = linkp->ll_linkid;
+		if (linkp->ll_tomb != B_TRUE)
+			abort();
+
+		/*
+		 * We have to drop the table lock while going up into the
+		 * kernel. If we hold the table lock while deleting a vnic, we
+		 * may get blocked on the mac perimeter and the holder of it may
+		 * want something from dlmgmtd.
+		 */
+		dlmgmt_table_unlock();
+
+		if (ioctl(dladm_dld_fd(dld_handle),
+		    VNIC_IOC_DELETE, &ioc) < 0)
+			dlmgmt_log(LOG_WARNING, "dlmgmt_db_fini "
+			    "delete VNIC ioctl failed %d %d",
+			    ioc.vd_vnic_id, errno);
+
+		/*
+		 * Even though we've dropped the lock, we know that nothing else
+		 * could have removed us. Therefore, it should be safe to go
+		 * through and delete ourselves, but do nothing else. We'll have
+		 * to restart iteration from the beginning. This can be painful.
+		 */
+		dlmgmt_table_lock(B_TRUE);
+
+		(void) dlmgmt_destroy_common(linkp,
+		    DLMGMT_ACTIVE | DLMGMT_PERSIST);
+		goto again;
+	}
+
 }
--- old/usr/src/cmd/dlmgmtd/dlmgmt_door.c	Wed Oct  5 00:31:36 2016
+++ new/usr/src/cmd/dlmgmtd/dlmgmt_door.c	Wed Oct  5 00:31:36 2016
@@ -58,8 +58,13 @@
 #include <libsysevent.h>
 #include <libdlmgmt.h>
 #include <librcm.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
 #include "dlmgmt_impl.h"
 
+
 typedef void dlmgmt_door_handler_t(void *, void *, size_t *, zoneid_t,
     ucred_t *);
 
@@ -379,6 +384,11 @@
 	if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0)
 		goto done;
 
+	if (linkp->ll_tomb == B_TRUE) {
+		err = EINPROGRESS;
+		goto done;
+	}
+
 	if (((linkp->ll_flags & flags) & DLMGMT_ACTIVE) != 0) {
 		if ((err = dlmgmt_delete_db_entry(linkp, DLMGMT_ACTIVE)) != 0)
 			goto done;
@@ -648,6 +658,12 @@
 	if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0)
 		goto done;
 
+	if (linkp->ll_tomb == B_TRUE) {
+		err = EBUSY;
+		goto done;
+	}
+
+
 	if (link_by_name(remapid->ld_link, linkp->ll_zoneid) != NULL) {
 		err = EEXIST;
 		goto done;
@@ -709,6 +725,11 @@
 	if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0)
 		goto done;
 
+	if (linkp->ll_tomb == B_TRUE) {
+		err = EBUSY;
+		goto done;
+	}
+
 	if (linkp->ll_flags & DLMGMT_ACTIVE) {
 		err = EINVAL;
 		goto done;
@@ -1216,6 +1237,11 @@
 	if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0)
 		goto done;
 
+	if (linkp->ll_tomb == B_TRUE) {
+		err = EBUSY;
+		goto done;
+	}
+
 	/* We can only assign an active link to a zone. */
 	if (!(linkp->ll_flags & DLMGMT_ACTIVE)) {
 		err = EINVAL;
@@ -1309,7 +1335,11 @@
 	int			err = 0;
 	dlmgmt_door_zonehalt_t	*zonehalt = argp;
 	dlmgmt_zonehalt_retval_t *retvalp = retp;
+	static char my_pid[10];
 
+	if (my_pid[0] == NULL)
+		(void) snprintf(my_pid, sizeof (my_pid), "%d\n", getpid());
+
 	if ((err = dlmgmt_checkprivs(0, cred)) == 0) {
 		if (zoneid != GLOBAL_ZONEID) {
 			err = EACCES;
@@ -1316,9 +1346,26 @@
 		} else if (zonehalt->ld_zoneid == GLOBAL_ZONEID) {
 			err = EINVAL;
 		} else {
+			/*
+			 * dls and mac don't honor the locking rules defined in
+			 * mac. In order to try and make that case less likely
+			 * to happen, we try to serialize some of the zone
+			 * activity here between dlmgmtd and the brands on
+			 * /etc/dladm/zone.lck
+			 */
+			int fd;
+
+			while ((fd = open(ZONE_LOCK, O_WRONLY |
+			    O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) < 0)
+			(void) sleep(1);
+			(void) write(fd, my_pid, sizeof (my_pid));
+			(void) close(fd);
+
 			dlmgmt_table_lock(B_TRUE);
 			dlmgmt_db_fini(zonehalt->ld_zoneid);
 			dlmgmt_table_unlock();
+
+			(void) unlink(ZONE_LOCK);
 		}
 	}
 	retvalp->lr_err = err;
--- old/usr/src/cmd/dlmgmtd/dlmgmt_impl.h	Wed Oct  5 00:31:36 2016
+++ new/usr/src/cmd/dlmgmtd/dlmgmt_impl.h	Wed Oct  5 00:31:36 2016
@@ -67,6 +67,7 @@
 	avl_node_t		ll_loan_node;
 	uint32_t		ll_flags;
 	uint32_t		ll_gen;		/* generation number */
+	boolean_t		ll_tomb;	/* tombstombed */
 } dlmgmt_link_t;
 
 /*
@@ -84,6 +85,8 @@
 	avl_node_t		ld_node;
 } dlmgmt_dlconf_t;
 
+#define	ZONE_LOCK	"/etc/dladm/zone.lck"
+
 extern boolean_t	debug;
 extern const char	*progname;
 extern char		cachefile[];
@@ -138,7 +141,7 @@
 void		dlmgmt_log(int, const char *, ...);
 int		dlmgmt_write_db_entry(const char *, dlmgmt_link_t *, uint32_t);
 int		dlmgmt_delete_db_entry(dlmgmt_link_t *, uint32_t);
-int 		dlmgmt_db_init(zoneid_t);
+int 		dlmgmt_db_init(zoneid_t, char *);
 void		dlmgmt_db_fini(zoneid_t);
 
 #ifdef  __cplusplus
--- old/usr/src/cmd/dlmgmtd/dlmgmt_main.c	Wed Oct  5 00:31:36 2016
+++ new/usr/src/cmd/dlmgmtd/dlmgmt_main.c	Wed Oct  5 00:31:36 2016
@@ -22,6 +22,7 @@
 /*
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2014 Joyent, Inc.  All rights reserved.
  */
 
 /*
@@ -131,9 +132,18 @@
 	int	fd;
 	int	err = 0;
 	char	doorpath[MAXPATHLEN];
+	struct stat statbuf;
 
-	(void) snprintf(doorpath, sizeof (doorpath), "%s%s", rootdir,
-	    DLMGMT_DOOR);
+	/* Handle running in a non-native branded zone (i.e. has /native) */
+	(void) snprintf(doorpath, sizeof (doorpath), "%s/native%s",
+	    rootdir, DLMGMT_TMPFS_DIR);
+	if (stat(doorpath, &statbuf) == 0) {
+		(void) snprintf(doorpath, sizeof (doorpath), "%s/native%s",
+		    rootdir, DLMGMT_DOOR);
+	} else {
+		(void) snprintf(doorpath, sizeof (doorpath), "%s%s",
+		    rootdir, DLMGMT_DOOR);
+	}
 
 	/*
 	 * Create the door file for dlmgmtd.
@@ -192,8 +202,16 @@
 	(void) snprintf(tmpfsdir, sizeof (tmpfsdir), "%s%s", rootdir,
 	    DLMGMT_TMPFS_DIR);
 	if (stat(tmpfsdir, &statbuf) < 0) {
-		if (mkdir(tmpfsdir, (mode_t)0755) < 0)
-			return (errno);
+		if (mkdir(tmpfsdir, (mode_t)0755) < 0) {
+			/*
+			 * Handle running in a non-native branded zone
+			 * (i.e. has /native)
+			 */
+			(void) snprintf(tmpfsdir, sizeof (tmpfsdir),
+			    "%s/native%s", rootdir, DLMGMT_TMPFS_DIR);
+			if (mkdir(tmpfsdir, (mode_t)0755) < 0)
+				return (errno);
+		}
 	} else if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
 		return (ENOTDIR);
 	}
@@ -203,7 +221,7 @@
 		return (EPERM);
 	}
 
-	if ((err = dlmgmt_db_init(zoneid)) != 0)
+	if ((err = dlmgmt_db_init(zoneid, rootdir)) != 0)
 		return (err);
 	return (dlmgmt_door_attach(zoneid, rootdir));
 }
@@ -262,6 +280,8 @@
 		return (err);
 	}
 
+	(void) unlink(ZONE_LOCK);
+
 	/*
 	 * First derive the name of the cache file from the FMRI name. This
 	 * cache name is used to keep active datalink configuration.
--- old/usr/src/cmd/dlmgmtd/dlmgmt_util.c	Wed Oct  5 00:31:36 2016
+++ new/usr/src/cmd/dlmgmtd/dlmgmt_util.c	Wed Oct  5 00:31:36 2016
@@ -461,6 +461,7 @@
 	linkp->ll_linkid = dlmgmt_nextlinkid;
 	linkp->ll_zoneid = zoneid;
 	linkp->ll_gen = 0;
+	linkp->ll_tomb = B_FALSE;
 
 	if (avl_find(&dlmgmt_name_avl, linkp, &name_where) != NULL ||
 	    avl_find(&dlmgmt_id_avl, linkp, &id_where) != NULL) {
--- old/usr/src/cmd/fs.d/mount.c	Wed Oct  5 00:31:36 2016
+++ new/usr/src/cmd/fs.d/mount.c	Wed Oct  5 00:31:36 2016
@@ -21,6 +21,7 @@
 
 /*
  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
@@ -55,6 +56,7 @@
 #include	<stropts.h>
 #include	<sys/conf.h>
 #include	<locale.h>
+#include	<zone.h>
 #include	"fslib.h"
 
 #define	VFS_PATH	"/usr/lib/fs"
@@ -801,6 +803,7 @@
 void
 doexec(char *fstype, char *newargv[])
 {
+	const char *zroot = zone_get_nroot();
 	char	full_path[PATH_MAX];
 	char	alter_path[PATH_MAX];
 	char	*vfs_path = VFS_PATH;
@@ -808,7 +811,8 @@
 	int	i;
 
 	/* build the full pathname of the fstype dependent command. */
-	sprintf(full_path, "%s/%s/%s", vfs_path, fstype, myname);
+	(void) snprintf(full_path, sizeof (full_path), "%s/%s/%s/%s",
+	    (zroot != NULL ? zroot : ""), vfs_path, fstype, myname);
 	sprintf(alter_path, "%s/%s/%s", alt_path, fstype, myname);
 	newargv[1] = myname;
 
--- old/usr/src/cmd/fs.d/nfs/lib/nfs_sec.c	Wed Oct  5 00:31:36 2016
+++ new/usr/src/cmd/fs.d/nfs/lib/nfs_sec.c	Wed Oct  5 00:31:36 2016
@@ -45,6 +45,7 @@
 #include <stdlib.h>
 #include <syslog.h>
 #include <synch.h>
+#include <zone.h>
 #include <rpc/rpc.h>
 #include <nfs/nfs_sec.h>
 #include <rpc/rpcsec_gss.h>
@@ -707,12 +708,17 @@
 {
 	char	line[BUFSIZ];	/* holds each line of NFSSEC_CONF */
 	FILE	*fp;		/* file stream for NFSSEC_CONF */
+	char	nfssec_conf[MAXPATHLEN];
+	const char *zroot = zone_get_nroot();
 
 	if ((whichway == GETBYNAME) && (name == NULL))
 		return (SC_NOTFOUND);
 
+	(void) snprintf(nfssec_conf, sizeof (nfssec_conf), "%s%s", zroot != NULL ?
+	    zroot : "", NFSSEC_CONF);
+
 	(void) mutex_lock(&matching_lock);
-	if ((fp = fopen(NFSSEC_CONF, "r")) == NULL) {
+	if ((fp = fopen(nfssec_conf, "r")) == NULL) {
 		(void) mutex_unlock(&matching_lock);
 		return (SC_OPENFAIL);
 	}
--- old/usr/src/cmd/mdb/common/mdb/mdb_demangle.c	Wed Oct  5 00:31:36 2016
+++ new/usr/src/cmd/mdb/common/mdb/mdb_demangle.c	Wed Oct  5 00:31:36 2016
@@ -24,7 +24,9 @@
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
+/*
+ * Copyright (c) 2014, Joyent, Inc.  All rights reserved.
+ */
 
 #include <mdb/mdb_modapi.h>
 #include <mdb/mdb_demangle.h>
@@ -263,7 +265,8 @@
 cmd_demangle(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 {
 	mdb_demangler_t *dmp = mdb.m_demangler;
-	const char *path = LIB_DEMANGLE;
+	const char *path;
+	char buf[MAXPATHLEN];
 
 	if (argc > 1 || (argc > 0 && argv->a_type != MDB_TYPE_STRING))
 		return (DCMD_USAGE);
@@ -272,6 +275,10 @@
 		if (dmp != NULL)
 			mdb_dem_unload(mdb.m_demangler);
 		path = argv->a_un.a_str;
+	} else {
+		(void) snprintf(buf, MAXPATHLEN,
+		    "%s/%s", mdb.m_root, LIB_DEMANGLE);
+		path = buf;
 	}
 
 	if (dmp != NULL && argc == 0 && !(mdb.m_flags & MDB_FL_DEMANGLE)) {
--- old/usr/src/cmd/mdb/common/mdb/mdb_main.c	Wed Oct  5 00:31:37 2016
+++ new/usr/src/cmd/mdb/common/mdb/mdb_main.c	Wed Oct  5 00:31:37 2016
@@ -25,7 +25,7 @@
  */
 
 /*
- * Copyright (c) 2013, Joyent, Inc.  All rights reserved.
+ * Copyright (c) 2014, Joyent, Inc.  All rights reserved.
  */
 
 #include <sys/types.h>
@@ -51,6 +51,7 @@
 #include <libctf.h>
 #include <errno.h>
 #include <kvm.h>
+#include <zone.h>
 
 #include <mdb/mdb_lex.h>
 #include <mdb/mdb_debug.h>
@@ -797,9 +798,15 @@
 		if (strchr(pidarg, '/') != NULL)
 			(void) mdb_iob_snprintf(object, MAXPATHLEN,
 			    "%s/object/a.out", pidarg);
-		else
+		else {
+			const char *root;
+
 			(void) mdb_iob_snprintf(object, MAXPATHLEN,
-			    "/proc/%s/object/a.out", pidarg);
+			    "%s/proc/%s/object/a.out",
+			    (root = zone_get_nroot()) != NULL ? root : "",
+			    pidarg);
+		}
+
 		tgt_argv[tgt_argc++] = object;
 		tgt_argv[tgt_argc++] = pidarg;
 	}
--- old/usr/src/cmd/mdb/common/modules/ipc/ipc.c	Wed Oct  5 00:31:37 2016
+++ new/usr/src/cmd/mdb/common/modules/ipc/ipc.c	Wed Oct  5 00:31:37 2016
@@ -21,6 +21,7 @@
 /*
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
  */
 
 #include <mdb/mdb_modapi.h>
@@ -229,7 +230,9 @@
 	printtime_nice("ctime: ", shmid->shm_ctime);
 	mdb_printf("sptinfo: %-?p    sptseg: %-?p\n",
 	    shmid->shm_sptinfo, shmid->shm_sptseg);
-	mdb_printf("sptprot: <%lb>\n", shmid->shm_sptprot, prot_flag_bits);
+	mdb_printf("opts: rmpend: %d prot: <%b>\n",
+	    ((shmid->shm_opts & SHM_RM_PENDING) != 0),
+	    (shmid->shm_opts & SHM_PROT_MASK), prot_flag_bits);
 }
 
 
--- old/usr/src/cmd/mdb/common/modules/libc/libc.c	Wed Oct  5 00:31:37 2016
+++ new/usr/src/cmd/mdb/common/modules/libc/libc.c	Wed Oct  5 00:31:37 2016
@@ -135,6 +135,8 @@
 	    uc.uc_stack.ss_sp, uc.uc_stack.ss_size, stack_flags(&uc.uc_stack));
 	mdb_printf("  mcontext = 0x%p\n",
 	    addr + OFFSETOF(ucontext_t, uc_mcontext));
+	mdb_printf("  brand    = 0x%p 0x%p 0x%p\n",
+	    uc.uc_brand_data[0], uc.uc_brand_data[1], uc.uc_brand_data[2]);
 
 	return (DCMD_OK);
 }
@@ -846,15 +848,20 @@
 	    prt_addr(uberdata.all_lwps, 1),
 	    prt_addr(uberdata.all_zombies, 0));
 
-	HD("nthreads   nzombies   ndaemons   pid        sigacthandler");
-	mdb_printf(OFFSTR "%-10d %-10d %-10d %-10d %s\n",
+	HD("nthreads   nzombies   ndaemons   pid");
+	mdb_printf(OFFSTR "%-10d %-10d %-10d %-10d\n",
 	    OFFSET(nthreads),
 	    uberdata.nthreads,
 	    uberdata.nzombies,
 	    uberdata.ndaemons,
-	    (int)uberdata.pid,
-	    prt_addr((void *)uberdata.sigacthandler, 0));
+	    (int)uberdata.pid);
 
+	HD("sigacthandler         setctxt");
+	mdb_printf(OFFSTR "%s %s\n",
+	    OFFSET(sigacthandler),
+	    prt_addr((void *)uberdata.sigacthandler, 1),
+	    prt_addr((void *)uberdata.setctxt, 1));
+
 	HD("lwp_stacks            lwp_laststack         nfreestack stk_cache");
 	mdb_printf(OFFSTR "%s %s %-10d %d\n",
 	    OFFSET(lwp_stacks),
@@ -876,13 +883,18 @@
 	    prt_addr(uberdata.ulwp_replace_last, 1),
 	    prt_addr(uberdata.atforklist, 0));
 
-	HD("robustlocks           robustlist            progname");
-	mdb_printf(OFFSTR "%s %s %s\n",
+	HD("robustlocks           robustlist");
+	mdb_printf(OFFSTR "%s %s\n",
 	    OFFSET(robustlocks),
 	    prt_addr(uberdata.robustlocks, 1),
-	    prt_addr(uberdata.robustlist, 1),
-	    prt_addr(uberdata.progname, 0));
+	    prt_addr(uberdata.robustlist, 1));
 
+	HD("progname              ub_broot");
+	mdb_printf(OFFSTR "%s %s\n",
+	    OFFSET(progname),
+	    prt_addr(uberdata.progname, 1),
+	    prt_addr(uberdata.ub_broot, 1));
+
 	HD("tdb_bootstrap         tdb_sync_addr_hash    tdb_'count tdb_'fail");
 	mdb_printf(OFFSTR "%s %s %-10d %d\n",
 	    OFFSET(tdb_bootstrap),
--- old/usr/src/cmd/pgrep/pgrep.c	Wed Oct  5 00:31:37 2016
+++ new/usr/src/cmd/pgrep/pgrep.c	Wed Oct  5 00:31:37 2016
@@ -597,6 +597,8 @@
 	const char *optstr;
 	optdesc_t *optd;
 	int nmatches, c;
+	const char *zroot;
+	char buf[PATH_MAX];
 
 	DIR *dirp;
 
@@ -626,6 +628,12 @@
 
 	opterr = 0;
 
+	zroot = zone_get_nroot();
+	if (zroot != NULL) {
+		(void) snprintf(buf, sizeof (buf), "%s/%s", zroot, g_procdir);
+		g_procdir = buf;
+	}
+
 	while (optind < argc) {
 		while ((c = getopt(argc, argv, optstr)) != (int)EOF) {
 
--- old/usr/src/cmd/prstat/prstat.c	Wed Oct  5 00:31:37 2016
+++ new/usr/src/cmd/prstat/prstat.c	Wed Oct  5 00:31:37 2016
@@ -181,6 +181,33 @@
 	-1			/* sort in decreasing order */
 };
 
+
+static int
+proc_snprintf(char *_RESTRICT_KYWD s, size_t n,
+    const char *_RESTRICT_KYWD fmt, ...)
+{
+	static boolean_t ptools_zroot_valid = B_FALSE;
+	static const char *ptools_zroot = NULL;
+	va_list args;
+	int ret, nret = 0;
+
+	if (ptools_zroot_valid == B_FALSE) {
+		ptools_zroot_valid = B_TRUE;
+		ptools_zroot = zone_get_nroot();
+	}
+
+	if (ptools_zroot != NULL) {
+		nret = snprintf(s, n, "%s", ptools_zroot);
+		if (nret > n)
+			return (nret);
+	}
+	va_start(args, fmt);
+	ret = vsnprintf(s + nret, n - nret, fmt, args);
+	va_end(args);
+
+	return (ret + nret);
+}
+
 /*
  * Print timestamp as decimal reprentation of time_t value (-d u was specified)
  * or the standard date format (-d d was specified).
@@ -848,9 +875,9 @@
 static int
 read_procfile(fd_t **fd, char *pidstr, char *file, void *buf, size_t bufsize)
 {
-	char procfile[MAX_PROCFS_PATH];
+	char procfile[PATH_MAX];
 
-	(void) snprintf(procfile, MAX_PROCFS_PATH,
+	(void) proc_snprintf(procfile, PATH_MAX,
 	    "/proc/%s/%s", pidstr, file);
 	if ((*fd = fd_open(procfile, O_RDONLY, *fd)) == NULL)
 		return (1);
@@ -1376,6 +1403,7 @@
 	int timeout;
 	struct pollfd pollset;
 	char key;
+	char procpath[PATH_MAX];
 
 	(void) setlocale(LC_ALL, "");
 	(void) textdomain(TEXT_DOMAIN);
@@ -1575,7 +1603,8 @@
 	list_setkeyfunc(NULL, &opts, &lgroups, LT_LGRPS);
 	if (opts.o_outpmode & OPT_TERMCAP)
 		curses_on();
-	if ((procdir = opendir("/proc")) == NULL)
+	(void) proc_snprintf(procpath, sizeof (procpath), "/proc");
+	if ((procdir = opendir(procpath)) == NULL)
 		Die(gettext("cannot open /proc directory\n"));
 	if (opts.o_outpmode & OPT_TTY) {
 		(void) printf(gettext("Please wait...\r"));
--- old/usr/src/cmd/ps/ps.c	Wed Oct  5 00:31:37 2016
+++ new/usr/src/cmd/ps/ps.c	Wed Oct  5 00:31:37 2016
@@ -27,7 +27,7 @@
  */
 
 /*
- * Copyright (c) 2012, Joyent, Inc. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
  */
 
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
@@ -286,7 +286,7 @@
 static	int	kbytes_per_page;
 static	int	pidwidth;
 
-static	char	*procdir = "/proc";	/* standard /proc directory */
+static	char	procdir[MAXPATHLEN];	/* standard /proc directory */
 
 static struct ughead	euid_tbl;	/* table to store selected euid's */
 static struct ughead	ruid_tbl;	/* table to store selected real uid's */
@@ -339,8 +339,16 @@
 main(int argc, char **argv)
 {
 	const char *me;
+	const char *zroot = zone_get_nroot();
 
 	/*
+	 * If this is a branded zone, the native procfs may mounted in a
+	 * non-standard location.  Apply such a path prefix if it exists.
+	 */
+	(void) snprintf(procdir, sizeof (procdir), "%s/proc", zroot != NULL ?
+	    zroot : "");
+
+	/*
 	 * The original two ps'es are linked in a single binary;
 	 * their main()s are renamed to stdmain for /usr/bin/ps and
 	 * ucbmain for /usr/ucb/ps.
@@ -2077,7 +2085,7 @@
 
 static void
 pr_fields(psinfo_t *psinfo, const char *ttyp,
-	void (*print_fld)(psinfo_t *, struct field *, const char *))
+    void (*print_fld)(psinfo_t *, struct field *, const char *))
 {
 	struct field *f;
 
--- old/usr/src/cmd/ptools/Makefile.bld	Wed Oct  5 00:31:37 2016
+++ new/usr/src/cmd/ptools/Makefile.bld	Wed Oct  5 00:31:37 2016
@@ -69,6 +69,12 @@
 
 CERRWARN += $(CERRWARN_$(PROG))
 
+#
+# Common code definitions
+#
+COBJS =	ptools_common.o
+CINC = -I../../common
+
 # pargs depends on ../../common/elfcap components
 # pmadvise depends on pmap components
 
@@ -79,18 +85,42 @@
 OBJS_pargs =		elfcap.o
 SRCS_pargs =		$(ELFCAP)/elfcap.c
 
-CPPFLAGS_pmap =		-I$(PMAP)
-OBJS_pmap =		pmap_common.o
+CPPFLAGS_pmap =		-I$(PMAP) $(CINC)
+OBJS_pmap =		pmap_common.o $(COBJS)
 SRCS_pmap =		$(PMAP)/pmap_common.c
 
-CPPFLAGS_pmadvise =	-I$(PMAP)
-OBJS_pmadvise =		pmap_common.o
+CPPFLAGS_pmadvise =	-I$(PMAP) $(CINC)
+OBJS_pmadvise =		pmap_common.o $(COBJS)
 SRCS_pmadvise =		$(PMAP)/pmap_common.c
 
+CPPFLAGS_preap =	$(CINC)
+OBJS_preap =		$(COBJS)
+
+CPPFLAGS_psig =		$(CINC)
+OBJS_psig =		$(COBJS)
+
+CPPFLAGS_ptime =	$(CINC)
+OBJS_ptime =		$(COBJS)
+
+CPPFLAGS_ptree =	$(CINC)
+OBJS_ptree =		$(COBJS)
+
+CPPFLAGS_pwait =	$(CINC)
+OBJS_pwait =		$(COBJS)
+
+CPPFLAGS_pwdx =		$(CINC)
+OBJS_pwdx =		$(COBJS)
+
 CPPFLAGS +=	$(CPPFLAGS_$(PROG))
 OBJS +=		$(OBJS_$(PROG))
 SRCS +=		$(SRCS_$(PROG))
 
+# For lint, always include ptools_common.c, but suppress the "defined but
+# not used" error, in case a given ptool doesn't need it.
+SRCS +=		../../common/ptools_common.c
+LINTFLAGS +=	-xerroff=E_NAME_DEF_NOT_USED2
+LINTFLAGS64 +=	-xerroff=E_NAME_DEF_NOT_USED2
+
 INSTALL_NEW=
 INSTALL_LEGACY=$(RM) $(ROOTPROCBINSYMLINK) ; \
 	$(LN) -s ../../bin/$(PROG) $(ROOTPROCBINSYMLINK)
@@ -106,6 +136,10 @@
 %.o:	../%.c
 	$(COMPILE.c) $<
 
+%.o:	../../common/%.c
+	$(COMPILE.c) $<
+	$(POST_PROCESS_O)
+
 all:	$(PROG)
 
 ROOTBINPROG=$(ROOTBIN)/$(PROG)
--- /dev/null	Wed Oct  5 00:31:38 2016
+++ new/usr/src/cmd/ptools/common/ptools_common.c	Wed Oct  5 00:31:38 2016
@@ -0,0 +1,50 @@
+/*
+ * 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 (c) 2014 Joyent, Inc.  All rights reserved.
+ */
+
+#include <sys/feature_tests.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <zone.h>
+
+/*
+ * Common routines for ptools.
+ */
+
+int
+proc_snprintf(char *_RESTRICT_KYWD s, size_t n,
+    const char *_RESTRICT_KYWD fmt, ...)
+{
+	static boolean_t ptools_zroot_valid = B_FALSE;
+	static const char *ptools_zroot = NULL;
+	va_list args;
+	int ret, nret = 0;
+
+	if (ptools_zroot_valid == B_FALSE) {
+		ptools_zroot_valid = B_TRUE;
+		ptools_zroot = zone_get_nroot();
+	}
+
+	if (ptools_zroot != NULL) {
+		nret = snprintf(s, n, "%s", ptools_zroot);
+		if (nret > n)
+			return (nret);
+	}
+	va_start(args, fmt);
+	ret = vsnprintf(s + nret, n - nret, fmt, args);
+	va_end(args);
+
+	return (ret + nret);
+}
--- /dev/null	Wed Oct  5 00:31:38 2016
+++ new/usr/src/cmd/ptools/common/ptools_common.h	Wed Oct  5 00:31:38 2016
@@ -0,0 +1,36 @@
+/*
+ * 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 (c) 2014 Joyent, Inc.  All rights reserved.
+ */
+
+#ifndef _PTOOLS_COMMON_H
+#define	_PTOOLS_COMMON_H
+
+#include <sys/feature_tests.h>
+
+/*
+ * Common functions for the ptools.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int proc_snprintf(char *_RESTRICT_KYWD, size_t,
+    const char *_RESTRICT_KYWD, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PTOOLS_COMMON_H */
--- old/usr/src/cmd/ptools/pargs/pargs.c	Wed Oct  5 00:31:38 2016
+++ new/usr/src/cmd/ptools/pargs/pargs.c	Wed Oct  5 00:31:38 2016
@@ -23,7 +23,7 @@
  * Use is subject to license terms.
  */
 /*
- * Copyright (c) 2013, Joyent, Inc.  All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 
 /*
@@ -797,6 +797,7 @@
 	{ AT_BASE,		"AT_BASE",		at_null	},
 	{ AT_FLAGS,		"AT_FLAGS",		at_null	},
 	{ AT_ENTRY,		"AT_ENTRY",		at_null	},
+	{ AT_RANDOM,		"AT_RANDOM",		at_null	},
 	{ AT_SUN_UID,		"AT_SUN_UID",		at_uid	},
 	{ AT_SUN_RUID,		"AT_SUN_RUID",		at_uid	},
 	{ AT_SUN_GID,		"AT_SUN_GID",		at_gid	},
@@ -816,9 +817,12 @@
 	{ AT_SUN_AUXFLAGS,	"AT_SUN_AUXFLAGS",	at_flags },
 	{ AT_SUN_EMULATOR,	"AT_SUN_EMULATOR",	at_str	},
 	{ AT_SUN_BRANDNAME,	"AT_SUN_BRANDNAME",	at_str	},
+	{ AT_SUN_BRAND_NROOT,	"AT_SUN_BRAND_NROOT",	at_str	},
 	{ AT_SUN_BRAND_AUX1,	"AT_SUN_BRAND_AUX1",	at_null	},
 	{ AT_SUN_BRAND_AUX2,	"AT_SUN_BRAND_AUX2",	at_null	},
-	{ AT_SUN_BRAND_AUX3,	"AT_SUN_BRAND_AUX3",	at_null	}
+	{ AT_SUN_BRAND_AUX3,	"AT_SUN_BRAND_AUX3",	at_null	},
+	{ AT_SUN_BRAND_AUX4,	"AT_SUN_BRAND_AUX4",	at_null	},
+	{ AT_SUN_COMMPAGE,	"AT_SUN_COMMPAGE",	at_null	}
 };
 
 #define	N_AT_ENTS (sizeof (aux_arr) / sizeof (struct aux_id))
--- old/usr/src/cmd/ptools/pfiles/pfiles.c	Wed Oct  5 00:31:38 2016
+++ new/usr/src/cmd/ptools/pfiles/pfiles.c	Wed Oct  5 00:31:38 2016
@@ -24,7 +24,7 @@
  * Copyright 2012 DEY Storage Systems, Inc.  All rights reserved.
  */
 /*
- * Copyright (c) 2013 Joyent, Inc.  All Rights reserved.
+ * Copyright (c) 2014 Joyent, Inc.  All Rights reserved.
  */
 
 #include <stdio.h>
@@ -557,6 +557,7 @@
 	case AF_IPX:		p = "AF_IPX";		break;
 	case AF_ROUTE:		p = "AF_ROUTE";		break;
 	case AF_LINK:		p = "AF_LINK";		break;
+	case AF_LX_NETLINK:	p = "AF_LX_NETLINK";	break;
 	}
 
 	(void) printf("\t%s: %s\n", str, p);
--- old/usr/src/cmd/ptools/pflags/pflags.c	Wed Oct  5 00:31:38 2016
+++ new/usr/src/cmd/ptools/pflags/pflags.c	Wed Oct  5 00:31:38 2016
@@ -25,7 +25,7 @@
  */
 
 /*
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
  */
 
 #include <stdio.h>
@@ -469,6 +469,9 @@
 	case PR_SUSPENDED:
 		str = "PR_SUSPENDED";
 		break;
+	case PR_BRAND:
+		str = "PR_BRAND";
+		break;
 	default:
 		str = buf;
 		(void) sprintf(str, "%d", why);
--- old/usr/src/cmd/ptools/pmap/pmap.c	Wed Oct  5 00:31:38 2016
+++ new/usr/src/cmd/ptools/pmap/pmap.c	Wed Oct  5 00:31:38 2016
@@ -22,6 +22,7 @@
 /*
  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright (c) 2014, Joyent, Inc.  All rights reserved.
  */
 
 #include <stdio.h>
@@ -42,6 +43,7 @@
 #include <sys/mman.h>
 #include <sys/lgrp_user.h>
 #include <libproc.h>
+#include "ptools_common.h"
 
 #include "pmap_common.h"
 
@@ -199,7 +201,7 @@
 	const char *bar;
 	struct rlimit rlim;
 	struct stat64 statbuf;
-	char buf[128];
+	char buf[PATH_MAX];
 	int mapfd;
 	int prg_gflags = PGRAB_RDONLY;
 	int prr_flags = 0;
@@ -358,7 +360,7 @@
 		proc_unctrl_psinfo(&psinfo);
 
 		if (Pstate(Pr) != PS_DEAD) {
-			(void) snprintf(buf, sizeof (buf),
+			(void) proc_snprintf(buf, sizeof (buf),
 			    "/proc/%d/map", (int)psinfo.pr_pid);
 			if ((mapfd = open(buf, O_RDONLY)) < 0) {
 				(void) fprintf(stderr, "%s: cannot "
@@ -590,7 +592,7 @@
 	prmap_t *prmapp, *pmp;
 	ssize_t n;
 
-	(void) snprintf(mapname, sizeof (mapname),
+	(void) proc_snprintf(mapname, sizeof (mapname),
 	    "/proc/%d/rmap", (int)Pstatus(Pr)->pr_pid);
 
 	if ((mapfd = open(mapname, O_RDONLY)) < 0 || fstat(mapfd, &st) != 0) {
@@ -631,7 +633,7 @@
 	prxmap_t *prmapp, *pmp;
 	ssize_t n;
 
-	(void) snprintf(mapname, sizeof (mapname),
+	(void) proc_snprintf(mapname, sizeof (mapname),
 	    "/proc/%d/xmap", (int)Pstatus(Pr)->pr_pid);
 
 	if ((mapfd = open(mapname, O_RDONLY)) < 0 || fstat(mapfd, &st) != 0) {
--- old/usr/src/cmd/ptools/pmap/pmap_common.c	Wed Oct  5 00:31:38 2016
+++ new/usr/src/cmd/ptools/pmap/pmap_common.c	Wed Oct  5 00:31:38 2016
@@ -37,6 +37,7 @@
 #include <sys/types.h>
 
 #include "pmap_common.h"
+#include "ptools_common.h"
 
 /*
  * We compare the high memory addresses since stacks are faulted in from
@@ -88,7 +89,7 @@
 		return (NULL);
 
 	/* first see if we can find a path via /proc */
-	(void) snprintf(path, sizeof (path), "/proc/%d/path/%s",
+	(void) proc_snprintf(path, sizeof (path), "/proc/%d/path/%s",
 	    (int)Psp->pr_pid, mapname);
 	len = readlink(path, buf, bufsz - 1);
 	if (len >= 0) {
@@ -97,7 +98,7 @@
 	}
 
 	/* fall back to object information reported by /proc */
-	(void) snprintf(path, sizeof (path),
+	(void) proc_snprintf(path, sizeof (path),
 	    "/proc/%d/object/%s", (int)Psp->pr_pid, mapname);
 	if (stat(path, &statb) == 0) {
 		dev_t dev = statb.st_dev;
--- old/usr/src/cmd/ptools/preap/preap.c	Wed Oct  5 00:31:38 2016
+++ new/usr/src/cmd/ptools/preap/preap.c	Wed Oct  5 00:31:38 2016
@@ -24,8 +24,6 @@
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -37,6 +35,8 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <libproc.h>
+#include <limits.h>
+#include "ptools_common.h"
 
 #define	NOREAP_TIME 60		/* wait 60 seconds before allow a reap */
 
@@ -53,11 +53,11 @@
 static int
 open_usage(pid_t pid, int *perr)
 {
-	char path[64];
+	char path[PATH_MAX];
 	struct stat64 st;
 	int fd;
 
-	(void) snprintf(path, sizeof (path), "/proc/%d/usage", (int)pid);
+	(void) proc_snprintf(path, sizeof (path), "/proc/%d/usage", (int)pid);
 
 	/*
 	 * Attempt to open the usage file, and return the fd if we can
--- old/usr/src/cmd/ptools/psig/psig.c	Wed Oct  5 00:31:38 2016
+++ new/usr/src/cmd/ptools/psig/psig.c	Wed Oct  5 00:31:38 2016
@@ -21,10 +21,9 @@
 /*
  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright (c) 2014, Joyent, Inc.  All rights reserved.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <stdio.h>
 #include <stdio_ext.h>
 #include <stdlib.h>
@@ -37,6 +36,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <libproc.h>
+#include "ptools_common.h"
 
 /* evil knowledge of libc internals */
 #include "../../../lib/libc/inc/thr_uberdata.h"
@@ -172,7 +172,7 @@
 static int
 look(char *arg)
 {
-	char pathname[100];
+	char pathname[PATH_MAX];
 	struct stat statb;
 	int fd = -1;
 	int sig, gcode;
@@ -199,7 +199,8 @@
 	(void) memcpy(&psinfo, psinfop, sizeof (psinfo_t));
 	proc_unctrl_psinfo(&psinfo);
 
-	(void) sprintf(pathname, "/proc/%d/sigact", (int)psinfo.pr_pid);
+	(void) proc_snprintf(pathname, sizeof (pathname), "/proc/%d/sigact",
+	    (int)psinfo.pr_pid);
 	if ((fd = open(pathname, O_RDONLY)) < 0) {
 		perr("open sigact");
 		goto look_error;
@@ -213,8 +214,8 @@
 	action = malloc(maxsig * sizeof (struct sigaction));
 	if (action == NULL) {
 		(void) fprintf(stderr,
-		"%s: cannot malloc() space for %d sigaction structures\n",
-			command, maxsig);
+		    "%s: cannot malloc() space for %d sigaction structures\n",
+		    command, maxsig);
 		goto look_error;
 	}
 	if (read(fd, (char *)action, maxsig * sizeof (struct sigaction)) !=
@@ -239,7 +240,7 @@
 		if (psinfo.pr_dmodel != PR_MODEL_NATIVE) {
 			caddr32_t addr;
 			aharraddr = uberaddr +
-				offsetof(uberdata32_t, siguaction);
+			    offsetof(uberdata32_t, siguaction);
 			aharrlen = sizeof (siguaction32_t) * NSIG;
 			(void) Pread(Pr, &addr, sizeof (addr),
 			    uberaddr + offsetof(uberdata32_t, sigacthandler));
@@ -248,7 +249,7 @@
 #endif
 		{
 			aharraddr = uberaddr +
-				offsetof(uberdata_t, siguaction);
+			    offsetof(uberdata_t, siguaction);
 			aharrlen = sizeof (siguaction_t) * NSIG;
 			(void) Pread(Pr, &intfnaddr, sizeof (intfnaddr),
 			    uberaddr + offsetof(uberdata_t, sigacthandler));
@@ -323,7 +324,7 @@
 					(void) printf("\t%-8s", hname);
 				else
 					(void) printf("\t0x%-8lx",
-						(ulong_t)haddr);
+					    (ulong_t)haddr);
 
 				s = sigflags(sig, sp->sa_flags);
 				(void) printf("%s", (*s != '\0')? s : "\t0");
@@ -334,7 +335,7 @@
 			}
 		} else if (sig == SIGCLD) {
 			s = sigflags(sig,
-				sp->sa_flags & (SA_NOCLDWAIT|SA_NOCLDSTOP));
+			    sp->sa_flags & (SA_NOCLDWAIT|SA_NOCLDSTOP));
 			if (*s != '\0')
 				(void) printf("\t\t%s", s);
 		}
@@ -371,7 +372,7 @@
 	static char code_buf[100];
 	char *str = code_buf;
 	int flagmask =
-		(SA_ONSTACK|SA_RESETHAND|SA_RESTART|SA_SIGINFO|SA_NODEFER);
+	    (SA_ONSTACK|SA_RESETHAND|SA_RESTART|SA_SIGINFO|SA_NODEFER);
 
 	if (sig == SIGCLD)
 		flagmask |= (SA_NOCLDSTOP|SA_NOCLDWAIT);
@@ -414,19 +415,19 @@
 #ifdef _LP64
 	if (psinfo->pr_dmodel != PR_MODEL_NATIVE) {
 		struct sigaction32 *sa32 = (struct sigaction32 *)
-			((uintptr_t)aharr + sig * sizeof (siguaction32_t) +
-			offsetof(siguaction32_t, sig_uaction));
+		    ((uintptr_t)aharr + sig * sizeof (siguaction32_t) +
+		    offsetof(siguaction32_t, sig_uaction));
 
 		sp->sa_flags = sa32->sa_flags;
 		sp->sa_handler = (void (*)())(uintptr_t)sa32->sa_handler;
 		(void) memcpy(&sp->sa_mask, &sa32->sa_mask,
-			sizeof (sp->sa_mask));
+		    sizeof (sp->sa_mask));
 	} else
 #endif
 	{
 		struct sigaction *sa = (struct sigaction *)
-			((uintptr_t)aharr + sig * sizeof (siguaction_t) +
-			offsetof(siguaction_t, sig_uaction));
+		    ((uintptr_t)aharr + sig * sizeof (siguaction_t) +
+		    offsetof(siguaction_t, sig_uaction));
 
 		sp->sa_flags = sa->sa_flags;
 		sp->sa_handler = sa->sa_handler;
--- old/usr/src/cmd/ptools/ptime/ptime.c	Wed Oct  5 00:31:38 2016
+++ new/usr/src/cmd/ptools/ptime/ptime.c	Wed Oct  5 00:31:38 2016
@@ -41,6 +41,8 @@
 #include <sys/time.h>
 #include <signal.h>
 #include <libproc.h>
+#include <limits.h>
+#include "ptools_common.h"
 
 static	int	look(pid_t);
 static	void	hr_min_sec(char *, long);
@@ -189,7 +191,7 @@
 static int
 look(pid_t pid)
 {
-	char pathname[100];
+	char pathname[PATH_MAX];
 	int rval = 0;
 	int fd;
 	psinfo_t psinfo;
@@ -203,7 +205,8 @@
 	if (proc_get_psinfo(pid, &psinfo) < 0)
 		return (perr("read psinfo"));
 
-	(void) sprintf(pathname, "/proc/%d/usage", (int)pid);
+	(void) proc_snprintf(pathname, sizeof (pathname), "/proc/%d/usage",
+	    (int)pid);
 	if ((fd = open(pathname, O_RDONLY)) < 0)
 		return (perr("open usage"));
 
--- old/usr/src/cmd/ptools/ptree/ptree.c	Wed Oct  5 00:31:39 2016
+++ new/usr/src/cmd/ptools/ptree/ptree.c	Wed Oct  5 00:31:39 2016
@@ -21,6 +21,7 @@
 /*
  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright (c) 2014, Joyent, Inc.  All rights reserved.
  */
 
 /*
@@ -27,8 +28,6 @@
  * ptree -- print family tree of processes
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <assert.h>
 #include <stdio.h>
 #include <string.h>
@@ -48,6 +47,7 @@
 #include <sys/ctfs.h>
 #include <libcontract_priv.h>
 #include <sys/stat.h>
+#include "ptools_common.h"
 
 #define	FAKEDPID0(p)	(p->pid == 0 && p->psargs[0] == '\0')
 
@@ -103,10 +103,11 @@
 	char *s;
 	int n;
 	int retc = 0;
+	char ppath[PATH_MAX];
 
 	DIR *dirp;
 	struct dirent *dentp;
-	char	pname[100];
+	char	pname[PATH_MAX];
 	int	pdlen;
 
 	ps_t *p;
@@ -169,16 +170,18 @@
 	psize = 0;
 	ps = NULL;
 
+	(void) proc_snprintf(ppath, sizeof (ppath), "/proc");
+
 	/*
 	 * Search the /proc directory for all processes.
 	 */
-	if ((dirp = opendir("/proc")) == NULL) {
-		(void) fprintf(stderr, "%s: cannot open /proc directory\n",
-		    command);
+	if ((dirp = opendir(ppath)) == NULL) {
+		(void) fprintf(stderr, "%s: cannot open %s directory\n",
+		    command, ppath);
 		return (1);
 	}
 
-	(void) strcpy(pname, "/proc");
+	(void) strcpy(pname, ppath);
 	pdlen = strlen(pname);
 	pname[pdlen++] = '/';
 
--- old/usr/src/cmd/ptools/pwait/pwait.c	Wed Oct  5 00:31:39 2016
+++ new/usr/src/cmd/ptools/pwait/pwait.c	Wed Oct  5 00:31:39 2016
@@ -23,8 +23,6 @@
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <stdio.h>
 #include <stdio_ext.h>
 #include <ctype.h>
@@ -39,6 +37,8 @@
 #include <poll.h>
 #include <procfs.h>
 #include <sys/resource.h>
+#include <limits.h>
+#include "ptools_common.h"
 
 static int count_my_files();
 static char *command;
@@ -49,6 +49,7 @@
 int
 main(int argc, char **argv)
 {
+	char buf[PATH_MAX];
 	unsigned long remain = 0;
 	struct pollfd *pollfd;
 	struct pollfd *pfd;
@@ -75,10 +76,12 @@
 		(void) fprintf(stderr, "usage:\t%s [-v] pid ...\n", command);
 		(void) fprintf(stderr, "  (wait for processes to terminate)\n");
 		(void) fprintf(stderr,
-			"  -v: verbose; report terminations to standard out\n");
+		    "  -v: verbose; report terminations to standard out\n");
 		return (2);
 	}
 
+	(void) proc_snprintf(buf, sizeof (buf), "/proc/");
+
 	/* make sure we have enough file descriptors */
 	if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
 		int nfiles = count_my_files();
@@ -87,8 +90,8 @@
 			rlim.rlim_cur = argc + nfiles + SLOP;
 			if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) {
 				(void) fprintf(stderr,
-					"%s: insufficient file descriptors\n",
-					command);
+				    "%s: insufficient file descriptors\n",
+				    command);
 				return (2);
 			}
 		}
@@ -108,11 +111,11 @@
 		if (strchr(arg, '/') != NULL)
 			(void) strncpy(psinfofile, arg, sizeof (psinfofile));
 		else {
-			(void) strcpy(psinfofile, "/proc/");
+			(void) strcpy(psinfofile, buf);
 			(void) strncat(psinfofile, arg, sizeof (psinfofile)-6);
 		}
 		(void) strncat(psinfofile, "/psinfo",
-			sizeof (psinfofile)-strlen(psinfofile));
+		    sizeof (psinfofile)-strlen(psinfofile));
 
 		pfd = &pollfd[i];
 		if ((pfd->fd = open(psinfofile, O_RDONLY)) >= 0) {
@@ -126,7 +129,7 @@
 			pfd->revents = 0;
 		} else if (errno == ENOENT) {
 			(void) fprintf(stderr, "%s: no such process: %s\n",
-				command, arg);
+			    command, arg);
 		} else {
 			perror(arg);
 		}
@@ -160,9 +163,9 @@
 					if (pread(pfd->fd, &psinfo,
 					    sizeof (psinfo), (off_t)0)
 					    == sizeof (psinfo)) {
-						(void) printf(
-					"%s: terminated, wait status 0x%.4x\n",
-							arg, psinfo.pr_wstat);
+						(void) printf("%s: terminated, "
+						    "wait status 0x%.4x\n",
+						    arg, psinfo.pr_wstat);
 					} else {
 						(void) printf(
 						    "%s: terminated\n", arg);
@@ -170,10 +173,10 @@
 				}
 				if (pfd->revents & POLLNVAL)
 					(void) printf("%s: system process\n",
-						arg);
+					    arg);
 				if (pfd->revents & ~(POLLPRI|POLLHUP|POLLNVAL))
 					(void) printf("%s: unknown error\n",
-						arg);
+					    arg);
 			}
 
 			(void) close(pfd->fd);
--- old/usr/src/cmd/ptools/pwdx/pwdx.c	Wed Oct  5 00:31:39 2016
+++ new/usr/src/cmd/ptools/pwdx/pwdx.c	Wed Oct  5 00:31:39 2016
@@ -22,10 +22,9 @@
 /*
  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright (c) 2014, Joyent, Inc.  All rights reserved.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <stdio.h>
 #include <unistd.h>
 #include <string.h>
@@ -33,6 +32,8 @@
 #include <libproc.h>
 #include <sys/param.h>
 
+#include "ptools_common.h"
+
 static char *command;
 
 static int
@@ -49,7 +50,7 @@
 		return (1);
 	}
 
-	(void) snprintf(proc, sizeof (proc), "/proc/%d/path/cwd",
+	(void) proc_snprintf(proc, sizeof (proc), "/proc/%d/path/cwd",
 	    (int)p.pr_pid);
 
 	if ((ret = readlink(proc, cwd, sizeof (cwd) - 1)) <= 0) {
--- old/usr/src/cmd/rcap/common/utils.c	Wed Oct  5 00:31:39 2016
+++ new/usr/src/cmd/rcap/common/utils.c	Wed Oct  5 00:31:39 2016
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, Joyent, Inc. All rights reserved.
  */
 
 #include <sys/param.h>
@@ -256,78 +257,4 @@
 	} else {
 		return (i);
 	}
-}
-
-/*
- * get_running_zones() calls zone_list(2) to find out how many zones are
- * running.  It then calls zone_list(2) again to fetch the list of running
- * zones (stored in *zents).
- */
-int
-get_running_zones(uint_t *nzents, zone_entry_t **zents)
-{
-	zoneid_t *zids;
-	uint_t nzents_saved;
-	int i;
-	zone_entry_t *zentp;
-	zone_state_t zstate;
-
-	*zents = NULL;
-	if (zone_list(NULL, nzents) != 0) {
-		warn(gettext("could not get zoneid list\n"));
-		return (E_ERROR);
-	}
-
-again:
-	if (*nzents == 0)
-		return (E_SUCCESS);
-
-	if ((zids = (zoneid_t *)calloc(*nzents, sizeof (zoneid_t))) == NULL) {
-		warn(gettext("out of memory: zones will not be capped\n"));
-		return (E_ERROR);
-	}
-
-	nzents_saved = *nzents;
-
-	if (zone_list(zids, nzents) != 0) {
-		warn(gettext("could not get zone list\n"));
-		free(zids);
-		return (E_ERROR);
-	}
-	if (*nzents != nzents_saved) {
-		/* list changed, try again */
-		free(zids);
-		goto again;
-	}
-
-	*zents = calloc(*nzents, sizeof (zone_entry_t));
-	if (*zents == NULL) {
-		warn(gettext("out of memory: zones will not be capped\n"));
-		free(zids);
-		return (E_ERROR);
-	}
-
-	zentp = *zents;
-	for (i = 0; i < *nzents; i++) {
-		char name[ZONENAME_MAX];
-
-		if (getzonenamebyid(zids[i], name, sizeof (name)) < 0) {
-			warn(gettext("could not get name for "
-			    "zoneid %d\n"), zids[i]);
-			continue;
-		}
-
-		(void) strlcpy(zentp->zname, name, sizeof (zentp->zname));
-		zentp->zid = zids[i];
-		if (zone_get_state(name, &zstate) != Z_OK ||
-		    zstate != ZONE_STATE_RUNNING)
-			continue;
-
-
-		zentp++;
-	}
-	*nzents = zentp - *zents;
-
-	free(zids);
-	return (E_SUCCESS);
 }
--- old/usr/src/cmd/rcap/common/utils.h	Wed Oct  5 00:31:39 2016
+++ new/usr/src/cmd/rcap/common/utils.h	Wed Oct  5 00:31:39 2016
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, Joyent, Inc. All rights reserved.
  */
 
 #ifndef	_UTILS_H
@@ -98,7 +99,6 @@
 extern void dprintfe(int, char *, ...);
 extern void hrt2ts(hrtime_t, timestruc_t *);
 extern int xatoi(char *);
-extern int get_running_zones(uint_t *, zone_entry_t **);
 
 #ifdef	__cplusplus
 }
--- old/usr/src/cmd/rcap/rcapadm/Makefile	Wed Oct  5 00:31:39 2016
+++ new/usr/src/cmd/rcap/rcapadm/Makefile	Wed Oct  5 00:31:39 2016
@@ -40,7 +40,7 @@
 
 $(NOT_RELEASE_BUILD)CPPFLAGS 	+= -DDEBUG
 CPPFLAGS 			+= -I$(COMMON_DIR)
-LDLIBS 				+= -lumem -lscf -lzonecfg
+LDLIBS 				+= -lscf -lzonecfg
 
 LINTFLAGS 			+= $(LDLIBS) -mnu
 
--- old/usr/src/cmd/rcap/rcapadm/rcapadm.c	Wed Oct  5 00:31:39 2016
+++ new/usr/src/cmd/rcap/rcapadm/rcapadm.c	Wed Oct  5 00:31:39 2016
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, Joyent, Inc. All rights reserved.
  */
 
 #include <sys/types.h>
@@ -145,6 +146,16 @@
 	scf_handle_destroy(h);
 }
 
+static int
+set_zone_cap(char *zonename, uint64_t mcap)
+{
+	char cmd[128 + ZONENAME_MAX];
+
+	(void) snprintf(cmd, sizeof (cmd), "/usr/bin/prctl -r "
+	    "-n zone.max-physical-memory -v %llu -i zone %s", mcap, zonename);
+	return (system(cmd));
+}
+
 /*
  * Update the in-kernel memory cap for the specified zone.
  */
@@ -151,7 +162,6 @@
 static int
 update_zone_mcap(char *zonename, char *maxrss)
 {
-	zoneid_t zone_id;
 	uint64_t num;
 
 	if (getzoneid() != GLOBAL_ZONEID || zonecfg_in_alt_root())
@@ -158,7 +168,7 @@
 		return (E_SUCCESS);
 
 	/* get the running zone from the kernel */
-	if ((zone_id = getzoneidbyname(zonename)) == -1) {
+	if (getzoneidbyname(zonename) == -1) {
 		(void) fprintf(stderr, gettext("zone '%s' must be running\n"),
 		    zonename);
 		return (E_ERROR);
@@ -169,7 +179,7 @@
 		return (E_ERROR);
 	}
 
-	if (zone_setattr(zone_id, ZONE_ATTR_PHYS_MCAP, &num, 0) == -1) {
+	if (set_zone_cap(zonename, num) == -1) {
 		(void) fprintf(stderr, gettext("could not set memory "
 		    "cap for zone '%s'\n"), zonename);
 		return (E_ERROR);
--- old/usr/src/cmd/rcap/rcapd/Makefile.rcapd	Wed Oct  5 00:31:39 2016
+++ new/usr/src/cmd/rcap/rcapd/Makefile.rcapd	Wed Oct  5 00:31:39 2016
@@ -55,7 +55,7 @@
 $(NOT_RELEASE_BUILD)CPPFLAGS 	+= -DDEBUG
 CPPFLAGS			+= -DDEBUG_MSG
 CPPFLAGS 			+= -I$(COMMON_DIR)
-LDLIBS 				+= -lkstat -lproc -lproject -lzonecfg -lumem -lscf
+LDLIBS 				+= -lkstat -lproc -lproject -lumem -lscf
 LDLIBS				+= $(EXTRA_LDLIBS)
 
 LINTFLAGS			+= -u
--- old/usr/src/cmd/rcap/rcapd/rcapd_collection_zone.c	Wed Oct  5 00:31:39 2016
+++ new/usr/src/cmd/rcap/rcapd/rcapd_collection_zone.c	Wed Oct  5 00:31:39 2016
@@ -21,10 +21,9 @@
 /*
  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2011 Joyent, Inc.  All rights reserved.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <procfs.h>
 #include <project.h>
 #include <stdlib.h>
@@ -31,6 +30,8 @@
 #include <strings.h>
 #include <zone.h>
 #include <libzonecfg.h>
+#include <dirent.h>
+#include <libproc.h>
 #include "rcapd.h"
 #include "utils.h"
 
@@ -39,61 +40,117 @@
 				/* round up to next y = 2^n */
 #define	ROUNDUP(x, y)		(((x) + ((y) - 1)) & ~((y) - 1))
 
-static void
-update_zone(zone_entry_t *zent, void *walk_data)
+static struct ps_prochandle *
+grab_zone_proc(zoneid_t zid)
 {
-	void(*update_notification_cb)(char *, char *, int, uint64_t, int) =
-	    (void(*)(char *, char *, int, uint64_t, int))walk_data;
-	int changes;
-	int64_t max_rss;
+	DIR *dirp;
+	struct dirent *dentp;
+	int pid, pid_self, tmp;
+	psinfo_t psinfo;
+	struct ps_prochandle *pr = NULL;
+
+	pid_self = getpid();
+
+	if ((dirp = opendir("/proc")) == NULL)
+		return (NULL);
+
+	while (dentp = readdir(dirp)) {
+		pid = atoi(dentp->d_name);
+
+		/* Skip self */
+		if (pid == pid_self)
+			continue;
+
+		if (proc_get_psinfo(pid, &psinfo) != 0)
+			continue;
+
+		if (psinfo.pr_zoneid != zid)
+			continue;
+
+		/* attempt to grab process */
+		if ((pr = Pgrab(pid, 0, &tmp)) != NULL) {
+			if (Psetflags(pr, PR_RLC) != 0) {
+				Prelease(pr, 0);
+			}
+			if (Pcreate_agent(pr) == 0) {
+				if (pr_getzoneid(pr) != zid) {
+					Prelease(pr, 0);
+					continue;
+				}
+
+				(void) closedir(dirp);
+				return (pr);
+			} else {
+				Prelease(pr, 0);
+			}
+		}
+	}
+
+	(void) closedir(dirp);
+	return (NULL);
+}
+
+static uint64_t
+get_zone_cap(zoneid_t zid)
+{
+	rctlblk_t *rblk;
 	uint64_t mcap;
-	lcollection_t *lcol;
-	rcid_t colid;
+	struct ps_prochandle *pr;
 
-	if (zone_getattr(zent->zid, ZONE_ATTR_PHYS_MCAP, &mcap,
-	    sizeof (mcap)) != -1 && mcap != 0)
-		max_rss = ROUNDUP(mcap, 1024) / 1024;
-	else
-		max_rss = 0;
+	if ((rblk = (rctlblk_t *)malloc(rctlblk_size())) == NULL)
+		return (UINT64_MAX);
 
-	if (zent->zid == GLOBAL_ZONEID) {
-		if (max_rss > 0)
-			gz_capped = B_TRUE;
-		else
-			gz_capped = B_FALSE;
+	if ((pr = grab_zone_proc(zid)) == NULL) {
+		free(rblk);
+		return (UINT64_MAX);
 	}
 
+	if (pr_getrctl(pr, "zone.max-physical-memory", NULL, rblk,
+	    RCTL_FIRST)) {
+		Pdestroy_agent(pr);
+		Prelease(pr, 0);
+		free(rblk);
+		return (UINT64_MAX);
+	}
 
-	colid.rcid_type = RCIDT_ZONE;
-	colid.rcid_val = zent->zid;
+	Pdestroy_agent(pr);
+	Prelease(pr, 0);
 
-	lcol = lcollection_insert_update(&colid, max_rss, zent->zname,
-	    &changes);
-	if (update_notification_cb != NULL)
-		update_notification_cb("zone", zent->zname, changes, max_rss,
-		    (lcol != NULL) ? lcol->lcol_mark : 0);
+	mcap = rctlblk_get_value(rblk);
+	free(rblk);
+	return (mcap);
 }
 
-
+/*
+ * For zones, rcapd only caps the global zone, since each non-global zone
+ * caps itself.
+ */
 /* ARGSUSED */
 void
 lcollection_update_zone(lcollection_update_type_t ut,
     void(*update_notification_cb)(char *, char *, int, uint64_t, int))
 {
-	int i;
-	uint_t nzents;
-	zone_entry_t *zents;
+	int changes;
+	int64_t max_rss;
+	uint64_t mcap;
+	lcollection_t *lcol;
+	rcid_t colid;
 
-	/*
-	 * Enumerate running zones.
-	 */
-	if (get_running_zones(&nzents, &zents) != 0)
-		return;
-
-	for (i = 0; i < nzents; i++) {
-		update_zone(&zents[i], (void *)update_notification_cb);
-
+	mcap = get_zone_cap(GLOBAL_ZONEID);
+	if (mcap != 0 && mcap != UINT64_MAX) {
+		max_rss = ROUNDUP(mcap, 1024) / 1024;
+		gz_capped = B_TRUE;
+	} else {
+		max_rss = UINT64_MAX / 1024;
+		gz_capped = B_FALSE;
 	}
 
-	free(zents);
+	colid.rcid_type = RCIDT_ZONE;
+	colid.rcid_val = GLOBAL_ZONEID;
+
+	lcol = lcollection_insert_update(&colid, max_rss, GLOBAL_ZONENAME,
+	    &changes);
+	if (update_notification_cb != NULL)
+		update_notification_cb("zone", GLOBAL_ZONENAME, changes,
+		    max_rss, (lcol != NULL) ? lcol->lcol_mark : 0);
 }
--- old/usr/src/cmd/rcap/rcapd/rcapd_scanner.c	Wed Oct  5 00:31:39 2016
+++ new/usr/src/cmd/rcap/rcapd/rcapd_scanner.c	Wed Oct  5 00:31:39 2016
@@ -21,6 +21,7 @@
 /*
  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2012 Joyent, Inc.  All rights reserved.
  */
 
 #pragma ident	"%Z%%M%	%I%	%E% SMI"
@@ -551,7 +552,7 @@
 
 	errno = 0;
 	res = pr_memcntl(Pr, start, (end - start), MC_SYNC,
-	    (caddr_t)(MS_ASYNC | MS_INVALIDATE), 0, 0);
+	    (caddr_t)(MS_ASYNC | MS_INVALCURPROC), 0, 0);
 	debug_high("pr_memcntl [%p-%p): %d", (void *)start, (void *)end, res);
 
 	/*
--- old/usr/src/cmd/rcap/rcapstat/Makefile	Wed Oct  5 00:31:40 2016
+++ new/usr/src/cmd/rcap/rcapstat/Makefile	Wed Oct  5 00:31:40 2016
@@ -43,7 +43,7 @@
 
 $(NOT_RELEASE_BUILD)CPPFLAGS 	+= -DDEBUG
 CPPFLAGS 			+= -I$(COMMON_DIR) -I$(STATCOMMONDIR)
-LDLIBS 				+= -lumem -lzonecfg -lscf
+LDLIBS 				+= -lumem -lscf
 LDFLAGS				+= $(MAPFILE.NGB:%=-M%)
 
 LINTFLAGS			+= $(LDLIBS) -mnu
--- old/usr/src/cmd/sgs/libconv/common/corenote.c	Wed Oct  5 00:31:40 2016
+++ new/usr/src/cmd/sgs/libconv/common/corenote.c	Wed Oct  5 00:31:40 2016
@@ -25,7 +25,7 @@
  */
 /*
  * Copyright 2012 DEY Storage Systems, Inc.  All rights reserved.
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 
 /*
@@ -76,7 +76,7 @@
 conv_cnote_auxv_type(Word type, Conv_fmt_flags_t fmt_flags,
     Conv_inv_buf_t *inv_buf)
 {
-	static const Msg	types_0_22[] = {
+	static const Msg	types_0_25[] = {
 		MSG_AUXV_AT_NULL,		MSG_AUXV_AT_IGNORE,
 		MSG_AUXV_AT_EXECFD,		MSG_AUXV_AT_PHDR,
 		MSG_AUXV_AT_PHENT,		MSG_AUXV_AT_PHNUM,
@@ -88,10 +88,11 @@
 		MSG_AUXV_AT_HWCAP,		MSG_AUXV_AT_CLKTCK,
 		MSG_AUXV_AT_FPUCW,		MSG_AUXV_AT_DCACHEBSIZE,
 		MSG_AUXV_AT_ICACHEBSIZE,	MSG_AUXV_AT_UCACHEBSIZE,
-		MSG_AUXV_AT_IGNOREPPC
+		MSG_AUXV_AT_IGNOREPPC,		MSG_AUXV_AT_SECURE,
+		MSG_AUXV_AT_BASE_PLATFORM,	MSG_AUXV_AT_RANDOM
 	};
-	static const conv_ds_msg_t ds_types_0_22 = {
-	    CONV_DS_MSG_INIT(0, types_0_22) };
+	static const conv_ds_msg_t ds_types_0_25 = {
+	    CONV_DS_MSG_INIT(0, types_0_25) };
 
 	static const Msg	types_2000_2011[] = {
 		MSG_AUXV_AT_SUN_UID,		MSG_AUXV_AT_SUN_RUID,
@@ -104,19 +105,20 @@
 	static const conv_ds_msg_t ds_types_2000_2011 = {
 	    CONV_DS_MSG_INIT(2000, types_2000_2011) };
 
-	static const Msg	types_2014_2023[] = {
+	static const Msg	types_2014_2025[] = {
 		MSG_AUXV_AT_SUN_EXECNAME,	MSG_AUXV_AT_SUN_MMU,
 		MSG_AUXV_AT_SUN_LDDATA,		MSG_AUXV_AT_SUN_AUXFLAGS,
 		MSG_AUXV_AT_SUN_EMULATOR,	MSG_AUXV_AT_SUN_BRANDNAME,
 		MSG_AUXV_AT_SUN_BRAND_AUX1,	MSG_AUXV_AT_SUN_BRAND_AUX2,
-		MSG_AUXV_AT_SUN_BRAND_AUX3,	MSG_AUXV_AT_SUN_HWCAP2
+		MSG_AUXV_AT_SUN_BRAND_AUX3,	MSG_AUXV_AT_SUN_HWCAP2,
+		MSG_AUXV_AT_SUN_BRAND_NROOT,	MSG_AUXV_AT_SUN_COMMPAGE
 	};
-	static const conv_ds_msg_t ds_types_2014_2023 = {
-	    CONV_DS_MSG_INIT(2014, types_2014_2023) };
+	static const conv_ds_msg_t ds_types_2014_2025 = {
+	    CONV_DS_MSG_INIT(2014, types_2014_2025) };
 
 	static const conv_ds_t	*ds[] = {
-		CONV_DS_ADDR(ds_types_0_22), CONV_DS_ADDR(ds_types_2000_2011),
-		CONV_DS_ADDR(ds_types_2014_2023), NULL };
+		CONV_DS_ADDR(ds_types_0_25), CONV_DS_ADDR(ds_types_2000_2011),
+		CONV_DS_ADDR(ds_types_2014_2025), NULL };
 
 	return (conv_map_ds(ELFOSABI_NONE, EM_NONE, type, ds, fmt_flags,
 	    inv_buf));
--- old/usr/src/cmd/sgs/libconv/common/corenote.msg	Wed Oct  5 00:31:40 2016
+++ new/usr/src/cmd/sgs/libconv/common/corenote.msg	Wed Oct  5 00:31:40 2016
@@ -24,7 +24,7 @@
 # Use is subject to license terms.
 #
 # Copyright 2012 DEY Storage Systems, Inc.  All rights reserved.
-# Copyright (c) 2013, Joyent, Inc. All rights reserved.
+# Copyright 2016 Joyent, Inc.
 #
 
 @ MSG_NT_PRSTATUS		"[ NT_PRSTATUS ]"
@@ -78,6 +78,9 @@
 @ MSG_AUXV_AT_ICACHEBSIZE		"ICACHEBSIZE"
 @ MSG_AUXV_AT_UCACHEBSIZE		"UCACHEBSIZE"
 @ MSG_AUXV_AT_IGNOREPPC			"IGNOREPPC"
+@ MSG_AUXV_AT_SECURE			"SECURE"
+@ MSG_AUXV_AT_BASE_PLATFORM		"BASE_PLATFORM"
+@ MSG_AUXV_AT_RANDOM			"RANDOM"
 @ MSG_AUXV_AT_SUN_UID			"SUN_UID"
 @ MSG_AUXV_AT_SUN_RUID			"SUN_RUID"
 @ MSG_AUXV_AT_SUN_GID			"SUN_GID"
@@ -100,6 +103,8 @@
 @ MSG_AUXV_AT_SUN_BRAND_AUX2		"SUN_BRAND_AUX2"
 @ MSG_AUXV_AT_SUN_BRAND_AUX3		"SUN_BRAND_AUX3"
 @ MSG_AUXV_AT_SUN_HWCAP2		"SUN_HWCAP2"
+@ MSG_AUXV_AT_SUN_BRAND_NROOT		"SUN_BRAND_NROOT"
+@ MSG_AUXV_AT_SUN_COMMPAGE		"SUN_COMMPAGE"
 
 
 @ MSG_CC_CONTENT_STACK		"STACK"
--- old/usr/src/cmd/sgs/librtld_db/common/librtld_db.msg	Wed Oct  5 00:31:40 2016
+++ new/usr/src/cmd/sgs/librtld_db/common/librtld_db.msg	Wed Oct  5 00:31:40 2016
@@ -24,6 +24,10 @@
 # Use is subject to license terms.
 #
 
+#
+# Copyright (c) 2014 Joyent, Inc.  All rights reserved.
+#
+
 # Message file for cmd/sgs/librtld_db.
 
 @ MSG_ID_LIBRTLD_DB
@@ -104,7 +108,7 @@
 @ MSG_DB_RDOBJPADE	"rtld_db: rd_objpad_enable(padsize=0x%llx)"
 @ MSG_DB_64BIT_PREFIX	"64/"
 @ MSG_DB_BRAND_HELPERPATH_PREFIX "%s/%s/%s/%s%s_librtld_db.so.1"
-@ MSG_DB_BRAND_HELPERPATH "%s/%s/%s%s_librtld_db.so.1"
+@ MSG_DB_BRAND_HELPERPATH "%s/%s/%s/%s%s_librtld_db.so.1"
 @ MSG_DB_HELPERNOOPS	"rtld_db: helper lib loaded but ops not preset"
 @ MSG_DB_HELPERLOADED	"rtld_db: helper library loaded for brand \"%s\""
 @ MSG_DB_HELPERLOADFAILED "rtld_db: couldn't load brand helper library %s"
--- old/usr/src/cmd/sgs/librtld_db/common/rd_elf.c	Wed Oct  5 00:31:40 2016
+++ new/usr/src/cmd/sgs/librtld_db/common/rd_elf.c	Wed Oct  5 00:31:40 2016
@@ -23,6 +23,10 @@
  * Use is subject to license terms.
  */
 
+/*
+ * Copyright (c) 2014 Joyent, Inc.  All rights reserved.
+ */
+
 #include	<stdlib.h>
 #include	<stdio.h>
 #include	<proc_service.h>
@@ -38,6 +42,14 @@
 #include	<sys/param.h>
 
 /*
+ * We want to include zone.h to pull in the prototype for zone_get_nroot(),
+ * but we need to avoid pulling in <sys/stream.h>, which has a definition
+ * of M_DATA that conflicts with the ELF-related definition in machdep_*.h.
+ */
+#define		_SYS_STREAM_H
+#include	<zone.h>
+
+/*
  * 64-bit builds are going to compile this module twice, the
  * second time with _ELF64 defined.  These defines should make
  * all the necessary adjustments to the code.
@@ -283,7 +295,9 @@
 	 * If we are debugging a branded executable, load the appropriate
 	 * helper library, and call its initialization routine.  Being unable
 	 * to load the helper library is not a critical error.  (Hopefully
-	 * we'll still be able to access some objects in the target.)
+	 * we'll still be able to access some objects in the target.)  Note
+	 * that we pull in the native root here to allow for helper libraries
+	 * to be properly found from within the branded zone.
 	 */
 	ps_pbrandname = (ps_pbrandname_fp_t)dlsym(RTLD_PROBE, "ps_pbrandname");
 	while ((ps_pbrandname != NULL) &&
@@ -294,17 +308,23 @@
 		isa = MSG_ORIG(MSG_DB_64BIT_PREFIX);
 #endif /* _LP64 */
 
-		if (rtld_db_helper_path[0] != '\0')
+		if (rtld_db_helper_path[0] != '\0') {
 			(void) snprintf(brandlib, MAXPATHLEN,
 			    MSG_ORIG(MSG_DB_BRAND_HELPERPATH_PREFIX),
 			    rtld_db_helper_path,
 			    MSG_ORIG(MSG_DB_HELPER_PREFIX), brandname, isa,
 			    brandname);
-		else
+		} else {
+			const char *nroot = zone_get_nroot();
+
+			if (nroot == NULL)
+				nroot = "";
+
 			(void) snprintf(brandlib, MAXPATHLEN,
-			    MSG_ORIG(MSG_DB_BRAND_HELPERPATH),
+			    MSG_ORIG(MSG_DB_BRAND_HELPERPATH), nroot,
 			    MSG_ORIG(MSG_DB_HELPER_PREFIX), brandname, isa,
 			    brandname);
+		}
 
 		rap->rd_helper.rh_dlhandle = dlopen(brandlib,
 		    RTLD_LAZY | RTLD_LOCAL);
--- old/usr/src/cmd/sgs/rtld/common/external.c	Wed Oct  5 00:31:40 2016
+++ new/usr/src/cmd/sgs/rtld/common/external.c	Wed Oct  5 00:31:40 2016
@@ -22,7 +22,7 @@
 /*
  * Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2014 Garrett D'Amore <garrett@damore.org>
- * Copyright 2015 Joyent, Inc.
+ * Copyright 2016 Joyent, Inc.
  */
 
 /*
@@ -721,7 +721,22 @@
 	return ((isalpha(c) || isdigit(c)) ? 1 : 0);
 }
 
+#if defined(__i386) || defined(__amd64)
 /*
+ * Instead of utilizing the comm page for clock_gettime, rtld uses the raw
+ * syscall instead.  Doing so decreases the surface of symbols needed from libc
+ * for a modest performance cost.
+ */
+extern int __clock_gettime_sys(clockid_t, struct timespec *);
+
+int
+__clock_gettime(clockid_t clock_id, struct timespec *tp)
+{
+	return (__clock_gettime_sys(clock_id, tp));
+}
+#endif /* defined(__i386) || defined(__amd64) */
+
+/*
  * In a similar vein to the is* functions above, we also have to define our own
  * version of strerror, as it is implemented in terms of the locale aware
  * strerror_l, and we'd rather not have the full set of libc symbols used here.
--- old/usr/src/cmd/truss/print.c	Wed Oct  5 00:31:41 2016
+++ new/usr/src/cmd/truss/print.c	Wed Oct  5 00:31:41 2016
@@ -873,7 +873,9 @@
 			return;
 
 		case MC_SYNC:
-			if ((val & ~(MS_SYNC|MS_ASYNC|MS_INVALIDATE)) == 0) {
+			if ((val &
+			    ~(MS_SYNC|MS_ASYNC|MS_INVALIDATE|MS_INVALCURPROC))
+			    == 0) {
 				*(s = pri->code_buf) = '\0';
 				if (val & MS_SYNC)
 					(void) strlcat(s, "|MS_SYNC", CBSIZE);
@@ -882,6 +884,9 @@
 				if (val & MS_INVALIDATE)
 					(void) strlcat(s, "|MS_INVALIDATE",
 					    CBSIZE);
+				if (val & MS_INVALCURPROC)
+					(void) strlcat(s, "|MS_INVALCURPROC",
+					    CBSIZE);
 			}
 			break;
 
@@ -1998,6 +2003,7 @@
 	case UDP_EXCLBIND:		return ("UDP_EXCLBIND");
 	case UDP_RCVHDR:		return ("UDP_RCVHDR");
 	case UDP_NAT_T_ENDPOINT:	return ("UDP_NAT_T_ENDPOINT");
+	case UDP_SND_TO_CONNECTED:	return ("UDP_SND_TO_CONNECTED");
 
 	default:			(void) snprintf(pri->code_buf,
 					    sizeof (pri->code_buf), "0x%lx",
@@ -2446,7 +2452,10 @@
 		case ZONE_ATTR_BOOTARGS:	s = "ZONE_ATTR_BOOTARGS"; break;
 		case ZONE_ATTR_BRAND:	s = "ZONE_ATTR_BRAND"; break;
 		case ZONE_ATTR_FLAGS:	s = "ZONE_ATTR_FLAGS"; break;
-		case ZONE_ATTR_PHYS_MCAP: s = "ZONE_ATTR_PHYS_MCAP"; break;
+		case ZONE_ATTR_DID:	s = "ZONE_ATTR_DID"; break;
+		case ZONE_ATTR_PMCAP_NOVER: s = "ZONE_ATTR_PMCAP_NOVER"; break;
+		case ZONE_ATTR_PMCAP_PAGEOUT: s = "ZONE_ATTR_PMCAP_PAGEOUT";
+					break;
 		}
 	}
 
--- old/usr/src/cmd/truss/systable.c	Wed Oct  5 00:31:41 2016
+++ new/usr/src/cmd/truss/systable.c	Wed Oct  5 00:31:41 2016
@@ -29,6 +29,9 @@
 
 /* Copyright (c) 2013, OmniTI Computer Consulting, Inc. All rights reserved. */
 
+/*
+ * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ */
 #include <stdio.h>
 #include <stdlib.h>
 #include <fcntl.h>
@@ -1716,9 +1719,10 @@
 	"POLICY",	/* 29 */
 	"RDS",		/* 30 */
 	"TRILL",	/* 31 */
-	"PACKET"	/* 32 */
+	"PACKET",	/* 32 */
+	"LX_NETLINK"	/* 33 */
 };
-#if MAX_AFCODES != 33
+#if MAX_AFCODES != 34
 #error Need to update address-family table
 #endif
 
--- old/usr/src/cmd/zlogin/zlogin.c	Wed Oct  5 00:31:41 2016
+++ new/usr/src/cmd/zlogin/zlogin.c	Wed Oct  5 00:31:41 2016
@@ -23,10 +23,11 @@
  * Copyright 2013 DEY Storage Systems, Inc.
  * Copyright (c) 2014 Gary Mills
  * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 
 /*
- * zlogin provides three types of login which allow users in the global
+ * zlogin provides five types of login which allow users in the global
  * zone to access non-global zones.
  *
  * - "interactive login" is similar to rlogin(1); for example, the user could
@@ -42,6 +43,13 @@
  *   In this mode, zlogin sets up pipes as the communication channel, and
  *   'su' is used to do the login setup work.
  *
+ * - "interactive command" is a combination of the above two modes where
+ *   a command is provide like the non-interactive case, but the -i option is
+ *   also provided to make things interactive. For example, the user could
+ *   issue 'zlogin -i my-zone /bin/sh'. In this mode neither 'login -c' nor
+ *   'su root -c' is prepended to the command invocation. Because of this
+ *   there will be no wtmpx login record within the zone.
+ *
  * - "console login" is the equivalent to accessing the tip line for a
  *   zone.  For example, the user can issue 'zlogin -C my-zone'.
  *   In this mode, zlogin contacts the zoneadmd process via unix domain
@@ -48,6 +56,9 @@
  *   socket.  If zoneadmd is not running, it starts it.  This allows the
  *   console to be available anytime the zone is installed, regardless of
  *   whether it is running.
+ *
+ * - "standalone-processs interactive" is specified with -I and connects to
+ *   the zone's stdin, stdout and stderr zfd(7D) devices.
  */
 
 #include <sys/socket.h>
@@ -92,7 +103,8 @@
 #include <auth_attr.h>
 #include <secdb.h>
 
-static int masterfd;
+static int masterfd = -1;
+static int ctlfd = -1;
 static struct termios save_termios;
 static struct termios effective_termios;
 static int save_fd;
@@ -101,12 +113,13 @@
 static volatile pid_t child_pid = -1;
 static int interactive = 0;
 static priv_set_t *dropprivs;
+static unsigned int connect_flags = 0;
 
 static int nocmdchar = 0;
 static int failsafe = 0;
-static int disconnect = 0;
 static char cmdchar = '~';
 static int quiet = 0;
+static char zonebrand[MAXNAMELEN];
 
 static int pollerr = 0;
 
@@ -123,11 +136,15 @@
 #define	TEXT_DOMAIN	"SYS_TEST"	/* Use this only if it wasn't */
 #endif
 
-#define	SUPATH	"/usr/bin/su"
+#define	SUPATH1	"/usr/bin/su"
+#define	SUPATH2	"/bin/su"
 #define	FAILSAFESHELL	"/sbin/sh"
 #define	DEFAULTSHELL	"/sbin/sh"
 #define	DEF_PATH	"/usr/sbin:/usr/bin"
+#define	LX_DEF_PATH	"/bin:/usr/sbin:/usr/bin"
 
+#define	MAX_RETRY	30
+
 #define	CLUSTER_BRAND_NAME	"cluster"
 
 /*
@@ -153,7 +170,7 @@
 static void
 usage(void)
 {
-	(void) fprintf(stderr, gettext("usage: %s [ -dnQCES ] [ -e cmdchar ] "
+	(void) fprintf(stderr, gettext("usage: %s [-dinCEINQS] [-e cmdchar] "
 	    "[-l user] zonename [command [args ...] ]\n"), pname);
 	exit(2);
 }
@@ -248,22 +265,15 @@
 	}
 }
 
-/*
- * Create the unix domain socket and call the zoneadmd server; handshake
- * with it to determine whether it will allow us to connect.
- */
 static int
-get_console_master(const char *zname)
+connect_zone_sock(const char *zname, const char *suffix, boolean_t verbose)
 {
 	int sockfd = -1;
 	struct sockaddr_un servaddr;
-	char clientid[MAXPATHLEN];
-	char handshake[MAXPATHLEN], c;
-	int msglen;
-	int i = 0, err = 0;
 
 	if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
-		zperror(gettext("could not create socket"));
+		if (verbose)
+			zperror(gettext("could not create socket"));
 		return (-1);
 	}
 
@@ -270,35 +280,45 @@
 	bzero(&servaddr, sizeof (servaddr));
 	servaddr.sun_family = AF_UNIX;
 	(void) snprintf(servaddr.sun_path, sizeof (servaddr.sun_path),
-	    "%s/%s.console_sock", ZONES_TMPDIR, zname);
-
+	    "%s/%s.%s", ZONES_TMPDIR, zname, suffix);
 	if (connect(sockfd, (struct sockaddr *)&servaddr,
 	    sizeof (servaddr)) == -1) {
-		zperror(gettext("Could not connect to zone console"));
-		goto bad;
+		if (verbose)
+			zperror(gettext("Could not connect to zone"));
+		(void) close(sockfd);
+		return (-1);
 	}
-	masterfd = sockfd;
+	return (sockfd);
+}
 
-	msglen = snprintf(clientid, sizeof (clientid), "IDENT %lu %s %d\n",
-	    getpid(), setlocale(LC_MESSAGES, NULL), disconnect);
 
+static int
+handshake_zone_sock(int sockfd, unsigned int flags)
+{
+	char clientid[MAXPATHLEN];
+	char handshake[MAXPATHLEN], c;
+	int msglen;
+	int i = 0, err = 0;
+
+	msglen = snprintf(clientid, sizeof (clientid), "IDENT %s %u\n",
+	    setlocale(LC_MESSAGES, NULL), flags);
+
 	if (msglen >= sizeof (clientid) || msglen < 0) {
 		zerror("protocol error");
-		goto bad;
+		return (-1);
 	}
 
-	if (write(masterfd, clientid, msglen) != msglen) {
+	if (write(sockfd, clientid, msglen) != msglen) {
 		zerror("protocol error");
-		goto bad;
+		return (-1);
 	}
 
-	bzero(handshake, sizeof (handshake));
-
 	/*
 	 * Take care not to accumulate more than our fill, and leave room for
 	 * the NUL at the end.
 	 */
-	while ((err = read(masterfd, &c, 1)) == 1) {
+	bzero(handshake, sizeof (handshake));
+	while ((err = read(sockfd, &c, 1)) == 1) {
 		if (i >= (sizeof (handshake) - 1))
 			break;
 		if (c == '\n')
@@ -308,26 +328,48 @@
 	}
 
 	/*
-	 * If something went wrong during the handshake we bail; perhaps
-	 * the server died off.
+	 * If something went wrong during the handshake we bail.
+	 * Perhaps the server died off.
 	 */
 	if (err == -1) {
-		zperror(gettext("Could not connect to zone console"));
-		goto bad;
+		zperror(gettext("Could not connect to zone"));
+		return (-1);
 	}
 
-	if (strncmp(handshake, "OK", sizeof (handshake)) == 0)
-		return (0);
+	if (strncmp(handshake, "OK", sizeof (handshake)) != 0) {
+		zerror(gettext("Zone is already in use by process ID %s."),
+		    handshake);
+		return (-1);
+	}
 
-	zerror(gettext("Console is already in use by process ID %s."),
-	    handshake);
-bad:
-	(void) close(sockfd);
-	masterfd = -1;
-	return (-1);
+	return (0);
 }
 
-
+static int
+send_ctl_sock(const char *buf, size_t len)
+{
+	char rbuf[BUFSIZ];
+	int i;
+	if (ctlfd == -1) {
+		return (-1);
+	}
+	if (write(ctlfd, buf, len) != len) {
+		return (-1);
+	}
+	/* read the response */
+	for (i = 0; i < (BUFSIZ - 1); i++) {
+		char c;
+		if (read(ctlfd, &c, 1) != 1 || c == '\n' || c == '\0') {
+			break;
+		}
+		rbuf[i] = c;
+	}
+	rbuf[i+1] = '\0';
+	if (strncmp("OK", rbuf, BUFSIZ) != 0) {
+		return (-1);
+	}
+	return (0);
+}
 /*
  * Routines to handle pty creation upon zone entry and to shuttle I/O back
  * and forth between the two terminals.  We also compute and store the
@@ -516,10 +558,34 @@
 {
 	struct winsize ws;
 
-	if (ioctl(0, TIOCGWINSZ, &ws) == 0)
-		(void) ioctl(masterfd, TIOCSWINSZ, &ws);
+	if (ioctl(0, TIOCGWINSZ, &ws) == 0) {
+		if (ctlfd != -1) {
+			char buf[BUFSIZ];
+			(void) snprintf(buf, sizeof (buf),
+			    "TIOCSWINSZ %hu %hu\n", ws.ws_row, ws.ws_col);
+			(void) send_ctl_sock(buf, strlen(buf));
+		} else {
+			(void) ioctl(masterfd, TIOCSWINSZ, &ws);
+		}
+	}
 }
 
+/*
+ * Toggle zfd EOF mode and notify zoneadmd
+ */
+/*ARGSUSED*/
+static void
+sigusr1(int s)
+{
+	connect_flags ^= ZLOGIN_ZFD_EOF;
+	if (ctlfd != -1) {
+		char buf[BUFSIZ];
+		(void) snprintf(buf, sizeof (buf), "SETFLAGS %u\n",
+		    connect_flags);
+		(void) send_ctl_sock(buf, strlen(buf));
+	}
+}
+
 static volatile int close_on_sig = -1;
 
 static void
@@ -862,28 +928,28 @@
 			break;
 		}
 
-		/* event from master side stdout */
-		if (pollfds[0].revents) {
-			if (pollfds[0].revents &
+		/* event from master side stderr */
+		if (pollfds[1].revents) {
+			if (pollfds[1].revents &
 			    (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) {
-				if (process_output(stdout_fd, STDOUT_FILENO)
+				if (process_output(stderr_fd, STDERR_FILENO)
 				    != 0)
 					break;
 			} else {
-				pollerr = pollfds[0].revents;
+				pollerr = pollfds[1].revents;
 				break;
 			}
 		}
 
-		/* event from master side stderr */
-		if (pollfds[1].revents) {
-			if (pollfds[1].revents &
+		/* event from master side stdout */
+		if (pollfds[0].revents) {
+			if (pollfds[0].revents &
 			    (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) {
-				if (process_output(stderr_fd, STDERR_FILENO)
+				if (process_output(stdout_fd, STDOUT_FILENO)
 				    != 0)
 					break;
 			} else {
-				pollerr = pollfds[1].revents;
+				pollerr = pollfds[0].revents;
 				break;
 			}
 		}
@@ -1053,7 +1119,7 @@
 	 * but we're going to be very simplistic about it and break stuff
 	 * up based on spaces.  We're not even going to support any kind
 	 * of quoting or escape characters.  It's truly amazing that
-	 * there is no library function in OpenSolaris to do this for us.
+	 * there is no library function in Illumos to do this for us.
 	 */
 
 	/*
@@ -1092,69 +1158,155 @@
 }
 
 /*
- * Prepare argv array for exec'd process; if we're passing commands to the
- * new process, then use su(1M) to do the invocation.  Otherwise, use
+ * Prepare argv array for exec'd process.  If commands are passed to the new
+ * process and su(1M) is avalable, use it for the invocation.  Otherwise, use
  * 'login -z <from_zonename> -f' (-z is an undocumented option which tells
  * login that we're coming from another zone, and to disregard its CONSOLE
  * checks).
  */
 static char **
-prep_args(brand_handle_t bh, const char *login, char **argv)
+prep_args(brand_handle_t bh, char *zonename, const char *login, char **argv)
 {
-	int argc = 0, a = 0, i, n = -1;
-	char **new_argv;
+	int argc = 0, i;
+	size_t subshell_len = 1;
+	char *subshell = NULL, *supath = NULL;
+	char **new_argv = NULL;
 
-	if (argv != NULL) {
-		size_t subshell_len = 1;
-		char *subshell;
+	if (argv == NULL) {
+		if (failsafe) {
+			if ((new_argv = malloc(sizeof (char *) * 2)) == NULL)
+				return (NULL);
+			new_argv[0] = FAILSAFESHELL;
+			new_argv[1] = NULL;
+		} else {
+			new_argv = zone_login_cmd(bh, login);
+		}
+		return (new_argv);
+	}
 
-		while (argv[argc] != NULL)
-			argc++;
+	/*
+	 * Attempt to locate a 'su' binary if not using the failsafe shell.
+	 */
+	if (!failsafe) {
+		struct stat sb;
+		char zonepath[MAXPATHLEN];
+		char supath_check[MAXPATHLEN];
 
-		for (i = 0; i < argc; i++) {
-			subshell_len += strlen(argv[i]) + 1;
+		if (zone_get_zonepath(zonename, zonepath,
+		    sizeof (zonepath)) != Z_OK) {
+			zerror(gettext("unable to determine zone "
+			    "path"));
+			return (NULL);
 		}
-		if ((subshell = calloc(1, subshell_len)) == NULL)
+
+		(void) snprintf(supath_check, sizeof (supath), "%s/root/%s",
+		    zonepath, SUPATH1);
+		if (stat(supath_check, &sb) == 0) {
+			supath = SUPATH1;
+		} else {
+			(void) snprintf(supath_check, sizeof (supath_check),
+			    "%s/root/%s", zonepath, SUPATH2);
+			if (stat(supath_check, &sb) == 0) {
+				supath = SUPATH2;
+			}
+		}
+	}
+
+	/*
+	 * With no failsafe shell or supath to wrap the incoming command, the
+	 * arguments are passed straight through.
+	 */
+	if (!failsafe && supath == NULL) {
+		/*
+		 * Such an outcome is not acceptable, however, if the caller
+		 * expressed a desire to switch users.
+		 */
+		if (strcmp(login, "root") != 0) {
+			zerror(gettext("unable to find 'su' command"));
 			return (NULL);
+		}
+		return (argv);
+	}
 
-		for (i = 0; i < argc; i++) {
-			(void) strcat(subshell, argv[i]);
+	/*
+	 * Inventory arguments and allocate a buffer to escape them for the
+	 * subshell.
+	 */
+	while (argv[argc] != NULL) {
+		/*
+		 * Allocate enough space for the delimiter and 2
+		 * quotes which might be needed.
+		 */
+		subshell_len += strlen(argv[argc]) + 3;
+		argc++;
+	}
+	if ((subshell = calloc(1, subshell_len)) == NULL) {
+		return (NULL);
+	}
+
+	/*
+	 * The handling of quotes in the following block may seem unusual, but
+	 * it is done this way for backward compatibility.
+	 * When running a command, zlogin is documented as:
+	 *    zlogin zonename command args
+	 * However, some code has come to depend on the following usage:
+	 *    zlogin zonename 'command args'
+	 * This relied on the fact that the single argument would be re-parsed
+	 * within the zone and excuted as a command with an argument. To remain
+	 * compatible with this (incorrect) usage, if there is only a single
+	 * argument, it is not quoted, even if it has embedded spaces.
+	 *
+	 * Here are two examples which both need to work:
+	 * 1) zlogin foo 'echo hello'
+	 *    This has a single argv member with a space in it but will not be
+	 *    quoted on the command passed into the zone.
+	 * 2) zlogin foo bash -c 'echo hello'
+	 *    This has 3 argv members. The 3rd arg has a space and must be
+	 *    quoted on the command passed into the zone.
+	 */
+	for (i = 0; i < argc; i++) {
+		if (i > 0)
 			(void) strcat(subshell, " ");
+
+		if (argc > 1 && (strchr(argv[i], ' ') != NULL ||
+		    strchr(argv[i], '\t') != NULL)) {
+			(void) strcat(subshell, "'");
+			(void) strcat(subshell, argv[i]);
+			(void) strcat(subshell, "'");
+		} else {
+			(void) strcat(subshell, argv[i]);
 		}
+	}
 
-		if (failsafe) {
-			n = 4;
-			if ((new_argv = malloc(sizeof (char *) * n)) == NULL)
-				return (NULL);
+	if (failsafe) {
+		int a = 0, n = 4;
 
-			new_argv[a++] = FAILSAFESHELL;
-		} else {
-			n = 5;
-			if ((new_argv = malloc(sizeof (char *) * n)) == NULL)
-				return (NULL);
+		if ((new_argv = malloc(sizeof (char *) * n)) == NULL)
+			return (NULL);
 
-			new_argv[a++] = SUPATH;
-			if (strcmp(login, "root") != 0) {
-				new_argv[a++] = "-";
-				n++;
-			}
-			new_argv[a++] = (char *)login;
-		}
+		new_argv[a++] = FAILSAFESHELL;
 		new_argv[a++] = "-c";
 		new_argv[a++] = subshell;
 		new_argv[a++] = NULL;
 		assert(a == n);
 	} else {
-		if (failsafe) {
-			n = 2;
-			if ((new_argv = malloc(sizeof (char *) * n)) == NULL)
-				return (NULL);
-			new_argv[a++] = FAILSAFESHELL;
-			new_argv[a++] = NULL;
-			assert(n == a);
+		int a = 0, n = 6;
+
+		assert(supath != NULL);
+		if ((new_argv = malloc(sizeof (char *) * n)) == NULL)
+			return (NULL);
+
+		new_argv[a++] = supath;
+		if (strcmp(login, "root") != 0) {
+			new_argv[a++] = "-";
 		} else {
-			new_argv = zone_login_cmd(bh, login);
+			n--;
 		}
+		new_argv[a++] = (char *)login;
+		new_argv[a++] = "-c";
+		new_argv[a++] = subshell;
+		new_argv[a++] = NULL;
+		assert(a == n);
 	}
 
 	return (new_argv);
@@ -1185,6 +1337,7 @@
 	int e = 0, size = 1;
 	char **new_env, *estr;
 	char *term = getenv("TERM");
+	char *path;
 
 	size++;	/* for $PATH */
 	if (term != NULL)
@@ -1201,7 +1354,12 @@
 	if ((new_env = malloc(sizeof (char *) * size)) == NULL)
 		return (NULL);
 
-	if ((estr = add_env("PATH", DEF_PATH)) == NULL)
+	if (strcmp(zonebrand, "lx") == 0)
+		path = LX_DEF_PATH;
+	else
+		path = DEF_PATH;
+
+	if ((estr = add_env("PATH", path)) == NULL)
 		return (NULL);
 	new_env[e++] = estr;
 
@@ -1723,13 +1881,50 @@
 	return (nptr->pw_name);
 }
 
+static boolean_t
+zlog_mode_logging(char *zonename, boolean_t *found)
+{
+	boolean_t lm = B_FALSE;
+	zone_dochandle_t handle;
+	struct zone_attrtab attr;
+
+	*found = B_FALSE;
+	if ((handle = zonecfg_init_handle()) == NULL)
+		return (lm);
+
+	if (zonecfg_get_handle(zonename, handle) != Z_OK)
+		goto done;
+
+	if (zonecfg_setattrent(handle) != Z_OK)
+		goto done;
+	while (zonecfg_getattrent(handle, &attr) == Z_OK) {
+		if (strcmp("zlog-mode", attr.zone_attr_name) == 0) {
+			int len = strlen(attr.zone_attr_value);
+
+			*found = B_TRUE;
+			if (strncmp("log", attr.zone_attr_value, 3) == 0 ||
+			    strncmp("nolog", attr.zone_attr_value, 5) == 0 ||
+			    (len >= 3 && attr.zone_attr_value[len - 2] == '-'))
+				lm = B_TRUE;
+			break;
+		}
+	}
+	(void) zonecfg_endattrent(handle);
+
+done:
+	zonecfg_fini_handle(handle);
+	return (lm);
+}
+
 int
 main(int argc, char **argv)
 {
-	int arg, console = 0;
+	int arg, console = 0, imode = 0;
+	int estatus = 0;
 	zoneid_t zoneid;
 	zone_state_t st;
 	char *login = "root";
+	int iflag = 0;
 	int lflag = 0;
 	int nflag = 0;
 	char *zonename = NULL;
@@ -1736,11 +1931,11 @@
 	char **proc_args = NULL;
 	char **new_args, **new_env;
 	sigset_t block_cld;
+	siginfo_t si;
 	char devroot[MAXPATHLEN];
 	char *slavename, slaveshortname[MAXPATHLEN];
 	priv_set_t *privset;
 	int tmpl_fd;
-	char zonebrand[MAXNAMELEN];
 	char default_brand[MAXNAMELEN];
 	struct stat sb;
 	char kernzone[ZONENAME_MAX];
@@ -1754,7 +1949,7 @@
 	(void) getpname(argv[0]);
 	username = get_username();
 
-	while ((arg = getopt(argc, argv, "dnECR:Se:l:Q")) != EOF) {
+	while ((arg = getopt(argc, argv, "diNnECIR:Se:l:Q")) != EOF) {
 		switch (arg) {
 		case 'C':
 			console = 1;
@@ -1762,6 +1957,16 @@
 		case 'E':
 			nocmdchar = 1;
 			break;
+		case 'I':
+			/*
+			 * interactive mode is just a slight variation on the
+			 * console mode.
+			 */
+			console = 1;
+			imode = 1;
+			/* The default is HUP, disconnect on EOF */
+			connect_flags ^= ZLOGIN_ZFD_EOF;
+			break;
 		case 'R':	/* undocumented */
 			if (*optarg != '/') {
 				zerror(gettext("root path must be absolute."));
@@ -1781,15 +1986,22 @@
 			failsafe = 1;
 			break;
 		case 'd':
-			disconnect = 1;
+			connect_flags |= ZLOGIN_DISCONNECT;
 			break;
 		case 'e':
 			set_cmdchar(optarg);
 			break;
+		case 'i':
+			iflag = 1;
+			break;
 		case 'l':
 			login = optarg;
 			lflag = 1;
 			break;
+		case 'N':
+			/* NOHUP - do not send EOF */
+			connect_flags ^= ZLOGIN_ZFD_EOF;
+			break;
 		case 'n':
 			nflag = 1;
 			break;
@@ -1800,6 +2012,12 @@
 
 	if (console != 0) {
 
+		/*
+		 * The only connect option in console mode is ZLOGIN_DISCONNECT
+		 */
+		if (imode == 0)
+			connect_flags &= ZLOGIN_DISCONNECT;
+
 		if (lflag != 0) {
 			zerror(gettext(
 			    "-l may not be specified for console login"));
@@ -1826,17 +2044,27 @@
 
 	}
 
+	if (iflag != 0 && nflag != 0) {
+		zerror(gettext("-i and -n flags are incompatible"));
+		usage();
+	}
+
 	if (failsafe != 0 && lflag != 0) {
 		zerror(gettext("-l may not be specified for failsafe login"));
 		usage();
 	}
 
-	if (!console && disconnect != 0) {
+	if (!console && (connect_flags & ZLOGIN_DISCONNECT) != 0) {
 		zerror(gettext(
 		    "-d may only be specified with console login"));
 		usage();
 	}
 
+	if (imode == 0 && (connect_flags & ZLOGIN_ZFD_EOF) != 0) {
+		zerror(gettext("-N may only be specified with -I"));
+		usage();
+	}
+
 	if (optind == (argc - 1)) {
 		/*
 		 * zone name, no process name; this should be an interactive
@@ -1859,7 +2087,8 @@
 		/* zone name and process name, and possibly some args */
 		zonename = argv[optind];
 		proc_args = &argv[optind + 1];
-		interactive = 0;
+		if (iflag && isatty(STDIN_FILENO))
+			interactive = 1;
 	} else {
 		usage();
 	}
@@ -1945,10 +2174,31 @@
 	}
 
 	/*
-	 * The console is a separate case from the rest of the code; handle
-	 * it first.
+	 * The console (or standalong interactive mode) is a separate case from
+	 * the rest of the code; handle it first.
 	 */
 	if (console) {
+		int gz_stderr_fd = -1;
+		int retry;
+		boolean_t set_raw = B_TRUE;
+
+		if (imode) {
+			boolean_t has_zfd_config;
+
+			if (zlog_mode_logging(zonename, &has_zfd_config))
+				set_raw = B_FALSE;
+
+			/*
+			 * Asked for standalone interactive mode but the
+			 * zlog-mode attribute is not configured on the zone.
+			 */
+			if (!has_zfd_config) {
+				zerror(gettext("'%s' is not configured on "
+				    "the zone"), "zlog-mode");
+				return (1);
+			}
+		}
+
 		/*
 		 * Ensure that zoneadmd for this zone is running.
 		 */
@@ -1957,16 +2207,56 @@
 
 		/*
 		 * Make contact with zoneadmd.
+		 *
+		 * Handshake with the control socket first. We handle retries
+		 * here since the relevant thread in zoneadmd might not have
+		 * finished setting up yet.
 		 */
-		if (get_console_master(zonename) == -1)
+		for (retry = 0; retry < MAX_RETRY; retry++) {
+			masterfd = connect_zone_sock(zonename,
+			    (imode ? "server_ctl" : "console_sock"), B_FALSE);
+			if (masterfd != -1)
+				break;
+			(void) sleep(1);
+		}
+
+		if (retry == MAX_RETRY) {
+			zerror(gettext("unable to connect for %d seconds"),
+			    MAX_RETRY);
 			return (1);
+		}
 
-		if (!quiet)
-			(void) printf(
-			    gettext("[Connected to zone '%s' console]\n"),
-			    zonename);
+		if (handshake_zone_sock(masterfd, connect_flags) != 0) {
+			(void) close(masterfd);
+			return (1);
+		}
 
-		if (set_tty_rawmode(STDIN_FILENO) == -1) {
+		if (imode) {
+			ctlfd = masterfd;
+
+			/* Now open the io-related sockets */
+			masterfd = connect_zone_sock(zonename, "server_out",
+			    B_TRUE);
+			gz_stderr_fd = connect_zone_sock(zonename,
+			    "server_err", B_TRUE);
+			if (masterfd == -1 || gz_stderr_fd == -1) {
+				(void) close(ctlfd);
+				(void) close(masterfd);
+				(void) close(gz_stderr_fd);
+				return (1);
+			}
+		}
+
+		if (!quiet) {
+			if (imode)
+				(void) printf(gettext("[Connected to zone '%s' "
+				    "interactively]\n"), zonename);
+			else
+				(void) printf(gettext("[Connected to zone '%s' "
+				    "console]\n"), zonename);
+		}
+
+		if (set_raw && set_tty_rawmode(STDIN_FILENO) == -1) {
 			reset_tty();
 			zperror(gettext("failed to set stdin pty to raw mode"));
 			return (1);
@@ -1975,15 +2265,25 @@
 		(void) sigset(SIGWINCH, sigwinch);
 		(void) sigwinch(0);
 
+		if (imode) {
+			/* Allow EOF mode toggling via SIGUSR1 */
+			(void) sigset(SIGUSR1, sigusr1);
+		}
+
 		/*
 		 * Run the I/O loop until we get disconnected.
 		 */
-		doio(masterfd, -1, masterfd, -1, -1, B_FALSE);
+		doio(masterfd, -1, masterfd, gz_stderr_fd, -1, B_FALSE);
 		reset_tty();
-		if (!quiet)
-			(void) printf(
-			    gettext("\n[Connection to zone '%s' console "
-			    "closed]\n"), zonename);
+		if (!quiet) {
+			if (imode)
+				(void) printf(gettext("\n[Interactive "
+				    "connection to zone '%s' closed]\n"),
+				    zonename);
+			else
+				(void) printf(gettext("\n[Connection to zone "
+				    "'%s' console closed]\n"), zonename);
+		}
 
 		return (0);
 	}
@@ -2051,11 +2351,23 @@
 		return (1);
 	}
 
-	if ((new_args = prep_args(bh, login, proc_args)) == NULL) {
-		zperror(gettext("could not assemble new arguments"));
-		brand_close(bh);
-		return (1);
+	/*
+	 * The 'interactive' parameter (-i option) indicates that we're running
+	 * a command interactively. In this case we skip prep_args so that we
+	 * don't prepend the 'su root -c' preamble to the command invocation
+	 * since the 'su' command typically will execute a setpgrp which will
+	 * disassociate the actual command from the controlling terminal that
+	 * we (zlogin) setup.
+	 */
+	if (!iflag) {
+		if ((new_args = prep_args(bh, zonename, login, proc_args))
+		    == NULL) {
+			zperror(gettext("could not assemble new arguments"));
+			brand_close(bh);
+			return (1);
+		}
 	}
+
 	/*
 	 * Get the brand specific user_cmd.  This command is used to get
 	 * a passwd(4) entry for login.
@@ -2201,6 +2513,8 @@
 			return (1);
 		}
 
+		/* Note: we're now inside the zone, can't use gettext anymore */
+
 		if (slavefd != STDERR_FILENO)
 			(void) close(STDERR_FILENO);
 
@@ -2242,8 +2556,18 @@
 		/*
 		 * In failsafe mode, we don't use login(1), so don't try
 		 * setting up a utmpx entry.
+		 *
+		 * A branded zone may have very different utmpx semantics.
+		 * At the moment, we only have two brand types:
+		 * Illumos-like (native, sn1) and Linux.  In the Illumos
+		 * case, we know exactly how to do the necessary utmpx
+		 * setup.  Fortunately for us, the Linux /bin/login is
+		 * prepared to deal with a non-initialized utmpx entry, so
+		 * we can simply skip it.  If future brands don't fall into
+		 * either category, we'll have to add a per-brand utmpx
+		 * setup hook.
 		 */
-		if (!failsafe)
+		if (!failsafe && (strcmp(zonebrand, "lx") != 0))
 			if (setup_utmpx(slaveshortname) == -1)
 				return (1);
 
@@ -2252,13 +2576,17 @@
 		 * execute the brand's login program.
 		 */
 		if (setuid(0) == -1) {
-			zperror(gettext("insufficient privilege"));
+			zperror("insufficient privilege");
 			return (1);
 		}
 
-		(void) execve(new_args[0], new_args, new_env);
-		zperror(gettext("exec failure"));
-		return (1);
+		if (iflag) {
+			(void) execve(proc_args[0], proc_args, new_env);
+		} else {
+			(void) execve(new_args[0], new_args, new_env);
+		}
+		zperror("exec failure");
+		return (ENOEXEC);
 	}
 
 	(void) ct_tmpl_clear(tmpl_fd);
@@ -2283,8 +2611,19 @@
 	if (pollerr != 0) {
 		(void) fprintf(stderr, gettext("Error: connection closed due "
 		    "to unexpected pollevents=0x%x.\n"), pollerr);
-		return (1);
+		return (EPIPE);
 	}
 
-	return (0);
+	/* reap child and get its status */
+	if (waitid(P_PID, child_pid, &si, WEXITED | WNOHANG) == -1) {
+		estatus = errno;
+	} else if (si.si_pid == 0) {
+		estatus = ECHILD;
+	} else if (si.si_code == CLD_EXITED) {
+		estatus = si.si_status;
+	} else {
+		estatus = ECONNABORTED;
+	}
+
+	return (estatus);
 }
--- old/usr/src/cmd/zoneadm/svc-zones	Wed Oct  5 00:31:41 2016
+++ new/usr/src/cmd/zoneadm/svc-zones	Wed Oct  5 00:31:41 2016
@@ -32,7 +32,7 @@
 shutdown_zones()
 {
 	zoneadm list -p | nawk -F: '{
-		if ($2 != "global") {
+		if (($5 != "lx") && ($2 != "global")) {
 			print $2
 		}
 	}'
--- old/usr/src/cmd/zoneadm/zoneadm.c	Wed Oct  5 00:31:41 2016
+++ new/usr/src/cmd/zoneadm/zoneadm.c	Wed Oct  5 00:31:41 2016
@@ -23,6 +23,7 @@
  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
  * Copyright (c) 2015 by Delphix. All rights reserved.
+ * Copyright 2015, Joyent Inc. All rights reserved.
  */
 
 /*
@@ -101,6 +102,7 @@
 	char		zroot[MAXPATHLEN];
 	char		zuuid[UUID_PRINTABLE_STRING_LENGTH];
 	zone_iptype_t	ziptype;
+	zoneid_t	zdid;
 } zone_entry_t;
 
 #define	CLUSTER_BRAND_NAME	"cluster"
@@ -443,6 +445,7 @@
 	}
 	if (!verbose) {
 		char *cp, *clim;
+		char zdid[80];
 
 		if (!parsable) {
 			(void) printf("%s\n", zent->zname);
@@ -458,8 +461,12 @@
 			(void) printf("%.*s\\:", clim - cp, cp);
 			cp = clim + 1;
 		}
-		(void) printf("%s:%s:%s:%s\n", cp, zent->zuuid, zent->zbrand,
-		    ip_type_str);
+		if (zent->zdid == -1)
+			zdid[0] = '\0';
+		else
+			(void) snprintf(zdid, sizeof (zdid), "%d", zent->zdid);
+		(void) printf("%s:%s:%s:%s:%s\n", cp, zent->zuuid, zent->zbrand,
+		    ip_type_str, zdid);
 		return;
 	}
 	if (zent->zstate_str != NULL) {
@@ -554,6 +561,22 @@
 		return (Z_OK);
 	}
 
+	if ((handle = zonecfg_init_handle()) == NULL) {
+		zperror2(zent->zname, gettext("could not init handle"));
+		return (Z_ERR);
+	}
+	if ((err = zonecfg_get_handle(zent->zname, handle)) != Z_OK) {
+		zperror2(zent->zname, gettext("could not get handle"));
+		zonecfg_fini_handle(handle);
+		return (Z_ERR);
+	}
+
+	if ((err = zonecfg_get_iptype(handle, &zent->ziptype)) != Z_OK) {
+		zperror2(zent->zname, gettext("could not get ip-type"));
+		zonecfg_fini_handle(handle);
+		return (Z_ERR);
+	}
+
 	/*
 	 * There is a race condition where the zone could boot while
 	 * we're walking the index file.  In this case the zone state
@@ -574,25 +597,11 @@
 				zent->ziptype = ZS_EXCLUSIVE;
 			else
 				zent->ziptype = ZS_SHARED;
-			return (Z_OK);
 		}
 	}
 
-	if ((handle = zonecfg_init_handle()) == NULL) {
-		zperror2(zent->zname, gettext("could not init handle"));
-		return (Z_ERR);
-	}
-	if ((err = zonecfg_get_handle(zent->zname, handle)) != Z_OK) {
-		zperror2(zent->zname, gettext("could not get handle"));
-		zonecfg_fini_handle(handle);
-		return (Z_ERR);
-	}
+	zent->zdid = zonecfg_get_did(handle);
 
-	if ((err = zonecfg_get_iptype(handle, &zent->ziptype)) != Z_OK) {
-		zperror2(zent->zname, gettext("could not get ip-type"));
-		zonecfg_fini_handle(handle);
-		return (Z_ERR);
-	}
 	zonecfg_fini_handle(handle);
 
 	return (Z_OK);
@@ -766,18 +775,22 @@
  * Retrieve a zone entry by name.  Returns NULL if no such zone exists.
  */
 static zone_entry_t *
-lookup_running_zone(const char *str)
+lookup_running_zone(const char *name)
 {
-	int i;
+	zoneid_t zid;
+	zone_entry_t *zent;
 
-	if (fetch_zents() != Z_OK)
+	if ((zid = getzoneidbyname(name)) == -1)
 		return (NULL);
 
-	for (i = 0; i < nzents; i++) {
-		if (strcmp(str, zents[i].zname) == 0)
-			return (&zents[i]);
+	if ((zent = malloc(sizeof (zone_entry_t))) == NULL)
+		return (NULL);
+
+	if (lookup_zone_info(name, zid, zent) != Z_OK) {
+		free(zent);
+		return (NULL);
 	}
-	return (NULL);
+	return (zent);
 }
 
 /*
@@ -2783,6 +2796,61 @@
 	return (return_code);
 }
 
+/*
+ * Called when readying or booting a zone.  We double check that the zone's
+ * debug ID is set and is unique.  This covers the case of pre-existing zones
+ * with no ID.  Also, its possible that a zone was migrated to this host
+ * and as a result it has a duplicate ID.  In this case we preserve the ID
+ * of the first zone we match on in the index file (since it was there before
+ * the current zone) and we assign a new unique ID to the current zone.
+ * Return true if we assigned a new ID, indicating that the zone configuration
+ * needs to be saved.
+ */
+static boolean_t
+verify_fix_did(zone_dochandle_t handle)
+{
+	zoneid_t mydid;
+	zone_entry_t zent;
+	FILE *cookie;
+	char *name;
+	boolean_t fix = B_FALSE;
+
+	mydid = zonecfg_get_did(handle);
+	if (mydid == -1) {
+		zonecfg_set_did(handle);
+		return (B_TRUE);
+	}
+
+	/* Get the full list of zones from the configuration. */
+	cookie = setzoneent();
+	while ((name = getzoneent(cookie)) != NULL) {
+		if (strcmp(target_zone, name) == 0) {
+			free(name);
+			break;	/* Once we find our entry, stop. */
+		}
+
+		if (strcmp(name, "global") == 0 ||
+		    lookup_zone_info(name, ZONE_ID_UNDEFINED, &zent) != Z_OK) {
+			free(name);
+			continue;
+		}
+
+		free(name);
+		if (zent.zdid == mydid) {
+			fix = B_TRUE;
+			break;
+		}
+	}
+	endzoneent(cookie);
+
+	if (fix) {
+		zonecfg_set_did(handle);
+		return (B_TRUE);
+	}
+
+	return (B_FALSE);
+}
+
 static int
 verify_details(int cmd_num, char *argv[])
 {
@@ -2842,6 +2910,18 @@
 	if (verify_handle(cmd_num, handle, argv) != Z_OK)
 		return_code = Z_ERR;
 
+	if (cmd_num == CMD_READY || cmd_num == CMD_BOOT) {
+		int vcommit = 0, obscommit = 0;
+
+		vcommit = verify_fix_did(handle);
+		obscommit = zonecfg_fix_obsolete(handle);
+
+		if (vcommit || obscommit)
+			if (zonecfg_save(handle) != Z_OK)
+				(void) fprintf(stderr, gettext("Could not save "
+				    "updated configuration.\n"));
+	}
+
 	zonecfg_fini_handle(handle);
 	if (return_code == Z_ERR)
 		(void) fprintf(stderr,
@@ -3865,10 +3945,10 @@
 			 * exist if the zone was force-attached after a
 			 * migration.
 			 */
-	char		*std_entries[] = {"dev", "lu", "root",
-			    "SUNWattached.xml", NULL};
-			/* (MAXPATHLEN * 3) is for the 3 std_entries dirs */
-	char		cmdbuf[sizeof (RMCOMMAND) + (MAXPATHLEN * 3) + 64];
+	char		*std_entries[] = {"dev", "lastexited", "logs", "lu",
+			    "root", "SUNWattached.xml", NULL};
+			/* (MAXPATHLEN * 5) is for the 5 std_entries dirs */
+	char		cmdbuf[sizeof (RMCOMMAND) + (MAXPATHLEN * 5) + 64];
 
 	/*
 	 * We shouldn't need these checks but lets be paranoid since we
@@ -5375,7 +5455,7 @@
 	priv_set_t *privset;
 	zoneid_t zoneid;
 	zone_dochandle_t handle;
-	struct zone_mcaptab mcap;
+	uint64_t mcap;
 	char pool_err[128];
 
 	zoneid = getzoneid();
@@ -5466,19 +5546,12 @@
 	}
 
 	/*
-	 * If a memory cap is configured, set the cap in the kernel using
-	 * zone_setattr() and make sure the rcapd SMF service is enabled.
+	 * If a memory cap is configured, make sure the rcapd SMF service is
+	 * enabled.
 	 */
-	if (zonecfg_getmcapent(handle, &mcap) == Z_OK) {
-		uint64_t num;
+	if (zonecfg_get_aliased_rctl(handle, ALIAS_MAXPHYSMEM, &mcap) == Z_OK) {
 		char smf_err[128];
 
-		num = (uint64_t)strtoll(mcap.zone_physmem_cap, NULL, 10);
-		if (zone_setattr(zoneid, ZONE_ATTR_PHYS_MCAP, &num, 0) == -1) {
-			zerror(gettext("could not set zone memory cap"));
-			res = Z_ERR;
-		}
-
 		if (zonecfg_enable_rcapd(smf_err, sizeof (smf_err)) != Z_OK) {
 			zerror(gettext("enabling system/rcap service failed: "
 			    "%s"), smf_err);
--- old/usr/src/cmd/zoneadmd/Makefile	Wed Oct  5 00:31:42 2016
+++ new/usr/src/cmd/zoneadmd/Makefile	Wed Oct  5 00:31:42 2016
@@ -24,6 +24,7 @@
 #
 # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
 # Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+# Copyright (c) 2011, Joyent, Inc. All rights reserved.
 #
 
 PROG= zoneadmd
@@ -32,7 +33,7 @@
 
 ROOTCMDDIR=	$(ROOTLIB)/zones
 
-OBJS= zoneadmd.o zcons.o vplat.o
+OBJS= zoneadmd.o zcons.o vplat.o mcap.o zfd.o
 SRCS = $(OBJS:.o=.c)
 POFILE=zoneadmd_all.po
 POFILES= $(OBJS:%.o=%.po)
@@ -44,7 +45,7 @@
 
 LDLIBS += -lsocket -lzonecfg -lnsl -ldevinfo -ldevice -lnvpair \
 	-lgen -lbsm -lcontract -lzfs -luuid -lbrand -ldladm -ltsnet -ltsol \
-	-linetutil -lscf
+	-linetutil -lscf -lproc
 XGETFLAGS += -a -x zoneadmd.xcl
 
 .KEEP_STATE:
--- /dev/null	Wed Oct  5 00:31:42 2016
+++ new/usr/src/cmd/zoneadmd/mcap.c	Wed Oct  5 00:31:42 2016
@@ -0,0 +1,1188 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2014, Joyent, Inc.  All rights reserved.
+ */
+
+/*
+ * This file implements the code which runs a thread inside zoneadmd to cap
+ * the associated zone's physical memory.  A thread to do this is started
+ * when the zone boots and is halted when the zone shuts down.
+ *
+ * Because of the way that the VM system is currently implemented, there is no
+ * way to go from the bottom up (page to process to zone).  Thus, there is no
+ * obvious way to hook an rctl into the kernel's paging code to enforce a hard
+ * memory cap.  Instead, we implement a soft physical memory cap which looks
+ * at the zone's overall rss and once it is over the cap, works from the top
+ * down (zone to process to page), looking at zone processes, to determine
+ * what to try to pageout to get the zone under its memory cap.
+ *
+ * The code uses the fast, cheap, but potentially very inaccurate sum of the
+ * rss values from psinfo_t to first approximate the zone's rss and will
+ * fallback to the vm_getusage syscall to determine the zone's rss if needed.
+ * It then checks the rss against the zone's zone.max-physical-memory rctl.
+ * Once the zone goes over its cap, then this thread will work through the
+ * zone's /proc process list, Pgrab-bing each process and stepping through the
+ * address space segments attempting to use pr_memcntl(...MS_INVALCURPROC...)
+ * to pageout pages, until the zone is again under its cap.
+ *
+ * Although zone memory capping is implemented as a soft cap by this user-level
+ * thread, the interfaces around memory caps that are exposed to the user are
+ * the standard ones; an rctl and kstats.  This thread uses the rctl value
+ * to obtain the cap and works with the zone kernel code to update the kstats.
+ * If the implementation ever moves into the kernel, these exposed interfaces
+ * do not need to change.
+ *
+ * The thread adaptively sleeps, periodically checking the state of the
+ * zone.  As the zone's rss gets closer to the cap, the thread will wake up
+ * more often to check the zone's status.  Once the zone is over the cap,
+ * the thread will work to pageout until the zone is under the cap, as shown
+ * by updated vm_usage data.
+ *
+ * NOTE: The pagedata page maps (at least on x86) are not useful.  Those flags
+ * are set by hrm_setbits() and on x86 that code path is only executed by
+ *     segvn_pagelock -> hat_setstat -> hrm_setbits
+ *     segvn_softunlock -^
+ * On SPARC there is an additional code path which may make this data
+ * useful (sfmmu_ttesync), but since it is not generic, we ignore the page
+ * maps.  If we ever fix this issue, then we could generalize this mcap code to
+ * do more with the data on active pages.
+ *
+ * For debugging, touch the file {zonepath}/mcap_debug.log.  This will
+ * cause the thread to start logging its actions into that file (it may take
+ * a minute or two if the thread is currently sleeping).  Removing that
+ * file will cause logging to stop.
+ */
+
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libproc.h>
+#include <limits.h>
+#include <procfs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/priocntl.h>
+#include <dirent.h>
+#include <zone.h>
+#include <libzonecfg.h>
+#include <thread.h>
+#include <values.h>
+#include <sys/vm_usage.h>
+#include <sys/resource.h>
+#include <sys/debug.h>
+#include <synch.h>
+#include <wait.h>
+#include <libcontract.h>
+#include <libcontract_priv.h>
+#include <sys/contract/process.h>
+#include "zoneadmd.h"
+
+					/* round up to next y = 2^n */
+#define	ROUNDUP(x, y)	(((x) + ((y) - 1)) & ~((y) - 1))
+
+#define	CAP_REFRESH	((uint64_t)300 * NANOSEC) /* every 5 minutes */
+
+/*
+ * zonecfg attribute tunables for memory capping.
+ *    phys-mcap-cmd
+ *	type: string
+ *	specifies a command that can be run when over the cap
+ *    phys-mcap-no-vmusage
+ *	type: boolean
+ *	true disables vm_getusage and just uses zone's proc. rss sum
+ *    phys-mcap-no-pageout
+ *	type: boolean
+ *	true disables pageout when over
+ *    phys-mcap-no-pf-throttle
+ *	type: boolean
+ *	true disables page fault throttling when over
+ */
+#define	TUNE_CMD	"phys-mcap-cmd"
+#define	TUNE_NVMU	"phys-mcap-no-vmusage"
+#define	TUNE_NPAGE	"phys-mcap-no-pageout"
+#define	TUNE_NPFTHROT	"phys-mcap-no-pf-throttle"
+
+/*
+ * The large mapping value was derived empirically by seeing that mappings
+ * much bigger than 16mb sometimes take a relatively long time to invalidate
+ * (significant fraction of a second).
+ */
+#define	SEC_INTERIM	4	/* num secs to pause after stopped too long */
+#define	MSEC_TOO_LONG	100	/* release proc. after stopped for 100ms */
+#define	LARGE_MAPPING	16384	/* >= 16MB in KB - pageout in chunks */
+
+/*
+ * These are only used in get_mem_info but global. We always need scale_rss and
+ * prev_fast_rss to be persistent but we also have the other two global so we
+ * can easily see these with mdb.
+ */
+uint64_t	scale_rss = 0;
+uint64_t	prev_fast_rss = 0;
+uint64_t	fast_rss = 0;
+uint64_t	accurate_rss = 0;
+
+static char	zoneproc[MAXPATHLEN];
+static char	debug_log[MAXPATHLEN];
+static zoneid_t	zid;
+static mutex_t	shutdown_mx;
+static cond_t	shutdown_cv;
+static int	shutting_down = 0;
+static thread_t mcap_tid;
+static FILE	*debug_log_fp = NULL;
+static uint64_t zone_rss_cap;		/* RSS cap(KB) */
+static char	over_cmd[2 * BUFSIZ];	/* same size as zone_attr_value */
+static boolean_t skip_vmusage = B_FALSE;
+static boolean_t skip_pageout = B_FALSE;
+static boolean_t skip_pf_throttle = B_FALSE;
+
+static int64_t check_suspend();
+static void get_mcap_tunables();
+
+/*
+ * Structure to hold current state about a process address space that we're
+ * working on.
+ */
+typedef struct {
+	int pr_curr;		/* the # of the mapping we're working on */
+	int pr_nmap;		/* number of mappings in address space */
+	prmap_t *pr_mapp;	/* process's map array */
+} proc_map_t;
+
+typedef struct zsd_vmusage64 {
+	id_t vmu_zoneid;
+	uint_t vmu_type;
+	id_t vmu_id;
+	/*
+	 * An amd64 kernel will align the following uint64_t members, but a
+	 * 32bit i386 process will not without help.
+	 */
+	int vmu_align_next_members_on_8_bytes;
+	uint64_t vmu_rss_all;
+	uint64_t vmu_rss_private;
+	uint64_t vmu_rss_shared;
+	uint64_t vmu_swap_all;
+	uint64_t vmu_swap_private;
+	uint64_t vmu_swap_shared;
+} zsd_vmusage64_t;
+
+/*
+ * Output a debug log message.
+ */
+/*PRINTFLIKE1*/
+static void
+debug(char *fmt, ...)
+{
+	va_list ap;
+
+	if (debug_log_fp == NULL)
+		return;
+
+	va_start(ap, fmt);
+	(void) vfprintf(debug_log_fp, fmt, ap);
+	va_end(ap);
+	(void) fflush(debug_log_fp);
+}
+
+/*
+ * Like sleep(3C) but can be interupted by cond_signal which is posted when
+ * we're shutting down the mcap thread.
+ */
+static void
+sleep_shutdown(int secs)
+{
+	timestruc_t to;
+
+	to.tv_sec = secs;
+	to.tv_nsec = 0;
+
+	(void) mutex_lock(&shutdown_mx);
+	if (!shutting_down)
+		(void) cond_reltimedwait(&shutdown_cv, &shutdown_mx, &to);
+	(void) mutex_unlock(&shutdown_mx);
+}
+
+static boolean_t
+proc_issystem(pid_t pid)
+{
+	char pc_clname[PC_CLNMSZ];
+
+	if (priocntl(P_PID, pid, PC_GETXPARMS, NULL, PC_KY_CLNAME, pc_clname,
+	    PC_KY_NULL) != -1)
+		return (strcmp(pc_clname, "SYS") == 0);
+
+	return (B_TRUE);
+}
+
+/*
+ * Fork a child that enters the zone and runs the "phys-mcap-cmd" command.
+ */
+static void
+run_over_cmd()
+{
+	int		ctfd;
+	int		err;
+	pid_t		childpid;
+	siginfo_t	info;
+	ctid_t		ct;
+
+	/*
+	 * Before we enter the zone, we need to create a new process contract
+	 * for the child, as required by zone_enter().
+	 */
+	if ((ctfd = open64("/system/contract/process/template", O_RDWR)) == -1)
+		return;
+	if (ct_tmpl_set_critical(ctfd, 0) != 0 ||
+	    ct_tmpl_set_informative(ctfd, 0) != 0 ||
+	    ct_pr_tmpl_set_fatal(ctfd, CT_PR_EV_HWERR) != 0 ||
+	    ct_pr_tmpl_set_param(ctfd, CT_PR_PGRPONLY) != 0 ||
+	    ct_tmpl_activate(ctfd) != 0) {
+		(void) close(ctfd);
+		return;
+	}
+
+	childpid = fork();
+	switch (childpid) {
+	case -1:
+		(void) ct_tmpl_clear(ctfd);
+		(void) close(ctfd);
+		break;
+	case 0:	/* Child */
+		(void) ct_tmpl_clear(ctfd);
+		(void) close(ctfd);
+		if (zone_enter(zid) == -1)
+			_exit(errno);
+		err = system(over_cmd);
+		_exit(err);
+		break;
+	default:	/* Parent */
+		if (contract_latest(&ct) == -1)
+			ct = -1;
+		(void) ct_tmpl_clear(ctfd);
+		(void) close(ctfd);
+		err = waitid(P_PID, childpid, &info, WEXITED);
+		(void) contract_abandon_id(ct);
+		if (err == -1 || info.si_status != 0)
+			debug("over_cmd failed");
+		break;
+	}
+}
+
+/*
+ * Get the next mapping.
+ */
+static prmap_t *
+nextmapping(proc_map_t *pmp)
+{
+	if (pmp->pr_mapp == NULL || pmp->pr_curr >= pmp->pr_nmap)
+		return (NULL);
+
+	return (&pmp->pr_mapp[pmp->pr_curr++]);
+}
+
+/*
+ * Initialize the proc_map_t to access the first mapping of an address space.
+ */
+static prmap_t *
+init_map(proc_map_t *pmp, pid_t pid)
+{
+	int fd;
+	int res;
+	struct stat st;
+	char pathbuf[MAXPATHLEN];
+
+	bzero(pmp, sizeof (proc_map_t));
+	pmp->pr_nmap = -1;
+
+	(void) snprintf(pathbuf, sizeof (pathbuf), "%s/%d/map", zoneproc, pid);
+	if ((fd = open(pathbuf, O_RDONLY, 0)) < 0)
+		return (NULL);
+
+redo:
+	errno = 0;
+	if (fstat(fd, &st) != 0)
+		goto done;
+
+	if ((pmp->pr_mapp = malloc(st.st_size)) == NULL) {
+		debug("cannot malloc() %ld bytes for xmap", st.st_size);
+		goto done;
+	}
+	(void) bzero(pmp->pr_mapp, st.st_size);
+
+	errno = 0;
+	if ((res = pread(fd, pmp->pr_mapp, st.st_size, 0)) != st.st_size) {
+		free(pmp->pr_mapp);
+		pmp->pr_mapp = NULL;
+		if (res > 0 || errno == E2BIG) {
+			goto redo;
+		} else {
+			debug("pid %ld cannot read xmap\n", pid);
+			goto done;
+		}
+	}
+
+	pmp->pr_nmap = st.st_size / sizeof (prmap_t);
+
+done:
+	(void) close(fd);
+	return (nextmapping(pmp));
+}
+
+/*
+ * Attempt to invalidate the entire mapping from within the given process's
+ * address space. May return nonzero with errno as:
+ *    ESRCH  - process not found
+ *    ENOMEM - segment not found
+ *    EINVAL - mapping exceeds a single segment
+ */
+static int
+pageout_mapping(pid_t pid, prmap_t *pmp)
+{
+	int res;
+
+	if (pmp->pr_mflags & MA_ISM || pmp->pr_mflags & MA_SHM)
+		return (0);
+
+	errno = 0;
+	res = syscall(SYS_rusagesys, _RUSAGESYS_INVALMAP, pid, pmp->pr_vaddr,
+	    pmp->pr_size);
+
+	return (res);
+}
+
+/*
+ * Work through a process paging out mappings until the whole address space was
+ * examined or the excess is < 0.  Return our estimate of the updated excess.
+ */
+static int64_t
+pageout_process(pid_t pid, int64_t excess)
+{
+	int			psfd;
+	prmap_t			*pmap;
+	proc_map_t		cur;
+	int64_t			sum_d_rss, d_rss;
+	int64_t			old_rss;
+	int			map_cnt;
+	psinfo_t		psinfo;
+	char			pathbuf[MAXPATHLEN];
+
+	(void) snprintf(pathbuf, sizeof (pathbuf), "%s/%d/psinfo", zoneproc,
+	    pid);
+	if ((psfd = open(pathbuf, O_RDONLY, 0000)) < 0)
+		return (excess);
+
+	cur.pr_mapp = NULL;
+
+	if (pread(psfd, &psinfo, sizeof (psinfo), 0) != sizeof (psinfo))
+		goto done;
+
+	old_rss = (int64_t)psinfo.pr_rssize;
+	map_cnt = 0;
+
+	/* If unscannable, skip it. */
+	if (psinfo.pr_nlwp == 0 || proc_issystem(pid)) {
+		debug("pid %ld: system process, skipping %s\n",
+		    pid, psinfo.pr_psargs);
+		goto done;
+	}
+
+	/* If tiny RSS (16KB), skip it. */
+	if (old_rss <= 16) {
+		debug("pid %ld: skipping, RSS %lldKB %s\n",
+		    pid, old_rss, psinfo.pr_psargs);
+		goto done;
+	}
+
+	/* Get segment residency information. */
+	pmap = init_map(&cur, pid);
+
+	/* Skip process if it has no mappings. */
+	if (pmap == NULL) {
+		debug("pid %ld: map unreadable; ignoring\n", pid);
+		goto done;
+	}
+
+	debug("pid %ld: nmap %d sz %dKB rss %lldKB %s\n",
+	    pid, cur.pr_nmap, psinfo.pr_size, old_rss, psinfo.pr_psargs);
+
+	/*
+	 * Within the process's address space, attempt to page out mappings.
+	 */
+	sum_d_rss = 0;
+	while (excess > 0 && pmap != NULL && !shutting_down) {
+		/* invalidate the entire mapping */
+		if (pageout_mapping(pid, pmap) < 0)
+			debug("pid %ld: mapping 0x%p %ldkb unpageable (%d)\n",
+			    pid, (void *)pmap->pr_vaddr,
+			    (long)pmap->pr_size / 1024L, errno);
+
+		map_cnt++;
+
+		/*
+		 * Re-check the process rss and get the delta.
+		 */
+		if (pread(psfd, &psinfo, sizeof (psinfo), 0)
+		    != sizeof (psinfo)) {
+			excess -= old_rss;
+			goto done;
+		}
+
+		d_rss = (int64_t)psinfo.pr_rssize - old_rss;
+		old_rss = (int64_t)psinfo.pr_rssize;
+		sum_d_rss += d_rss;
+
+		/*
+		 * d_rss hopefully should be negative (or 0 if nothing
+		 * invalidated) but can be positive if more got paged in.
+		 */
+		excess += d_rss;
+
+		if (excess <= 0) {
+			debug("pid %ld: (part.) nmap %d delta_rss %lldKB "
+			    "excess %lldKB\n", pid, map_cnt,
+			    (unsigned long long)sum_d_rss, (long long)excess);
+			map_cnt = 0;
+
+			/*
+			 * If we're actually under, this will suspend checking
+			 * in the middle of this process's address space.
+			 */
+			excess = check_suspend();
+			if (shutting_down)
+				goto done;
+
+			/*
+			 * since we might have suspended, re-read process's rss
+			 */
+			if (pread(psfd, &psinfo, sizeof (psinfo), 0)
+			    != sizeof (psinfo)) {
+				excess -= old_rss;
+				goto done;
+			}
+
+			old_rss = (int64_t)psinfo.pr_rssize;
+
+			debug("pid %ld: resume pageout; excess %lld\n", pid,
+			    (long long)excess);
+			sum_d_rss = 0;
+		}
+
+		pmap = nextmapping(&cur);
+	}
+
+	debug("pid %ld: nmap %d delta_rss %lldKB excess %lldKB\n",
+	    pid, map_cnt, (unsigned long long)sum_d_rss, (long long)excess);
+
+done:
+	if (cur.pr_mapp != NULL)
+		free(cur.pr_mapp);
+
+	(void) close(psfd);
+
+	if (shutting_down)
+		return (0);
+
+	return (excess);
+}
+
+/*
+ * Get the zone's RSS data.
+ */
+static uint64_t
+get_mem_info()
+{
+	uint64_t		n = 1;
+	zsd_vmusage64_t		buf;
+	uint64_t		tmp_rss;
+	DIR			*pdir = NULL;
+	struct dirent		*dent;
+
+	/*
+	 * Start by doing the fast, cheap RSS calculation using the rss value
+	 * in psinfo_t.  Because that's per-process, it can lead to double
+	 * counting some memory and overestimating how much is being used, but
+	 * as long as that's not over the cap, then we don't need do the
+	 * expensive calculation.
+	 *
+	 * If we have to do the expensive calculation, we remember the scaling
+	 * factor so that we can try to use that on subsequent iterations for
+	 * the fast rss.
+	 */
+	if (shutting_down)
+		return (0);
+
+	if ((pdir = opendir(zoneproc)) == NULL)
+		return (0);
+
+	accurate_rss = 0;
+	fast_rss = 0;
+	while (!shutting_down && (dent = readdir(pdir)) != NULL) {
+		pid_t		pid;
+		int		psfd;
+		int64_t		rss;
+		char		pathbuf[MAXPATHLEN];
+		psinfo_t	psinfo;
+
+		if (strcmp(".", dent->d_name) == 0 ||
+		    strcmp("..", dent->d_name) == 0)
+			continue;
+
+		pid = atoi(dent->d_name);
+		if (pid == 0 || pid == 1)
+			continue;
+
+		(void) snprintf(pathbuf, sizeof (pathbuf), "%s/%d/psinfo",
+		    zoneproc, pid);
+
+		rss = 0;
+		if ((psfd = open(pathbuf, O_RDONLY, 0000)) != -1) {
+			if (pread(psfd, &psinfo, sizeof (psinfo), 0) ==
+			    sizeof (psinfo))
+				rss = (int64_t)psinfo.pr_rssize;
+
+			(void) close(psfd);
+		}
+
+		fast_rss += rss;
+	}
+
+	(void) closedir(pdir);
+
+	if (shutting_down)
+		return (0);
+
+	debug("fast rss: %lluKB, scale: %llu, prev: %lluKB\n", fast_rss,
+	    scale_rss, prev_fast_rss);
+
+	/* see if we can get by with a scaled fast rss */
+	tmp_rss = fast_rss;
+	if (scale_rss > 1 && prev_fast_rss > 0) {
+		/*
+		 * Only scale the fast value if it hasn't ballooned too much
+		 * to trust.
+		 */
+		if (fast_rss / prev_fast_rss < 2) {
+			fast_rss /= scale_rss;
+			debug("scaled fast rss: %lluKB\n", fast_rss);
+		}
+	}
+
+	if (fast_rss <= zone_rss_cap || skip_vmusage) {
+		uint64_t zone_rss_bytes;
+
+		zone_rss_bytes = fast_rss * 1024;
+		/* Use the zone's approx. RSS in the kernel */
+		(void) zone_setattr(zid, ZONE_ATTR_RSS, &zone_rss_bytes, 0);
+		return (fast_rss);
+	}
+
+	buf.vmu_id = zid;
+
+	/* get accurate usage (cached data may be up to 5 seconds old) */
+	if (syscall(SYS_rusagesys, _RUSAGESYS_GETVMUSAGE, VMUSAGE_A_ZONE, 5,
+	    (uintptr_t)&buf, (uintptr_t)&n) != 0) {
+		debug("vmusage failed\n");
+		(void) sleep_shutdown(1);
+		return (0);
+	}
+
+	if (n > 1) {
+		/* This should never happen */
+		debug("vmusage returned more than one result\n");
+		(void) sleep_shutdown(1);
+		return (0);
+	}
+
+	if (buf.vmu_id != zid) {
+		/* This should never happen */
+		debug("vmusage returned the incorrect zone\n");
+		(void) sleep_shutdown(1);
+		return (0);
+	}
+
+	accurate_rss = buf.vmu_rss_all / 1024;
+
+	/* calculate scaling factor to use for fast_rss from now on */
+	if (accurate_rss > 0) {
+		scale_rss = fast_rss / accurate_rss;
+		debug("new scaling factor: %llu\n", scale_rss);
+		/* remember the fast rss when we had to get the accurate rss */
+		prev_fast_rss = tmp_rss;
+	}
+
+	debug("accurate rss: %lluKB, scale: %llu, prev: %lluKB\n", accurate_rss,
+	    scale_rss, prev_fast_rss);
+	return (accurate_rss);
+}
+
+/*
+ * Needed to read the zones physical-memory-cap rctl.
+ */
+static struct ps_prochandle *
+grab_zone_proc()
+{
+	DIR *dirp;
+	struct dirent *dentp;
+	struct ps_prochandle *ph = NULL;
+	int tmp;
+
+	if ((dirp = opendir(zoneproc)) == NULL)
+		return (NULL);
+
+	while (!shutting_down && (dentp = readdir(dirp))) {
+		int pid;
+
+		if (strcmp(".", dentp->d_name) == 0 ||
+		    strcmp("..", dentp->d_name) == 0)
+			continue;
+
+		pid = atoi(dentp->d_name);
+		/* attempt to grab process */
+		if ((ph = Pgrab(pid, 0, &tmp)) != NULL) {
+			if (Psetflags(ph, PR_RLC) == 0) {
+				if (Pcreate_agent(ph) == 0) {
+					(void) closedir(dirp);
+					return (ph);
+				}
+			}
+			Prelease(ph, 0);
+		}
+	}
+
+	(void) closedir(dirp);
+	return (NULL);
+}
+
+static uint64_t
+get_zone_cap()
+{
+	rctlblk_t *rblk;
+	uint64_t mcap;
+	struct ps_prochandle *ph;
+
+	if ((rblk = (rctlblk_t *)malloc(rctlblk_size())) == NULL)
+		return (UINT64_MAX);
+
+	if ((ph = grab_zone_proc()) == NULL) {
+		free(rblk);
+		return (UINT64_MAX);
+	}
+
+	if (pr_getrctl(ph, "zone.max-physical-memory", NULL, rblk,
+	    RCTL_FIRST)) {
+		Pdestroy_agent(ph);
+		Prelease(ph, 0);
+		free(rblk);
+		return (UINT64_MAX);
+	}
+
+	Pdestroy_agent(ph);
+	Prelease(ph, 0);
+
+	mcap = rctlblk_get_value(rblk);
+	free(rblk);
+	return (mcap);
+}
+
+/*
+ * check_suspend is invoked at the beginning of every pass through the process
+ * list or after we've paged out enough so that we think the excess is under
+ * the cap.  The purpose is to periodically check the zone's rss and return
+ * the excess when the zone is over the cap.  The rest of the time this
+ * function will sleep, periodically waking up to check the current rss.
+ *
+ * Depending on the percentage of penetration of the zone's rss into the
+ * cap we sleep for longer or shorter amounts. This reduces the impact of this
+ * work on the system, which is important considering that each zone will be
+ * monitoring its rss.
+ */
+static int64_t
+check_suspend()
+{
+	static hrtime_t last_cap_read = 0;
+	static uint64_t addon;
+	static uint64_t lo_thresh;	/* Thresholds for how long to  sleep */
+	static uint64_t hi_thresh;	/* when under the cap (80% & 90%). */
+	static uint64_t prev_zone_rss = 0;
+	static uint32_t pfdelay = 0;	/* usec page fault delay when over */
+
+	/* Wait a second to give the async pageout a chance to catch up. */
+	(void) sleep_shutdown(1);
+
+	while (!shutting_down) {
+		int64_t new_excess;
+		int sleep_time;
+		hrtime_t now;
+		struct stat st;
+		uint64_t zone_rss;		/* total RSS(KB) */
+
+		/*
+		 * Check if the debug log files exists and enable or disable
+		 * debug.
+		 */
+		if (debug_log_fp == NULL) {
+			if (stat(debug_log, &st) == 0)
+				debug_log_fp = fopen(debug_log, "w");
+		} else {
+			if (stat(debug_log, &st) == -1) {
+				(void) fclose(debug_log_fp);
+				debug_log_fp = NULL;
+			}
+		}
+
+		/*
+		 * If the CAP_REFRESH interval has passed, re-get the current
+		 * cap in case it has been dynamically updated.
+		 */
+		now = gethrtime();
+		if (now - last_cap_read > CAP_REFRESH) {
+			uint64_t mcap;
+
+			last_cap_read = now;
+
+			mcap = get_zone_cap();
+			if (mcap != 0 && mcap != UINT64_MAX)
+				zone_rss_cap = ROUNDUP(mcap, 1024) / 1024;
+			else
+				zone_rss_cap = UINT64_MAX;
+
+			lo_thresh = (uint64_t)(zone_rss_cap * .8);
+			hi_thresh = (uint64_t)(zone_rss_cap * .9);
+			addon = (uint64_t)(zone_rss_cap * 0.05);
+
+			/*
+			 * We allow the memory cap tunables to be changed on
+			 * the fly.
+			 */
+			get_mcap_tunables();
+
+			debug("%s: %s\n", TUNE_CMD, over_cmd);
+			debug("%s: %d\n", TUNE_NVMU, skip_vmusage);
+			debug("%s: %d\n", TUNE_NPAGE, skip_pageout);
+			debug("%s: %d\n", TUNE_NPFTHROT, skip_pf_throttle);
+			debug("current cap %lluKB lo %lluKB hi %lluKB\n",
+			    zone_rss_cap, lo_thresh, hi_thresh);
+		}
+
+		/* No cap, nothing to do. */
+		if (zone_rss_cap == 0 || zone_rss_cap == UINT64_MAX) {
+			debug("no cap, sleep 120 seconds\n");
+			(void) sleep_shutdown(120);
+			continue;
+		}
+
+		zone_rss = get_mem_info();
+
+		/* calculate excess */
+		new_excess = zone_rss - zone_rss_cap;
+
+		debug("rss %lluKB, cap %lluKB, excess %lldKB\n",
+		    zone_rss, zone_rss_cap, new_excess);
+
+		/*
+		 * If necessary, updates stats.
+		 */
+
+		/*
+		 * If it looks like we did some paging out since last over the
+		 * cap then update the kstat so we can approximate how much was
+		 * paged out.
+		 */
+		if (prev_zone_rss > zone_rss_cap && zone_rss < prev_zone_rss) {
+			uint64_t diff;
+
+			/* assume diff is num bytes we paged out */
+			diff = (prev_zone_rss - zone_rss) * 1024;
+
+			(void) zone_setattr(zid, ZONE_ATTR_PMCAP_PAGEOUT,
+			    &diff, 0);
+		}
+		prev_zone_rss = zone_rss;
+
+		if (new_excess > 0) {
+			uint64_t n = 1;
+
+			/* Increment "nover" kstat. */
+			(void) zone_setattr(zid, ZONE_ATTR_PMCAP_NOVER, &n, 0);
+
+			if (!skip_pf_throttle) {
+				/*
+				 * Tell the kernel to start throttling page
+				 * faults by some number of usecs to help us
+				 * catch up. If we are persistently over the
+				 * cap the delay ramps up to a max of 2000usecs.
+				 * Note that for delays less than 1 tick
+				 * (i.e. all of these) we busy-wait in as_fault.
+				 *	delay	faults/sec
+				 *	 125	8000
+				 *	 250	4000
+				 *	 500	2000
+				 *	1000	1000
+				 *	2000	 500
+				 */
+				if (pfdelay == 0)
+					pfdelay = 125;
+				else if (pfdelay < 2000)
+					pfdelay *= 2;
+
+				(void) zone_setattr(zid, ZONE_ATTR_PG_FLT_DELAY,
+				    &pfdelay, 0);
+			}
+
+			/*
+			 * Once we go over the cap, then we want to
+			 * page out a little extra instead of stopping
+			 * right at the cap. To do this we add 5% to
+			 * the excess so that pageout_proces will work
+			 * a little longer before stopping.
+			 */
+			return ((int64_t)(new_excess + addon));
+		}
+
+		/*
+		 * At this point we are under the cap.
+		 *
+		 * Tell the kernel to stop throttling page faults.
+		 *
+		 * Scale the amount of time we sleep before rechecking the
+		 * zone's memory usage.  Also, scale the accpetable age of
+		 * cached results from vm_getusage.  We do this based on the
+		 * penetration into the capped limit.
+		 */
+		if (pfdelay > 0) {
+			pfdelay = 0;
+			(void) zone_setattr(zid, ZONE_ATTR_PG_FLT_DELAY,
+			    &pfdelay, 0);
+		}
+
+		if (zone_rss <= lo_thresh) {
+			sleep_time = 120;
+		} else if (zone_rss <= hi_thresh) {
+			sleep_time = 60;
+		} else {
+			sleep_time = 30;
+		}
+
+		debug("sleep %d seconds\n", sleep_time);
+		(void) sleep_shutdown(sleep_time);
+	}
+
+	/* Shutting down, tell the kernel so it doesn't throttle */
+	if (pfdelay > 0) {
+		pfdelay = 0;
+		(void) zone_setattr(zid, ZONE_ATTR_PG_FLT_DELAY, &pfdelay, 0);
+	}
+
+	return (0);
+}
+
+static void
+get_mcap_tunables()
+{
+	zone_dochandle_t handle;
+	struct zone_attrtab attr;
+
+	over_cmd[0] = '\0';
+	if ((handle = zonecfg_init_handle()) == NULL)
+		return;
+
+	if (zonecfg_get_handle(zone_name, handle) != Z_OK)
+		goto done;
+
+	/* Reset to defaults in case rebooting and settings have changed */
+	over_cmd[0] = '\0';
+	skip_vmusage = B_FALSE;
+	skip_pageout = B_FALSE;
+	skip_pf_throttle = B_FALSE;
+
+	if (zonecfg_setattrent(handle) != Z_OK)
+		goto done;
+	while (zonecfg_getattrent(handle, &attr) == Z_OK) {
+		if (strcmp(TUNE_CMD, attr.zone_attr_name) == 0) {
+			(void) strlcpy(over_cmd, attr.zone_attr_value,
+			    sizeof (over_cmd));
+		} else if (strcmp(TUNE_NVMU, attr.zone_attr_name) == 0) {
+			if (strcmp("true", attr.zone_attr_value) == 0)
+				skip_vmusage = B_TRUE;
+		} else if (strcmp(TUNE_NPAGE, attr.zone_attr_name) == 0) {
+			if (strcmp("true", attr.zone_attr_value) == 0)
+				skip_pageout = B_TRUE;
+		} else if (strcmp(TUNE_NPFTHROT, attr.zone_attr_name) == 0) {
+			if (strcmp("true", attr.zone_attr_value) == 0)
+				skip_pf_throttle = B_TRUE;
+		}
+	}
+	(void) zonecfg_endattrent(handle);
+
+done:
+	zonecfg_fini_handle(handle);
+}
+
+/* ARGSUSED */
+static int
+chk_proc_fs(void *data, const char *spec, const char *dir,
+    const char *fstype, const char *opt)
+{
+	if (fstype != NULL && strcmp(fstype, "proc") == 0)
+		*((boolean_t *)data) = B_TRUE;
+
+	return (0);
+}
+
+static boolean_t
+has_proc()
+{
+	brand_handle_t bh;
+	boolean_t fnd = B_FALSE;
+
+	if ((bh = brand_open(brand_name)) != NULL) {
+		(void) brand_platform_iter_mounts(bh, chk_proc_fs, &fnd);
+	}
+
+	brand_close(bh);
+	return (fnd);
+}
+
+/*
+ * We run this loop for brands with no /proc to simply update the RSS, using
+ * the cheap GZ /proc data, every 5 minutes.
+ */
+static void
+no_procfs()
+{
+	DIR			*pdir = NULL;
+	struct dirent		*dent;
+	uint64_t		zone_rss_bytes;
+
+	(void) sleep_shutdown(30);
+	while (!shutting_down) {
+		/*
+		 * Just do the fast, cheap RSS calculation using the rss value
+		 * in psinfo_t.  Because that's per-process, it can lead to
+		 * double counting some memory and overestimating how much is
+		 * being used. Since there is no /proc in the zone, we use the
+		 * GZ /proc and check for the correct zone.
+		 */
+		if ((pdir = opendir("/proc")) == NULL)
+			return;
+
+		fast_rss = 0;
+		while (!shutting_down && (dent = readdir(pdir)) != NULL) {
+			pid_t		pid;
+			int		psfd;
+			int64_t		rss;
+			char		pathbuf[MAXPATHLEN];
+			psinfo_t	psinfo;
+
+			if (strcmp(".", dent->d_name) == 0 ||
+			    strcmp("..", dent->d_name) == 0)
+				continue;
+
+			pid = atoi(dent->d_name);
+			if (pid == 0 || pid == 1)
+				continue;
+
+			(void) snprintf(pathbuf, sizeof (pathbuf),
+			    "/proc/%d/psinfo", pid);
+
+			rss = 0;
+			if ((psfd = open(pathbuf, O_RDONLY, 0000)) != -1) {
+				if (pread(psfd, &psinfo, sizeof (psinfo), 0) ==
+				    sizeof (psinfo)) {
+					if (psinfo.pr_zoneid == zid)
+						rss = (int64_t)psinfo.pr_rssize;
+				}
+
+				(void) close(psfd);
+			}
+
+			fast_rss += rss;
+		}
+
+		(void) closedir(pdir);
+
+		if (shutting_down)
+			return;
+
+		zone_rss_bytes = fast_rss * 1024;
+		/* Use the zone's approx. RSS in the kernel */
+		(void) zone_setattr(zid, ZONE_ATTR_RSS, &zone_rss_bytes, 0);
+
+		(void) sleep_shutdown(300);
+	}
+}
+
+/*
+ * Thread that checks zone's memory usage and when over the cap, goes through
+ * the zone's process list trying to pageout processes to get under the cap.
+ */
+static void
+mcap_zone()
+{
+	DIR *pdir = NULL;
+	int64_t excess;
+
+	debug("thread startup\n");
+
+	get_mcap_tunables();
+
+	/*
+	 * If the zone has no /proc filesystem, we can't use the fast algorithm
+	 * to check RSS or pageout any processes. All we can do is periodically
+	 * update it's RSS kstat using the expensive sycall.
+	 */
+	if (!has_proc()) {
+		no_procfs();
+		debug("thread shutdown\n");
+		return;
+	}
+
+	/*
+	 * When first starting it is likely lots of other zones are starting
+	 * too because the system is booting.  Since we just started the zone
+	 * we're not worried about being over the cap right away, so we let
+	 * things settle a bit and tolerate some older data here to minimize
+	 * the load on the system.
+	 */
+	(void) sleep_shutdown(15); /* wait 15 secs. so the zone can get going */
+
+	/* Wait until zone's /proc is mounted */
+	while (!shutting_down) {
+		struct stat st;
+
+		if (stat(zoneproc, &st) == 0 &&
+		    strcmp(st.st_fstype, "proc") == 0)
+			break;
+		sleep_shutdown(5);
+	}
+
+	/* Open zone's /proc and walk entries. */
+	while (!shutting_down) {
+		if ((pdir = opendir(zoneproc)) != NULL)
+			break;
+		sleep_shutdown(5);
+	}
+
+	while (!shutting_down) {
+		struct dirent *dirent;
+
+		/* Wait until we've gone over the cap. */
+		excess = check_suspend();
+
+		debug("starting to scan, excess %lldk\n", (long long)excess);
+
+		if (over_cmd[0] != '\0') {
+			uint64_t zone_rss;	/* total RSS(KB) */
+
+			debug("run phys_mcap_cmd: %s\n", over_cmd);
+			run_over_cmd();
+
+			zone_rss = get_mem_info();
+			excess = zone_rss - zone_rss_cap;
+			debug("rss %lluKB, cap %lluKB, excess %lldKB\n",
+			    zone_rss, zone_rss_cap, excess);
+			if (excess <= 0)
+				continue;
+		}
+
+		while (!shutting_down && (dirent = readdir(pdir)) != NULL) {
+			pid_t pid;
+
+			if (strcmp(".", dirent->d_name) == 0 ||
+			    strcmp("..", dirent->d_name) == 0)
+				continue;
+
+			pid = atoi(dirent->d_name);
+			if (pid == 0 || pid == 1)
+				continue;
+
+			if (skip_pageout)
+				(void) sleep_shutdown(2);
+			else
+				excess = pageout_process(pid, excess);
+
+			if (excess <= 0) {
+				debug("apparently under; excess %lld\n",
+				    (long long)excess);
+				/* Double check the current excess */
+				excess = check_suspend();
+			}
+		}
+
+		debug("process pass done; excess %lld\n", (long long)excess);
+		rewinddir(pdir);
+
+		if (skip_pageout)
+			(void) sleep_shutdown(120);
+	}
+
+	if (pdir != NULL)
+		(void) closedir(pdir);
+	debug("thread shutdown\n");
+}
+
+void
+create_mcap_thread(zlog_t *zlogp, zoneid_t id)
+{
+	int		res;
+
+	shutting_down = 0;
+	zid = id;
+
+	/* all but the lx brand currently use /proc */
+	if (strcmp(brand_name, "lx") == 0) {
+		(void) snprintf(zoneproc, sizeof (zoneproc),
+		    "%s/root/native/proc", zonepath);
+	} else {
+		(void) snprintf(zoneproc, sizeof (zoneproc), "%s/root/proc",
+		    zonepath);
+	}
+
+	(void) snprintf(debug_log, sizeof (debug_log), "%s/mcap_debug.log",
+	    zonepath);
+
+	res = thr_create(NULL, NULL, (void *(*)(void *))mcap_zone, NULL, NULL,
+	    &mcap_tid);
+	if (res != 0) {
+		zerror(zlogp, B_FALSE, "error %d creating memory cap thread",
+		    res);
+		mcap_tid = 0;
+	}
+}
+
+void
+destroy_mcap_thread()
+{
+	if (mcap_tid != 0) {
+		shutting_down = 1;
+		(void) cond_signal(&shutdown_cv);
+		(void) thr_join(mcap_tid, NULL, NULL);
+		mcap_tid = 0;
+	}
+}
--- old/usr/src/cmd/zoneadmd/vplat.c	Wed Oct  5 00:31:42 2016
+++ new/usr/src/cmd/zoneadmd/vplat.c	Wed Oct  5 00:31:42 2016
@@ -21,8 +21,8 @@
 
 /*
  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2013, Joyent Inc. All rights reserved.
  * Copyright (c) 2015 by Delphix. All rights reserved.
+ * Copyright 2016, Joyent Inc.
  */
 
 /*
@@ -136,8 +136,6 @@
 #define	DFSTYPES	"/etc/dfs/fstypes"
 #define	MAXTNZLEN	2048
 
-#define	ALT_MOUNT(mount_cmd) 	((mount_cmd) != Z_MNT_BOOT)
-
 /* a reasonable estimate for the number of lwps per process */
 #define	LWPS_PER_PROCESS	10
 
@@ -161,6 +159,19 @@
 
 static const char *DFLT_FS_ALLOWED = "hsfs,smbfs,nfs,nfs3,nfs4,nfsdyn";
 
+typedef struct zone_proj_rctl_map {
+	char *zpr_zone_rctl;
+	char *zpr_project_rctl;
+} zone_proj_rctl_map_t;
+
+static zone_proj_rctl_map_t zone_proj_rctl_map[] = {
+	{"zone.max-msg-ids",	"project.max-msg-ids"},
+	{"zone.max-sem-ids",	"project.max-sem-ids"},
+	{"zone.max-shm-ids",	"project.max-shm-ids"},
+	{"zone.max-shm-memory",	"project.max-shm-memory"},
+	{NULL,			NULL}
+};
+
 /* from libsocket, not in any header file */
 extern int getnetmaskbyaddr(struct in_addr, struct in_addr *);
 
@@ -1150,12 +1161,12 @@
 		zerror(zlogp, B_FALSE, "can't initialize zone handle");
 		goto cleanup;
 	}
-	if (err = zonecfg_get_handle(zone_name, handle)) {
+	if ((err = zonecfg_get_handle(zone_name, handle)) != 0) {
 		zerror(zlogp, B_FALSE, "can't get handle for zone "
 		    "%s: %s", zone_name, zonecfg_strerror(err));
 		goto cleanup;
 	}
-	if (err = zonecfg_setdevent(handle)) {
+	if ((err = zonecfg_setdevent(handle)) != 0) {
 		zerror(zlogp, B_FALSE, "%s: %s", zone_name,
 		    zonecfg_strerror(err));
 		goto cleanup;
@@ -1671,7 +1682,6 @@
 mount_filesystems(zlog_t *zlogp, zone_mnt_t mount_cmd)
 {
 	char rootpath[MAXPATHLEN];
-	char zonepath[MAXPATHLEN];
 	char brand[MAXNAMELEN];
 	char luroot[MAXPATHLEN];
 	int i, num_fs = 0;
@@ -1690,11 +1700,6 @@
 		goto bad;
 	}
 
-	if (zone_get_zonepath(zone_name, zonepath, sizeof (zonepath)) != Z_OK) {
-		zerror(zlogp, B_TRUE, "unable to determine zone path");
-		goto bad;
-	}
-
 	if (zone_get_rootpath(zone_name, rootpath, sizeof (rootpath)) != Z_OK) {
 		zerror(zlogp, B_TRUE, "unable to determine zone root");
 		goto bad;
@@ -1795,23 +1800,40 @@
 	qsort(fs_ptr, num_fs, sizeof (*fs_ptr), fs_compare);
 
 	for (i = 0; i < num_fs; i++) {
-		if (ALT_MOUNT(mount_cmd) &&
-		    strcmp(fs_ptr[i].zone_fs_dir, "/dev") == 0) {
-			size_t slen = strlen(rootpath) - 2;
+		if (ALT_MOUNT(mount_cmd)) {
+			if (strcmp(fs_ptr[i].zone_fs_dir, "/dev") == 0) {
+				size_t slen = strlen(rootpath) - 2;
 
-			/*
-			 * By default we'll try to mount /dev as /a/dev
-			 * but /dev is special and always goes at the top
-			 * so strip the trailing '/a' from the rootpath.
-			 */
-			assert(strcmp(&rootpath[slen], "/a") == 0);
-			rootpath[slen] = '\0';
-			if (mount_one(zlogp, &fs_ptr[i], rootpath, mount_cmd)
-			    != 0)
-				goto bad;
-			rootpath[slen] = '/';
-			continue;
+				/*
+				 * By default we'll try to mount /dev
+				 * as /a/dev but /dev is special and
+				 * always goes at the top so strip the
+				 * trailing '/a' from the rootpath.
+				 */
+				assert(strcmp(&rootpath[slen], "/a") == 0);
+				rootpath[slen] = '\0';
+				if (mount_one(zlogp, &fs_ptr[i], rootpath,
+				    mount_cmd) != 0)
+					goto bad;
+				rootpath[slen] = '/';
+				continue;
+			} else if (strcmp(brand_name, default_brand) != 0) {
+				/*
+				 * If mounting non-native brand, skip
+				 * mounting global mounts and
+				 * filesystem entries since they are
+				 * only needed for native pkg upgrade
+				 * tools.
+				 *
+				 * The only exception right now is
+				 * /dev (handled above), which is
+				 * needed in the luroot in order to
+				 * zlogin -S into the zone.
+				 */
+				continue;
+			}
 		}
+
 		if (mount_one(zlogp, &fs_ptr[i], rootpath, mount_cmd) != 0)
 			goto bad;
 	}
@@ -2888,6 +2910,25 @@
 }
 
 /*
+ * For IP networking, we need to use the illumos-native device tree.  For most
+ * zones, this is $ZONEROOT/dev.  For LX ones, it's $ZONEROOT/native/dev.
+ * Return the appropriate post-$ZONEROOT path.
+ */
+static char *
+get_brand_dev(void)
+{
+	static char *lxpath = "/native/dev";
+	/* Cheesy hard-coding of strlen("/native") */
+	char *default_path = lxpath + 7;
+
+	/* LX zones are the exception... */
+	if (strcmp(brand_name, "lx") == 0)
+		return (lxpath);
+
+	return (default_path);
+}
+
+/*
  * Add the kernel access control information for the interface names.
  * If anything goes wrong, we log a general error message, attempt to tear down
  * whatever we set up, and return an error.
@@ -2933,7 +2974,7 @@
 				return (-1);
 			}
 			(void) snprintf(path, sizeof (path), "%s%s", rootpath,
-			    "/dev");
+			    get_brand_dev());
 			if (di_prof_init(path, &prof) != 0) {
 				(void) zonecfg_endnwifent(handle);
 				zonecfg_fini_handle(handle);
@@ -3317,6 +3358,19 @@
 	return (error);
 }
 
+static char *
+zone_proj_rctl(const char *name)
+{
+	int i;
+
+	for (i = 0; zone_proj_rctl_map[i].zpr_zone_rctl != NULL; i++) {
+		if (strcmp(name, zone_proj_rctl_map[i].zpr_zone_rctl) == 0) {
+			return (zone_proj_rctl_map[i].zpr_project_rctl);
+		}
+	}
+	return (NULL);
+}
+
 static int
 get_rctls(zlog_t *zlogp, char **bufp, size_t *bufsizep)
 {
@@ -3331,6 +3385,7 @@
 	rctlblk_t *rctlblk = NULL;
 	uint64_t maxlwps;
 	uint64_t maxprocs;
+	int rproc, rlwp;
 
 	*bufp = NULL;
 	*bufsizep = 0;
@@ -3353,19 +3408,28 @@
 
 	/*
 	 * Allow the administrator to control both the maximum number of
-	 * process table slots and the maximum number of lwps with just the
-	 * max-processes property.  If only the max-processes property is set,
-	 * we add a max-lwps property with a limit derived from max-processes.
+	 * process table slots, and the maximum number of lwps, with a single
+	 * max-processes or max-lwps property. If only the max-processes
+	 * property is set, we add a max-lwps property with a limit derived
+	 * from max-processes. If only the max-lwps property is set, we add a
+	 * max-processes property with the same limit as max-lwps.
 	 */
-	if (zonecfg_get_aliased_rctl(handle, ALIAS_MAXPROCS, &maxprocs)
-	    == Z_OK &&
-	    zonecfg_get_aliased_rctl(handle, ALIAS_MAXLWPS, &maxlwps)
-	    == Z_NO_ENTRY) {
-		if (zonecfg_set_aliased_rctl(handle, ALIAS_MAXLWPS,
+	rproc = zonecfg_get_aliased_rctl(snap_hndl, ALIAS_MAXPROCS, &maxprocs);
+	rlwp = zonecfg_get_aliased_rctl(snap_hndl, ALIAS_MAXLWPS, &maxlwps);
+	if (rproc == Z_OK && rlwp == Z_NO_ENTRY) {
+		if (zonecfg_set_aliased_rctl(snap_hndl, ALIAS_MAXLWPS,
 		    maxprocs * LWPS_PER_PROCESS) != Z_OK) {
 			zerror(zlogp, B_FALSE, "unable to set max-lwps alias");
 			goto out;
 		}
+	} else if (rlwp == Z_OK && rproc == Z_NO_ENTRY) {
+		/* no scaling for max-proc value */
+		if (zonecfg_set_aliased_rctl(snap_hndl, ALIAS_MAXPROCS,
+		    maxlwps) != Z_OK) {
+			zerror(zlogp, B_FALSE,
+			    "unable to set max-processes alias");
+			goto out;
+		}
 	}
 
 	if (zonecfg_setrctlent(handle) != Z_OK) {
@@ -3381,6 +3445,7 @@
 		struct zone_rctlvaltab *rctlval;
 		uint_t i, count;
 		const char *name = rctltab.zone_rctl_name;
+		char *proj_nm;
 
 		/* zoneadm should have already warned about unknown rctls. */
 		if (!zonecfg_is_rctl(name)) {
@@ -3447,6 +3512,26 @@
 		}
 		zonecfg_free_rctl_value_list(rctltab.zone_rctl_valptr);
 		rctltab.zone_rctl_valptr = NULL;
+
+		/*
+		 * With no action on our part we will start zsched with the
+		 * project rctl values for our (zoneadmd) current project. For
+		 * brands running a variant of Illumos, that's not a problem
+		 * since they will setup their own projects, but for a
+		 * non-native brand like lx, where there are no projects, we
+		 * want to start things up with the same project rctls as the
+		 * corresponding zone rctls, since nothing within the zone will
+		 * ever change the project rctls.
+		 */
+		if ((proj_nm = zone_proj_rctl(name)) != NULL) {
+			if (nvlist_add_nvlist_array(nvl, proj_nm, nvlv, count)
+			    != 0) {
+				zerror(zlogp, B_FALSE,
+				    "nvlist_add_nvlist_arrays failed");
+				goto out;
+			}
+		}
+
 		if (nvlist_add_nvlist_array(nvl, (char *)name, nvlv, count)
 		    != 0) {
 			zerror(zlogp, B_FALSE, "%s failed",
@@ -3708,17 +3793,11 @@
 	zfs_handle_t	*zhp;
 	libzfs_handle_t	*hdl;
 	m_label_t	ds_sl;
-	char		zonepath[MAXPATHLEN];
 	char		ds_hexsl[MAXNAMELEN];
 
 	if (!is_system_labeled())
 		return (0);
 
-	if (zone_get_zonepath(zone_name, zonepath, sizeof (zonepath)) != Z_OK) {
-		zerror(zlogp, B_TRUE, "unable to determine zone path");
-		return (-1);
-	}
-
 	if (!is_zonepath_zfs(zonepath))
 		return (0);
 
@@ -4389,8 +4468,7 @@
 }
 
 /*
- * Set memory cap and pool info for the zone's resource management
- * configuration.
+ * Set pool info for the zone's resource management configuration.
  */
 static int
 setup_zone_rm(zlog_t *zlogp, char *zone_name, zoneid_t zoneid)
@@ -4397,7 +4475,6 @@
 {
 	int res;
 	uint64_t tmp;
-	struct zone_mcaptab mcap;
 	char sched[MAXNAMELEN];
 	zone_dochandle_t handle = NULL;
 	char pool_err[128];
@@ -4413,29 +4490,6 @@
 		return (res);
 	}
 
-	/*
-	 * If a memory cap is configured, set the cap in the kernel using
-	 * zone_setattr() and make sure the rcapd SMF service is enabled.
-	 */
-	if (zonecfg_getmcapent(handle, &mcap) == Z_OK) {
-		uint64_t num;
-		char smf_err[128];
-
-		num = (uint64_t)strtoull(mcap.zone_physmem_cap, NULL, 10);
-		if (zone_setattr(zoneid, ZONE_ATTR_PHYS_MCAP, &num, 0) == -1) {
-			zerror(zlogp, B_TRUE, "could not set zone memory cap");
-			zonecfg_fini_handle(handle);
-			return (Z_INVAL);
-		}
-
-		if (zonecfg_enable_rcapd(smf_err, sizeof (smf_err)) != Z_OK) {
-			zerror(zlogp, B_FALSE, "enabling system/rcap service "
-			    "failed: %s", smf_err);
-			zonecfg_fini_handle(handle);
-			return (Z_INVAL);
-		}
-	}
-
 	/* Get the scheduling class set in the zone configuration. */
 	if (zonecfg_get_sched_class(handle, sched, sizeof (sched)) == Z_OK &&
 	    strlen(sched) > 0) {
@@ -4444,7 +4498,34 @@
 			zerror(zlogp, B_TRUE, "WARNING: unable to set the "
 			    "default scheduling class");
 
-	} else if (zonecfg_get_aliased_rctl(handle, ALIAS_SHARES, &tmp)
+		if (strcmp(sched, "FX") == 0) {
+			/*
+			 * When FX is specified then by default all processes
+			 * will start at the lowest priority level (0) and
+			 * stay there. We support an optional attr which
+			 * indicates that all the processes should be "high
+			 * priority". We set this on the zone so that starting
+			 * init will set the priority high.
+			 */
+			struct zone_attrtab a;
+
+			bzero(&a, sizeof (a));
+			(void) strlcpy(a.zone_attr_name, "fixed-hi-prio",
+			    sizeof (a.zone_attr_name));
+
+			if (zonecfg_lookup_attr(snap_hndl, &a) == Z_OK &&
+			    strcmp(a.zone_attr_value, "true") == 0) {
+				boolean_t hi = B_TRUE;
+
+				if (zone_setattr(zoneid,
+				    ZONE_ATTR_SCHED_FIXEDHI, (void *)hi,
+				    sizeof (hi)) == -1)
+					zerror(zlogp, B_TRUE, "WARNING: unable "
+					    "to set high priority");
+			}
+		}
+
+	} else if (zonecfg_get_aliased_rctl(snap_hndl, ALIAS_SHARES, &tmp)
 	    == Z_OK) {
 		/*
 		 * If the zone has the zone.cpu-shares rctl set then we want to
@@ -4991,7 +5072,7 @@
 int
 vplat_bringup(zlog_t *zlogp, zone_mnt_t mount_cmd, zoneid_t zoneid)
 {
-	char zonepath[MAXPATHLEN];
+	char zpath[MAXPATHLEN];
 
 	if (mount_cmd == Z_MNT_BOOT && validate_datasets(zlogp) != 0) {
 		lofs_discard_mnttab();
@@ -5002,15 +5083,11 @@
 	 * Before we try to mount filesystems we need to create the
 	 * attribute backing store for /dev
 	 */
-	if (zone_get_zonepath(zone_name, zonepath, sizeof (zonepath)) != Z_OK) {
-		lofs_discard_mnttab();
-		return (-1);
-	}
-	resolve_lofs(zlogp, zonepath, sizeof (zonepath));
+	(void) strlcpy(zpath, zonepath, sizeof (zpath));
+	resolve_lofs(zlogp, zpath, sizeof (zpath));
 
 	/* Make /dev directory owned by root, grouped sys */
-	if (make_one_dir(zlogp, zonepath, "/dev", DEFAULT_DIR_MODE,
-	    0, 3) != 0) {
+	if (make_one_dir(zlogp, zpath, "/dev", DEFAULT_DIR_MODE, 0, 3) != 0) {
 		lofs_discard_mnttab();
 		return (-1);
 	}
@@ -5126,7 +5203,6 @@
 	zoneid_t zoneid;
 	int res;
 	char pool_err[128];
-	char zpath[MAXPATHLEN];
 	char cmdbuf[MAXPATHLEN];
 	brand_handle_t bh = NULL;
 	dladm_status_t status;
@@ -5182,12 +5258,6 @@
 		goto error;
 	}
 
-	/* Get the zonepath of this zone */
-	if (zone_get_zonepath(zone_name, zpath, sizeof (zpath)) != Z_OK) {
-		zerror(zlogp, B_FALSE, "unable to determine zone path");
-		goto error;
-	}
-
 	/* Get a handle to the brand info for this zone */
 	if ((bh = brand_open(brand_name)) == NULL) {
 		zerror(zlogp, B_FALSE, "unable to determine zone brand");
@@ -5198,7 +5268,7 @@
 	 * brand a chance to cleanup any custom configuration.
 	 */
 	(void) strcpy(cmdbuf, EXEC_PREFIX);
-	if (brand_get_halt(bh, zone_name, zpath, cmdbuf + EXEC_LEN,
+	if (brand_get_halt(bh, zone_name, zonepath, cmdbuf + EXEC_LEN,
 	    sizeof (cmdbuf) - EXEC_LEN) < 0) {
 		brand_close(bh);
 		zerror(zlogp, B_FALSE, "unable to determine branded zone's "
--- old/usr/src/cmd/zoneadmd/zcons.c	Wed Oct  5 00:31:42 2016
+++ new/usr/src/cmd/zoneadmd/zcons.c	Wed Oct  5 00:31:42 2016
@@ -22,7 +22,7 @@
 /*
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
- * Copyright 2012 Joyent, Inc.  All rights reserved.
+ * Copyright 2015 Joyent, Inc.
  * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
  */
 
@@ -118,9 +118,10 @@
 
 #define	CONSOLE_SOCKPATH	ZONES_TMPDIR "/%s.console_sock"
 
+#define	ZCONS_RETRY		10
+
 static int	serverfd = -1;	/* console server unix domain socket fd */
 char boot_args[BOOTARGS_MAX];
-char bad_boot_arg[BOOTARGS_MAX];
 
 /*
  * The eventstream is a simple one-directional flow of messages from the
@@ -130,8 +131,11 @@
  */
 static int eventstream[2];
 
+/* flag used to cope with race creating master zcons devlink */
+static boolean_t master_zcons_failed = B_FALSE;
+/* flag to track if we've seen a state change when there is no master zcons */
+static boolean_t state_changed = B_FALSE;
 
-
 int
 eventstream_init()
 {
@@ -408,6 +412,11 @@
 	 * Open the master side of the console and issue the ZC_HOLDSLAVE ioctl,
 	 * which will cause the master to retain a reference to the slave.
 	 * This prevents ttymon from blowing through the slave's STREAMS anchor.
+	 *
+	 * In very rare cases the open returns ENOENT if devfs doesn't have
+	 * everything setup yet due to heavy zone startup load. Wait for
+	 * 1 sec. and retry a few times. Even if we can't setup the zone's
+	 * console, we still go ahead and boot the zone.
 	 */
 	(void) snprintf(conspath, sizeof (conspath), "/dev/zcons/%s/%s",
 	    zone_name, ZCONS_MASTER_NAME);
@@ -414,37 +423,45 @@
 	if ((masterfd = open(conspath, O_RDWR | O_NOCTTY)) == -1) {
 		zerror(zlogp, B_TRUE, "ERROR: could not open master side of "
 		    "zone console for %s to acquire slave handle", zone_name);
-		goto error;
+		master_zcons_failed = B_TRUE;
 	}
 	(void) snprintf(conspath, sizeof (conspath), "/dev/zcons/%s/%s",
 	    zone_name, ZCONS_SLAVE_NAME);
-	if ((slavefd = open(conspath, O_RDWR | O_NOCTTY)) == -1) {
+	for (i = 0; i < ZCONS_RETRY; i++) {
+		slavefd = open(conspath, O_RDWR | O_NOCTTY);
+		if (slavefd >= 0 || errno != ENOENT)
+			break;
+		(void) sleep(1);
+	}
+	if (slavefd == -1)
 		zerror(zlogp, B_TRUE, "ERROR: could not open slave side of zone"
 		    " console for %s to acquire slave handle", zone_name);
-		(void) close(masterfd);
-		goto error;
-	}
+
 	/*
 	 * This ioctl can occasionally return ENXIO if devfs doesn't have
 	 * everything plumbed up yet due to heavy zone startup load. Wait for
 	 * 1 sec. and retry a few times before we fail to boot the zone.
 	 */
-	for (i = 0; i < 5; i++) {
-		if (ioctl(masterfd, ZC_HOLDSLAVE, (caddr_t)(intptr_t)slavefd)
-		    == 0) {
-			rv = 0;
-			break;
-		} else if (errno != ENXIO) {
-			break;
+	if (masterfd != -1 && slavefd != -1) {
+		for (i = 0; i < ZCONS_RETRY; i++) {
+			if (ioctl(masterfd, ZC_HOLDSLAVE,
+			    (caddr_t)(intptr_t)slavefd) == 0) {
+				rv = 0;
+				break;
+			} else if (errno != ENXIO) {
+				break;
+			}
+			(void) sleep(1);
 		}
-		(void) sleep(1);
+		if (rv != 0)
+			zerror(zlogp, B_TRUE, "ERROR: error while acquiring "
+			    "slave handle of zone console for %s", zone_name);
 	}
-	if (rv != 0)
-		zerror(zlogp, B_TRUE, "ERROR: error while acquiring slave "
-		    "handle of zone console for %s", zone_name);
 
-	(void) close(slavefd);
-	(void) close(masterfd);
+	if (slavefd != -1)
+		(void) close(slavefd);
+	if (masterfd != -1)
+		(void) close(masterfd);
 
 error:
 	if (ddef_hdl)
@@ -517,6 +534,7 @@
 	size_t buflen = sizeof (buf);
 	char c = '\0';
 	int i = 0, r;
+	ucred_t *cred = NULL;
 
 	/* "eat up the ident string" case, for simplicity */
 	if (pid == NULL) {
@@ -550,9 +568,16 @@
 				break;
 	}
 
+	if (getpeerucred(clifd, &cred) == 0) {
+		*pid = ucred_getpid((const ucred_t *)cred);
+		ucred_free(cred);
+	} else {
+		return (-1);
+	}
+
 	/*
 	 * Parse buffer for message of the form:
-	 * IDENT <pid> <locale> <disconnect flag>
+	 * IDENT <locale> <disconnect flag>
 	 */
 	bufp = buf;
 	if (strncmp(bufp, "IDENT ", 6) != 0)
@@ -559,9 +584,6 @@
 		return (-1);
 	bufp += 6;
 	errno = 0;
-	*pid = strtoll(bufp, &bufp, 10);
-	if (errno != 0)
-		return (-1);
 
 	while (*bufp != '\0' && isspace(*bufp))
 		bufp++;
@@ -667,14 +689,6 @@
 		else
 			str = "NOTICE: Zone boot failed";
 		break;
-	case Z_EVT_ZONE_BADARGS:
-		/*LINTED*/
-		(void) snprintf(lmsg, sizeof (lmsg),
-		    localize_msg(clilocale,
-		    "WARNING: Ignoring invalid boot arguments: %s"),
-		    bad_boot_arg);
-		lstr = lmsg;
-		break;
 	default:
 		return;
 	}
@@ -878,7 +892,6 @@
 	if (init_console_dev(zlogp) == -1) {
 		zerror(zlogp, B_FALSE,
 		    "console setup: device initialization failed");
-		return (-1);
 	}
 
 	if ((serverfd = init_console_sock(zlogp)) == -1) {
@@ -890,6 +903,17 @@
 }
 
 /*
+ * Maintain a simple flag that tracks if we have seen at least one state
+ * change. This is currently only used to handle the special case where we are
+ * running without a console device, which is what normally drives shutdown.
+ */
+void
+zcons_statechanged()
+{
+	state_changed = B_TRUE;
+}
+
+/*
  * serve_console() is the master loop for driving console I/O.  It is also the
  * routine which is ultimately responsible for "pulling the plug" on zoneadmd
  * when it realizes that the daemon should shut down.
@@ -907,6 +931,7 @@
 	int masterfd;
 	zone_state_t zstate;
 	char conspath[MAXPATHLEN];
+	static boolean_t cons_warned = B_FALSE;
 
 	(void) snprintf(conspath, sizeof (conspath),
 	    "/dev/zcons/%s/%s", zone_name, ZCONS_MASTER_NAME);
@@ -914,6 +939,46 @@
 	for (;;) {
 		masterfd = open(conspath, O_RDWR|O_NONBLOCK|O_NOCTTY);
 		if (masterfd == -1) {
+			if (master_zcons_failed) {
+				/*
+				 * If we don't have a console and the zone is
+				 * not shutting down, there may have been a
+				 * race/failure with devfs while creating the
+				 * console. In this case we want to leave the
+				 * zone up, even without a console, so
+				 * periodically recheck.
+				 */
+				int i;
+
+				/*
+				 * In the normal flow of this loop, we use
+				 * do_console_io to give things a chance to get
+				 * going first. However, in this case we can't
+				 * use that, so we have to wait for at least
+				 * one state change before checking the state.
+				 */
+				for (i = 0; i < 60; i++) {
+					if (state_changed)
+						break;
+					(void) sleep(1);
+				}
+
+				if (i < 60 && zone_get_state(zone_name,
+				    &zstate) == Z_OK &&
+				    (zstate == ZONE_STATE_READY ||
+				    zstate == ZONE_STATE_RUNNING)) {
+					if (!cons_warned) {
+						zerror(zlogp, B_FALSE,
+						    "WARNING: missing zone "
+						    "console for %s",
+						    zone_name);
+						cons_warned = B_TRUE;
+					}
+					(void) sleep(ZCONS_RETRY);
+					continue;
+				}
+			}
+
 			zerror(zlogp, B_TRUE, "failed to open console master");
 			(void) mutex_lock(&lock);
 			goto death;
--- /dev/null	Wed Oct  5 00:31:43 2016
+++ new/usr/src/cmd/zoneadmd/zfd.c	Wed Oct  5 00:31:43 2016
@@ -0,0 +1,1429 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.  All rights reserved.
+ */
+
+/*
+ * Zone file descriptor support is used as a mechanism for a process inside the
+ * zone to log messages to the GZ zoneadmd and also as a way to interact
+ * directly with the process (via zlogin -I). The zfd thread is modeled on
+ * the zcons thread so see the comment header in zcons.c for a general overview.
+ * Unlike with zcons, which has a single endpoint within the zone and a single
+ * endpoint used by zoneadmd, we setup multiple endpoints within the zone.
+ *
+ * The mode, which is controlled by the zone attribute "zlog-mode" is somewhat
+ * of a misnomer since its purpose has evolved. The attribute currently
+ * can have six values which are used to control:
+ *    - how the zfd devices are used inside the zone
+ *    - if the output on the device(s) is also teed into another stream within
+ *      the zone
+ *    - if we do logging in the GZ
+ * See the comment on get_mode_logmax() in this file, and the comment in
+ * uts/common/io/zfd.c for more details.
+ *
+ * Internally the zfd_mode_t struct holds the number of stdio devs (1 or 3),
+ * the number of additional devs corresponding to the zone attr value and the
+ * GZ logging flag.
+ *
+ * Note that although the mode indicates the number of devices needed, we always
+ * create all possible zfd devices for simplicity.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/termios.h>
+#include <sys/zfd.h>
+#include <sys/mkdev.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <stropts.h>
+#include <thread.h>
+#include <ucred.h>
+#include <unistd.h>
+#include <zone.h>
+#include <signal.h>
+#include <wchar.h>
+
+#include <libdevinfo.h>
+#include <libdevice.h>
+#include <libzonecfg.h>
+
+#include <syslog.h>
+#include <sys/modctl.h>
+
+#include "zoneadmd.h"
+
+static zlog_t	*zlogp;
+static int	shutting_down = 0;
+static thread_t logger_tid;
+static int	logfd = -1;
+static size_t	log_sz = 0;
+static size_t	log_rot_sz = 0;
+
+static void rotate_log();
+
+/*
+ * The eventstream is a simple one-directional flow of messages implemented
+ * with a pipe. It is used to wake up the poller when it needs to shutdown.
+ */
+static int eventstream[2] = {-1, -1};
+
+#define	LOGNAME			"stdio.log"
+#define	ZLOG_MODE		"zlog-mode"
+#define	LOG_MAXSZ		"zlog-max-size"
+#define	ZFDNEX_DEVTREEPATH	"/pseudo/zfdnex@2"
+#define	ZFDNEX_FILEPATH		"/devices/pseudo/zfdnex@2"
+#define	SERVER_SOCKPATH		ZONES_TMPDIR "/%s.server_%s"
+#define	ZTTY_RETRY		5
+
+#define	NUM_ZFD_DEVS		5
+
+typedef struct zfd_mode {
+	uint_t		zmode_n_stddevs;
+	uint_t		zmode_n_addl_devs;
+	boolean_t	zmode_gzlogging;
+} zfd_mode_t;
+static zfd_mode_t mode;
+
+/*
+ * cb_data is only used by destroy_cb.
+ */
+struct cb_data {
+	zlog_t *zlogp;
+	int killed;
+};
+
+/*
+ * destroy_zfd_devs() and its helper destroy_cb() tears down any zfd instances
+ * associated with this zone. If things went very wrong, we might have an
+ * incorrect number of instances hanging around.  This routine hunts down and
+ * tries to remove all of them. Of course, if the fd is open, the instance will
+ * not detach, which is a potential issue.
+ */
+static int
+destroy_cb(di_node_t node, void *arg)
+{
+	struct cb_data *cb = (struct cb_data *)arg;
+	char *prop_data;
+	char *tmp;
+	char devpath[MAXPATHLEN];
+	devctl_hdl_t hdl;
+
+	if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, "zfd_zname",
+	    &prop_data) == -1)
+		return (DI_WALK_CONTINUE);
+
+	assert(prop_data != NULL);
+	if (strcmp(prop_data, zone_name) != 0) {
+		/* this is a zfd for a different zone */
+		return (DI_WALK_CONTINUE);
+	}
+
+	tmp = di_devfs_path(node);
+	(void) snprintf(devpath, sizeof (devpath), "/devices/%s", tmp);
+	di_devfs_path_free(tmp);
+
+	if ((hdl = devctl_device_acquire(devpath, 0)) == NULL) {
+		zerror(cb->zlogp, B_TRUE, "WARNING: zfd %s found, "
+		    "but it could not be controlled.", devpath);
+		return (DI_WALK_CONTINUE);
+	}
+	if (devctl_device_remove(hdl) == 0) {
+		cb->killed++;
+	} else {
+		zerror(cb->zlogp, B_TRUE, "WARNING: zfd %s found, "
+		    "but it could not be removed.", devpath);
+	}
+	devctl_release(hdl);
+	return (DI_WALK_CONTINUE);
+}
+
+static int
+destroy_zfd_devs(zlog_t *zlogp)
+{
+	di_node_t root;
+	struct cb_data cb;
+
+	bzero(&cb, sizeof (cb));
+	cb.zlogp = zlogp;
+
+	if ((root = di_init(ZFDNEX_DEVTREEPATH, DINFOCPYALL)) == DI_NODE_NIL) {
+		zerror(zlogp, B_TRUE, "di_init failed");
+		return (-1);
+	}
+
+	(void) di_walk_node(root, DI_WALK_CLDFIRST, (void *)&cb, destroy_cb);
+
+	di_fini(root);
+	return (0);
+}
+
+static void
+make_tty(zlog_t *zlogp, int id)
+{
+	int i;
+	int fd = -1;
+	char stdpath[MAXPATHLEN];
+
+	/*
+	 * Open the master side of the dev and issue the ZFD_MAKETTY ioctl,
+	 * which will cause the the various tty-related streams modules to be
+	 * pushed when the slave opens the device.
+	 *
+	 * In very rare cases the open returns ENOENT if devfs doesn't have
+	 * everything setup yet due to heavy zone startup load. Wait for
+	 * 1 sec. and retry a few times. Even if we can't setup tty mode
+	 * we still move on.
+	 */
+	(void) snprintf(stdpath, sizeof (stdpath), "/dev/zfd/%s/master/%d",
+	    zone_name, id);
+
+	for (i = 0; !shutting_down && i < ZTTY_RETRY; i++) {
+		fd = open(stdpath, O_RDWR | O_NOCTTY);
+		if (fd >= 0 || errno != ENOENT)
+			break;
+		(void) sleep(1);
+	}
+	if (fd == -1) {
+		zerror(zlogp, B_TRUE, "ERROR: could not open zfd %d for "
+		    "zone %s to set tty mode", id, zone_name);
+	} else {
+		/*
+		 * This ioctl can occasionally return ENXIO if devfs doesn't
+		 * have everything plumbed up yet due to heavy zone startup
+		 * load. Wait for 1 sec. and retry a few times before we give
+		 * up.
+		 */
+		for (i = 0; !shutting_down && i < ZTTY_RETRY; i++) {
+			if (ioctl(fd, ZFD_MAKETTY) == 0) {
+				break;
+			} else if (errno != ENXIO) {
+				break;
+			}
+			(void) sleep(1);
+		}
+	}
+
+	if (fd != -1)
+		(void) close(fd);
+}
+
+/*
+ * init_zfd_devs() drives the device-tree configuration of the zone fd devices.
+ * The general strategy is to use the libdevice (devctl) interfaces to
+ * instantiate all of new zone fd nodes.  We do a lot of sanity checking, and
+ * are careful to reuse a dev if one exists.
+ *
+ * Once the devices are in the device tree, we kick devfsadm via
+ * di_devlink_init() to ensure that the appropriate symlinks (to the master and
+ * slave fd devices) are placed in /dev in the global zone.
+ */
+static int
+init_zfd_dev(zlog_t *zlogp, devctl_hdl_t bus_hdl, int id)
+{
+	int rv = -1;
+	devctl_ddef_t ddef_hdl = NULL;
+	devctl_hdl_t dev_hdl = NULL;
+
+	if ((ddef_hdl = devctl_ddef_alloc("zfd", 0)) == NULL) {
+		zerror(zlogp, B_TRUE, "failed to allocate ddef handle");
+		goto error;
+	}
+
+	/*
+	 * Set four properties on this node; the name of the zone, the dev name
+	 * seen inside the zone, a flag which lets pseudo know that it is OK to
+	 * automatically allocate an instance # for this device, and the last
+	 * one tells the device framework not to auto-detach this node - we
+	 * need the node to still be there when we ask devfsadmd to make links,
+	 * and when we need to open it.
+	 */
+	if (devctl_ddef_string(ddef_hdl, "zfd_zname", zone_name) == -1) {
+		zerror(zlogp, B_TRUE, "failed to create zfd_zname property");
+		goto error;
+	}
+	if (devctl_ddef_int(ddef_hdl, "zfd_id", id) == -1) {
+		zerror(zlogp, B_TRUE, "failed to create zfd_id property");
+		goto error;
+	}
+	if (devctl_ddef_int(ddef_hdl, "auto-assign-instance", 1) == -1) {
+		zerror(zlogp, B_TRUE, "failed to create auto-assign-instance "
+		    "property");
+		goto error;
+	}
+	if (devctl_ddef_int(ddef_hdl, "ddi-no-autodetach", 1) == -1) {
+		zerror(zlogp, B_TRUE, "failed to create ddi-no-auto-detach "
+		    "property");
+		goto error;
+	}
+	if (devctl_bus_dev_create(bus_hdl, ddef_hdl, 0, &dev_hdl) == -1) {
+		zerror(zlogp, B_TRUE, "failed to create zfd node");
+		goto error;
+	}
+	rv = 0;
+
+error:
+	if (ddef_hdl)
+		devctl_ddef_free(ddef_hdl);
+	if (dev_hdl)
+		devctl_release(dev_hdl);
+	return (rv);
+}
+
+static int
+init_zfd_devs(zlog_t *zlogp, zfd_mode_t *mode)
+{
+	devctl_hdl_t bus_hdl = NULL;
+	di_devlink_handle_t dl = NULL;
+	int rv = -1;
+	int i;
+
+	/*
+	 * Time to make the devices.
+	 */
+	if ((bus_hdl = devctl_bus_acquire(ZFDNEX_FILEPATH, 0)) == NULL) {
+		zerror(zlogp, B_TRUE, "devctl_bus_acquire failed");
+		goto error;
+	}
+
+	for (i = 0; i < NUM_ZFD_DEVS; i++) {
+		if (init_zfd_dev(zlogp, bus_hdl, i) != 0)
+			goto error;
+	}
+
+	if ((dl = di_devlink_init("zfd", DI_MAKE_LINK)) == NULL) {
+		zerror(zlogp, B_TRUE, "failed to create devlinks");
+		goto error;
+	}
+
+	(void) di_devlink_fini(&dl);
+	rv = 0;
+
+	if (mode->zmode_n_stddevs == 1) {
+		/* We want the primary stream to look like a tty. */
+		make_tty(zlogp, 0);
+	}
+
+error:
+	if (bus_hdl)
+		devctl_release(bus_hdl);
+	return (rv);
+}
+
+static int
+init_server_sock(zlog_t *zlogp, int *servfd, char *nm)
+{
+	int resfd = -1;
+	struct sockaddr_un servaddr;
+
+	bzero(&servaddr, sizeof (servaddr));
+	servaddr.sun_family = AF_UNIX;
+	(void) snprintf(servaddr.sun_path, sizeof (servaddr.sun_path),
+	    SERVER_SOCKPATH, zone_name, nm);
+
+	if ((resfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
+		zerror(zlogp, B_TRUE, "server setup: could not create socket");
+		goto err;
+	}
+	(void) unlink(servaddr.sun_path);
+
+	if (bind(resfd, (struct sockaddr *)&servaddr, sizeof (servaddr))
+	    == -1) {
+		zerror(zlogp, B_TRUE,
+		    "server setup: could not bind to socket");
+		goto err;
+	}
+
+	if (listen(resfd, 4) == -1) {
+		zerror(zlogp, B_TRUE,
+		    "server setup: could not listen on socket");
+		goto err;
+	}
+
+	*servfd = resfd;
+	return (0);
+
+err:
+	(void) unlink(servaddr.sun_path);
+	if (resfd != -1)
+		(void) close(resfd);
+	return (-1);
+}
+
+static void
+destroy_server_sock(int servfd, char *nm)
+{
+	char path[MAXPATHLEN];
+
+	(void) snprintf(path, sizeof (path), SERVER_SOCKPATH, zone_name, nm);
+	(void) unlink(path);
+	(void) shutdown(servfd, SHUT_RDWR);
+	(void) close(servfd);
+}
+
+/*
+ * Read the "ident" string from the client's descriptor; this routine also
+ * tolerates being called with pid=NULL, for times when you want to "eat"
+ * the ident string from a client without saving it.
+ */
+static int
+get_client_ident(int clifd, pid_t *pid, char *locale, size_t locale_len,
+    uint_t *flagsp)
+{
+	char buf[BUFSIZ], *bufp;
+	size_t buflen = sizeof (buf);
+	char c = '\0';
+	int i = 0, r;
+	ucred_t *cred = NULL;
+
+	/* "eat up the ident string" case, for simplicity */
+	if (pid == NULL) {
+		assert(locale == NULL && locale_len == 0);
+		while (read(clifd, &c, 1) == 1) {
+			if (c == '\n')
+				return (0);
+		}
+	}
+
+	bzero(buf, sizeof (buf));
+	while ((buflen > 1) && (r = read(clifd, &c, 1)) == 1) {
+		buflen--;
+		if (c == '\n')
+			break;
+
+		buf[i] = c;
+		i++;
+	}
+	if (r == -1)
+		return (-1);
+
+	/*
+	 * We've filled the buffer, but still haven't seen \n.  Keep eating
+	 * until we find it; we don't expect this to happen, but this is
+	 * defensive.
+	 */
+	if (c != '\n') {
+		while ((r = read(clifd, &c, sizeof (c))) > 0)
+			if (c == '\n')
+				break;
+	}
+
+	/*
+	 * Parse buffer for message of the form:
+	 * IDENT <locale> <flags>
+	 */
+	bufp = buf;
+	if (strncmp(bufp, "IDENT ", 6) != 0)
+		return (-1);
+	bufp += 6;
+
+	if (getpeerucred(clifd, &cred) == 0) {
+		*pid = ucred_getpid((const ucred_t *)cred);
+		ucred_free(cred);
+	} else {
+		return (-1);
+	}
+
+	while (*bufp != '\0' && isspace(*bufp))
+		bufp++;
+	buflen = strlen(bufp) - 1;
+	bufp[buflen - 1] = '\0';
+	(void) strlcpy(locale, bufp, locale_len);
+
+	*flagsp = atoi(&bufp[buflen]);
+
+	return (0);
+}
+
+static int
+accept_client(int servfd, pid_t *pid, char *locale, size_t locale_len,
+    uint_t *flagsp)
+{
+	int connfd;
+	struct sockaddr_un cliaddr;
+	socklen_t clilen;
+	int flags;
+
+	clilen = sizeof (cliaddr);
+	connfd = accept(servfd, (struct sockaddr *)&cliaddr, &clilen);
+	if (connfd == -1)
+		return (-1);
+	if (pid != NULL) {
+		if (get_client_ident(connfd, pid, locale, locale_len, flagsp)
+		    == -1) {
+			(void) shutdown(connfd, SHUT_RDWR);
+			(void) close(connfd);
+			return (-1);
+		}
+		(void) write(connfd, "OK\n", 3);
+	}
+
+	flags = fcntl(connfd, F_GETFL, 0);
+	if (flags != -1)
+		(void) fcntl(connfd, F_SETFL, flags | O_NONBLOCK | FD_CLOEXEC);
+
+	return (connfd);
+}
+
+static void
+reject_client(int servfd, pid_t clientpid)
+{
+	int connfd;
+	struct sockaddr_un cliaddr;
+	socklen_t clilen;
+	char nak[MAXPATHLEN];
+
+	clilen = sizeof (cliaddr);
+	connfd = accept(servfd, (struct sockaddr *)&cliaddr, &clilen);
+
+	/*
+	 * After getting its ident string, tell client to get lost.
+	 */
+	if (get_client_ident(connfd, NULL, NULL, 0, NULL) == 0) {
+		(void) snprintf(nak, sizeof (nak), "%lu\n",
+		    clientpid);
+		(void) write(connfd, nak, strlen(nak));
+	}
+	(void) shutdown(connfd, SHUT_RDWR);
+	(void) close(connfd);
+}
+
+static int
+accept_socket(int servfd, pid_t verpid)
+{
+	int connfd;
+	struct sockaddr_un cliaddr;
+	socklen_t clilen = sizeof (cliaddr);
+	ucred_t *cred = NULL;
+	pid_t rpid = -1;
+	int flags;
+
+	connfd = accept(servfd, (struct sockaddr *)&cliaddr, &clilen);
+	if (connfd == -1)
+		return (-1);
+
+	/* Confirm connecting process is who we expect */
+	if (getpeerucred(connfd, &cred) == 0) {
+		rpid = ucred_getpid((const ucred_t *)cred);
+		ucred_free(cred);
+	}
+	if (rpid == -1 || rpid != verpid) {
+		(void) shutdown(connfd, SHUT_RDWR);
+		(void) close(connfd);
+		return (-1);
+	}
+
+	flags = fcntl(connfd, F_GETFL, 0);
+	if (flags != -1)
+		(void) fcntl(connfd, F_SETFL, flags | O_NONBLOCK | FD_CLOEXEC);
+
+	return (connfd);
+}
+
+static void
+ctlcmd_process(int sockfd, int stdoutfd, unsigned int *flags)
+{
+	char buf[BUFSIZ];
+	int i;
+	for (i = 0; i < BUFSIZ-1; i++) {
+		char c;
+		if (read(sockfd, &c, 1) != 1 ||
+		    c == '\n' || c == '\0') {
+			break;
+		}
+		buf[i] = c;
+	}
+	if (i == 0) {
+		goto fail;
+	}
+	buf[i+1] = '\0';
+
+	if (strncmp(buf, "TIOCSWINSZ ", 11) == 0) {
+		char *next = buf + 11;
+		struct winsize ws;
+		errno = 0;
+		ws.ws_row = strtol(next, &next, 10);
+		if (errno == EINVAL) {
+			goto fail;
+		}
+		ws.ws_col = strtol(next + 1, &next, 10);
+		if (errno == EINVAL) {
+			goto fail;
+		}
+		if (ioctl(stdoutfd, TIOCSWINSZ, &ws) == 0) {
+			(void) write(sockfd, "OK\n", 3);
+			return;
+		}
+	}
+	if (strncmp(buf, "SETFLAGS ", 9) == 0) {
+		char *next = buf + 9;
+		unsigned int result;
+		errno = 0;
+		result = strtoul(next, &next, 10);
+		if (errno == EINVAL) {
+			goto fail;
+		}
+		*flags = result;
+		(void) write(sockfd, "OK\n", 3);
+		return;
+	}
+fail:
+	(void) write(sockfd, "FAIL\n", 5);
+}
+
+/*
+ * Check to see if the client at the other end of the socket is still alive; we
+ * know it is not if it throws EPIPE at us when we try to write an otherwise
+ * harmless 0-length message to it.
+ */
+static int
+test_client(int clifd)
+{
+	if ((write(clifd, "", 0) == -1) && errno == EPIPE)
+		return (-1);
+	return (0);
+}
+
+/*
+ * Modify the input string with json escapes. Since the destination can thus
+ * be larger than the source, it may get truncated, although we do use a
+ * larger buffer.
+ */
+static void
+escape_json(char *sbuf, int slen, char *dbuf, int dlen)
+{
+	int i;
+	mbstate_t mbr;
+	wchar_t c;
+	size_t sz;
+
+	bzero(&mbr, sizeof (mbr));
+
+	sbuf[slen] = '\0';
+	i = 0;
+	while (i < dlen && (sz = mbrtowc(&c, sbuf, MB_CUR_MAX, &mbr)) > 0) {
+		switch (c) {
+		case '\\':
+			dbuf[i++] = '\\';
+			dbuf[i++] = '\\';
+			break;
+
+		case '"':
+			dbuf[i++] = '\\';
+			dbuf[i++] = '"';
+			break;
+
+		case '\b':
+			dbuf[i++] = '\\';
+			dbuf[i++] = 'b';
+			break;
+
+		case '\f':
+			dbuf[i++] = '\\';
+			dbuf[i++] = 'f';
+			break;
+
+		case '\n':
+			dbuf[i++] = '\\';
+			dbuf[i++] = 'n';
+			break;
+
+		case '\r':
+			dbuf[i++] = '\\';
+			dbuf[i++] = 'r';
+			break;
+
+		case '\t':
+			dbuf[i++] = '\\';
+			dbuf[i++] = 't';
+			break;
+
+		default:
+			if ((c >= 0x00 && c <= 0x1f) ||
+			    (c > 0x7f && c <= 0xffff)) {
+
+				i += snprintf(&dbuf[i], (dlen - i), "\\u%04x",
+				    (int)(0xffff & c));
+			} else if (c >= 0x20 && c <= 0x7f) {
+				dbuf[i++] = 0xff & c;
+			}
+
+			break;
+		}
+		sbuf += sz;
+	}
+
+	if (i == dlen)
+		dbuf[--i] = '\0';
+	else
+		dbuf[i] = '\0';
+}
+
+/*
+ * We output to the log file as json.
+ * ex. for string 'msg\n' on the zone's stdout:
+ *    {"log":"msg\n","stream":"stdout","time":"2014-10-24T20:12:11.101973117Z"}
+ *
+ * We use ns in the last field of the timestamp for compatability.
+ *
+ * We keep track of the size of the log file and rotate it when we exceed
+ * the log size limit (if one is set).
+ */
+static void
+wr_log_msg(char *buf, int len, int from)
+{
+	struct timeval tv;
+	int olen;
+	char ts[64];
+	char nbuf[BUFSIZ * 2];
+	char obuf[BUFSIZ * 2];
+	static boolean_t log_wr_err = B_FALSE;
+
+	if (logfd == -1)
+		return;
+
+	escape_json(buf, len, nbuf, sizeof (nbuf));
+
+	if (gettimeofday(&tv, NULL) != 0)
+		return;
+	(void) strftime(ts, sizeof (ts), "%FT%T", gmtime(&tv.tv_sec));
+
+	olen = snprintf(obuf, sizeof (obuf),
+	    "{\"log\":\"%s\",\"stream\":\"%s\",\"time\":\"%s.%ldZ\"}\n",
+	    nbuf, (from == 1) ? "stdout" : "stderr", ts, tv.tv_usec * 1000);
+
+	if (write(logfd, obuf, olen) != olen) {
+		if (!log_wr_err) {
+			zerror(zlogp, B_TRUE, "log file write error");
+			log_wr_err = B_TRUE;
+		}
+		return;
+	}
+
+	log_sz += olen;
+	if (log_rot_sz > 0 && log_sz >= log_rot_sz)
+		rotate_log();
+}
+
+/*
+ * We want to sleep for a little while but need to be responsive if the zone is
+ * halting. We poll/sleep on the event stream so we can notice if we're halting.
+ * Return true if halting, otherwise false.
+ */
+static boolean_t
+halt_sleep(int slptime)
+{
+	struct pollfd evfd[1];
+
+	evfd[0].fd = eventstream[1];
+	evfd[0].events = POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI;
+
+	if (poll(evfd, 1, slptime) > 0) {
+		/* zone halting */
+		return (B_TRUE);
+	}
+	return (B_FALSE);
+}
+
+/*
+ * This routine drives the logging and interactive I/O loop. It polls for
+ * input from the zone side of the fd (output to stdout/stderr), and from the
+ * client (input to the zone's stdin).  Additionally, it polls on the server
+ * fd, and disconnects any clients that might try to hook up with the zone
+ * while the fd's are in use.
+ *
+ * Data from the zone's stdout and stderr is formatted in json and written to
+ * the log file whether an interactive client is connected or not.
+ *
+ * When the client first calls us up, it is expected to send a line giving its
+ * "identity"; this consists of the string 'IDENT <pid> <locale>'. This is so
+ * that we can report that the fd's are busy, along with some diagnostics
+ * about who has them busy; the locale is ignore here but kept for compatability
+ * with the zlogin code when running on the zone's console.
+ *
+ * We need to handle the case where there is no server within the zone (or
+ * the server gets stuck) and data that we're writing to the zone server's
+ * stdin fills the pipe. Because of the way the zfd device works writes can
+ * flow into the stream and simply be dropped, if there is no server, or writes
+ * could return -1 with EAGAIN if the server is stuck. Since we ignore errors
+ * on the write to stdin, we won't get blocked in that case but we'd like to
+ * avoid dropping initial input if the server within the zone hasn't started
+ * yet. To handle this we wait to read initial input until we detect that there
+ * is a server inside the zone. We have to poll for this so that we can
+ * re-run the ioctl to notice when a server shows up. This poll/wait is handled
+ * by halt_sleep() so that we can be responsive if the zone wants to halt.
+ * We only do this check to avoid dropping initial input so it is possible for
+ * the server within the zone to go away later. At that point zfd will just
+ * drop any new input flowing into the stream.
+ */
+static void
+do_zfd_io(int gzctlfd, int gzservfd, int gzerrfd, int stdinfd, int stdoutfd,
+    int stderrfd)
+{
+	struct pollfd pollfds[8];
+	char ibuf[BUFSIZ + 1];
+	int cc, ret;
+	int ctlfd = -1;
+	int clifd = -1;
+	int clierrfd = -1;
+	int pollerr = 0;
+	char clilocale[MAXPATHLEN];
+	pid_t clipid = 0;
+	uint_t flags = 0;
+	boolean_t stdin_ready = B_FALSE;
+	int slptime = 250;	/* initial poll sleep time in ms */
+
+	/* client control socket, watch for read events */
+	pollfds[0].fd = ctlfd;
+	pollfds[0].events = POLLIN | POLLRDNORM | POLLRDBAND |
+	    POLLPRI | POLLERR | POLLHUP | POLLNVAL;
+
+	/* client socket, watch for read events */
+	pollfds[1].fd = clifd;
+	pollfds[1].events = pollfds[0].events;
+
+	/* stdout, watch for read events */
+	pollfds[2].fd = stdoutfd;
+	pollfds[2].events = pollfds[0].events;
+
+	/* stderr, watch for read events */
+	pollfds[3].fd = stderrfd;
+	pollfds[3].events = pollfds[0].events;
+
+	/* the server control socket; watch for new connections */
+	pollfds[4].fd = gzctlfd;
+	pollfds[4].events = POLLIN | POLLRDNORM;
+
+	/* the server stdin/out socket; watch for new connections */
+	pollfds[5].fd = gzservfd;
+	pollfds[5].events = POLLIN | POLLRDNORM;
+
+	/* the server stderr socket; watch for new connections */
+	pollfds[6].fd = gzerrfd;
+	pollfds[6].events = POLLIN | POLLRDNORM;
+
+	/* the eventstream; any input means the zone is halting */
+	pollfds[7].fd = eventstream[1];
+	pollfds[7].events = pollfds[0].events;
+
+	while (!shutting_down) {
+		pollfds[0].revents = pollfds[1].revents = 0;
+		pollfds[2].revents = pollfds[3].revents = 0;
+		pollfds[4].revents = pollfds[5].revents = 0;
+		pollfds[6].revents = pollfds[7].revents = 0;
+
+		ret = poll(pollfds, 8, -1);
+		if (ret == -1 && errno != EINTR) {
+			zerror(zlogp, B_TRUE, "poll failed");
+			/* we are hosed, close connection */
+			break;
+		}
+
+		/* control events from client */
+		if (pollfds[0].revents &
+		    (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) {
+			/* process control message */
+			ctlcmd_process(ctlfd, stdoutfd, &flags);
+		} else if (pollfds[0].revents) {
+			/* bail if any error occurs */
+			pollerr = pollfds[0].revents;
+			zerror(zlogp, B_FALSE, "closing connection "
+			    "with control channel, pollerr %d\n", pollerr);
+			break;
+		}
+
+		/* event from client side */
+		if (pollfds[1].revents) {
+			if (stdin_ready) {
+				if (pollfds[1].revents & (POLLIN |
+				    POLLRDNORM | POLLRDBAND | POLLPRI)) {
+					errno = 0;
+					cc = read(clifd, ibuf, BUFSIZ);
+					if (cc > 0) {
+						/*
+						 * See comment for this
+						 * function on what happens if
+						 * there is no reader in the
+						 * zone. EOF is handled below.
+						 */
+						(void) write(stdinfd, ibuf, cc);
+					}
+				} else if (pollfds[1].revents & (POLLERR |
+				    POLLNVAL))  {
+					pollerr = pollfds[1].revents;
+					zerror(zlogp, B_FALSE,
+					    "closing connection "
+					    "with client, pollerr %d\n",
+					    pollerr);
+					break;
+				}
+
+				if (pollfds[1].revents & POLLHUP) {
+					if (flags & ZLOGIN_ZFD_EOF) {
+						/*
+						 * Let the client know. We've
+						 * already serviced any pending
+						 * regular input. Let the
+						 * stream clear since the EOF
+						 * ioctl jumps to the head.
+						 */
+						(void) ioctl(stdinfd, I_FLUSH);
+						if (halt_sleep(250))
+							break;
+						(void) ioctl(stdinfd, ZFD_EOF);
+					}
+					break;
+				}
+			} else {
+				if (ioctl(stdinfd, ZFD_HAS_SLAVE) == 0) {
+					stdin_ready = B_TRUE;
+				} else {
+					/*
+					 * There is nothing in the zone to read
+					 * our input. Presumably the user
+					 * providing input expects something to
+					 * show up, but that is no guarantee.
+					 * Since we haven't serviced the pending
+					 * input poll yet, we don't want to
+					 * immediately loop around but we also
+					 * need to be responsive if the zone is
+					 * halting.
+					 */
+					if (halt_sleep(slptime))
+						break;
+
+					if (slptime < 5000)
+						slptime += 250;
+				}
+			}
+		}
+
+		/* event from the zone's stdout */
+		if (pollfds[2].revents) {
+			if (pollfds[2].revents &
+			    (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) {
+				errno = 0;
+				cc = read(stdoutfd, ibuf, BUFSIZ);
+				if (cc <= 0 && (errno != EINTR) &&
+				    (errno != EAGAIN))
+					break;
+				if (cc > 0) {
+					wr_log_msg(ibuf, cc, 1);
+
+					/*
+					 * Lose output if no one is listening,
+					 * otherwise pass it on.
+					 */
+					if (clifd != -1)
+						(void) write(clifd, ibuf, cc);
+				}
+			} else {
+				pollerr = pollfds[2].revents;
+				zerror(zlogp, B_FALSE,
+				    "closing connection with stdout zfd, "
+				    "pollerr %d\n", pollerr);
+				break;
+			}
+		}
+
+		/* event from the zone's stderr */
+		if (pollfds[3].revents) {
+			if (pollfds[3].revents &
+			    (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) {
+				errno = 0;
+				cc = read(stderrfd, ibuf, BUFSIZ);
+				if (cc <= 0 && (errno != EINTR) &&
+				    (errno != EAGAIN))
+					break;
+				if (cc > 0) {
+					wr_log_msg(ibuf, cc, 2);
+
+					/*
+					 * Lose output if no one is listening,
+					 * otherwise pass it on.
+					 */
+					if (clierrfd != -1)
+						(void) write(clierrfd, ibuf,
+						    cc);
+				}
+			} else {
+				pollerr = pollfds[3].revents;
+				zerror(zlogp, B_FALSE,
+				    "closing connection with stderr zfd, "
+				    "pollerr %d\n", pollerr);
+				break;
+			}
+		}
+
+		/* connect event from server control socket */
+		if (pollfds[4].revents) {
+			if (ctlfd != -1) {
+				/*
+				 * Test the client to see if it is really
+				 * still alive.  If it has died but we
+				 * haven't yet detected that, we might
+				 * deny a legitimate connect attempt.  If it
+				 * is dead, we break out; once we tear down
+				 * the old connection, the new connection
+				 * will happen.
+				 */
+				if (test_client(ctlfd) == -1) {
+					break;
+				}
+				/* we're already handling a client */
+				reject_client(gzctlfd, clipid);
+			} else {
+				ctlfd = accept_client(gzctlfd, &clipid,
+				    clilocale, sizeof (clilocale), &flags);
+				if (ctlfd != -1) {
+					pollfds[0].fd = ctlfd;
+				} else {
+					break;
+				}
+			}
+		}
+
+		/* connect event from server stdin/out socket */
+		if (pollfds[5].revents) {
+			if (ctlfd == -1) {
+				/*
+				 * This shouldn't happen since the client is
+				 * expected to connect on the control socket
+				 * first. If we see this, tear everything down
+				 * and start over.
+				 */
+				zerror(zlogp, B_FALSE, "GZ zfd stdin/stdout "
+				    "connection attempt with no GZ control\n");
+				break;
+			}
+			assert(clifd == -1);
+			if ((clifd = accept_socket(gzservfd, clipid)) != -1) {
+				/* No need to watch for other new connections */
+				pollfds[5].fd = -1;
+				/* Client input is of interest, though */
+				pollfds[1].fd = clifd;
+			} else {
+				break;
+			}
+		}
+
+		/* connection event from server stderr socket */
+		if (pollfds[6].revents) {
+			if (ctlfd == -1) {
+				/*
+				 * Same conditions apply to stderr as stdin/out.
+				 */
+				zerror(zlogp, B_FALSE, "GZ zfd stderr "
+				    "connection attempt with no GZ control\n");
+				break;
+			}
+			assert(clierrfd == -1);
+			if ((clierrfd = accept_socket(gzerrfd, clipid)) != -1) {
+				/* No need to watch for other new connections */
+				pollfds[6].fd = -1;
+			} else {
+				break;
+			}
+		}
+
+		/*
+		 * Watch for events on the eventstream.  This is how we get
+		 * notified of the zone halting, etc.  It provides us a
+		 * "wakeup" from poll when important things happen, which
+		 * is good.
+		 */
+		if (pollfds[7].revents) {
+			break;
+		}
+	}
+
+	if (clifd != -1) {
+		(void) shutdown(clifd, SHUT_RDWR);
+		(void) close(clifd);
+	}
+
+	if (clierrfd != -1) {
+		(void) shutdown(clierrfd, SHUT_RDWR);
+		(void) close(clierrfd);
+	}
+}
+
+static int
+open_fd(zlog_t *zlogp, int id, int rw)
+{
+	int fd;
+	int flag = O_NONBLOCK | O_NOCTTY | O_CLOEXEC;
+	int retried = 0;
+	char stdpath[MAXPATHLEN];
+
+	(void) snprintf(stdpath, sizeof (stdpath), "/dev/zfd/%s/master/%d",
+	    zone_name, id);
+	flag |= rw;
+
+	while (!shutting_down) {
+		if ((fd = open(stdpath, flag)) != -1) {
+			/*
+			 * Setting RPROTDIS on the stream means that the
+			 * control portion of messages received (which we don't
+			 * care about) will be discarded by the stream head. If
+			 * we allowed such messages, we wouldn't be able to use
+			 * read(2), as it fails (EBADMSG) when a message with a
+			 * control element is received.
+			 */
+			if (ioctl(fd, I_SRDOPT, RNORM|RPROTDIS) == -1) {
+				zerror(zlogp, B_TRUE,
+				    "failed to set options on zfd");
+				return (-1);
+			}
+			return (fd);
+		}
+
+		if (retried++ > 60)
+			break;
+
+		(void) sleep(1);
+	}
+
+	zerror(zlogp, B_TRUE, "failed to open zfd");
+	return (-1);
+}
+
+static void
+open_logfile()
+{
+	char logpath[MAXPATHLEN];
+
+	logfd = -1;
+	log_sz = 0;
+
+	(void) snprintf(logpath, sizeof (logpath), "%s/logs", zonepath);
+	(void) mkdir(logpath, 0700);
+
+	(void) snprintf(logpath, sizeof (logpath), "%s/logs/%s", zonepath,
+	    LOGNAME);
+
+	if ((logfd = open(logpath, O_WRONLY | O_APPEND | O_CREAT,
+	    0600)) == -1) {
+		zerror(zlogp, B_TRUE, "failed to open log file");
+	} else {
+		struct stat64 sb;
+
+		if (fstat64(logfd, &sb) == 0)
+			log_sz = sb.st_size;
+	}
+}
+
+static void
+rotate_log()
+{
+	time_t t;
+	struct tm gtm;
+	char onm[MAXPATHLEN], rnm[MAXPATHLEN];
+
+	if ((t = time(NULL)) == (time_t)-1 || gmtime_r(&t, &gtm) == NULL) {
+		zerror(zlogp, B_TRUE, "failed to format time");
+		return;
+	}
+
+	(void) snprintf(rnm, sizeof (rnm),
+	    "%s/logs/%s.%d%02d%02dT%02d%02d%02dZ",
+	    zonepath, LOGNAME, gtm.tm_year + 1900, gtm.tm_mon + 1, gtm.tm_mday,
+	    gtm.tm_hour, gtm.tm_min, gtm.tm_sec);
+	(void) snprintf(onm, sizeof (onm), "%s/logs/%s", zonepath, LOGNAME);
+
+	(void) close(logfd);
+	if (rename(onm, rnm) != 0)
+		zerror(zlogp, B_TRUE, "failed to rotate log file");
+	open_logfile();
+}
+
+
+/* ARGSUSED */
+void
+hup_handler(int i)
+{
+	if (logfd != -1) {
+		(void) close(logfd);
+		open_logfile();
+	}
+}
+
+/*
+ * Body of the worker thread to log the zfd's stdout and stderr to a log file
+ * and to perform interactive IO to the stdin, stdout and stderr zfd's.
+ *
+ * The stdin, stdout and stderr are from the perspective of the process inside
+ * the zone, so the zoneadmd view is opposite (i.e. we write to the stdin fd
+ * and read from the stdout/stderr fds).
+ */
+static void
+srvr(void *modearg)
+{
+	zfd_mode_t *mode = (zfd_mode_t *)modearg;
+	int gzctlfd = -1;
+	int gzoutfd = -1;
+	int stdinfd = -1;
+	int stdoutfd = -1;
+	sigset_t blockset;
+	int gzerrfd = -1;
+	int stderrfd = -1;
+	int flags;
+	int len;
+	char ibuf[BUFSIZ + 1];
+
+	if (!shutting_down && mode->zmode_gzlogging)
+		open_logfile();
+
+	/*
+	 * This thread should receive SIGHUP so that it can close the log
+	 * file, and reopen it, during log rotation.
+	 */
+	(void) sigset(SIGHUP, hup_handler);
+	(void) sigfillset(&blockset);
+	(void) sigdelset(&blockset, SIGHUP);
+	(void) thr_sigsetmask(SIG_BLOCK, &blockset, NULL);
+
+	if (!shutting_down) {
+		if (pipe(eventstream) != 0) {
+			zerror(zlogp, B_TRUE, "failed to open logger control "
+			    "pipe");
+			return;
+		}
+	}
+
+	while (!shutting_down) {
+		if (init_server_sock(zlogp, &gzctlfd, "ctl") == -1) {
+			zerror(zlogp, B_FALSE,
+			    "server setup: control socket init failed");
+			goto death;
+		}
+		if (init_server_sock(zlogp, &gzoutfd, "out") == -1) {
+			zerror(zlogp, B_FALSE,
+			    "server setup: stdout socket init failed");
+			goto death;
+		}
+		if (init_server_sock(zlogp, &gzerrfd, "err") == -1) {
+			zerror(zlogp, B_FALSE,
+			    "server setup: stderr socket init failed");
+			goto death;
+		}
+
+		if (mode->zmode_n_stddevs == 1) {
+			if ((stdinfd = open_fd(zlogp, 0, O_RDWR)) == -1) {
+				goto death;
+			}
+			stdoutfd = stdinfd;
+		} else {
+			if ((stdinfd = open_fd(zlogp, 0, O_WRONLY)) == -1 ||
+			    (stdoutfd = open_fd(zlogp, 1, O_RDONLY)) == -1 ||
+			    (stderrfd = open_fd(zlogp, 2, O_RDONLY)) == -1) {
+				goto death;
+			}
+		}
+
+		do_zfd_io(gzctlfd, gzoutfd, gzerrfd, stdinfd, stdoutfd,
+		    stderrfd);
+death:
+		destroy_server_sock(gzctlfd, "ctl");
+		destroy_server_sock(gzoutfd, "out");
+		destroy_server_sock(gzerrfd, "err");
+
+		/* when shutting down, leave open until drained */
+		if (!shutting_down) {
+			(void) close(stdinfd);
+			if (mode->zmode_n_stddevs == 3) {
+				(void) close(stdoutfd);
+				(void) close(stderrfd);
+			}
+		}
+	}
+
+	/*
+	 * Attempt to drain remaining log output from the zone prior to closing
+	 * the file descriptors. This helps ensure that complete logs are
+	 * captured during shutdown.
+	 */
+	flags = fcntl(stdoutfd, F_GETFL, 0);
+	if (fcntl(stdoutfd, F_SETFL, flags | O_NONBLOCK) != -1) {
+		while ((len = read(stdoutfd, ibuf, BUFSIZ)) > 0)
+			wr_log_msg(ibuf, len, 1);
+	}
+	(void) close(stdoutfd);
+
+	if (mode->zmode_n_stddevs > 1) {
+		(void) close(stdinfd);
+		flags = fcntl(stderrfd, F_GETFL, 0);
+		if (fcntl(stderrfd, F_SETFL, flags | O_NONBLOCK) != -1) {
+			while ((len = read(stderrfd, ibuf, BUFSIZ)) > 0)
+				wr_log_msg(ibuf, len, 2);
+		}
+		(void) close(stderrfd);
+	}
+
+
+	(void) close(eventstream[0]);
+	eventstream[0] = -1;
+	(void) close(eventstream[1]);
+	eventstream[1] = -1;
+	if (logfd != -1)
+		(void) close(logfd);
+}
+
+/*
+ * The meaning of the original legacy values for the zlog-mode evolved over
+ * time, to the point where the old names no longer made sense. The current
+ * values are simply positional letters used to indicate various capabilities.
+ * The following table shows the meaning of the mode values, along with the
+ * legacy name which we continue to support for compatability. Any future
+ * capability can add a letter to the left and '-' is implied for existing
+ * strings.
+ *
+ * zlog-mode    gz log - tty - ngz log
+ * ---------    ------   ---   -------
+ * gt- (int)       y      y       n
+ * g-- (log)       y      n       n
+ * gtn (nlint)     y      y       y
+ * g-n (nolog)     y      n       y
+ * -t-             n      y       n
+ * ---             n      n       n
+ *
+ * This function also obtains a maximum log size while it is reading the
+ * zone configuration.
+ */
+static void
+get_mode_logmax(zfd_mode_t *mode)
+{
+	zone_dochandle_t handle;
+	struct zone_attrtab attr;
+
+	bzero(mode, sizeof (zfd_mode_t));
+
+	if ((handle = zonecfg_init_handle()) == NULL)
+		return;
+
+	if (zonecfg_get_handle(zone_name, handle) != Z_OK)
+		goto done;
+
+	if (zonecfg_setattrent(handle) != Z_OK)
+		goto done;
+	while (zonecfg_getattrent(handle, &attr) == Z_OK) {
+		if (strcmp(ZLOG_MODE, attr.zone_attr_name) == 0) {
+			if (strcmp("g--", attr.zone_attr_value) == 0 ||
+			    strncmp("log", attr.zone_attr_value, 3) == 0) {
+				mode->zmode_gzlogging = B_TRUE;
+				mode->zmode_n_stddevs = 3;
+				mode->zmode_n_addl_devs = 0;
+			} else if (strcmp("g-n", attr.zone_attr_value) == 0 ||
+			    strncmp("nolog", attr.zone_attr_value, 5) == 0) {
+				mode->zmode_gzlogging = B_TRUE;
+				mode->zmode_n_stddevs = 3;
+				mode->zmode_n_addl_devs = 2;
+			} else if (strcmp("gt-", attr.zone_attr_value) == 0 ||
+			    strncmp("int", attr.zone_attr_value, 3) == 0) {
+				mode->zmode_gzlogging = B_TRUE;
+				mode->zmode_n_stddevs = 1;
+				mode->zmode_n_addl_devs = 0;
+			} else if (strcmp("gtn", attr.zone_attr_value) == 0 ||
+			    strncmp("nlint", attr.zone_attr_value, 5) == 0) {
+				mode->zmode_gzlogging = B_TRUE;
+				mode->zmode_n_stddevs = 1;
+				mode->zmode_n_addl_devs = 1;
+			} else if (strcmp("-t-", attr.zone_attr_value) == 0) {
+				mode->zmode_gzlogging = B_FALSE;
+				mode->zmode_n_stddevs = 1;
+				mode->zmode_n_addl_devs = 0;
+			} else if (strcmp("---", attr.zone_attr_value) == 0) {
+				mode->zmode_gzlogging = B_FALSE;
+				mode->zmode_n_stddevs = 3;
+				mode->zmode_n_addl_devs = 0;
+			}
+
+		} else if (strcmp(LOG_MAXSZ, attr.zone_attr_name) == 0) {
+			char *p;
+			long lval;
+
+			p = attr.zone_attr_value;
+			lval = strtol(p, &p, 10);
+			if (*p == '\0')
+				log_rot_sz = (size_t)lval;
+		}
+	}
+	(void) zonecfg_endattrent(handle);
+
+done:
+	zonecfg_fini_handle(handle);
+}
+
+/* ARGSUSED -- "id" might be used in the future. */
+void
+create_log_thread(zlog_t *logp, zoneid_t id)
+{
+	int res;
+
+	shutting_down = 0;
+	zlogp = logp;
+
+	get_mode_logmax(&mode);
+	if (mode.zmode_n_stddevs == 0)
+		return;
+
+	if (init_zfd_devs(zlogp, &mode) == -1) {
+		zerror(zlogp, B_FALSE,
+		    "zfd setup: device initialization failed");
+		return;
+	}
+
+	res = thr_create(NULL, 0, (void * (*)(void *))srvr, (void *)&mode, 0,
+	    &logger_tid);
+	if (res != 0) {
+		zerror(zlogp, B_FALSE, "error %d creating logger thread", res);
+		logger_tid = 0;
+	}
+}
+
+void
+destroy_log_thread()
+{
+	if (logger_tid != 0) {
+		int stop = 1;
+
+		shutting_down = 1;
+		/* break out of poll to shutdown */
+		if (eventstream[0] != -1)
+			(void) write(eventstream[0], &stop, sizeof (stop));
+		(void) thr_join(logger_tid, NULL, NULL);
+		logger_tid = 0;
+	}
+
+	(void) destroy_zfd_devs(zlogp);
+}
--- old/usr/src/cmd/zoneadmd/zoneadmd.c	Wed Oct  5 00:31:43 2016
+++ new/usr/src/cmd/zoneadmd/zoneadmd.c	Wed Oct  5 00:31:43 2016
@@ -22,6 +22,7 @@
 /*
  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 
 /*
@@ -68,6 +69,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/sysmacros.h>
+#include <sys/time.h>
 
 #include <bsm/adt.h>
 #include <bsm/adt_event.h>
@@ -108,6 +110,8 @@
 
 static char *progname;
 char *zone_name;	/* zone which we are managing */
+zone_dochandle_t snap_hndl;	/* handle for snapshot created when ready */
+char zonepath[MAXNAMELEN];
 char pool_name[MAXNAMELEN];
 char default_brand[MAXNAMELEN];
 char brand_name[MAXNAMELEN];
@@ -141,6 +145,9 @@
 
 #define	DEFAULT_LOCALE	"C"
 
+#define	RSRC_NET	"net"
+#define	RSRC_DEV	"device"
+
 static const char *
 z_cmd_name(zone_cmd_t zcmd)
 {
@@ -257,10 +264,21 @@
 }
 
 /*
- * Emit a warning for any boot arguments which are unrecognized.  Since
- * Solaris boot arguments are getopt(3c) compatible (see kernel(1m)), we
+ * Append src to dest, modifying dest in the process. Prefix src with
+ * a space character if dest is a non-empty string.
+ */
+static void
+strnappend(char *dest, size_t n, const char *src)
+{
+	(void) snprintf(dest, n, "%s%s%s", dest,
+	    dest[0] == '\0' ? "" : " ", src);
+}
+
+/*
+ * Since illumos boot arguments are getopt(3c) compatible (see kernel(1m)), we
  * put the arguments into an argv style array, use getopt to process them,
- * and put the resultant argument string back into outargs.
+ * and put the resultant argument string back into outargs. Non-native brands
+ * may support alternate forms of boot arguments so we must handle that as well.
  *
  * During the filtering, we pull out any arguments which are truly "boot"
  * arguments, leaving only those which are to be passed intact to the
@@ -267,13 +285,11 @@
  * progenitor process.  The one we support at the moment is -i, which
  * indicates to the kernel which program should be launched as 'init'.
  *
- * A return of Z_INVAL indicates specifically that the arguments are
- * not valid; this is a non-fatal error.  Except for Z_OK, all other return
- * values are treated as fatal.
+ * Except for Z_OK, all other return values are treated as fatal.
  */
 static int
 filter_bootargs(zlog_t *zlogp, const char *inargs, char *outargs,
-    char *init_file, char *badarg)
+    char *init_file)
 {
 	int argc = 0, argc_save;
 	int i;
@@ -281,10 +297,10 @@
 	char *arg, *lasts, **argv = NULL, **argv_save;
 	char zonecfg_args[BOOTARGS_MAX];
 	char scratchargs[BOOTARGS_MAX], *sargs;
+	char scratchopt[3];
 	char c;
 
 	bzero(outargs, BOOTARGS_MAX);
-	bzero(badarg, BOOTARGS_MAX);
 
 	/*
 	 * If the user didn't specify transient boot arguments, check
@@ -347,14 +363,22 @@
 	}
 
 	/*
-	 * We preserve compatibility with the Solaris system boot behavior,
+	 * We preserve compatibility with the illumos system boot behavior,
 	 * which allows:
 	 *
 	 * 	# reboot kernel/unix -s -m verbose
 	 *
-	 * In this example, kernel/unix tells the booter what file to
-	 * boot.  We don't want reboot in a zone to be gratuitously different,
-	 * so we silently ignore the boot file, if necessary.
+	 * In this example, kernel/unix tells the booter what file to boot. The
+	 * original intent of this was that we didn't want reboot in a zone to
+	 * be gratuitously different, so we would silently ignore the boot
+	 * file, if necessary. However, this usage is archaic and has never
+	 * been common, since it is impossible to boot a zone onto a different
+	 * kernel. Ignoring the first argument breaks for non-native brands
+	 * which pass boot arguments in a different style. e.g.
+	 *	systemd.log_level=debug
+	 * Thus, for backward compatibility we only ignore the first argument
+	 * if it appears to be in the illumos form and attempting to specify a
+	 * kernel.
 	 */
 	if (argv[0] == NULL)
 		goto done;
@@ -362,7 +386,7 @@
 	assert(argv[0][0] != ' ');
 	assert(argv[0][0] != '\t');
 
-	if (argv[0][0] != '-' && argv[0][0] != '\0') {
+	if (strncmp(argv[0], "kernel/", 7) == 0) {
 		argv = &argv[1];
 		argc--;
 	}
@@ -385,41 +409,35 @@
 		case 'm':
 		case 's':
 			/* These pass through unmolested */
-			(void) snprintf(outargs, BOOTARGS_MAX,
-			    "%s -%c %s ", outargs, c, optarg ? optarg : "");
+			(void) snprintf(scratchopt, sizeof (scratchopt),
+			    "-%c", c);
+			strnappend(outargs, BOOTARGS_MAX, scratchopt);
+			if (optarg != NULL)
+				strnappend(outargs, BOOTARGS_MAX, optarg);
 			break;
 		case '?':
 			/*
-			 * We warn about unknown arguments but pass them
-			 * along anyway-- if someone wants to develop their
-			 * own init replacement, they can pass it whatever
-			 * args they want.
+			 * If a brand has its own init, we need to pass along
+			 * whatever the user provides. We use the entire
+			 * unknown string here so that we correctly handle
+			 * unknown long options (e.g. --debug).
 			 */
-			err = Z_INVAL;
-			(void) snprintf(outargs, BOOTARGS_MAX,
-			    "%s -%c", outargs, optopt);
-			(void) snprintf(badarg, BOOTARGS_MAX,
-			    "%s -%c", badarg, optopt);
+			strnappend(outargs, BOOTARGS_MAX, argv[optind - 1]);
 			break;
 		}
 	}
 
 	/*
-	 * For Solaris Zones we warn about and discard non-option arguments.
-	 * Hence 'boot foo bar baz gub' --> 'boot'.  However, to be similar
-	 * to the kernel, we concat up all the other remaining boot args.
-	 * and warn on them as a group.
+	 * We need to pass along everything else since we don't know what
+	 * the brand's init is expecting. For example, an argument list like:
+	 *   --confdir /foo --debug
+	 * will cause the getopt parsing to stop at '/foo' but we need to pass
+	 * that on, along with the '--debug'. This does mean that we require
+	 * any of our known options (-ifms) to preceed the brand-specific ones.
 	 */
-	if (optind < argc) {
-		err = Z_INVAL;
-		while (optind < argc) {
-			(void) snprintf(badarg, BOOTARGS_MAX, "%s%s%s",
-			    badarg, strlen(badarg) > 0 ? " " : "",
-			    argv[optind]);
-			optind++;
-		}
-		zerror(zlogp, B_FALSE, "WARNING: Unused or invalid boot "
-		    "arguments `%s'.", badarg);
+	while (optind < argc) {
+		strnappend(outargs, BOOTARGS_MAX, argv[optind]);
+		optind++;
 	}
 
 done:
@@ -537,7 +555,8 @@
 {
 	int err;
 
-	if (brand_prestatechg(zlogp, zstate, Z_READY) != 0)
+	if (!ALT_MOUNT(mount_cmd) &&
+	    brand_prestatechg(zlogp, zstate, Z_READY) != 0)
 		return (-1);
 
 	if ((err = zonecfg_create_snapshot(zone_name)) != Z_OK) {
@@ -561,7 +580,8 @@
 		goto bad;
 	}
 
-	if (brand_poststatechg(zlogp, zstate, Z_READY) != 0)
+	if (!ALT_MOUNT(mount_cmd) &&
+	    brand_poststatechg(zlogp, zstate, Z_READY) != 0)
 		goto bad;
 
 	return (0);
@@ -571,7 +591,8 @@
 	 * If something goes wrong, we up the zones's state to the target
 	 * state, READY, and then invoke the hook as if we're halting.
 	 */
-	(void) brand_poststatechg(zlogp, ZONE_STATE_READY, Z_HALT);
+	if (!ALT_MOUNT(mount_cmd))
+		(void) brand_poststatechg(zlogp, ZONE_STATE_READY, Z_HALT);
 	return (-1);
 }
 
@@ -623,15 +644,8 @@
 
 	/* determine the zone rootpath */
 	if (mount_cmd) {
-		char zonepath[MAXPATHLEN];
 		char luroot[MAXPATHLEN];
 
-		if (zone_get_zonepath(zone_name,
-		    zonepath, sizeof (zonepath)) != Z_OK) {
-			zerror(zlogp, B_FALSE, "unable to determine zone path");
-			return (-1);
-		}
-
 		(void) snprintf(luroot, sizeof (luroot), "%s/lu", zonepath);
 		resolve_lofs(zlogp, luroot, sizeof (luroot));
 		(void) strlcpy(rootpath, luroot, sizeof (rootpath));
@@ -686,6 +700,8 @@
 		char opt_buf[MAX_MNTOPT_STR];
 		int optlen = 0;
 		int mflag = MS_DATA;
+		int i;
+		int ret;
 
 		(void) ct_tmpl_clear(tmpl_fd);
 		/*
@@ -713,9 +729,26 @@
 			optlen = MAX_MNTOPT_STR;
 			mflag = MS_OPTIONSTR;
 		}
-		if (mount(spec, dir, mflag, fstype, NULL, 0, opt, optlen) != 0)
-			_exit(errno);
-		_exit(0);
+
+		/*
+		 * There is an obscure race condition which can cause mount
+		 * to return EBUSY. This happens for example on the mount
+		 * of the zone's /etc/svc/volatile file system if there is
+		 * a GZ process running svcs -Z, which will touch the
+		 * mountpoint, just as we're trying to do the mount. To cope
+		 * with this, we retry up to 3 times to let this transient
+		 * process get out of the way.
+		 */
+		for (i = 0; i < 3; i++) {
+			ret = 0;
+			if (mount(spec, dir, mflag, fstype, NULL, 0, opt,
+			    optlen) != 0)
+				ret = errno;
+			if (ret != EBUSY)
+				break;
+			(void) sleep(1);
+		}
+		_exit(ret);
 	}
 
 	/* parent */
@@ -739,6 +772,115 @@
 }
 
 /*
+ * env variable name format
+ *	_ZONECFG;{resource name};{identifying attr. name};{property name}
+ */
+static void
+set_zonecfg_env(char *rsrc, char *attr, char *name, char *val)
+{
+	char *p;
+	/* Enough for maximal name, rsrc + attr, & slop for ZONECFG & _'s */
+	char nm[2 * MAXNAMELEN + 32];
+
+	if (attr == NULL)
+		(void) snprintf(nm, sizeof (nm), "_ZONECFG_%s_%s", rsrc,
+		    name);
+	else
+		(void) snprintf(nm, sizeof (nm), "_ZONECFG_%s_%s_%s", rsrc,
+		    attr, name);
+
+	p = nm;
+	while ((p = strchr(p, '-')) != NULL)
+		*p++ = '_';
+
+	(void) setenv(nm, val, 1);
+}
+
+/*
+ * Export zonecfg network and device properties into environment for the boot
+ * and state change hooks.
+ * If debug is true, export the brand hook debug env. variable as well.
+ *
+ * We could export more of the config in the future, as necessary.
+ */
+static int
+setup_subproc_env()
+{
+	int res;
+	zone_dochandle_t handle;
+	struct zone_nwiftab ntab;
+	struct zone_devtab dtab;
+	char net_resources[MAXNAMELEN * 2];
+	char dev_resources[MAXNAMELEN * 2];
+
+	if ((handle = zonecfg_init_handle()) == NULL)
+		exit(Z_NOMEM);
+
+	if ((res = zonecfg_get_handle(zone_name, handle)) != Z_OK)
+		goto done;
+
+	if ((res = zonecfg_setnwifent(handle)) != Z_OK)
+		goto done;
+
+	while (zonecfg_getnwifent(handle, &ntab) == Z_OK) {
+		struct zone_res_attrtab *rap;
+		char *phys;
+
+		phys = ntab.zone_nwif_physical;
+
+		(void) strlcat(net_resources, phys, sizeof (net_resources));
+		(void) strlcat(net_resources, " ", sizeof (net_resources));
+
+		set_zonecfg_env(RSRC_NET, phys, "physical", phys);
+
+		set_zonecfg_env(RSRC_NET, phys, "address",
+		    ntab.zone_nwif_address);
+		set_zonecfg_env(RSRC_NET, phys, "allowed-address",
+		    ntab.zone_nwif_allowed_address);
+		set_zonecfg_env(RSRC_NET, phys, "defrouter",
+		    ntab.zone_nwif_defrouter);
+		set_zonecfg_env(RSRC_NET, phys, "global-nic",
+		    ntab.zone_nwif_gnic);
+		set_zonecfg_env(RSRC_NET, phys, "mac-addr", ntab.zone_nwif_mac);
+		set_zonecfg_env(RSRC_NET, phys, "vlan-id",
+		    ntab.zone_nwif_vlan_id);
+
+		for (rap = ntab.zone_nwif_attrp; rap != NULL;
+		    rap = rap->zone_res_attr_next)
+			set_zonecfg_env(RSRC_NET, phys, rap->zone_res_attr_name,
+			    rap->zone_res_attr_value);
+	}
+
+	(void) zonecfg_endnwifent(handle);
+
+	if ((res = zonecfg_setdevent(handle)) != Z_OK)
+		goto done;
+
+	while (zonecfg_getdevent(handle, &dtab) == Z_OK) {
+		struct zone_res_attrtab *rap;
+		char *match;
+
+		match = dtab.zone_dev_match;
+
+		(void) strlcat(dev_resources, match, sizeof (dev_resources));
+		(void) strlcat(dev_resources, " ", sizeof (dev_resources));
+
+		for (rap = dtab.zone_dev_attrp; rap != NULL;
+		    rap = rap->zone_res_attr_next)
+			set_zonecfg_env(RSRC_DEV, match,
+			    rap->zone_res_attr_name, rap->zone_res_attr_value);
+	}
+
+	(void) zonecfg_enddevent(handle);
+
+	res = Z_OK;
+
+done:
+	zonecfg_fini_handle(handle);
+	return (res);
+}
+
+/*
  * If retstr is not NULL, the output of the subproc is returned in the str,
  * otherwise it is output using zerror().  Any memory allocated for retstr
  * should be freed by the caller.
@@ -763,6 +905,11 @@
 		inbuf = buf;
 	}
 
+	if (setup_subproc_env() != Z_OK) {
+		zerror(zlogp, B_FALSE, "failed to setup environment");
+		return (-1);
+	}
+
 	file = popen(cmdbuf, "r");
 	if (file == NULL) {
 		zerror(zlogp, B_TRUE, "could not launch: %s", cmdbuf);
@@ -802,22 +949,48 @@
 	return (WEXITSTATUS(status));
 }
 
+/*
+ * Get the app-svc-dependent flag for this zone's init process. This is a
+ * zone-specific attr which controls the type of contract we create for the
+ * zone's init. When true, the contract will include CT_PR_EV_EXIT in the fatal
+ * set, so that when any service which is in the same contract exits, the init
+ * application will be terminated.
+ *
+ * We use the global "snap_hndl", so no parameters get passed here.
+ */
+static boolean_t
+is_app_svc_dep(void)
+{
+	struct zone_attrtab a;
+
+	bzero(&a, sizeof (a));
+	(void) strlcpy(a.zone_attr_name, "app-svc-dependent",
+	    sizeof (a.zone_attr_name));
+
+	if (zonecfg_lookup_attr(snap_hndl, &a) == Z_OK &&
+	    strcmp(a.zone_attr_value, "true") == 0) {
+		return (B_TRUE);
+	}
+
+	return (B_FALSE);
+}
+
 static int
 zone_bootup(zlog_t *zlogp, const char *bootargs, int zstate)
 {
 	zoneid_t zoneid;
 	struct stat st;
-	char zpath[MAXPATHLEN], initpath[MAXPATHLEN], init_file[MAXPATHLEN];
+	char rpath[MAXPATHLEN], initpath[MAXPATHLEN], init_file[MAXPATHLEN];
 	char nbootargs[BOOTARGS_MAX];
 	char cmdbuf[MAXPATHLEN];
 	fs_callback_t cb;
 	brand_handle_t bh;
 	zone_iptype_t iptype;
-	boolean_t links_loaded = B_FALSE;
 	dladm_status_t status;
 	char errmsg[DLADM_STRSIZE];
 	int err;
 	boolean_t restart_init;
+	boolean_t app_svc_dep;
 
 	if (brand_prestatechg(zlogp, zstate, Z_BOOT) != 0)
 		return (-1);
@@ -852,13 +1025,8 @@
 	/*
 	 * Get the brand's boot callback if it exists.
 	 */
-	if (zone_get_zonepath(zone_name, zpath, sizeof (zpath)) != Z_OK) {
-		zerror(zlogp, B_FALSE, "unable to determine zone path");
-		brand_close(bh);
-		goto bad;
-	}
 	(void) strcpy(cmdbuf, EXEC_PREFIX);
-	if (brand_get_boot(bh, zone_name, zpath, cmdbuf + EXEC_LEN,
+	if (brand_get_boot(bh, zone_name, zonepath, cmdbuf + EXEC_LEN,
 	    sizeof (cmdbuf) - EXEC_LEN) != 0) {
 		zerror(zlogp, B_FALSE,
 		    "unable to determine branded zone's boot callback");
@@ -877,31 +1045,41 @@
 	/* See if this zone's brand should restart init if it dies. */
 	restart_init = brand_restartinit(bh);
 
+	/*
+	 * See if we need to setup contract dependencies between the zone's
+	 * primary application and any of its services.
+	 */
+	app_svc_dep = is_app_svc_dep();
+
 	brand_close(bh);
 
-	err = filter_bootargs(zlogp, bootargs, nbootargs, init_file,
-	    bad_boot_arg);
-	if (err == Z_INVAL)
-		eventstream_write(Z_EVT_ZONE_BADARGS);
-	else if (err != Z_OK)
+	err = filter_bootargs(zlogp, bootargs, nbootargs, init_file);
+	if (err != Z_OK)
 		goto bad;
 
 	assert(init_file[0] != '\0');
 
-	/* Try to anticipate possible problems: Make sure init is executable. */
-	if (zone_get_rootpath(zone_name, zpath, sizeof (zpath)) != Z_OK) {
+	/*
+	 * Try to anticipate possible problems: If possible, make sure init is
+	 * executable.
+	 */
+	if (zone_get_rootpath(zone_name, rpath, sizeof (rpath)) != Z_OK) {
 		zerror(zlogp, B_FALSE, "unable to determine zone root");
 		goto bad;
 	}
 
-	(void) snprintf(initpath, sizeof (initpath), "%s%s", zpath, init_file);
+	(void) snprintf(initpath, sizeof (initpath), "%s%s", rpath, init_file);
 
-	if (stat(initpath, &st) == -1) {
+	if (lstat(initpath, &st) == -1) {
 		zerror(zlogp, B_TRUE, "could not stat %s", initpath);
 		goto bad;
 	}
 
-	if ((st.st_mode & S_IXUSR) == 0) {
+	/*
+	 * If a symlink, we'll have to wait and resolve when we boot,
+	 * otherwise check the executable bits now.
+	 */
+	if ((st.st_mode & S_IFMT) != S_IFLNK && (st.st_mode & S_IXUSR) == 0) {
 		zerror(zlogp, B_FALSE, "%s is not executable", initpath);
 		goto bad;
 	}
@@ -919,7 +1097,6 @@
 			    " %s", dladm_status2str(status, errmsg));
 			goto bad;
 		}
-		links_loaded = B_TRUE;
 	}
 
 	/*
@@ -949,6 +1126,12 @@
 		goto bad;
 	}
 
+	if (app_svc_dep && zone_setattr(zoneid, ZONE_ATTR_APP_SVC_CT,
+	    (void *)B_TRUE, sizeof (boolean_t)) == -1) {
+		zerror(zlogp, B_TRUE, "could not set zone app-die");
+		goto bad;
+	}
+
 	/*
 	 * Inform zonestatd of a new zone so that it can install a door for
 	 * the zone to contact it.
@@ -963,6 +1146,12 @@
 	if (brand_poststatechg(zlogp, zstate, Z_BOOT) != 0)
 		goto bad;
 
+	/* Startup a thread to perform zfd logging/tty svc for the zone. */
+	create_log_thread(zlogp, zone_id);
+
+	/* Startup a thread to perform memory capping for the zone. */
+	create_mcap_thread(zlogp, zone_id);
+
 	return (0);
 
 bad:
@@ -971,8 +1160,7 @@
 	 * state, RUNNING, and then invoke the hook as if we're halting.
 	 */
 	(void) brand_poststatechg(zlogp, ZONE_STATE_RUNNING, Z_HALT);
-	if (links_loaded)
-		(void) dladm_zone_halt(dld_handle, zoneid);
+
 	return (-1);
 }
 
@@ -981,22 +1169,31 @@
 {
 	int err;
 
-	if (brand_prestatechg(zlogp, zstate, Z_HALT) != 0)
+	if (unmount_cmd == B_FALSE &&
+	    brand_prestatechg(zlogp, zstate, Z_HALT) != 0)
 		return (-1);
 
+	/* Shutting down, stop the memcap thread */
+	destroy_mcap_thread();
+
 	if (vplat_teardown(zlogp, unmount_cmd, rebooting) != 0) {
 		if (!bringup_failure_recovery)
 			zerror(zlogp, B_FALSE, "unable to destroy zone");
+		destroy_log_thread();
 		return (-1);
 	}
 
+	/* Shut down is done, stop the log thread */
+	destroy_log_thread();
+
+	if (unmount_cmd == B_FALSE &&
+	    brand_poststatechg(zlogp, zstate, Z_HALT) != 0)
+		return (-1);
+
 	if ((err = zonecfg_destroy_snapshot(zone_name)) != Z_OK)
 		zerror(zlogp, B_FALSE, "destroying snapshot: %s",
 		    zonecfg_strerror(err));
 
-	if (brand_poststatechg(zlogp, zstate, Z_HALT) != 0)
-		return (-1);
-
 	return (0);
 }
 
@@ -1007,7 +1204,6 @@
 	pid_t child;
 	char cmdbuf[MAXPATHLEN];
 	brand_handle_t bh = NULL;
-	char zpath[MAXPATHLEN];
 	ctid_t ct;
 	int tmpl_fd;
 	int child_status;
@@ -1028,18 +1224,12 @@
 		return (-1);
 	}
 
-	if (zone_get_zonepath(zone_name, zpath, sizeof (zpath)) != Z_OK) {
-		zerror(zlogp, B_FALSE, "unable to determine zone path");
-		brand_close(bh);
-		return (-1);
-	}
-
 	/*
 	 * If there is a brand 'shutdown' callback, execute it now to give the
 	 * brand a chance to cleanup any custom configuration.
 	 */
 	(void) strcpy(cmdbuf, EXEC_PREFIX);
-	if (brand_get_shutdown(bh, zone_name, zpath, cmdbuf + EXEC_LEN,
+	if (brand_get_shutdown(bh, zone_name, zonepath, cmdbuf + EXEC_LEN,
 	    sizeof (cmdbuf) - EXEC_LEN) != 0 || strlen(cmdbuf) <= EXEC_LEN) {
 		(void) strcat(cmdbuf, SHUTDOWN_DEFAULT);
 	}
@@ -1177,6 +1367,36 @@
 }
 
 /*
+ * Log the exit time and status of the zone's init process into
+ * {zonepath}/lastexited. If the zone shutdown normally, the exit status will
+ * be -1, otherwise it will be the exit status as described in wait.3c.
+ * If the zone is configured to restart init, then nothing will be logged if
+ * init exits unexpectedly (the kernel will never upcall in this case).
+ */
+static void
+log_init_exit(int status)
+{
+	char p[MAXPATHLEN];
+	char buf[128];
+	struct timeval t;
+	int fd;
+
+	if (snprintf(p, sizeof (p), "%s/lastexited", zonepath) > sizeof (p))
+		return;
+	if (gettimeofday(&t, NULL) != 0)
+		return;
+	if (snprintf(buf, sizeof (buf), "%ld.%ld %d\n", t.tv_sec, t.tv_usec,
+	    status) > sizeof (buf))
+		return;
+	if ((fd = open(p, O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0)
+		return;
+
+	(void) write(fd, buf, strlen(buf));
+
+	(void) close(fd);
+}
+
+/*
  * The main routine for the door server that deals with zone state transitions.
  */
 /* ARGSUSED */
@@ -1189,6 +1409,7 @@
 
 	zone_state_t zstate;
 	zone_cmd_t cmd;
+	int init_status;
 	zone_cmd_arg_t *zargp;
 
 	boolean_t kernelcall;
@@ -1241,6 +1462,7 @@
 		goto out;
 	}
 	cmd = zargp->cmd;
+	init_status = zargp->status;
 
 	if (door_ucred(&uc) != 0) {
 		zerror(&logsys, B_TRUE, "door_ucred");
@@ -1350,6 +1572,7 @@
 			rval = zone_ready(zlogp, Z_MNT_BOOT, zstate);
 			if (rval == 0)
 				eventstream_write(Z_EVT_ZONE_READIED);
+			zcons_statechanged();
 			break;
 		case Z_BOOT:
 		case Z_FORCEBOOT:
@@ -1360,6 +1583,7 @@
 				    zstate);
 			}
 			audit_put_record(zlogp, uc, rval, "boot");
+			zcons_statechanged();
 			if (rval != 0) {
 				bringup_failure_recovery = B_TRUE;
 				(void) zone_halt(zlogp, B_FALSE, B_FALSE,
@@ -1482,6 +1706,7 @@
 			eventstream_write(Z_EVT_ZONE_BOOTING);
 			rval = zone_bootup(zlogp, zargp->bootbuf, zstate);
 			audit_put_record(zlogp, uc, rval, "boot");
+			zcons_statechanged();
 			if (rval != 0) {
 				bringup_failure_recovery = B_TRUE;
 				(void) zone_halt(zlogp, B_FALSE, B_TRUE,
@@ -1496,6 +1721,7 @@
 			if ((rval = zone_halt(zlogp, B_FALSE, B_FALSE, zstate))
 			    != 0)
 				break;
+			zcons_statechanged();
 			eventstream_write(Z_EVT_ZONE_HALTED);
 			break;
 		case Z_SHUTDOWN:
@@ -1543,6 +1769,7 @@
 			if ((rval = zone_halt(zlogp, B_FALSE, B_TRUE, zstate))
 			    != 0)
 				break;
+			zcons_statechanged();
 			if ((rval = zone_ready(zlogp, Z_MNT_BOOT, zstate)) == 0)
 				eventstream_write(Z_EVT_ZONE_READIED);
 			else
@@ -1559,10 +1786,16 @@
 			rval = 0;
 			break;
 		case Z_HALT:
+			if (kernelcall) {
+				log_init_exit(init_status);
+			} else {
+				log_init_exit(-1);
+			}
 			if ((rval = zone_halt(zlogp, B_FALSE, B_FALSE, zstate))
 			    != 0)
 				break;
 			eventstream_write(Z_EVT_ZONE_HALTED);
+			zcons_statechanged();
 			break;
 		case Z_REBOOT:
 			(void) strlcpy(boot_args, zargp->bootbuf,
@@ -1574,8 +1807,9 @@
 				boot_args[0] = '\0';
 				break;
 			}
-			if ((rval = zone_ready(zlogp, Z_MNT_BOOT, zstate))
-			    != 0) {
+			zcons_statechanged();
+			if ((rval = zone_ready(zlogp, Z_MNT_BOOT, zstate)) !=
+			    0) {
 				eventstream_write(Z_EVT_ZONE_BOOTFAILED);
 				boot_args[0] = '\0';
 				break;
@@ -1759,11 +1993,38 @@
 		 * state.
 		 */
 		if (zstate > ZONE_STATE_INSTALLED) {
+			static zoneid_t zid;
+
 			zerror(zlogp, B_FALSE,
 			    "zone '%s': WARNING: zone is in state '%s', but "
 			    "zoneadmd does not appear to be available; "
 			    "restarted zoneadmd to recover.",
 			    zone_name, zone_state_str(zstate));
+
+			/*
+			 * Startup a thread to perform the zfd logging/tty svc
+			 * and a thread to perform memory capping for the
+			 * zone. zlogp won't be valid for much longer so use
+			 * logsys.
+			 */
+			if ((zid = getzoneidbyname(zone_name)) != -1) {
+				create_log_thread(&logsys, zid);
+				create_mcap_thread(&logsys, zid);
+			}
+
+			/* recover the global configuration snapshot */
+			if (snap_hndl == NULL) {
+				if ((snap_hndl = zonecfg_init_handle())
+				    == NULL ||
+				    zonecfg_create_snapshot(zone_name)
+				    != Z_OK ||
+				    zonecfg_get_snapshot_handle(zone_name,
+				    snap_hndl) != Z_OK) {
+					zerror(zlogp, B_FALSE, "recovering "
+					    "zone configuration handle");
+					goto out;
+				}
+			}
 		}
 
 		(void) fdetach(zone_door_path);
@@ -1783,15 +2044,10 @@
 static int
 brand_callback_init(brand_handle_t bh, char *zone_name)
 {
-	char zpath[MAXPATHLEN];
-
-	if (zone_get_zonepath(zone_name, zpath, sizeof (zpath)) != Z_OK)
-		return (-1);
-
 	(void) strlcpy(pre_statechg_hook, EXEC_PREFIX,
 	    sizeof (pre_statechg_hook));
 
-	if (brand_get_prestatechange(bh, zone_name, zpath,
+	if (brand_get_prestatechange(bh, zone_name, zonepath,
 	    pre_statechg_hook + EXEC_LEN,
 	    sizeof (pre_statechg_hook) - EXEC_LEN) != 0)
 		return (-1);
@@ -1802,7 +2058,7 @@
 	(void) strlcpy(post_statechg_hook, EXEC_PREFIX,
 	    sizeof (post_statechg_hook));
 
-	if (brand_get_poststatechange(bh, zone_name, zpath,
+	if (brand_get_poststatechange(bh, zone_name, zonepath,
 	    post_statechg_hook + EXEC_LEN,
 	    sizeof (post_statechg_hook) - EXEC_LEN) != 0)
 		return (-1);
@@ -1813,7 +2069,7 @@
 	(void) strlcpy(query_hook, EXEC_PREFIX,
 	    sizeof (query_hook));
 
-	if (brand_get_query(bh, zone_name, zpath, query_hook + EXEC_LEN,
+	if (brand_get_query(bh, zone_name, zonepath, query_hook + EXEC_LEN,
 	    sizeof (query_hook) - EXEC_LEN) != 0)
 		return (-1);
 
@@ -1941,6 +2197,11 @@
 		return (1);
 	}
 
+	if (zone_get_zonepath(zone_name, zonepath, sizeof (zonepath)) != Z_OK) {
+		zerror(zlogp, B_FALSE, "unable to determine zone path");
+		return (-1);
+	}
+
 	if (zonecfg_default_brand(default_brand,
 	    sizeof (default_brand)) != Z_OK) {
 		zerror(zlogp, B_FALSE, "unable to determine default brand");
--- old/usr/src/cmd/zoneadmd/zoneadmd.h	Wed Oct  5 00:31:43 2016
+++ new/usr/src/cmd/zoneadmd/zoneadmd.h	Wed Oct  5 00:31:43 2016
@@ -22,6 +22,7 @@
 /*
  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 
 #ifndef	_ZONEADMD_H
@@ -69,6 +70,7 @@
 #define	DEFAULT_DIR_USER -1	/* user ID for chown: -1 means don't change */
 #define	DEFAULT_DIR_GROUP -1	/* grp ID for chown: -1 means don't change */
 
+#define	ALT_MOUNT(mount_cmd)	((mount_cmd) != Z_MNT_BOOT)
 
 typedef struct zlog {
 	FILE *logfile;	/* file to log to */
@@ -90,11 +92,12 @@
 extern boolean_t in_death_throes;
 extern boolean_t bringup_failure_recovery;
 extern char *zone_name;
+extern char zonepath[MAXNAMELEN];
+extern zone_dochandle_t snap_hndl;
 extern char pool_name[MAXNAMELEN];
 extern char brand_name[MAXNAMELEN];
 extern char default_brand[MAXNAMELEN];
 extern char boot_args[BOOTARGS_MAX];
-extern char bad_boot_arg[BOOTARGS_MAX];
 extern boolean_t zone_isnative;
 extern boolean_t zone_iscluster;
 extern dladm_handle_t dld_handle;
@@ -112,8 +115,7 @@
 	Z_EVT_ZONE_HALTED,
 	Z_EVT_ZONE_READIED,
 	Z_EVT_ZONE_UNINSTALLING,
-	Z_EVT_ZONE_BOOTFAILED,
-	Z_EVT_ZONE_BADARGS
+	Z_EVT_ZONE_BOOTFAILED
 } zone_evt_t;
 
 extern int eventstream_init();
@@ -154,8 +156,21 @@
  */
 extern int init_console(zlog_t *);
 extern void serve_console(zlog_t *);
+extern void zcons_statechanged();
 
 /*
+ * Memory capping thread creation.
+ */
+extern void create_mcap_thread(zlog_t *, zoneid_t);
+extern void destroy_mcap_thread();
+
+/*
+ * Zone FD log thread creation.
+ */
+extern void create_log_thread(zlog_t *, zoneid_t);
+extern void destroy_log_thread();
+
+/*
  * Contract handling.
  */
 extern int init_template(void);
--- old/usr/src/cmd/zonecfg/zonecfg.c	Wed Oct  5 00:31:43 2016
+++ new/usr/src/cmd/zonecfg/zonecfg.c	Wed Oct  5 00:31:43 2016
@@ -23,6 +23,7 @@
  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
  * Copyright 2014 Gary Mills
+ * Copyright 2016, Joyent Inc.
  */
 
 /*
@@ -234,6 +235,11 @@
 	"fs-allowed",
 	ALIAS_MAXPROCS,
 	"allowed-address",
+	ALIAS_ZFSPRI,
+	"mac-addr",
+	"vlan-id",
+	"global-nic",
+	"property",
 	NULL
 };
 
@@ -406,9 +412,20 @@
 	"exit",
 	"help",
 	"info",
+	"add property ",
+	"clear allowed-address",
+	"clear defrouter",
+	"clear global-nic",
+	"clear mac-addr",
+	"clear vlan-id",
+	"remove property ",
 	"set address=",
-	"set physical=",
+	"set allowed-address=",
 	"set defrouter=",
+	"set global-nic=",
+	"set mac-addr=",
+	"set physical=",
+	"set vlan-id=",
 	NULL
 };
 
@@ -418,6 +435,7 @@
 	"exit",
 	"help",
 	"info",
+	"add property ",
 	"set match=",
 	NULL
 };
@@ -579,7 +597,6 @@
 static struct zone_attrtab	old_attrtab, in_progress_attrtab;
 static struct zone_dstab	old_dstab, in_progress_dstab;
 static struct zone_psettab	old_psettab, in_progress_psettab;
-static struct zone_mcaptab	old_mcaptab, in_progress_mcaptab;
 static struct zone_admintab	old_admintab, in_progress_admintab;
 
 static GetLine *gl;	/* The gl_get_line() resource object */
@@ -1078,11 +1095,20 @@
 			(void) fprintf(fp, gettext("Valid commands:\n"));
 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
 			    pt_to_str(PT_ADDRESS), gettext("<IP-address>"));
+			(void) fprintf(fp, "\t%s %s (%s=<value>,%s=<value>)\n",
+			    cmd_to_str(CMD_ADD), pt_to_str(PT_NPROP),
+			    pt_to_str(PT_NAME), pt_to_str(PT_VALUE));
 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
 			    pt_to_str(PT_ALLOWED_ADDRESS),
 			    gettext("<IP-address>"));
 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
 			    pt_to_str(PT_PHYSICAL), gettext("<interface>"));
+			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
+			    pt_to_str(PT_MAC), gettext("<mac-address>"));
+			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
+			    pt_to_str(PT_GNIC), gettext("<global zone NIC>"));
+			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
+			    pt_to_str(PT_VLANID), gettext("<vlan ID>"));
 			(void) fprintf(fp, gettext("See ifconfig(1M) for "
 			    "details of the <interface> string.\n"));
 			(void) fprintf(fp, gettext("%s %s is valid "
@@ -1090,10 +1116,12 @@
 			    "must not be set.\n"),
 			    cmd_to_str(CMD_SET), pt_to_str(PT_ADDRESS),
 			    pt_to_str(PT_IPTYPE), gettext("shared"));
-			(void) fprintf(fp, gettext("%s %s is valid "
-			    "if the %s property is set to %s, otherwise it "
-			    "must not be set.\n"),
-			    cmd_to_str(CMD_SET), pt_to_str(PT_ALLOWED_ADDRESS),
+			(void) fprintf(fp, gettext("%s (%s, %s, %s, %s) are "
+			    "valid if the %s property is set to %s, otherwise "
+			    "they must not be set.\n"),
+			    cmd_to_str(CMD_SET),
+			    pt_to_str(PT_ALLOWED_ADDRESS), pt_to_str(PT_MAC),
+			    pt_to_str(PT_VLANID), pt_to_str(PT_GNIC),
 			    pt_to_str(PT_IPTYPE), gettext("exclusive"));
 			(void) fprintf(fp, gettext("\t%s %s=%s\n%s %s "
 			    "is valid if the %s or %s property is set, "
@@ -1109,6 +1137,9 @@
 			    "used to configure a device node.\n"),
 			    rt_to_str(resource_scope));
 			(void) fprintf(fp, gettext("Valid commands:\n"));
+			(void) fprintf(fp, "\t%s %s (%s=<value>,%s=<value>)\n",
+			    cmd_to_str(CMD_ADD), pt_to_str(PT_NPROP),
+			    pt_to_str(PT_NAME), pt_to_str(PT_VALUE));
 			(void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET),
 			    pt_to_str(PT_MATCH), gettext("<device-path>"));
 			break;
@@ -1336,11 +1367,14 @@
 		    rt_to_str(RT_FS), pt_to_str(PT_DIR),
 		    pt_to_str(PT_SPECIAL), pt_to_str(PT_RAW),
 		    pt_to_str(PT_TYPE), pt_to_str(PT_OPTIONS));
-		(void) fprintf(fp, "\t%s\t\t%s, %s, %s|%s\n", rt_to_str(RT_NET),
+		(void) fprintf(fp, "\t%s\t\t%s, %s, %s, %s, %s, %s, %s %s\n",
+		    rt_to_str(RT_NET),
 		    pt_to_str(PT_ADDRESS), pt_to_str(PT_ALLOWED_ADDRESS),
-		    pt_to_str(PT_PHYSICAL), pt_to_str(PT_DEFROUTER));
-		(void) fprintf(fp, "\t%s\t\t%s\n", rt_to_str(RT_DEVICE),
-		    pt_to_str(PT_MATCH));
+		    pt_to_str(PT_GNIC), pt_to_str(PT_MAC),
+		    pt_to_str(PT_PHYSICAL), pt_to_str(PT_NPROP),
+		    pt_to_str(PT_VLANID), pt_to_str(PT_DEFROUTER));
+		(void) fprintf(fp, "\t%s\t\t%s, %s\n", rt_to_str(RT_DEVICE),
+		    pt_to_str(PT_MATCH), pt_to_str(PT_NPROP));
 		(void) fprintf(fp, "\t%s\t\t%s, %s\n", rt_to_str(RT_RCTL),
 		    pt_to_str(PT_NAME), pt_to_str(PT_VALUE));
 		(void) fprintf(fp, "\t%s\t\t%s, %s, %s\n", rt_to_str(RT_ATTR),
@@ -1395,6 +1429,9 @@
 	if (zonecfg_check_handle(handle) != Z_OK) {
 		if ((err = zonecfg_get_handle(zone, handle)) == Z_OK) {
 			got_handle = B_TRUE;
+
+			(void) zonecfg_fix_obsolete(handle);
+
 			if (zonecfg_get_brand(handle, brandname,
 			    sizeof (brandname)) != Z_OK) {
 				zerr("Zone %s is inconsistent: missing "
@@ -1695,6 +1732,17 @@
 			(void) strlcpy(zone_template, optarg,
 			    sizeof (zone_template));
 			break;
+		case 'X':
+			(void) snprintf(zone_template, sizeof (zone_template),
+			    "%s/%s.xml", ZONE_CONFIG_ROOT, zone);
+			err = zonecfg_get_xml_handle(zone_template, handle);
+			if (err != Z_OK) {
+				zone_perror(execname, err, B_TRUE);
+				exit(Z_ERR);
+			}
+			got_handle = B_TRUE;
+			need_to_commit = B_TRUE;
+			return;
 		default:
 			short_usage(CMD_CREATE);
 			arg_err = B_TRUE;
@@ -1797,8 +1845,8 @@
 	struct zone_rctltab rctltab;
 	struct zone_dstab dstab;
 	struct zone_psettab psettab;
-	struct zone_mcaptab mcaptab;
 	struct zone_rctlvaltab *valptr;
+	struct zone_res_attrtab *rap;
 	struct zone_admintab admintab;
 	int err, arg;
 	char zonepath[MAXPATHLEN], outfile[MAXPATHLEN], pool[MAXNAMELEN];
@@ -1968,7 +2016,17 @@
 		export_prop(of, PT_ALLOWED_ADDRESS,
 		    nwiftab.zone_nwif_allowed_address);
 		export_prop(of, PT_PHYSICAL, nwiftab.zone_nwif_physical);
+		export_prop(of, PT_MAC, nwiftab.zone_nwif_mac);
+		export_prop(of, PT_VLANID, nwiftab.zone_nwif_vlan_id);
+		export_prop(of, PT_GNIC, nwiftab.zone_nwif_gnic);
 		export_prop(of, PT_DEFROUTER, nwiftab.zone_nwif_defrouter);
+		for (rap = nwiftab.zone_nwif_attrp; rap != NULL;
+		    rap = rap->zone_res_attr_next) {
+			fprintf(of, "%s %s (%s=%s,%s=\"%s\")\n",
+			    cmd_to_str(CMD_ADD), pt_to_str(PT_NPROP),
+			    pt_to_str(PT_NAME), rap->zone_res_attr_name,
+			    pt_to_str(PT_VALUE), rap->zone_res_attr_value);
+		}
 		(void) fprintf(of, "%s\n", cmd_to_str(CMD_END));
 	}
 	(void) zonecfg_endnwifent(handle);
@@ -1981,21 +2039,17 @@
 		(void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD),
 		    rt_to_str(RT_DEVICE));
 		export_prop(of, PT_MATCH, devtab.zone_dev_match);
+		for (rap = devtab.zone_dev_attrp; rap != NULL;
+		    rap = rap->zone_res_attr_next) {
+			fprintf(of, "%s %s (%s=%s,%s=\"%s\")\n",
+			    cmd_to_str(CMD_ADD), pt_to_str(PT_NPROP),
+			    pt_to_str(PT_NAME), rap->zone_res_attr_name,
+			    pt_to_str(PT_VALUE), rap->zone_res_attr_value);
+		}
 		(void) fprintf(of, "%s\n", cmd_to_str(CMD_END));
 	}
 	(void) zonecfg_enddevent(handle);
 
-	if (zonecfg_getmcapent(handle, &mcaptab) == Z_OK) {
-		char buf[128];
-
-		(void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD),
-		    rt_to_str(RT_MCAP));
-		bytes_to_units(mcaptab.zone_physmem_cap, buf, sizeof (buf));
-		(void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET),
-		    pt_to_str(PT_PHYSICAL), buf);
-		(void) fprintf(of, "%s\n", cmd_to_str(CMD_END));
-	}
-
 	if ((err = zonecfg_setrctlent(handle)) != Z_OK) {
 		zone_perror(zone, err, B_FALSE);
 		goto done;
@@ -2149,7 +2203,6 @@
 {
 	int type;
 	struct zone_psettab tmp_psettab;
-	struct zone_mcaptab tmp_mcaptab;
 	uint64_t tmp;
 	uint64_t tmp_mcap;
 	char pool[MAXNAMELEN];
@@ -2241,9 +2294,10 @@
 		 * Make sure there isn't already a mem-cap entry or max-swap
 		 * or max-locked rctl.
 		 */
-		if (zonecfg_lookup_mcap(handle, &tmp_mcaptab) == Z_OK ||
-		    zonecfg_get_aliased_rctl(handle, ALIAS_MAXSWAP, &tmp_mcap)
-		    == Z_OK ||
+		if (zonecfg_get_aliased_rctl(handle, ALIAS_MAXSWAP,
+		    &tmp_mcap) == Z_OK ||
+		    zonecfg_get_aliased_rctl(handle, ALIAS_MAXPHYSMEM,
+		    &tmp_mcap) == Z_OK ||
 		    zonecfg_get_aliased_rctl(handle, ALIAS_MAXLOCKEDMEM,
 		    &tmp_mcap) == Z_OK) {
 			zerr(gettext("The %s resource or a related resource "
@@ -2256,7 +2310,6 @@
 			    "to even the root user; "
 			    "this could render the system impossible\n"
 			    "to administer.  Please use caution."));
-		bzero(&in_progress_mcaptab, sizeof (in_progress_mcaptab));
 		return;
 	case RT_ADMIN:
 		bzero(&in_progress_admintab, sizeof (in_progress_admintab));
@@ -2359,7 +2412,69 @@
 	zonecfg_free_rctl_value_list(rctlvaltab);
 }
 
+/*
+ * Resource attribute ("property" resource embedded on net or dev resource)
+ */
 static void
+do_res_attr(struct zone_res_attrtab **headp, complex_property_ptr_t cpp)
+{
+	complex_property_ptr_t cp;
+	struct zone_res_attrtab *np;
+	int err;
+	boolean_t seen_name = B_FALSE, seen_value = B_FALSE;
+
+	if ((np = calloc(1, sizeof (struct zone_res_attrtab))) == NULL) {
+		zone_perror(zone, Z_NOMEM, B_TRUE);
+		exit(Z_ERR);
+	}
+
+	for (cp = cpp; cp != NULL; cp = cp->cp_next) {
+		switch (cp->cp_type) {
+		case PT_NAME:
+			if (seen_name) {
+				zerr(gettext("%s already specified"),
+				    pt_to_str(PT_NAME));
+				goto bad;
+			}
+			(void) strlcpy(np->zone_res_attr_name, cp->cp_value,
+			    sizeof (np->zone_res_attr_name));
+			seen_name = B_TRUE;
+			break;
+		case PT_VALUE:
+			if (seen_value) {
+				zerr(gettext("%s already specified"),
+				    pt_to_str(PT_VALUE));
+				goto bad;
+			}
+			(void) strlcpy(np->zone_res_attr_value, cp->cp_value,
+			    sizeof (np->zone_res_attr_value));
+			seen_value = B_TRUE;
+			break;
+		default:
+			zone_perror(pt_to_str(PT_NPROP), Z_NO_PROPERTY_TYPE,
+			    B_TRUE);
+			long_usage(CMD_ADD, B_TRUE);
+			usage(B_FALSE, HELP_PROPS);
+			zonecfg_free_res_attr_list(np);
+			return;
+		}
+	}
+
+	if (!seen_name)
+		zerr(gettext("%s not specified"), pt_to_str(PT_NAME));
+	if (!seen_value)
+		zerr(gettext("%s not specified"), pt_to_str(PT_VALUE));
+
+	err = zonecfg_add_res_attr(headp, np);
+	if (err != Z_OK)
+		zone_perror(pt_to_str(PT_NPROP), err, B_TRUE);
+	return;
+
+bad:
+	zonecfg_free_res_attr_list(np);
+}
+
+static void
 add_property(cmd_t *cmd)
 {
 	char *prop_id;
@@ -2426,6 +2541,44 @@
 			}
 		}
 		return;
+	case RT_NET:
+		if (prop_type != PT_NPROP) {
+			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
+			    B_TRUE);
+			long_usage(CMD_ADD, B_TRUE);
+			usage(B_FALSE, HELP_PROPS);
+			return;
+		}
+		pp = cmd->cmd_property_ptr[0];
+		if (pp->pv_type != PROP_VAL_COMPLEX) {
+			zerr(gettext("A %s value was expected here."),
+			    pvt_to_str(PROP_VAL_COMPLEX));
+			saw_error = B_TRUE;
+			return;
+		}
+
+		do_res_attr(&(in_progress_nwiftab.zone_nwif_attrp),
+		    pp->pv_complex);
+		return;
+	case RT_DEVICE:
+		if (prop_type != PT_NPROP) {
+			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
+			    B_TRUE);
+			long_usage(CMD_ADD, B_TRUE);
+			usage(B_FALSE, HELP_PROPS);
+			return;
+		}
+		pp = cmd->cmd_property_ptr[0];
+		if (pp->pv_type != PROP_VAL_COMPLEX) {
+			zerr(gettext("A %s value was expected here."),
+			    pvt_to_str(PROP_VAL_COMPLEX));
+			saw_error = B_TRUE;
+			return;
+		}
+
+		do_res_attr(&(in_progress_devtab.zone_dev_attrp),
+		    pp->pv_complex);
+		return;
 	case RT_RCTL:
 		if (prop_type != PT_VALUE) {
 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
@@ -2530,8 +2683,9 @@
 		resource_scope = cmd->cmd_res_type;
 		end_op = CMD_ADD;
 		add_resource(cmd);
-	} else
+	} else {
 		add_property(cmd);
+	}
 }
 
 /*
@@ -2729,6 +2883,21 @@
 			    pp->pv_simple,
 			    sizeof (nwiftab->zone_nwif_physical));
 			break;
+		case PT_MAC:
+			(void) strlcpy(nwiftab->zone_nwif_mac,
+			    pp->pv_simple,
+			    sizeof (nwiftab->zone_nwif_mac));
+			break;
+		case PT_VLANID:
+			(void) strlcpy(nwiftab->zone_nwif_vlan_id,
+			    pp->pv_simple,
+			    sizeof (nwiftab->zone_nwif_vlan_id));
+			break;
+		case PT_GNIC:
+			(void) strlcpy(nwiftab->zone_nwif_gnic,
+			    pp->pv_simple,
+			    sizeof (nwiftab->zone_nwif_gnic));
+			break;
 		case PT_DEFROUTER:
 			(void) strlcpy(nwiftab->zone_nwif_defrouter,
 			    pp->pv_simple,
@@ -3246,10 +3415,9 @@
 {
 	int err, res1, res2, res3;
 	uint64_t tmp;
-	struct zone_mcaptab mcaptab;
 	boolean_t revert = B_FALSE;
 
-	res1 = zonecfg_lookup_mcap(handle, &mcaptab);
+	res1 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXPHYSMEM, &tmp);
 	res2 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXSWAP, &tmp);
 	res3 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXLOCKEDMEM, &tmp);
 
@@ -3261,7 +3429,8 @@
 		return;
 	}
 	if (res1 == Z_OK) {
-		if ((err = zonecfg_delete_mcap(handle)) != Z_OK) {
+		if ((err = zonecfg_rm_aliased_rctl(handle, ALIAS_MAXPHYSMEM))
+		    != Z_OK) {
 			z_cmd_rt_perror(CMD_REMOVE, RT_MCAP, err, B_TRUE);
 			revert = B_TRUE;
 		} else {
@@ -3268,6 +3437,7 @@
 			need_to_commit = B_TRUE;
 		}
 	}
+
 	if (res2 == Z_OK) {
 		if ((err = zonecfg_rm_aliased_rctl(handle, ALIAS_MAXSWAP))
 		    != Z_OK) {
@@ -3410,6 +3580,7 @@
 	int err, res_type, prop_type;
 	property_value_ptr_t pp;
 	struct zone_rctlvaltab *rctlvaltab;
+	struct zone_res_attrtab *np;
 	complex_property_ptr_t cx;
 
 	res_type = resource_scope;
@@ -3470,6 +3641,56 @@
 			}
 		}
 		return;
+	case RT_NET:		/* FALLTHRU */
+	case RT_DEVICE:
+		if (prop_type != PT_NPROP) {
+			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
+			    B_TRUE);
+			long_usage(CMD_REMOVE, B_TRUE);
+			usage(B_FALSE, HELP_PROPS);
+			return;
+		}
+		pp = cmd->cmd_property_ptr[0];
+		if (pp->pv_type != PROP_VAL_COMPLEX) {
+			zerr(gettext("A %s value was expected here."),
+			    pvt_to_str(PROP_VAL_COMPLEX));
+			saw_error = B_TRUE;
+			return;
+		}
+
+		np = alloca(sizeof (struct zone_res_attrtab));
+		for (cx = pp->pv_complex; cx != NULL; cx = cx->cp_next) {
+			switch (cx->cp_type) {
+			case PT_NAME:
+				(void) strlcpy(np->zone_res_attr_name,
+				    cx->cp_value,
+				    sizeof (np->zone_res_attr_name));
+				break;
+			case PT_VALUE:
+				(void) strlcpy(np->zone_res_attr_value,
+				    cx->cp_value,
+				    sizeof (np->zone_res_attr_value));
+				break;
+			default:
+				zone_perror(pt_to_str(prop_type),
+				    Z_NO_PROPERTY_TYPE, B_TRUE);
+				long_usage(CMD_REMOVE, B_TRUE);
+				usage(B_FALSE, HELP_PROPS);
+				return;
+			}
+		}
+		np->zone_res_attr_next = NULL;
+
+		if (res_type == RT_NET) {
+			err = zonecfg_remove_res_attr(
+			    &(in_progress_nwiftab.zone_nwif_attrp), np);
+		} else {				/* RT_DEVICE */
+			err = zonecfg_remove_res_attr(
+			    &(in_progress_devtab.zone_dev_attrp), np);
+		}
+		if (err != Z_OK)
+			zone_perror(pt_to_str(prop_type), err, B_TRUE);
+		return;
 	case RT_RCTL:
 		if (prop_type != PT_VALUE) {
 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
@@ -3522,18 +3743,6 @@
 			zone_perror(pt_to_str(prop_type), err, B_TRUE);
 		zonecfg_free_rctl_value_list(rctlvaltab);
 		return;
-	case RT_NET:
-		if (prop_type != PT_DEFROUTER) {
-			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
-			    B_TRUE);
-			long_usage(CMD_REMOVE, B_TRUE);
-			usage(B_FALSE, HELP_PROPS);
-			return;
-		} else {
-			bzero(&in_progress_nwiftab.zone_nwif_defrouter,
-			    sizeof (in_progress_nwiftab.zone_nwif_defrouter));
-			return;
-		}
 	default:
 		zone_perror(rt_to_str(res_type), Z_NO_RESOURCE_TYPE, B_TRUE);
 		long_usage(CMD_REMOVE, B_TRUE);
@@ -3596,8 +3805,7 @@
 	case RT_MCAP:
 		switch (prop_type) {
 		case PT_PHYSICAL:
-			in_progress_mcaptab.zone_physmem_cap[0] = '\0';
-			need_to_commit = B_TRUE;
+			remove_aliased_rctl(PT_PHYSICAL, ALIAS_MAXPHYSMEM);
 			return;
 		case PT_SWAP:
 			remove_aliased_rctl(PT_SWAP, ALIAS_MAXSWAP);
@@ -3607,6 +3815,30 @@
 			return;
 		}
 		break;
+	case RT_NET:
+		switch (prop_type) {
+		case PT_ALLOWED_ADDRESS:
+			in_progress_nwiftab.zone_nwif_allowed_address[0] = '\0';
+			need_to_commit = B_TRUE;
+			return;
+		case PT_DEFROUTER:
+			in_progress_nwiftab.zone_nwif_defrouter[0] = '\0';
+			need_to_commit = B_TRUE;
+			return;
+		case PT_GNIC:
+			in_progress_nwiftab.zone_nwif_gnic[0] = '\0';
+			need_to_commit = B_TRUE;
+			return;
+		case PT_MAC:
+			in_progress_nwiftab.zone_nwif_mac[0] = '\0';
+			need_to_commit = B_TRUE;
+			return;
+		case PT_VLANID:
+			in_progress_nwiftab.zone_nwif_vlan_id[0] = '\0';
+			need_to_commit = B_TRUE;
+			return;
+		}
+		break;
 	default:
 		break;
 	}
@@ -3739,7 +3971,7 @@
 void
 select_func(cmd_t *cmd)
 {
-	int type, err, res;
+	int type, err;
 	uint64_t limit;
 	uint64_t tmp;
 
@@ -3834,7 +4066,8 @@
 		return;
 	case RT_MCAP:
 		/* if none of these exist, there is no resource to select */
-		if ((res = zonecfg_lookup_mcap(handle, &old_mcaptab)) != Z_OK &&
+		if (zonecfg_get_aliased_rctl(handle, ALIAS_MAXPHYSMEM, &limit)
+		    != Z_OK &&
 		    zonecfg_get_aliased_rctl(handle, ALIAS_MAXSWAP, &limit)
 		    != Z_OK &&
 		    zonecfg_get_aliased_rctl(handle, ALIAS_MAXLOCKEDMEM, &limit)
@@ -3843,12 +4076,6 @@
 			    B_TRUE);
 			global_scope = B_TRUE;
 		}
-		if (res == Z_OK)
-			bcopy(&old_mcaptab, &in_progress_mcaptab,
-			    sizeof (struct zone_mcaptab));
-		else
-			bzero(&in_progress_mcaptab,
-			    sizeof (in_progress_mcaptab));
 		return;
 	case RT_ADMIN:
 		if ((err = fill_in_admintab(cmd, &old_admintab, B_FALSE))
@@ -4115,9 +4342,8 @@
 	boolean_t autoboot;
 	zone_iptype_t iptype;
 	boolean_t force_set = B_FALSE;
-	size_t physmem_size = sizeof (in_progress_mcaptab.zone_physmem_cap);
 	uint64_t mem_cap, mem_limit;
-	float cap;
+	double cap;
 	char *unitp;
 	struct zone_psettab tmp_psettab;
 	boolean_t arg_err = B_FALSE;
@@ -4219,10 +4445,12 @@
 	 * A nasty expression but not that complicated:
 	 * 1. fs options are simple or list (tested below)
 	 * 2. rctl value's are complex or list (tested below)
+	 * 3. net attr's are complex (tested below)
 	 * Anything else should be simple.
 	 */
 	if (!(res_type == RT_FS && prop_type == PT_OPTIONS) &&
 	    !(res_type == RT_RCTL && prop_type == PT_VALUE) &&
+	    !(res_type == RT_NET && prop_type == PT_NPROP) &&
 	    (pp->pv_type != PROP_VAL_SIMPLE ||
 	    (prop_id = pp->pv_simple) == NULL)) {
 		zerr(gettext("A %s value was expected here."),
@@ -4482,6 +4710,21 @@
 			    prop_id,
 			    sizeof (in_progress_nwiftab.zone_nwif_physical));
 			break;
+		case PT_MAC:
+			(void) strlcpy(in_progress_nwiftab.zone_nwif_mac,
+			    prop_id,
+			    sizeof (in_progress_nwiftab.zone_nwif_mac));
+			break;
+		case PT_VLANID:
+			(void) strlcpy(in_progress_nwiftab.zone_nwif_vlan_id,
+			    prop_id,
+			    sizeof (in_progress_nwiftab.zone_nwif_vlan_id));
+			break;
+		case PT_GNIC:
+			(void) strlcpy(in_progress_nwiftab.zone_nwif_gnic,
+			    prop_id,
+			    sizeof (in_progress_nwiftab.zone_nwif_gnic));
+			break;
 		case PT_DEFROUTER:
 			if (validate_net_address_syntax(prop_id, B_TRUE)
 			    != Z_OK) {
@@ -4492,6 +4735,20 @@
 			    prop_id,
 			    sizeof (in_progress_nwiftab.zone_nwif_defrouter));
 			break;
+		case PT_NPROP:
+			if (pp->pv_type != PROP_VAL_COMPLEX) {
+				zerr(gettext("A %s value was expected here."),
+				    pvt_to_str(PROP_VAL_COMPLEX));
+				saw_error = B_TRUE;
+				return;
+			}
+			zonecfg_free_res_attr_list(
+			    in_progress_nwiftab.zone_nwif_attrp);
+			in_progress_nwiftab.zone_nwif_attrp = NULL;
+			if (!(pp->pv_type == PROP_VAL_LIST &&
+			    pp->pv_list == NULL))
+				add_property(cmd);
+			break;
 		default:
 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
 			    B_TRUE);
@@ -4507,6 +4764,20 @@
 			    prop_id,
 			    sizeof (in_progress_devtab.zone_dev_match));
 			break;
+		case PT_NPROP:
+			if (pp->pv_type != PROP_VAL_COMPLEX) {
+				zerr(gettext("A %s value was expected here."),
+				    pvt_to_str(PROP_VAL_COMPLEX));
+				saw_error = B_TRUE;
+				return;
+			}
+			zonecfg_free_res_attr_list(
+			    in_progress_devtab.zone_dev_attrp);
+			in_progress_devtab.zone_dev_attrp = NULL;
+			if (!(pp->pv_type == PROP_VAL_LIST &&
+			    pp->pv_list == NULL))
+				add_property(cmd);
+			break;
 		default:
 			zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE,
 			    B_TRUE);
@@ -4650,35 +4921,50 @@
 		 * the add_resource() function.
 		 */
 
-		if ((cap = strtof(prop_id, &unitp)) <= 0 || *unitp != '\0' ||
-		    (int)(cap * 100) < 1) {
+		if ((cap = strtod(prop_id, &unitp)) <= 0 || *unitp != '\0' ||
+		    (cap * 100.0) < 1) {
 			zerr(gettext("%s property is out of range."),
 			    pt_to_str(PT_NCPUS));
 			saw_error = B_TRUE;
 			return;
 		}
+		cap *= 100.0;
 
+		/* To avoid rounding issues add .5 to force correct value. */
 		if ((err = zonecfg_set_aliased_rctl(handle, ALIAS_CPUCAP,
-		    (int)(cap * 100))) != Z_OK)
+		    (uint_t)(cap + 0.5))) != Z_OK) {
 			zone_perror(zone, err, B_TRUE);
-		else
+		} else {
 			need_to_commit = B_TRUE;
+		}
 		return;
 	case RT_MCAP:
 		switch (prop_type) {
 		case PT_PHYSICAL:
+			/*
+			 * We have to check if an rctl is allowed here since
+			 * there might already be a rctl defined that blocks
+			 * the alias.
+			 */
+			if (!zonecfg_aliased_rctl_ok(handle,
+			    ALIAS_MAXPHYSMEM)) {
+				zone_perror(pt_to_str(PT_LOCKED),
+				    Z_ALIAS_DISALLOW, B_FALSE);
+				saw_error = B_TRUE;
+				return;
+			}
+
 			if (!zonecfg_valid_memlimit(prop_id, &mem_cap)) {
-				zerr(gettext("A positive number with a "
+				zerr(gettext("A non-negative number with a "
 				    "required scale suffix (K, M, G or T) was "
-				    "expected here."));
+				    "expected\nhere."));
 				saw_error = B_TRUE;
-			} else if (mem_cap < ONE_MB) {
-				zerr(gettext("%s value is too small.  It must "
-				    "be at least 1M."), pt_to_str(PT_PHYSICAL));
-				saw_error = B_TRUE;
 			} else {
-				snprintf(in_progress_mcaptab.zone_physmem_cap,
-				    physmem_size, "%llu", mem_cap);
+				if ((err = zonecfg_set_aliased_rctl(handle,
+				    ALIAS_MAXPHYSMEM, mem_cap)) != Z_OK)
+					zone_perror(zone, err, B_TRUE);
+				else
+					need_to_commit = B_TRUE;
 			}
 			break;
 		case PT_SWAP:
@@ -5031,12 +5317,25 @@
 static void
 output_net(FILE *fp, struct zone_nwiftab *nwiftab)
 {
+	struct zone_res_attrtab *np;
+
 	(void) fprintf(fp, "%s:\n", rt_to_str(RT_NET));
 	output_prop(fp, PT_ADDRESS, nwiftab->zone_nwif_address, B_TRUE);
 	output_prop(fp, PT_ALLOWED_ADDRESS,
 	    nwiftab->zone_nwif_allowed_address, B_TRUE);
-	output_prop(fp, PT_PHYSICAL, nwiftab->zone_nwif_physical, B_TRUE);
 	output_prop(fp, PT_DEFROUTER, nwiftab->zone_nwif_defrouter, B_TRUE);
+	output_prop(fp, PT_GNIC, nwiftab->zone_nwif_gnic, B_TRUE);
+	output_prop(fp, PT_MAC, nwiftab->zone_nwif_mac, B_TRUE);
+	output_prop(fp, PT_PHYSICAL, nwiftab->zone_nwif_physical, B_TRUE);
+	output_prop(fp, PT_VLANID, nwiftab->zone_nwif_vlan_id, B_TRUE);
+
+	for (np = nwiftab->zone_nwif_attrp; np != NULL;
+	    np = np->zone_res_attr_next) {
+		fprintf(fp, "\t%s: (%s=%s,%s=\"%s\")\n",
+		    pt_to_str(PT_NPROP),
+		    pt_to_str(PT_NAME), np->zone_res_attr_name,
+		    pt_to_str(PT_VALUE), np->zone_res_attr_value);
+	}
 }
 
 static void
@@ -5079,8 +5378,18 @@
 static void
 output_dev(FILE *fp, struct zone_devtab *devtab)
 {
+	struct zone_res_attrtab *np;
+
 	(void) fprintf(fp, "%s:\n", rt_to_str(RT_DEVICE));
 	output_prop(fp, PT_MATCH, devtab->zone_dev_match, B_TRUE);
+
+	for (np = devtab->zone_dev_attrp; np != NULL;
+	    np = np->zone_res_attr_next) {
+		fprintf(fp, "\t%s: (%s=%s,%s=\"%s\")\n",
+		    pt_to_str(PT_NPROP),
+		    pt_to_str(PT_NAME), np->zone_res_attr_name,
+		    pt_to_str(PT_VALUE), np->zone_res_attr_value);
+	}
 }
 
 static void
@@ -5339,15 +5648,18 @@
 }
 
 static void
-output_mcap(FILE *fp, struct zone_mcaptab *mcaptab, int showswap,
+output_mcap(FILE *fp, int showphys, uint64_t maxphys, int showswap,
     uint64_t maxswap, int showlocked, uint64_t maxlocked)
 {
 	char buf[128];
 
 	(void) fprintf(fp, "%s:\n", rt_to_str(RT_MCAP));
-	if (mcaptab->zone_physmem_cap[0] != '\0') {
-		bytes_to_units(mcaptab->zone_physmem_cap, buf, sizeof (buf));
-		output_prop(fp, PT_PHYSICAL, buf, B_TRUE);
+
+	if (showphys == Z_OK) {
+		(void) snprintf(buf, sizeof (buf), "%llu", maxphys);
+		bytes_to_units(buf, buf, sizeof (buf));
+		/* Print directly since "physical" also is a net property. */
+		(void) fprintf(fp, "\t[%s: %s]\n", pt_to_str(PT_PHYSICAL), buf);
 	}
 
 	if (showswap == Z_OK) {
@@ -5369,16 +5681,16 @@
 	int res1, res2, res3;
 	uint64_t swap_limit;
 	uint64_t locked_limit;
-	struct zone_mcaptab lookup;
+	uint64_t phys_limit;
 
-	bzero(&lookup, sizeof (lookup));
-	res1 = zonecfg_getmcapent(handle, &lookup);
+	res1 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXPHYSMEM, &phys_limit);
 	res2 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXSWAP, &swap_limit);
 	res3 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXLOCKEDMEM,
 	    &locked_limit);
 
 	if (res1 == Z_OK || res2 == Z_OK || res3 == Z_OK)
-		output_mcap(fp, &lookup, res2, swap_limit, res3, locked_limit);
+		output_mcap(fp, res1, phys_limit, res2, swap_limit,
+		    res3, locked_limit);
 }
 
 static void
@@ -5429,9 +5741,10 @@
 	FILE *fp = stdout;
 	boolean_t need_to_close = B_FALSE;
 	int type;
-	int res1, res2;
+	int res1, res2, res3;
 	uint64_t swap_limit;
 	uint64_t locked_limit;
+	uint64_t phys_limit;
 
 	assert(cmd != NULL);
 
@@ -5479,7 +5792,9 @@
 			    &swap_limit);
 			res2 = zonecfg_get_aliased_rctl(handle,
 			    ALIAS_MAXLOCKEDMEM, &locked_limit);
-			output_mcap(fp, &in_progress_mcaptab, res1, swap_limit,
+			res3 = zonecfg_get_aliased_rctl(handle,
+			    ALIAS_MAXPHYSMEM, &phys_limit);
+			output_mcap(fp, res3, phys_limit, res1, swap_limit,
 			    res2, locked_limit);
 			break;
 		case RT_ADMIN:
@@ -6101,6 +6416,12 @@
 
 	if (save) {
 		if (ret_val == Z_OK) {
+			/*
+			 * If the zone doesn't yet have a debug ID, set one now.
+			 */
+			if (zonecfg_get_did(handle) == -1)
+				zonecfg_set_did(handle);
+
 			if ((ret_val = zonecfg_save(handle)) == Z_OK) {
 				need_to_commit = B_FALSE;
 				(void) strlcpy(revert_zone, zone,
@@ -6275,6 +6596,7 @@
 	int err, arg, res1, res2, res3;
 	uint64_t swap_limit;
 	uint64_t locked_limit;
+	uint64_t phys_limit;
 	uint64_t proc_cap;
 
 	assert(cmd != NULL);
@@ -6578,8 +6900,8 @@
 		break;
 	case RT_MCAP:
 		/* Make sure everything was filled in. */
-		res1 = strlen(in_progress_mcaptab.zone_physmem_cap) == 0 ?
-		    Z_ERR : Z_OK;
+		res1 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXPHYSMEM,
+		    &phys_limit);
 		res2 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXSWAP,
 		    &swap_limit);
 		res3 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXLOCKEDMEM,
@@ -6595,11 +6917,6 @@
 
 		/* if phys & locked are both set, verify locked <= phys */
 		if (res1 == Z_OK && res3 == Z_OK) {
-			uint64_t phys_limit;
-			char *endp;
-
-			phys_limit = strtoull(
-			    in_progress_mcaptab.zone_physmem_cap, &endp, 10);
 			if (phys_limit < locked_limit) {
 				zerr(gettext("The %s cap must be less than or "
 				    "equal to the %s cap."),
@@ -6611,23 +6928,6 @@
 		}
 
 		err = Z_OK;
-		if (res1 == Z_OK) {
-			/*
-			 * We could be ending from either an add operation
-			 * or a select operation.  Since all of the properties
-			 * within this resource are optional, we always use
-			 * modify on the mcap entry.  zonecfg_modify_mcap()
-			 * will handle both adding and modifying a memory cap.
-			 */
-			err = zonecfg_modify_mcap(handle, &in_progress_mcaptab);
-		} else if (end_op == CMD_SELECT) {
-			/*
-			 * If we're ending from a select and the physical
-			 * memory cap is empty then the user could have cleared
-			 * the physical cap value, so try to delete the entry.
-			 */
-			(void) zonecfg_delete_mcap(handle);
-		}
 		break;
 	case RT_ADMIN:
 		/* First make sure everything was filled in. */
--- old/usr/src/cmd/zonecfg/zonecfg.h	Wed Oct  5 00:31:44 2016
+++ new/usr/src/cmd/zonecfg/zonecfg.h	Wed Oct  5 00:31:44 2016
@@ -137,9 +137,14 @@
 #define	PT_FS_ALLOWED	39
 #define	PT_MAXPROCS	40
 #define	PT_ALLOWED_ADDRESS	41
+#define	PT_ZFSPRI	42
+#define	PT_MAC		43
+#define	PT_VLANID	44
+#define	PT_GNIC		45
+#define	PT_NPROP	46
 
 #define	PT_MIN		PT_UNKNOWN
-#define	PT_MAX		PT_ALLOWED_ADDRESS
+#define	PT_MAX		PT_NPROP
 
 #define	MAX_EQ_PROP_PAIRS	3
 
--- old/usr/src/cmd/zonecfg/zonecfg_grammar.y	Wed Oct  5 00:31:44 2016
+++ new/usr/src/cmd/zonecfg/zonecfg_grammar.y	Wed Oct  5 00:31:44 2016
@@ -136,6 +136,7 @@
 %token OPEN_PAREN CLOSE_PAREN COMMA DATASET LIMITPRIV BOOTARGS BRAND PSET PCAP
 %token MCAP NCPUS IMPORTANCE SHARES MAXLWPS MAXSHMMEM MAXSHMIDS MAXMSGIDS
 %token MAXSEMIDS LOCKED SWAP SCHED CLEAR DEFROUTER ADMIN USER AUTHS MAXPROCS
+%token ZFSPRI MAC VLANID GNIC NPROP
 
 %type <strval> TOKEN EQUAL OPEN_SQ_BRACKET CLOSE_SQ_BRACKET
     property_value OPEN_PAREN CLOSE_PAREN COMMA simple_prop_val
@@ -145,7 +146,7 @@
 %type <ival> property_name SPECIAL RAW DIR OPTIONS TYPE ADDRESS PHYSICAL NAME
     MATCH ZONENAME ZONEPATH AUTOBOOT POOL LIMITPRIV BOOTARGS VALUE PRIV LIMIT
     ACTION BRAND SCHED IPTYPE DEFROUTER HOSTID USER AUTHS FS_ALLOWED
-    ALLOWED_ADDRESS
+    ALLOWED_ADDRESS MAC VLANID GNIC NPROP
 %type <cmd> command
 %type <cmd> add_command ADD
 %type <cmd> cancel_command CANCEL
@@ -976,6 +977,10 @@
 	| ALLOWED_ADDRESS	{ $$ = PT_ALLOWED_ADDRESS; }
 	| PHYSICAL	{ $$ = PT_PHYSICAL; }
 	| DEFROUTER	{ $$ = PT_DEFROUTER; }
+	| MAC		{ $$ = PT_MAC; }
+	| VLANID	{ $$ = PT_VLANID; }
+	| GNIC		{ $$ = PT_GNIC; }
+	| NPROP		{ $$ = PT_NPROP; }
 	| NAME		{ $$ = PT_NAME; }
 	| VALUE		{ $$ = PT_VALUE; }
 	| MATCH		{ $$ = PT_MATCH; }
--- old/usr/src/cmd/zonecfg/zonecfg_lex.l	Wed Oct  5 00:31:44 2016
+++ new/usr/src/cmd/zonecfg/zonecfg_lex.l	Wed Oct  5 00:31:44 2016
@@ -57,10 +57,11 @@
 static char *create_token(char *s);
 %}
 
-%a 7000
+%a 8000
 %p 5000
 %e 2000
 %n 1000
+%o 13000
 
 %{
 /*
@@ -236,6 +237,18 @@
 <TSTATE>defrouter	{ return DEFROUTER; }
 <CSTATE>defrouter	{ return DEFROUTER; }
 
+<TSTATE>mac-addr	{ return MAC; }
+<CSTATE>mac-addr	{ return MAC; }
+
+<TSTATE>vlan-id		{ return VLANID; }
+<CSTATE>vlan-id		{ return VLANID; }
+
+<TSTATE>global-nic	{ return GNIC; }
+<CSTATE>global-nic	{ return GNIC; }
+
+<TSTATE>property	{ return NPROP; }
+<CSTATE>property	{ return NPROP; }
+
 <TSTATE>dir	{ return DIR; }
 <CSTATE>dir	{ return DIR; }
 
@@ -356,6 +369,13 @@
 			yylval.strval = create_token(yytext);
 			return TOKEN;
 		}
+
+<CSTATE>\"[^\"\n]*[\"\n] {
+			yylval.strval = create_token(yytext + 1);
+			if (yylval.strval[yyleng - 2] == '"')
+				yylval.strval[yyleng - 2] = 0;
+			return TOKEN;
+		}
 
 <TSTATE>\"[^\"\n]*[\"\n] {
 			yylval.strval = create_token(yytext + 1);
--- old/usr/src/cmd/zonestat/zonestatd/zonestatd.c	Wed Oct  5 00:31:44 2016
+++ new/usr/src/cmd/zonestat/zonestatd/zonestatd.c	Wed Oct  5 00:31:44 2016
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, Joyent, Inc. All rights reserved.
  */
 #include <alloca.h>
 #include <assert.h>
@@ -2190,7 +2191,7 @@
 	return (rctlblk_get_value(rblk));
 }
 
-#define	ZSD_NUM_RCTL_VALS 19
+#define	ZSD_NUM_RCTL_VALS 20
 
 /*
  * Fetch the limit information for a zone.  This uses zone_enter() as the
@@ -2237,12 +2238,6 @@
 	*msgids = 0;
 	*lofi = 0;
 
-	/* Get the ram cap first since it is a zone attr */
-	ret = zone_getattr(zone->zsz_id, ZONE_ATTR_PHYS_MCAP,
-	    ram_cap, sizeof (*ram_cap));
-	if (ret < 0 || *ram_cap == 0)
-		*ram_cap = ZS_LIMIT_NONE;
-
 	/* Get the zone's default scheduling class */
 	ret = zone_getattr(zone->zsz_id, ZONE_ATTR_SCHED_CLASS,
 	    class, sizeof (class));
@@ -2298,6 +2293,7 @@
 		vals[i++] = zsd_get_zone_rctl_usage("zone.max-msg-ids");
 		vals[i++] = zsd_get_zone_rctl_limit("zone.max-lofi");
 		vals[i++] = zsd_get_zone_rctl_usage("zone.max-lofi");
+		vals[i++] = zsd_get_zone_rctl_usage("zone.max-physical-memory");
 
 		if (write(p[1], vals, ZSD_NUM_RCTL_VALS * sizeof (uint64_t)) !=
 		    ZSD_NUM_RCTL_VALS * sizeof (uint64_t)) {
@@ -2342,6 +2338,7 @@
 	*msgids = vals[i++];
 	*lofi_cap = vals[i++];
 	*lofi = vals[i++];
+	*ram_cap = vals[i++];
 
 	/* Interpret maximum values as no cap */
 	if (*cpu_cap == UINT32_MAX || *cpu_cap == 0)
--- /dev/null	Wed Oct  5 00:31:44 2016
+++ new/usr/src/common/brand/lx/lx_auxv.c	Wed Oct  5 00:31:44 2016
@@ -0,0 +1,96 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#include <sys/auxv.h>
+#include <sys/lx_brand.h>
+
+/*
+ * Linux does not make the distinction between 'int' and 'long' when it comes
+ * to the format of the aux vector.  In order to properly clear the struct
+ * padding present in the native auxv_t in 64-bit, we employ the Linux format.
+ */
+struct lx_auxv {
+	long la_type;
+	long la_val;
+};
+
+int
+lx_auxv_stol(const auxv_t *ap, auxv_t *oap, const lx_elf_data_t *edp)
+{
+	struct lx_auxv *loap = (struct lx_auxv *)oap;
+
+	switch (ap->a_type) {
+	case AT_BASE:
+		loap->la_val = edp->ed_base;
+		break;
+	case AT_ENTRY:
+		loap->la_val = edp->ed_entry;
+		break;
+	case AT_PHDR:
+		loap->la_val = edp->ed_phdr;
+		break;
+	case AT_PHENT:
+		loap->la_val = edp->ed_phent;
+		break;
+	case AT_PHNUM:
+		loap->la_val = edp->ed_phnum;
+		break;
+	case AT_SUN_BRAND_LX_SYSINFO_EHDR:
+		loap->la_type = AT_SYSINFO_EHDR;
+		loap->la_val = ap->a_un.a_val;
+		return (0);
+	case AT_SUN_BRAND_LX_CLKTCK:
+		loap->la_type = AT_CLKTCK;
+		loap->la_val = ap->a_un.a_val;
+		return (0);
+	case AT_SUN_AUXFLAGS:
+		if ((ap->a_un.a_val & AF_SUN_SETUGID) != 0) {
+			loap->la_type = AT_SECURE;
+			loap->la_val = 1;
+			return (0);
+		} else {
+			return (1);
+		}
+	case AT_SUN_GID:
+		loap->la_type = AT_LX_EGID;
+		loap->la_val = ap->a_un.a_val;
+		return (0);
+	case AT_SUN_RGID:
+		loap->la_type = AT_LX_GID;
+		loap->la_val = ap->a_un.a_val;
+		return (0);
+	case AT_SUN_UID:
+		loap->la_type = AT_LX_EUID;
+		loap->la_val = ap->a_un.a_val;
+		return (0);
+	case AT_SUN_RUID:
+		loap->la_type = AT_LX_UID;
+		loap->la_val = ap->a_un.a_val;
+		return (0);
+	case AT_EXECFD:
+	case AT_PAGESZ:
+	case AT_FLAGS:
+	case AT_RANDOM:
+	case AT_NULL:
+		/* No translate needed */
+		loap->la_val = ap->a_un.a_val;
+		break;
+	default:
+		/* All other unrecognized entries are ignored */
+		return (1);
+	}
+	loap->la_type = ap->a_type;
+	return (0);
+}
--- /dev/null	Wed Oct  5 00:31:44 2016
+++ new/usr/src/common/brand/lx/lx_auxv.h	Wed Oct  5 00:31:44 2016
@@ -0,0 +1,32 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+#ifndef	_LX_AUXV_H
+#define	_LX_AUXV_H
+
+#include <sys/auxv.h>
+#include <sys/lx_brand.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+extern int lx_auxv_stol(const auxv_t *, auxv_t *, const lx_elf_data_t *);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _LX_AUXV_H */
--- /dev/null	Wed Oct  5 00:31:44 2016
+++ new/usr/src/common/brand/lx/lx_errno.c	Wed Oct  5 00:31:44 2016
@@ -0,0 +1,206 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+/*
+ * This file contains a mapping table and lookup function for converting
+ * illumos native error numbers into error numbers appropriate for Linux
+ * emulation.
+ *
+ * The translation table is generated by the "gen_errno", built from and
+ * documented in "usr/src/common/brand/lx/tools".
+ */
+
+#include <sys/debug.h>
+
+const int
+lx_stol_errno[] = {
+	0,	/*   0: No Error					*/
+	1,	/*   1: EPERM		-->   1: EPERM			*/
+	2,	/*   2: ENOENT		-->   2: ENOENT			*/
+	3,	/*   3: ESRCH		-->   3: ESRCH			*/
+	4,	/*   4: EINTR		-->   4: EINTR			*/
+	5,	/*   5: EIO		-->   5: EIO			*/
+	6,	/*   6: ENXIO		-->   6: ENXIO			*/
+	7,	/*   7: E2BIG		-->   7: E2BIG			*/
+	8,	/*   8: ENOEXEC		-->   8: ENOEXEC		*/
+	9,	/*   9: EBADF		-->   9: EBADF			*/
+	10,	/*  10: ECHILD		-->  10: ECHILD			*/
+	11,	/*  11: EAGAIN		-->  11: EAGAIN			*/
+	12,	/*  12: ENOMEM		-->  12: ENOMEM			*/
+	13,	/*  13: EACCES		-->  13: EACCES			*/
+	14,	/*  14: EFAULT		-->  14: EFAULT			*/
+	15,	/*  15: ENOTBLK		-->  15: ENOTBLK		*/
+	16,	/*  16: EBUSY		-->  16: EBUSY			*/
+	17,	/*  17: EEXIST		-->  17: EEXIST			*/
+	18,	/*  18: EXDEV		-->  18: EXDEV			*/
+	19,	/*  19: ENODEV		-->  19: ENODEV			*/
+	20,	/*  20: ENOTDIR		-->  20: ENOTDIR		*/
+	21,	/*  21: EISDIR		-->  21: EISDIR			*/
+	22,	/*  22: EINVAL		-->  22: EINVAL			*/
+	23,	/*  23: ENFILE		-->  23: ENFILE			*/
+	24,	/*  24: EMFILE		-->  24: EMFILE			*/
+	25,	/*  25: ENOTTY		-->  25: ENOTTY			*/
+	26,	/*  26: ETXTBSY		-->  26: ETXTBSY		*/
+	27,	/*  27: EFBIG		-->  27: EFBIG			*/
+	28,	/*  28: ENOSPC		-->  28: ENOSPC			*/
+	29,	/*  29: ESPIPE		-->  29: ESPIPE			*/
+	30,	/*  30: EROFS		-->  30: EROFS			*/
+	31,	/*  31: EMLINK		-->  31: EMLINK			*/
+	32,	/*  32: EPIPE		-->  32: EPIPE			*/
+	33,	/*  33: EDOM		-->  33: EDOM			*/
+	34,	/*  34: ERANGE		-->  34: ERANGE			*/
+	42,	/*  35: ENOMSG		-->  42: ENOMSG			*/
+	43,	/*  36: EIDRM		-->  43: EIDRM			*/
+	44,	/*  37: ECHRNG		-->  44: ECHRNG			*/
+	45,	/*  38: EL2NSYNC	-->  45: EL2NSYNC		*/
+	46,	/*  39: EL3HLT		-->  46: EL3HLT			*/
+	47,	/*  40: EL3RST		-->  47: EL3RST			*/
+	48,	/*  41: ELNRNG		-->  48: ELNRNG			*/
+	49,	/*  42: EUNATCH		-->  49: EUNATCH		*/
+	50,	/*  43: ENOCSI		-->  50: ENOCSI			*/
+	51,	/*  44: EL2HLT		-->  51: EL2HLT			*/
+	35,	/*  45: EDEADLK		-->  35: EDEADLK		*/
+	37,	/*  46: ENOLCK		-->  37: ENOLCK			*/
+	125,	/*  47: ECANCELED	--> 125: ECANCELED		*/
+	38,	/*  48: ENOTSUP		-->  38: ENOSYS			*/
+	122,	/*  49: EDQUOT		--> 122: EDQUOT			*/
+	52,	/*  50: EBADE		-->  52: EBADE			*/
+	53,	/*  51: EBADR		-->  53: EBADR			*/
+	54,	/*  52: EXFULL		-->  54: EXFULL			*/
+	55,	/*  53: ENOANO		-->  55: ENOANO			*/
+	56,	/*  54: EBADRQC		-->  56: EBADRQC		*/
+	57,	/*  55: EBADSLT		-->  57: EBADSLT		*/
+	35,	/*  56: EDEADLOCK	-->  35: EDEADLK		*/
+	59,	/*  57: EBFONT		-->  59: EBFONT			*/
+	130,	/*  58: EOWNERDEAD	--> 130: EOWNERDEAD		*/
+	131,	/*  59: ENOTRECOVERABLE	--> 131: ENOTRECOVERABLE	*/
+	60,	/*  60: ENOSTR		-->  60: ENOSTR			*/
+	61,	/*  61: ENODATA		-->  61: ENODATA		*/
+	62,	/*  62: ETIME		-->  62: ETIME			*/
+	63,	/*  63: ENOSR		-->  63: ENOSR			*/
+	64,	/*  64: ENONET		-->  64: ENONET			*/
+	65,	/*  65: ENOPKG		-->  65: ENOPKG			*/
+	66,	/*  66: EREMOTE		-->  66: EREMOTE		*/
+	67,	/*  67: ENOLINK		-->  67: ENOLINK		*/
+	68,	/*  68: EADV		-->  68: EADV			*/
+	69,	/*  69: ESRMNT		-->  69: ESRMNT			*/
+	70,	/*  70: ECOMM		-->  70: ECOMM			*/
+	71,	/*  71: EPROTO		-->  71: EPROTO			*/
+	-2,	/*  72: ELOCKUNMAPPED	-->  -2: No Analogue		*/
+	-2,	/*  73: ENOTACTIVE	-->  -2: No Analogue		*/
+	72,	/*  74: EMULTIHOP	-->  72: EMULTIHOP		*/
+	-1,	/*  75: Unused Number					*/
+	-1,	/*  76: Unused Number					*/
+	74,	/*  77: EBADMSG		-->  74: EBADMSG		*/
+	36,	/*  78: ENAMETOOLONG	-->  36: ENAMETOOLONG		*/
+	75,	/*  79: EOVERFLOW	-->  75: EOVERFLOW		*/
+	76,	/*  80: ENOTUNIQ	-->  76: ENOTUNIQ		*/
+	77,	/*  81: EBADFD		-->  77: EBADFD			*/
+	78,	/*  82: EREMCHG		-->  78: EREMCHG		*/
+	79,	/*  83: ELIBACC		-->  79: ELIBACC		*/
+	80,	/*  84: ELIBBAD		-->  80: ELIBBAD		*/
+	81,	/*  85: ELIBSCN		-->  81: ELIBSCN		*/
+	82,	/*  86: ELIBMAX		-->  82: ELIBMAX		*/
+	83,	/*  87: ELIBEXEC	-->  83: ELIBEXEC		*/
+	84,	/*  88: EILSEQ		-->  84: EILSEQ			*/
+	38,	/*  89: ENOSYS		-->  38: ENOSYS			*/
+	40,	/*  90: ELOOP		-->  40: ELOOP			*/
+	85,	/*  91: ERESTART	-->  85: ERESTART		*/
+	86,	/*  92: ESTRPIPE	-->  86: ESTRPIPE		*/
+	39,	/*  93: ENOTEMPTY	-->  39: ENOTEMPTY		*/
+	87,	/*  94: EUSERS		-->  87: EUSERS			*/
+	88,	/*  95: ENOTSOCK	-->  88: ENOTSOCK		*/
+	89,	/*  96: EDESTADDRREQ	-->  89: EDESTADDRREQ		*/
+	90,	/*  97: EMSGSIZE	-->  90: EMSGSIZE		*/
+	91,	/*  98: EPROTOTYPE	-->  91: EPROTOTYPE		*/
+	92,	/*  99: ENOPROTOOPT	-->  92: ENOPROTOOPT		*/
+	-1,	/* 100: Unused Number					*/
+	-1,	/* 101: Unused Number					*/
+	-1,	/* 102: Unused Number					*/
+	-1,	/* 103: Unused Number					*/
+	-1,	/* 104: Unused Number					*/
+	-1,	/* 105: Unused Number					*/
+	-1,	/* 106: Unused Number					*/
+	-1,	/* 107: Unused Number					*/
+	-1,	/* 108: Unused Number					*/
+	-1,	/* 109: Unused Number					*/
+	-1,	/* 110: Unused Number					*/
+	-1,	/* 111: Unused Number					*/
+	-1,	/* 112: Unused Number					*/
+	-1,	/* 113: Unused Number					*/
+	-1,	/* 114: Unused Number					*/
+	-1,	/* 115: Unused Number					*/
+	-1,	/* 116: Unused Number					*/
+	-1,	/* 117: Unused Number					*/
+	-1,	/* 118: Unused Number					*/
+	-1,	/* 119: Unused Number					*/
+	93,	/* 120: EPROTONOSUPPORT	-->  93: EPROTONOSUPPORT	*/
+	94,	/* 121: ESOCKTNOSUPPORT	-->  94: ESOCKTNOSUPPORT	*/
+	95,	/* 122: EOPNOTSUPP	-->  95: EOPNOTSUPP		*/
+	96,	/* 123: EPFNOSUPPORT	-->  96: EPFNOSUPPORT		*/
+	97,	/* 124: EAFNOSUPPORT	-->  97: EAFNOSUPPORT		*/
+	98,	/* 125: EADDRINUSE	-->  98: EADDRINUSE		*/
+	99,	/* 126: EADDRNOTAVAIL	-->  99: EADDRNOTAVAIL		*/
+	100,	/* 127: ENETDOWN	--> 100: ENETDOWN		*/
+	101,	/* 128: ENETUNREACH	--> 101: ENETUNREACH		*/
+	102,	/* 129: ENETRESET	--> 102: ENETRESET		*/
+	103,	/* 130: ECONNABORTED	--> 103: ECONNABORTED		*/
+	104,	/* 131: ECONNRESET	--> 104: ECONNRESET		*/
+	105,	/* 132: ENOBUFS		--> 105: ENOBUFS		*/
+	106,	/* 133: EISCONN		--> 106: EISCONN		*/
+	107,	/* 134: ENOTCONN	--> 107: ENOTCONN		*/
+	-1,	/* 135: Unused Number					*/
+	-1,	/* 136: Unused Number					*/
+	-1,	/* 137: Unused Number					*/
+	-1,	/* 138: Unused Number					*/
+	-1,	/* 139: Unused Number					*/
+	-1,	/* 140: Unused Number					*/
+	-1,	/* 141: Unused Number					*/
+	-1,	/* 142: Unused Number					*/
+	108,	/* 143: ESHUTDOWN	--> 108: ESHUTDOWN		*/
+	109,	/* 144: ETOOMANYREFS	--> 109: ETOOMANYREFS		*/
+	110,	/* 145: ETIMEDOUT	--> 110: ETIMEDOUT		*/
+	111,	/* 146: ECONNREFUSED	--> 111: ECONNREFUSED		*/
+	112,	/* 147: EHOSTDOWN	--> 112: EHOSTDOWN		*/
+	113,	/* 148: EHOSTUNREACH	--> 113: EHOSTUNREACH		*/
+	114,	/* 149: EALREADY	--> 114: EALREADY		*/
+	115,	/* 150: EINPROGRESS	--> 115: EINPROGRESS		*/
+	116	/* 151: ESTALE		--> 116: ESTALE			*/
+};
+
+/*
+ * Convert an illumos native error number to a Linux error number and return
+ * it.  If no valid conversion is possible, the function fails back to the
+ * value of "defval".  In userland, passing a default error number of "-1"
+ * will abort the program if the error number could not be converted.
+ */
+int
+lx_errno(int native_errno, int defval)
+{
+#ifdef	_KERNEL
+	VERIFY3S(defval, >=, 0);
+#endif
+
+	if (native_errno < 0 || native_errno >= (sizeof (lx_stol_errno) /
+	    sizeof (lx_stol_errno[0]))) {
+#ifndef	_KERNEL
+		VERIFY3S(defval, >=, 0);
+#endif
+
+		return (defval);
+	}
+
+	return (lx_stol_errno[native_errno]);
+}
--- /dev/null	Wed Oct  5 00:31:44 2016
+++ new/usr/src/common/brand/lx/lx_errno.h	Wed Oct  5 00:31:44 2016
@@ -0,0 +1,29 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+#ifndef _LX_ERRNO_H
+#define	_LX_ERRNO_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int lx_errno(int, int);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LX_ERRNO_H */
--- /dev/null	Wed Oct  5 00:31:44 2016
+++ new/usr/src/common/brand/lx/lx_signum.c	Wed Oct  5 00:31:44 2016
@@ -0,0 +1,339 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
+ */
+
+#include <sys/signal.h>
+#include <sys/lx_siginfo.h>
+#include <lx_signum.h>
+#include <sys/debug.h>
+
+/*
+ * Delivering signals to a Linux process is complicated by differences in
+ * signal numbering, stack structure and contents, and the action taken when a
+ * signal handler exits.  In addition, many signal-related structures, such as
+ * sigset_ts, vary between Solaris and Linux.
+ *
+ * The simplest transformation that must be done when sending signals is to
+ * translate between Linux and Solaris signal numbers.
+ *
+ * These are the major signal number differences between Linux and Solaris:
+ *
+ * 	====================================
+ * 	| Number |   Linux    |  Solaris   |
+ * 	| ====== | =========  | ========== |
+ *	|    7   | SIGBUS     | SIGEMT     |
+ *	|   10   | SIGUSR1    | SIGBUS     |
+ *	|   12   | SIGUSR2    | SIGSYS     |
+ *	|   16   | SIGSTKFLT  | SIGUSR1    |
+ *	|   17   | SIGCHLD    | SIGUSR2    |
+ * 	|   18   | SIGCONT    | SIGCHLD    |
+ *	|   19   | SIGSTOP    | SIGPWR     |
+ * 	|   20   | SIGTSTP    | SIGWINCH   |
+ * 	|   21   | SIGTTIN    | SIGURG     |
+ * 	|   22   | SIGTTOU    | SIGPOLL    |
+ *	|   23   | SIGURG     | SIGSTOP    |
+ * 	|   24   | SIGXCPU    | SIGTSTP    |
+ *	|   25   | SIGXFSZ    | SIGCONT    |
+ *	|   26   | SIGVTALARM | SIGTTIN    |
+ *	|   27   | SIGPROF    | SIGTTOU    |
+ *	|   28   | SIGWINCH   | SIGVTALARM |
+ *	|   29   | SIGPOLL    | SIGPROF    |
+ *	|   30   | SIGPWR     | SIGXCPU    |
+ *	|   31   | SIGSYS     | SIGXFSZ    |
+ * 	====================================
+ *
+ * Not every Linux signal maps to a Solaris signal, nor does every Solaris
+ * signal map to a Linux counterpart. However, when signals do map, the
+ * mapping is unique.
+ *
+ * One mapping issue is that Linux supports 33 real time signals, with SIGRTMIN
+ * typically starting at or near 32 (SIGRTMIN) and proceeding to 64 (SIGRTMAX)
+ * (SIGRTMIN is "at or near" 32 because glibc usually "steals" one ore more of
+ * these signals for its own internal use, adjusting SIGRTMIN and SIGRTMAX as
+ * needed.)  Conversely, Solaris actively uses signals 32-40 for other purposes
+ * and supports exactly 32 real time signals, in the range 41 (SIGRTMIN)
+ * to 72 (SIGRTMAX).
+ *
+ * At present, attempting to translate a Linux signal equal to 63
+ * will generate an error (we allow SIGRTMAX because a program
+ * should be able to send SIGRTMAX without getting an EINVAL, though obviously
+ * anything that loops through the signals from SIGRTMIN to SIGRTMAX will
+ * fail.)
+ *
+ * Similarly, attempting to translate a native Solaris signal in the range
+ * 32-40 will also generate an error as we don't want to support the receipt of
+ * those signals from the Solaris global zone.
+ */
+
+/*
+ * Linux to Solaris signal map
+ *
+ * Usage:  solaris_signal = ltos_signum[lx_signal];
+ */
+const int
+ltos_signo[LX_NSIG + 1] = {
+	0,
+	SIGHUP,
+	SIGINT,
+	SIGQUIT,
+	SIGILL,
+	SIGTRAP,
+	SIGABRT,
+	SIGBUS,
+	SIGFPE,
+	SIGKILL,
+	SIGUSR1,
+	SIGSEGV,
+	SIGUSR2,
+	SIGPIPE,
+	SIGALRM,
+	SIGTERM,
+	SIGEMT,			/* 16:  Linux SIGSTKFLT; use Solaris SIGEMT */
+	SIGCHLD,
+	SIGCONT,
+	SIGSTOP,
+	SIGTSTP,
+	SIGTTIN,
+	SIGTTOU,
+	SIGURG,
+	SIGXCPU,
+	SIGXFSZ,
+	SIGVTALRM,
+	SIGPROF,
+	SIGWINCH,
+	SIGPOLL,
+	SIGPWR,
+	SIGSYS,
+	_SIGRTMIN,		/* 32:  Linux SIGRTMIN */
+	_SIGRTMIN + 1,
+	_SIGRTMIN + 2,
+	_SIGRTMIN + 3,
+	_SIGRTMIN + 4,
+	_SIGRTMIN + 5,
+	_SIGRTMIN + 6,
+	_SIGRTMIN + 7,
+	_SIGRTMIN + 8,
+	_SIGRTMIN + 9,
+	_SIGRTMIN + 10,
+	_SIGRTMIN + 11,
+	_SIGRTMIN + 12,
+	_SIGRTMIN + 13,
+	_SIGRTMIN + 14,
+	_SIGRTMIN + 15,
+	_SIGRTMIN + 16,
+	_SIGRTMIN + 17,
+	_SIGRTMIN + 18,
+	_SIGRTMIN + 19,
+	_SIGRTMIN + 20,
+	_SIGRTMIN + 21,
+	_SIGRTMIN + 22,
+	_SIGRTMIN + 23,
+	_SIGRTMIN + 24,
+	_SIGRTMIN + 25,
+	_SIGRTMIN + 26,
+	_SIGRTMIN + 27,
+	_SIGRTMIN + 28,
+	_SIGRTMIN + 29,
+	_SIGRTMIN + 30,
+	_SIGRTMIN + 31,
+	_SIGRTMAX,		/* 64:  Linux SIGRTMAX */
+};
+
+/*
+ * Solaris to Linux signal map
+ *
+ * Usage:  lx_signal = stol_signo[solaris_signal];
+ */
+const int
+stol_signo[NSIG] = {
+	0,
+	LX_SIGHUP,
+	LX_SIGINT,
+	LX_SIGQUIT,
+	LX_SIGILL,
+	LX_SIGTRAP,
+	LX_SIGABRT,
+	LX_SIGSTKFLT,		/* 7:  Solaris SIGEMT; use for LX_SIGSTKFLT */
+	LX_SIGFPE,
+	LX_SIGKILL,
+	LX_SIGBUS,
+	LX_SIGSEGV,
+	LX_SIGSYS,
+	LX_SIGPIPE,
+	LX_SIGALRM,
+	LX_SIGTERM,
+	LX_SIGUSR1,
+	LX_SIGUSR2,
+	LX_SIGCHLD,
+	LX_SIGPWR,
+	LX_SIGWINCH,
+	LX_SIGURG,
+	LX_SIGPOLL,
+	LX_SIGSTOP,
+	LX_SIGTSTP,
+	LX_SIGCONT,
+	LX_SIGTTIN,
+	LX_SIGTTOU,
+	LX_SIGVTALRM,
+	LX_SIGPROF,
+	LX_SIGXCPU,
+	LX_SIGXFSZ,
+	-1,			/* 32:  Solaris SIGWAITING */
+	-1,			/* 33:  Solaris SIGLWP */
+	-1,			/* 34:  Solaris SIGFREEZE */
+	-1,			/* 35:  Solaris SIGTHAW */
+	-1,			/* 36:  Solaris SIGCANCEL */
+	-1,			/* 37:  Solaris SIGLOST */
+	-1,			/* 38:  Solaris SIGXRES */
+	-1,			/* 39:  Solaris SIGJVM1 */
+	-1,			/* 40:  Solaris SIGJVM2 */
+	-1,			/* 41:  Solaris SIGINFO */
+	LX_SIGRTMIN,		/* 42:  Solaris _SIGRTMIN */
+	LX_SIGRTMIN + 1,
+	LX_SIGRTMIN + 2,
+	LX_SIGRTMIN + 3,
+	LX_SIGRTMIN + 4,
+	LX_SIGRTMIN + 5,
+	LX_SIGRTMIN + 6,
+	LX_SIGRTMIN + 7,
+	LX_SIGRTMIN + 8,
+	LX_SIGRTMIN + 9,
+	LX_SIGRTMIN + 10,
+	LX_SIGRTMIN + 11,
+	LX_SIGRTMIN + 12,
+	LX_SIGRTMIN + 13,
+	LX_SIGRTMIN + 14,
+	LX_SIGRTMIN + 15,
+	LX_SIGRTMIN + 16,
+	LX_SIGRTMIN + 17,
+	LX_SIGRTMIN + 18,
+	LX_SIGRTMIN + 19,
+	LX_SIGRTMIN + 20,
+	LX_SIGRTMIN + 21,
+	LX_SIGRTMIN + 22,
+	LX_SIGRTMIN + 23,
+	LX_SIGRTMIN + 24,
+	LX_SIGRTMIN + 25,
+	LX_SIGRTMIN + 26,
+	LX_SIGRTMIN + 27,
+	LX_SIGRTMIN + 28,
+	LX_SIGRTMIN + 29,
+	LX_SIGRTMIN + 30,
+	LX_SIGRTMIN + 31,
+	LX_SIGRTMAX,		/* 74: Solaris _SIGRTMAX */
+};
+
+/*
+ * Convert an illumos native signal number to a Linux signal number and return
+ * it.  If no valid conversion is possible, the function fails back to the
+ * value of "defsig".  In userland, passing a default signal number of "-1"
+ * will abort the program if the signal number could not be converted.
+ */
+int
+lx_stol_signo(int signo, int defsig)
+{
+	int rval;
+
+#ifdef	_KERNEL
+	VERIFY3S(defsig, >=, 0);
+#endif
+
+	if (signo < 0 || signo >= NSIG || (rval = stol_signo[signo]) < 1) {
+#ifndef	_KERNEL
+		VERIFY3S(defsig, >=, 0);
+#endif
+		return (defsig);
+	}
+
+	return (rval);
+}
+
+
+/*
+ * Convert a Linux signal number to an illumos signal number and return it.
+ * Error behavior is identical to lx_stol_signo.
+ */
+int
+lx_ltos_signo(int signo, int defsig)
+{
+#ifdef	_KERNEL
+	VERIFY3S(defsig, >=, 0);
+#endif
+
+	if (signo < 1 || signo >= NSIG) {
+#ifndef	_KERNEL
+		VERIFY3S(defsig, >=, 0);
+#endif
+		return (defsig);
+	}
+
+	return (ltos_signo[signo]);
+}
+
+/*
+ * Convert the "status" field of a SIGCLD siginfo_t.  We need to extract the
+ * illumos signal number and convert it to a Linux signal number while leaving
+ * the ptrace(2) event bits intact.  In userland, passing a default signal
+ * number of "-1" will abort the program if the signal number could not be
+ * converted, as for lx_stol_signo().
+ */
+int
+lx_stol_status(int s, int defsig)
+{
+	/*
+	 * We mask out the top bit here in case PTRACE_O_TRACESYSGOOD
+	 * is in use and 0x80 has been ORed with the signal number.
+	 */
+	int stat = lx_stol_signo(s & 0x7f, defsig);
+
+	/*
+	 * We must mix in the ptrace(2) event which may be stored in
+	 * the second byte of the status code.  We also re-include the
+	 * PTRACE_O_TRACESYSGOOD bit.
+	 */
+	return ((s & 0xff80) | stat);
+}
+
+int
+lx_stol_sigcode(int code)
+{
+	switch (code) {
+	case SI_USER:
+		return (LX_SI_USER);
+	case SI_LWP:
+		return (LX_SI_TKILL);
+	case SI_QUEUE:
+		return (LX_SI_QUEUE);
+	case SI_TIMER:
+		return (LX_SI_TIMER);
+	case SI_ASYNCIO:
+		return (LX_SI_ASYNCIO);
+	case SI_MESGQ:
+		return (LX_SI_MESGQ);
+	default:
+		return (code);
+	}
+}
--- /dev/null	Wed Oct  5 00:31:44 2016
+++ new/usr/src/common/brand/lx/lx_signum.h	Wed Oct  5 00:31:44 2016
@@ -0,0 +1,114 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
+ */
+
+#ifndef _LX_SIGNUM_H
+#define	_LX_SIGNUM_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#define	LX_SIGHUP	1
+#define	LX_SIGINT	2
+#define	LX_SIGQUIT	3
+#define	LX_SIGILL	4
+#define	LX_SIGTRAP	5
+#define	LX_SIGABRT	6
+#define	LX_SIGIOT	6
+#define	LX_SIGBUS	7
+#define	LX_SIGFPE	8
+#define	LX_SIGKILL	9
+#define	LX_SIGUSR1	10
+#define	LX_SIGSEGV	11
+#define	LX_SIGUSR2	12
+#define	LX_SIGPIPE	13
+#define	LX_SIGALRM	14
+#define	LX_SIGTERM	15
+#define	LX_SIGSTKFLT	16
+#define	LX_SIGCHLD	17
+#define	LX_SIGCONT	18
+#define	LX_SIGSTOP	19
+#define	LX_SIGTSTP	20
+#define	LX_SIGTTIN	21
+#define	LX_SIGTTOU	22
+#define	LX_SIGURG	23
+#define	LX_SIGXCPU	24
+#define	LX_SIGXFSZ	25
+#define	LX_SIGVTALRM	26
+#define	LX_SIGPROF	27
+#define	LX_SIGWINCH	28
+#define	LX_SIGIO	29
+#define	LX_SIGPOLL	LX_SIGIO
+#define	LX_SIGPWR	30
+#define	LX_SIGSYS	31
+#define	LX_SIGUNUSED	31
+
+#define	LX_NSIG		64	/* Linux _NSIG */
+
+#define	LX_SIGRTMIN	32
+#define	LX_SIGRTMAX	LX_NSIG
+
+extern const int ltos_signo[];
+extern const int stol_signo[];
+
+extern int lx_stol_signo(int, int);
+extern int lx_ltos_signo(int, int);
+extern int lx_stol_status(int, int);
+extern int lx_stol_sigcode(int);
+
+/*
+ * NOTE:  Linux uses different definitions for 'sigset_t's and 'sigaction_t's
+ *	  depending on whether the definition is for user space or the kernel.
+ *
+ *	  The definitions below MUST correspond to the Linux kernel versions,
+ *	  as glibc will do the necessary translation from the Linux user
+ *	  versions.
+ */
+#if defined(_LP64)
+#define	LX_NSIG_WORDS	1
+#define	LX_WSHIFT	6
+#elif defined(_ILP32)
+#define	LX_NSIG_WORDS	2
+#define	LX_WSHIFT	5
+#else
+#error "LX only supports LP64 and ILP32"
+#endif
+
+typedef struct {
+	ulong_t	__bits[LX_NSIG_WORDS];
+} lx_sigset_t;
+
+#define	LX_NBITS		(sizeof (ulong_t) * NBBY)
+#define	lx_sigmask(n)		(1UL << (((n) - 1) % LX_NBITS))
+#define	lx_sigword(n)		(((ulong_t)((n) - 1)) >> LX_WSHIFT)
+#define	lx_sigismember(s, n)	(lx_sigmask(n) & (s)->__bits[lx_sigword(n)])
+#define	lx_sigaddset(s, n)	((s)->__bits[lx_sigword(n)] |= lx_sigmask(n))
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _LX_SIGNUM_H */
--- /dev/null	Wed Oct  5 00:31:44 2016
+++ new/usr/src/common/brand/lx/lx_syscall.h	Wed Oct  5 00:31:44 2016
@@ -0,0 +1,95 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+#ifndef	_LX_SYSCALL_H
+#define	_LX_SYSCALL_H
+
+#include <sys/lx_brand.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The br_scall_args field of lx_lwp_data is going to be populated with
+ * pointers to structs. The types of these structs should be defined in this
+ * header file.  These are Linux specific arguments to system calls that don't
+ * exist in illumos. Each section should be labelled with which system call it
+ * belongs to.
+ */
+
+/* arguments for waitpid(2) */
+/* see comments in usr/src/lib/brand/lx/lx_brand/common/wait.c */
+#define	LX_WNOTHREAD	0x20000000 /* Do not wait on siblings' children */
+#define	LX_WALL		0x40000000 /* Wait on all children */
+#define	LX_WCLONE	0x80000000 /* Wait only on clone children */
+
+/* For arch_prctl(2) */
+#define	LX_ARCH_SET_GS	0x1001
+#define	LX_ARCH_SET_FS	0x1002
+#define	LX_ARCH_GET_FS	0x1003
+#define	LX_ARCH_GET_GS	0x1004
+
+/*
+ * For ptrace(2):
+ */
+#define	LX_PTRACE_TRACEME	0
+#define	LX_PTRACE_PEEKTEXT	1
+#define	LX_PTRACE_PEEKDATA	2
+#define	LX_PTRACE_PEEKUSER	3
+#define	LX_PTRACE_POKETEXT	4
+#define	LX_PTRACE_POKEDATA	5
+#define	LX_PTRACE_POKEUSER	6
+#define	LX_PTRACE_CONT		7
+#define	LX_PTRACE_KILL		8
+#define	LX_PTRACE_SINGLESTEP	9
+#define	LX_PTRACE_GETREGS	12
+#define	LX_PTRACE_SETREGS	13
+#define	LX_PTRACE_GETFPREGS	14
+#define	LX_PTRACE_SETFPREGS	15
+#define	LX_PTRACE_ATTACH	16
+#define	LX_PTRACE_DETACH	17
+#define	LX_PTRACE_GETFPXREGS	18
+#define	LX_PTRACE_SETFPXREGS	19
+#define	LX_PTRACE_SYSCALL	24
+#define	LX_PTRACE_SETOPTIONS	0x4200
+#define	LX_PTRACE_GETEVENTMSG	0x4201
+#define	LX_PTRACE_GETSIGINFO	0x4202
+
+/*
+ * For clone(2):
+ */
+#define	LX_CSIGNAL		0x000000ff
+#define	LX_CLONE_VM		0x00000100
+#define	LX_CLONE_FS		0x00000200
+#define	LX_CLONE_FILES		0x00000400
+#define	LX_CLONE_SIGHAND	0x00000800
+#define	LX_CLONE_PID		0x00001000
+#define	LX_CLONE_PTRACE		0x00002000
+#define	LX_CLONE_VFORK		0x00004000
+#define	LX_CLONE_PARENT		0x00008000
+#define	LX_CLONE_THREAD		0x00010000
+#define	LX_CLONE_SYSVSEM	0x00040000
+#define	LX_CLONE_SETTLS		0x00080000
+#define	LX_CLONE_PARENT_SETTID	0x00100000
+#define	LX_CLONE_CHILD_CLEARTID	0x00200000
+#define	LX_CLONE_DETACH		0x00400000
+#define	LX_CLONE_CHILD_SETTID	0x01000000
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _LX_SYSCALL_H */
--- /dev/null	Wed Oct  5 00:31:44 2016
+++ new/usr/src/common/brand/lx/tools/Makefile	Wed Oct  5 00:31:44 2016
@@ -0,0 +1,42 @@
+#
+# 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 2015 Joyent, Inc.
+#
+
+PROG =			gen_errno
+
+include ../../../../cmd/Makefile.cmd
+
+OBJS =			gen_errno.o
+
+CLOBBERFILES +=		$(PROG)
+
+NATIVECC_CFLAGS +=	$(CFLAGS) $(CCVERBOSE)
+NATIVECC_LDLIBS +=	-lcmdutils -lnvpair
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install: all
+
+lint:	lint_PROG
+
+clean:
+	$(RM) $(OBJS)
+
+$(PROG): $(OBJS)
+	$(NATIVECC) $(NATIVECC_CFLAGS) $(NATIVECC_LDLIBS) $(OBJS) -o $@
+	$(POST_PROCESS)
+
+include ../../../../cmd/Makefile.targ
--- /dev/null	Wed Oct  5 00:31:45 2016
+++ new/usr/src/common/brand/lx/tools/README.md	Wed Oct  5 00:31:45 2016
@@ -0,0 +1,39 @@
+# Updating Error Number Translations
+
+To create an updated error number translation table, you can use the
+`gen_errno` tool.  This tool requires, as input:
+
+* the illumos native `errno.h` file
+* a set of foreign operating system `errno.h` files
+
+The output is a set of translation table entries suitable for inclusion in a
+cstyled C array.  The index of the array is the native error number and the
+value at each index is the translated error number for use with the foreign
+operating system.
+
+## Example
+
+To generate a translation table for the LX Brand, you will require two files
+from the current Linux source:
+
+* `include/uapi/asm-generic/errno-base.h` (low-valued, or base, error numbers)
+* `include/uapi/asm-generic/errno.h` (extended error numbers)
+
+Assuming the files are in the current directory, you should run the tool as
+follows:
+
+    $ dmake
+    ...
+    $ ./gen_errno -F errno-base.h -F errno.h \
+                  -N $SRC/uts/common/sys/errno.h
+    0, /*  0: No Error                            */
+    1, /*  1: EPERM       -->   1: EPERM          */
+    2, /*  2: ENOENT      -->   2: ENOENT         */
+    3, /*  3: ESRCH       -->   3: ESRCH          */
+    4, /*  4: EINTR       -->   4: EINTR          */
+    5, /*  5: EIO         -->   5: EIO            */
+    6, /*  6: ENXIO       -->   6: ENXIO          */
+    7, /*  7: E2BIG       -->   7: E2BIG          */
+    ...
+
+The output may be used in the `$SRC/common/brand/lx/lx_errno.c` file.
--- /dev/null	Wed Oct  5 00:31:45 2016
+++ new/usr/src/common/brand/lx/tools/gen_errno.c	Wed Oct  5 00:31:45 2016
@@ -0,0 +1,444 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+/*
+ * Take the error number definitions from a foreign system and generate a
+ * translation table that converts illumos native error numbers to foreign
+ * system error numbers.
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <err.h>
+#include <sys/sysmacros.h>
+#include <libcmdutils.h>
+#include <libnvpair.h>
+
+nvlist_t *native_errors;
+nvlist_t *foreign_errors;
+
+struct override {
+	const char *ovr_from;
+	const char *ovr_to;
+} overrides[] = {
+	{ "ENOTSUP", "ENOSYS" },
+	{ 0 }
+};
+
+static const char *
+lookup_override(const char *from)
+{
+	int i;
+
+	for (i = 0; overrides[i].ovr_from != NULL; i++) {
+		if (strcmp(overrides[i].ovr_from, from) == 0) {
+			return (overrides[i].ovr_to);
+		}
+	}
+
+	return (NULL);
+}
+
+static int
+parse_int(const char *number, int *rval)
+{
+	long n;
+	char *endpos;
+
+	errno = 0;
+	if ((n = strtol(number, &endpos, 10)) == 0 && errno != 0) {
+		return (-1);
+	}
+
+	if (endpos != NULL && *endpos != '\0') {
+		errno = EINVAL;
+		return (-1);
+	}
+
+	if (n > INT_MAX || n < INT_MIN) {
+		errno = EOVERFLOW;
+		return (-1);
+	}
+
+	*rval = (int)n;
+	return (0);
+}
+
+static int
+errnum_add(nvlist_t *nvl, const char *name, const char *number)
+{
+	int val;
+
+	if (nvlist_exists(nvl, name)) {
+		(void) fprintf(stderr, "ERROR: duplicate definition: %s -> "
+		    "%s\n", name, number);
+		errno = EEXIST;
+		return (-1);
+	}
+
+	/*
+	 * Try and parse the error number:
+	 */
+	if (parse_int(number, &val) == 0) {
+		/*
+		 * The name refers to a number.
+		 */
+		if (nvlist_add_int32(nvl, name, val) != 0) {
+			(void) fprintf(stderr, "ERROR: nvlist_add_int32: %s\n",
+			    strerror(errno));
+			return (-1);
+		}
+	} else {
+		/*
+		 * The name refers to another definition.
+		 */
+		if (nvlist_add_string(nvl, name, number) != 0) {
+			(void) fprintf(stderr, "ERROR: nvlist_add_string: %s\n",
+			    strerror(errno));
+			return (-1);
+		}
+	}
+
+	return (0);
+}
+
+static int
+errnum_max(nvlist_t *nvl)
+{
+	int max = 0;
+	nvpair_t *nvp = NULL;
+
+	while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
+		if (nvpair_type(nvp) != DATA_TYPE_INT32) {
+			continue;
+		}
+
+		max = MAX(fnvpair_value_int32(nvp), max);
+	}
+
+	return (max);
+}
+
+static int
+errname_by_num(nvlist_t *nvl, int num, const char **name)
+{
+	nvpair_t *nvp = NULL;
+
+	while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
+		if (nvpair_type(nvp) != DATA_TYPE_INT32) {
+			continue;
+		}
+
+		if (fnvpair_value_int32(nvp) == num) {
+			*name = nvpair_name(nvp);
+			return (0);
+		}
+	}
+
+	errno = ENOENT;
+	return (-1);
+}
+
+static int
+errno_by_name(nvlist_t *nvl, const char *name, int *rval, const char **rname)
+{
+	nvpair_t *nvp = NULL;
+
+	if (nvlist_lookup_nvpair(nvl, name, &nvp) != 0) {
+		errno = ENOENT;
+		return (-1);
+	}
+
+	if (nvpair_type(nvp) == DATA_TYPE_STRING) {
+		return (errno_by_name(nvl, fnvpair_value_string(nvp), rval,
+		    rname));
+	} else {
+		*rval = fnvpair_value_int32(nvp);
+		if (rname != NULL) {
+			*rname = name;
+		}
+		return (0);
+	}
+}
+
+static int
+process_line(const char *line, nvlist_t *nvl)
+{
+	custr_t *nam = NULL, *num = NULL;
+	const char *c = line;
+
+	if (custr_alloc(&nam) != 0 || custr_alloc(&num) != 0) {
+		int en = errno;
+
+		custr_free(nam);
+		custr_free(num);
+
+		errno = en;
+		return (-1);
+	}
+
+	/*
+	 * Valid lines begin with "#define":
+	 */
+	if (*c++ != '#' || *c++ != 'd' || *c++ != 'e' || *c++ != 'f' ||
+	    *c++ != 'i' || *c++ != 'n' || *c++ != 'e') {
+		return (0);
+	}
+
+	/*
+	 * Eat whitespace:
+	 */
+	for (;;) {
+		if (*c == '\0') {
+			return (0);
+		}
+
+		if (*c != ' ' && *c != '\t') {
+			break;
+		}
+
+		c++;
+	}
+
+	/*
+	 * Read error number token:
+	 */
+	for (;;) {
+		if (*c == '\0') {
+			return (0);
+		}
+
+		if (*c == ' ' || *c == '\t') {
+			break;
+		}
+
+		if (custr_appendc(nam, *c) != 0) {
+			return (-1);
+		}
+
+		c++;
+	}
+
+	/*
+	 * Eat whitespace:
+	 */
+	for (;;) {
+		if (*c == '\0') {
+			return (0);
+		}
+
+		if (*c != ' ' && *c != '\t') {
+			break;
+		}
+
+		c++;
+	}
+
+	/*
+	 * Read error number token:
+	 */
+	for (;;) {
+		if (*c == '\0') {
+			break;
+		}
+
+		if (*c == ' ' || *c == '\t') {
+			break;
+		}
+
+		if (custr_appendc(num, *c) != 0) {
+			return (-1);
+		}
+
+		c++;
+	}
+
+	return (errnum_add(nvl, custr_cstr(nam), custr_cstr(num)));
+}
+
+static int
+read_file_into_list(const char *path, nvlist_t *nvl)
+{
+	int rval = 0, en = 0;
+	FILE *f;
+	custr_t *cu = NULL;
+
+	if (custr_alloc(&cu) != 0) {
+		return (-1);
+	}
+
+	if ((f = fopen(path, "r")) == NULL) {
+		custr_free(cu);
+		return (-1);
+	}
+
+	for (;;) {
+		int c;
+
+		errno = 0;
+		switch (c = fgetc(f)) {
+		case '\n':
+		case EOF:
+			if (errno != 0) {
+				en = errno;
+				rval = -1;
+				goto out;
+			}
+			if (process_line(custr_cstr(cu), nvl) != 0) {
+				en = errno;
+				rval = -1;
+				goto out;
+			}
+			custr_reset(cu);
+			if (c == EOF) {
+				goto out;
+			}
+			break;
+
+		case '\r':
+		case '\0':
+			/*
+			 * Ignore these characters.
+			 */
+			break;
+
+		default:
+			if (custr_appendc(cu, c) != 0) {
+				en = errno;
+				rval = -1;
+				goto out;
+			}
+			break;
+		}
+	}
+
+out:
+	(void) fclose(f);
+	custr_free(cu);
+	errno = en;
+	return (rval);
+}
+
+int
+main(int argc, char **argv)
+{
+	int max;
+	int fval;
+	int c;
+
+	if (nvlist_alloc(&native_errors, NV_UNIQUE_NAME, 0) != 0 ||
+	    nvlist_alloc(&foreign_errors, NV_UNIQUE_NAME, 0) != 0) {
+		err(1, "could not allocate memory");
+	}
+
+	while ((c = getopt(argc, argv, ":N:F:")) != -1) {
+		switch (c) {
+		case 'N':
+			if (read_file_into_list(optarg, native_errors) != 0) {
+				err(1, "could not read file: %s", optarg);
+			}
+			break;
+
+		case 'F':
+			if (read_file_into_list(optarg, foreign_errors) != 0) {
+				err(1, "could not read file: %s", optarg);
+			}
+			break;
+
+		case ':':
+			errx(1, "option -%c requires an operand", c);
+			break;
+
+		case '?':
+			errx(1, "option -%c unrecognised", c);
+			break;
+		}
+	}
+
+	/*
+	 * Print an array entry for each error number:
+	 */
+	max = errnum_max(native_errors);
+	for (fval = 0; fval <= max; fval++) {
+		const char *fname;
+		const char *tname = NULL;
+		int32_t tval;
+		const char *msg = NULL;
+		const char *comma = (fval != max) ? "," : "";
+
+		if (errname_by_num(native_errors, fval, &fname) == -1) {
+			fname = NULL;
+		}
+
+		if (fval == 0) {
+			/*
+			 * The error number "0" is special: it means no worries.
+			 */
+			msg = "No Error";
+			tval = 0;
+		} else if (fname == NULL) {
+			/*
+			 * There is no defined name for this error number; it
+			 * is unused.
+			 */
+			msg = "Unused Number";
+			tval = -1;
+		} else {
+			/*
+			 * Check if we want to override the name of this error
+			 * in the foreign error number lookup:
+			 */
+			const char *oname = lookup_override(fname);
+
+			/*
+			 * Do the lookup:
+			 */
+			if (errno_by_name(foreign_errors, oname != NULL ?
+			    oname : fname, &tval, &tname) != 0) {
+				/*
+				 * There was no foreign error number by that
+				 * name.
+				 */
+				tname = "No Analogue";
+				tval = -2;
+			}
+		}
+
+		if (msg == NULL) {
+			size_t flen = strlen(fname);
+			size_t tlen = strlen(tname);
+			const char *t = flen > 7 ? "\t" : "\t\t";
+			const char *tt = tlen < 7 ? "\t\t\t" : tlen < 15 ?
+			    "\t\t" : "\t";
+
+			(void) fprintf(stdout, "\t%d%s\t/* %3d: %s%s--> %3d: "
+			    "%s%s*/\n", tval, comma, fval, fname, t, tval,
+			    tname, tt);
+		} else {
+			const char *t = "\t\t\t\t\t";
+
+			(void) fprintf(stdout, "\t%d%s\t/* %3d: %s%s*/\n", tval,
+			    comma, fval, msg, t);
+		}
+	}
+
+	(void) nvlist_free(native_errors);
+	(void) nvlist_free(foreign_errors);
+
+	return (0);
+}
--- old/usr/src/common/list/list.c	Wed Oct  5 00:31:45 2016
+++ new/usr/src/common/list/list.c	Wed Oct  5 00:31:45 2016
@@ -38,7 +38,17 @@
 #endif
 
 #ifdef lint
-extern list_node_t *list_d2l(list_t *list, void *obj);
+static list_node_t *
+list_d2l(list_t *list, void *obj)
+{
+	/* Pretty version for lint... */
+	uint64_t *offset = (uint64_t *)obj;
+
+	if (!IS_P2ALIGNED(obj, 8))
+		return (NULL);
+
+	return ((list_node_t *)(offset + (list->list_offset >> 3)));
+}
 #else
 #define	list_d2l(a, obj) ((list_node_t *)(((char *)obj) + (a)->list_offset))
 #endif
--- old/usr/src/head/libzonecfg.h	Wed Oct  5 00:31:45 2016
+++ new/usr/src/head/libzonecfg.h	Wed Oct  5 00:31:45 2016
@@ -42,6 +42,7 @@
 #include <netinet/in.h>
 #include <sys/socket.h>
 #include <net/if.h>
+#include <sys/mac.h>
 #include <stdio.h>
 #include <rctl.h>
 #include <zone.h>
@@ -127,6 +128,8 @@
 #define	MAXAUTHS		4096
 #define	ZONE_MGMT_PROF		"Zone Management"
 
+#define	ZONE_INT32SZ		11		/* string to hold 32bit int. */
+
 /* Owner, group, and mode (defined by packaging) for the config directory */
 #define	ZONE_CONFIG_UID		0		/* root */
 #define	ZONE_CONFIG_GID		3		/* sys */
@@ -150,9 +153,11 @@
 #define	ALIAS_MAXSEMIDS		"max-sem-ids"
 #define	ALIAS_MAXLOCKEDMEM	"locked"
 #define	ALIAS_MAXSWAP		"swap"
+#define	ALIAS_MAXPHYSMEM	"physical"
 #define	ALIAS_SHARES		"cpu-shares"
 #define	ALIAS_CPUCAP		"cpu-cap"
 #define	ALIAS_MAXPROCS		"max-processes"
+#define	ALIAS_ZFSPRI		"zfs-io-priority"
 
 /* Default name for zone detached manifest */
 #define	ZONE_DETACHED	"SUNWdetached.xml"
@@ -191,15 +196,30 @@
 	char		zone_fs_raw[MAXPATHLEN];	/* device to fsck */
 };
 
+/*
+ * Generic resource attribute list.
+ * Key/value resource that can be attached to net or device.
+ */
+struct zone_res_attrtab {
+	char	zone_res_attr_name[MAXNAMELEN];
+	char	zone_res_attr_value[MAXNAMELEN];
+	struct zone_res_attrtab *zone_res_attr_next;
+};
+
 struct zone_nwiftab {
 	char	zone_nwif_address[INET6_ADDRSTRLEN]; /* shared-ip only */
 	char	zone_nwif_allowed_address[INET6_ADDRSTRLEN]; /* excl-ip only */
 	char	zone_nwif_physical[LIFNAMSIZ];
+	char	zone_nwif_mac[MAXMACADDRLEN];		/* excl-ip only */
+	char	zone_nwif_vlan_id[ZONE_INT32SZ];	/* excl-ip only */
+	char	zone_nwif_gnic[LIFNAMSIZ];		/* excl-ip only */
 	char	zone_nwif_defrouter[INET6_ADDRSTRLEN];
+	struct zone_res_attrtab *zone_nwif_attrp;
 };
 
 struct zone_devtab {
 	char	zone_dev_match[MAXPATHLEN];
+	struct zone_res_attrtab *zone_dev_attrp;
 };
 
 struct zone_rctlvaltab {
@@ -230,10 +250,6 @@
 	char	zone_importance[MAXNAMELEN];
 };
 
-struct zone_mcaptab {
-	char	zone_physmem_cap[MAXNAMELEN];
-};
-
 struct zone_pkgtab {
 	char	zone_pkg_name[MAXNAMELEN];
 	char	zone_pkg_version[ZONE_PKG_VERSMAX];
@@ -317,6 +333,8 @@
 extern	int	zonecfg_get_sched_class(zone_dochandle_t, char *, size_t);
 extern	int	zonecfg_set_sched(zone_dochandle_t, char *);
 extern	int	zonecfg_get_dflt_sched_class(zone_dochandle_t, char *, int);
+extern	zoneid_t zonecfg_get_did(zone_dochandle_t);
+extern	void	zonecfg_set_did(zone_dochandle_t);
 
 /*
  * Set/retrieve the brand for the zone
@@ -341,6 +359,15 @@
     void *), void *);
 
 /*
+ * Resource key/value attributes (properties).
+ */
+extern	int	zonecfg_add_res_attr(struct zone_res_attrtab **,
+    struct zone_res_attrtab *);
+extern	void	zonecfg_free_res_attr_list(struct zone_res_attrtab *);
+extern	int	zonecfg_remove_res_attr(struct zone_res_attrtab **,
+    struct zone_res_attrtab *);
+
+/*
  * Network interface configuration.
  */
 extern	int	zonecfg_add_nwif(zone_dochandle_t, struct zone_nwiftab *);
@@ -422,13 +449,6 @@
 extern	int	zonecfg_lookup_pset(zone_dochandle_t, struct zone_psettab *);
 
 /*
- * mem-cap configuration.
- */
-extern	int	zonecfg_delete_mcap(zone_dochandle_t);
-extern	int	zonecfg_modify_mcap(zone_dochandle_t, struct zone_mcaptab *);
-extern	int	zonecfg_lookup_mcap(zone_dochandle_t, struct zone_mcaptab *);
-
-/*
  * Temporary pool support functions.
  */
 extern	int	zonecfg_destroy_tmp_pool(char *, char *, int);
@@ -485,7 +505,6 @@
 extern	int	zonecfg_getdsent(zone_dochandle_t, struct zone_dstab *);
 extern	int	zonecfg_enddsent(zone_dochandle_t);
 extern	int	zonecfg_getpsetent(zone_dochandle_t, struct zone_psettab *);
-extern	int	zonecfg_getmcapent(zone_dochandle_t, struct zone_mcaptab *);
 extern	int	zonecfg_getpkgdata(zone_dochandle_t, uu_avl_pool_t *,
     uu_avl_t *);
 extern	int	zonecfg_setdevperment(zone_dochandle_t);
@@ -509,6 +528,7 @@
  * Higher-level routines.
  */
 extern  int	zone_get_brand(char *, char *, size_t);
+extern  zoneid_t zone_get_did(char *);
 extern	int	zone_get_rootpath(char *, char *, size_t);
 extern	int	zone_get_devroot(char *, char *, size_t);
 extern	int	zone_get_zonepath(char *, char *, size_t);
@@ -518,6 +538,7 @@
 extern	int	zonecfg_get_name_by_uuid(const uuid_t, char *, size_t);
 extern	int	zonecfg_get_uuid(const char *, uuid_t);
 extern	int	zonecfg_default_brand(char *, size_t);
+extern	int	zonecfg_fix_obsolete(zone_dochandle_t);
 
 /*
  * Iterator for configured zones.
--- old/usr/src/head/limits.h	Wed Oct  5 00:31:45 2016
+++ new/usr/src/head/limits.h	Wed Oct  5 00:31:45 2016
@@ -24,6 +24,7 @@
  *
  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.  All rights reserved.
  */
 
 /*	Copyright (c) 1988 AT&T	*/
@@ -36,6 +37,7 @@
 #include <sys/feature_tests.h>
 #include <sys/isa_defs.h>
 #include <iso/limits_iso.h>
+#include <sys/limits.h>
 
 /*
  * Include fixed width type limits as proposed by the ISO/JTC1/SC22/WG14 C
@@ -237,8 +239,6 @@
 #define	_XOPEN_NAME_MAX	255	/* max # bytes in filename excluding null */
 #define	_XOPEN_PATH_MAX	1024	/* max # bytes in a pathname */
 
-#define	IOV_MAX		_XOPEN_IOV_MAX
-
 #if defined(__EXTENSIONS__) || \
 	(!defined(_STRICT_STDC) && !defined(__XOPEN_OR_POSIX))
 
--- old/usr/src/head/regexp.h	Wed Oct  5 00:31:45 2016
+++ new/usr/src/head/regexp.h	Wed Oct  5 00:31:45 2016
@@ -394,12 +394,12 @@
 			/*FALLTHRU*/
 
 		case CBRA:
-			braslist[*ep++] = (char *)lp;
+			braslist[(int)*ep++] = (char *)lp;
 			continue;
 			/*FALLTHRU*/
 
 		case CKET:
-			braelist[*ep++] = (char *)lp;
+			braelist[(int)*ep++] = (char *)lp;
 			continue;
 			/*FALLTHRU*/
 
@@ -477,8 +477,8 @@
 			/*FALLTHRU*/
 
 		case CBACK:
-			bbeg = braslist[*ep];
-			ct = braelist[*ep++] - bbeg;
+			bbeg = braslist[(int)*ep];
+			ct = braelist[(int)*ep++] - bbeg;
 
 			if (ecmp(bbeg, lp, ct)) {
 				lp += ct;
@@ -488,8 +488,8 @@
 			/*FALLTHRU*/
 
 		case CBACK | STAR:
-			bbeg = braslist[*ep];
-			ct = braelist[*ep++] - bbeg;
+			bbeg = braslist[(int)*ep];
+			ct = braelist[(int)*ep++] - bbeg;
 			curlp = lp;
 			while (ecmp(bbeg, lp, ct))
 				lp += ct;
--- old/usr/src/head/zone.h	Wed Oct  5 00:31:45 2016
+++ new/usr/src/head/zone.h	Wed Oct  5 00:31:45 2016
@@ -21,6 +21,7 @@
 /*
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2015 Joyent Inc.
  */
 
 #ifndef _ZONE_H
@@ -46,10 +47,13 @@
  * NOTE
  *
  * The remaining contents of this file are private to the implementation
- * of Solaris and are subject to change at any time without notice,
+ * of Illumos and are subject to change at any time without notice,
  * Applications using these interfaces may fail to run on future releases.
  */
 
+#define	ZLOGIN_DISCONNECT	0x1	/* disconnect on zone halt */
+#define	ZLOGIN_ZFD_EOF		0x2	/* EOF on ZFD */
+
 extern int zonept(int, zoneid_t);
 extern int zone_get_id(const char *, zoneid_t *);
 
@@ -69,6 +73,7 @@
 extern int	zone_remove_datalink(zoneid_t, datalink_id_t);
 extern int	zone_check_datalink(zoneid_t *, datalink_id_t);
 extern int	zone_list_datalink(zoneid_t, int *, datalink_id_t *);
+extern const char *zone_get_nroot(void);
 
 #ifdef	__cplusplus
 }
--- old/usr/src/lib/Makefile	Wed Oct  5 00:31:45 2016
+++ new/usr/src/lib/Makefile	Wed Oct  5 00:31:45 2016
@@ -567,7 +567,7 @@
 # libc libm libmd libmp libnsl libnvpair libsocket
 abi:		libctf libmapmalloc libproc
 auditd_plugins: libbsm libsecdb libgss libmtmalloc
-brand:		libzonecfg libmapmalloc
+brand:		libzonecfg libmapmalloc libipadm libcmdutils libproc librpcsvc
 cfgadm_plugins:	libdevice libdevinfo libhotplug librcm hbaapi libkstat libscf
 fm:		libexacct libipmi libzfs scsi libdevinfo libdevid libcfgadm \
 		libcontract libsysevent ../cmd/sgs/libelf
--- old/usr/src/lib/Makefile.lib	Wed Oct  5 00:31:45 2016
+++ new/usr/src/lib/Makefile.lib	Wed Oct  5 00:31:46 2016
@@ -21,6 +21,7 @@
 # Copyright 2015 Gary Mills
 # Copyright 2015 Igor Kozhukhov <ikozhukhov@gmail.com>
 # Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2014, Joyent, Inc.
 #
 #
 # Definitions common to libraries.
@@ -135,7 +136,24 @@
 # combining relocations into one relocation table reduces startup costs.
 # All options are tunable to allow overload/omission from lower makefiles.
 
+#
+# DTrace related rules
+#
+# These allow for multiple USDT providers to be specified by a library.
+# If a library needs to break up the set of objects that are passed to
+# the dtrace -G invocation, then they can place the following in heir
+# Makefile.com:
+#
+# pics/<provider>.o	:= USDT_G_PICS = <files>
+#
+# <provider> should be replaced with the basename of one of the USDT
+# providers specified in USDT_PROVIDERS in their Makefile.com.
+#
+USDT_HEADERS=	$(USDT_PROVIDERS:%.d=%_impl.h)
+USDT_PICS=	$(USDT_PROVIDERS:%.d=pics/%.o)
+USDT_G_PICS=	$(PICS)
 
+
 HSONAME=	-h$(SONAME)
 DYNFLAGS=	$(HSONAME) $(ZTEXT) $(ZDEFS) $(BDIRECT) \
 		$(MAPFILES:%=-M%) $(MAPFILE.PGA:%=-M%) $(MAPFILE.NED:%=-M%)
@@ -157,7 +175,7 @@
 # overridden locally when extra processing is needed
 BUILD.AR=	$(AR) $(ARFLAGS) $@ $(AROBJS)
 BUILD.SO=	$(CC) $(CFLAGS) -o $@ $(GSHARED) $(DYNFLAGS) \
-		$(PICS) $(EXTPICS) $(LDLIBS)
+		$(PICS) $(EXTPICS) $(USDT_PICS) $(LDLIBS)
 BUILDCCC.SO=	$(CCC) $(CCFLAGS) -o $@ $(GSHARED) $(DYNFLAGS) \
 		$(PICS) $(EXTPICS) $(LDLIBS)
 
--- old/usr/src/lib/Makefile.targ	Wed Oct  5 00:31:46 2016
+++ new/usr/src/lib/Makefile.targ	Wed Oct  5 00:31:46 2016
@@ -20,6 +20,7 @@
 #
 #
 # Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2014, Joyent, Inc.
 #
 
 #
@@ -90,7 +91,7 @@
 
 $(DYNLIB): $$(MAPFILES)
 
-$(DYNLIB): pics .WAIT $$(PICS) $$(ALTPICS) $$(EXTPICS)
+$(DYNLIB): pics $(USDT_HEADERS) .WAIT $$(PICS) $$(ALTPICS) $$(EXTPICS) .WAIT $(USDT_PICS)
 	$(BUILD.SO)
 	$(POST_PROCESS_SO)
 
@@ -108,5 +109,5 @@
 	-$(RM) $(CLOBBERTARGFILES)
 
 clean:
-	-$(RM) $(OBJS)
+	-$(RM) $(OBJS) $(USDT_HEADERS) $(USDT_PICS)
 	-$(RM) $(PICS) $(DUPLICATE_SRC) $(LINTOUT) $(LINTLIB) $(CLEANFILES)
--- /dev/null	Wed Oct  5 00:31:46 2016
+++ new/usr/src/lib/Makefile.usdt	Wed Oct  5 00:31:46 2016
@@ -0,0 +1,28 @@
+#
+# 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 (c) 2014 Joyent, Inc.  All rights reserved.
+#
+
+#
+# This makefile contains the necessary targets for USDT providers; it should
+# be included after Makefile.targ.  (It is in a separate file rather than in
+# Makefile.targ because the dependency on $(USDT_G_PICS) is incompatible with
+# libraries that dynamically define $(OBJECTS).)
+#
+pics/%.o: $(SRCDIR)/%.d $(USDT_G_PICS)
+	$(COMPILE.d) -o $@ -s $< $(USDT_G_PICS)
+	$(POST_PROCESS_O)
+
+%_impl.h: $(SRCDIR)/%.d
+	$(DTRACE) -h -o $@ -s $<
+
--- old/usr/src/lib/brand/Makefile	Wed Oct  5 00:31:46 2016
+++ new/usr/src/lib/brand/Makefile	Wed Oct  5 00:31:46 2016
@@ -30,6 +30,9 @@
 # Build everything in parallel; use .WAIT for dependencies
 .PARALLEL:
 
+i386_SUBDIRS= lx
+i386_MSGSUBDIRS= lx
+
 SUBDIRS= shared .WAIT sn1 solaris10 ipkg labeled lipkg $($(MACH)_SUBDIRS)
 MSGSUBDIRS= solaris10 shared $($(MACH)_MSGSUBDIRS)
 
--- /dev/null	Wed Oct  5 00:31:46 2016
+++ new/usr/src/lib/brand/lx/Makefile	Wed Oct  5 00:31:46 2016
@@ -0,0 +1,55 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+# Copyright 2016 Joyent, Inc.
+#
+
+default: all
+
+include Makefile.lx
+
+# Build everything in parallel; use .WAIT for dependencies
+.PARALLEL:
+
+SUBDIRS=	librtld_db lx_support lx_init lx_brand netfiles \
+		zone lx_vdso testing .WAIT
+MSGSUBDIRS=	lx_brand lx_support zone
+
+all :=		TARGET= all
+install :=	TARGET= install
+clean :=	TARGET= clean
+clobber :=	TARGET= clobber
+lint :=		TARGET= lint
+_msg :=		TARGET= _msg
+
+.KEEP_STATE:
+
+all install clean clobber lint: $(SUBDIRS)
+
+_msg: $(MSGSUBDIRS)
+
+$(SUBDIRS): FRC
+	@cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
--- /dev/null	Wed Oct  5 00:31:46 2016
+++ new/usr/src/lib/brand/lx/Makefile.lx	Wed Oct  5 00:31:46 2016
@@ -0,0 +1,34 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+# lib/brand/lx/Makefile.lx
+#
+# include global definitions
+
+BRAND=	lx
+
+include $(SRC)/lib/brand/Makefile.brand
+
--- /dev/null	Wed Oct  5 00:31:46 2016
+++ new/usr/src/lib/brand/lx/librtld_db/Makefile	Wed Oct  5 00:31:46 2016
@@ -0,0 +1,54 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+
+default: all
+
+include $(SRC)/lib/Makefile.lib
+
+SUBDIRS = $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+LINT_SUBDIRS=	$(MACH)
+$(BUILD64)LINT_SUBDIRS += $(MACH64)
+
+all :=          TARGET= all
+clean :=        TARGET= clean
+clobber :=      TARGET= clobber
+install :=      TARGET= install
+lint :=         TARGET= lint
+
+.KEEP_STATE:
+
+all install clean clobber: $(SUBDIRS)
+
+lint: $(LINT_SUBDIRS)
+
+$(SUBDIRS): FRC
+	@cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
--- /dev/null	Wed Oct  5 00:31:46 2016
+++ new/usr/src/lib/brand/lx/librtld_db/Makefile.com	Wed Oct  5 00:31:46 2016
@@ -0,0 +1,83 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+
+LIBRARY =	lx_librtld_db.a
+VERS	=	.1
+COBJS	=	lx_librtld_db.o
+OBJECTS	=	$(COBJS) $(COBJS64)
+
+include $(SRC)/lib/Makefile.lib
+include ../../Makefile.lx
+
+CSRCS =       $(COBJS:%o=../common/%c)
+SRCS  =       $(CSRCS)
+
+SRCDIR =	../common
+UTSBASE	=	$(SRC)/uts
+
+#
+# ATTENTION:
+#	Librtl_db brand plugin libraries should NOT directly invoke any
+#	libproc.so interfaces or be linked against libproc.  If a librtl_db
+#	brand plugin library uses libproc.so interfaces then it may break
+#	any other librtld_db consumers (like mdb) that tries to attach
+#	to a branded process.  The only safe interfaces that the a librtld_db
+#	brand plugin library can use to access a target process are the
+#	proc_service(3PROC) apis.
+#
+DYNFLAGS +=	$(VERSREF) -M../common/mapfile-vers
+LIBS =		$(DYNLIB)
+LDLIBS +=	-lc -lrtld_db
+CFLAGS +=	$(CCVERBOSE)
+CPPFLAGS +=	-D_REENTRANT -I../ -I$(UTSBASE)/common/brand/lx \
+			-I$(SRC)/cmd/sgs/librtld_db/common \
+			-I$(SRC)/cmd/sgs/include \
+			-I$(SRC)/cmd/sgs/include/$(MACH)
+
+ROOTLIBDIR =	$(ROOT)/usr/lib/brand/lx
+ROOTLIBDIR64 =	$(ROOT)/usr/lib/brand/lx/$(MACH64)
+
+#
+# The top level Makefiles define define TEXT_DOMAIN.  But librtld_db.so.1
+# isn't internationalized and this library won't be either.  The only
+# messages that this library can generate are messages used for debugging
+# the operation of the library itself.
+#
+DTEXTDOM =
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+lint: lintcheck
+
+pics/%64.o:	../common/%.c
+		$(COMPILE.c) -D_ELF64 $(PICFLAGS) -o $@ $<
+		$(POST_PROCESS_O)
+
+include $(SRC)/lib/Makefile.targ
--- /dev/null	Wed Oct  5 00:31:46 2016
+++ new/usr/src/lib/brand/lx/librtld_db/amd64/Makefile	Wed Oct  5 00:31:46 2016
@@ -0,0 +1,38 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+
+COBJS64 =	lx_librtld_db64.o
+
+include ../Makefile.com
+include $(SRC)/lib/Makefile.lib.64
+
+DYNFLAGS +=	-Mmapfile-vers
+
+CLOBBERFILES = $(ROOTLIBDIR64)/$(DYNLIB)
+
+install: all $(ROOTLIBS64)
--- /dev/null	Wed Oct  5 00:31:46 2016
+++ new/usr/src/lib/brand/lx/librtld_db/amd64/mapfile-vers	Wed Oct  5 00:31:46 2016
@@ -0,0 +1,44 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING:  STOP NOW.  DO NOT MODIFY THIS FILE.
+# Object versioning must comply with the rules detailed in
+#
+#	usr/src/lib/README.mapfiles
+#
+# You should not be making modifications here until you've read the most current
+# copy of that file. If you need help, contact a gatekeeper for guidance.
+#
+# MAPFILE HEADER END
+#
+
+SUNWprivate_1.1 {
+	global:
+		rtld_db_brand_ops64;
+};
--- /dev/null	Wed Oct  5 00:31:46 2016
+++ new/usr/src/lib/brand/lx/librtld_db/common/lx_librtld_db.c	Wed Oct  5 00:31:46 2016
@@ -0,0 +1,853 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright 2015 Joyent, Inc. All rights reserved.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <sys/link.h>
+#include <libproc.h>
+#include <proc_service.h>
+#include <rtld_db.h>
+#include <synch.h>
+
+#include <sys/lx_brand.h>
+
+/*
+ * Overview of this library derived from the original "BrandZ" PSARC design
+ * document.
+ *
+ * Since Linux binaries are standard ELF objects, Illumos debug tools (i.e. mdb
+ * or ptools) are able to process them in essentially the same way that Illumos
+ * binaries are processed. The main objective is to retrieve symbols and
+ * thereby aid debugging and observability. Unfortunately, most Linux
+ * distributions strip(1) their binaries as a misguided "optimization" so the
+ * majority of the useful debugging information is lost.
+ *
+ * The debug tools use interfaces provided by librtld_db to debug live
+ * processes and core files. librtld_db discovers ELF objects which have been
+ * mapped into the target's address space and reports these back to the tool.
+ * The librtld_db library understands enough of the internals of the Illumos
+ * runtime linker to iterate over the linker's private link maps and process
+ * the objects it finds. librtld_db allows our tools to debug the Illumos
+ * portions of a branded process (e.g. the brand library, libc, etc.) but they
+ * can't understand any Linux objects that are mapped into the address space
+ * because the Illumos linker only has Illumos objects on its link maps.
+ *
+ * In order to give the tools visibility into Linux binaries, a brand helper
+ * framework is implemented in librtld_db. When librtld_db is asked to examine
+ * a branded target process or core file, it uses the AT_SUN_BRANDNAME aux
+ * vector to get our brand name (lx). It then dlopen-s this lx_librtld_db.so
+ * helper library.
+ *
+ * Once loaded, this helper library is responsible for finding any lx-specific
+ * information it needs, such as the Linux equivalent LDDATA aux entry and
+ * preparing to return details about the objects loaded into the address space
+ * by the Linux linker.
+ *
+ * When a debug tool asks to know what objects are loaded in the target,
+ * librtld_db walks the Illumos link maps and iterates over each object it
+ * finds there, handing information about each to the tool. It then calls down
+ * into this helper library, which does the same for the brand-specific objects
+ * used by the target.
+ *
+ * This debug-helper code contains a bunch of helpful ps_plog calls. To enable
+ * this output with the ptools (e.g. pmap) set LIBPROC_DEBUG=1 in your
+ * environment. To enable it with mdb set MDB_DEBUG=psvc in your environment.
+ */
+
+/*
+ * ATTENTION:
+ *	Librtl_db brand plugin libraries should NOT directly invoke any
+ *	libproc.so interfaces or be linked against libproc.  If a librtl_db
+ *	brand plugin library uses libproc.so interfaces then it may break
+ *	any other librtld_db consumers (like mdb) that tries to attach
+ *	to a branded process.  The only safe interfaces that the a librtld_db
+ *	brand plugin library can use to access a target process are the
+ *	proc_service(3PROC) apis.
+ */
+
+/*
+ * M_DATA comes from some streams header file but is also redifined in
+ * _rtld_db.h, so nuke the old streams definition here.
+ */
+#ifdef M_DATA
+#undef M_DATA
+#endif /* M_DATA */
+
+/*
+ * For 32-bit versions of this library, this file gets compiled once.
+ * For 64-bit versions of this library, this file gets compiled twice,
+ * once with _ELF64 defined and once without.  The expectation is that
+ * the 64-bit version of the library can properly deal with both 32-bit
+ * and 64-bit elf files, hence in the 64-bit library there are two copies
+ * of all the interfaces in this file, one set named *32 and one named *64.
+ *
+ * This also means that we need to be careful when declaring local pointers
+ * that point to objects in another processes address space, since these
+ * pointers may not match the current processes pointer width.  Basically,
+ * we should avoid using data types that change size between 32 and 64 bit
+ * modes like: long, void *, uintptr_t, caddr_t, psaddr_t, size_t, etc.
+ * Instead we should declare all pointers as uint32_t.  Then when we
+ * are compiled to deal with 64-bit targets we'll re-define uint32_t
+ * to be a uint64_t.
+ */
+#ifdef _LP64
+#ifdef _ELF64
+#define	lx_ldb_get_dyns32		lx_ldb_get_dyns64
+#define	lx_ldb_init32			lx_ldb_init64
+#define	lx_ldb_fini32			lx_ldb_fini64
+#define	lx_ldb_loadobj_iter32		lx_ldb_loadobj_iter64
+#define	lx_ldb_getauxval32		lx_ldb_getauxval64
+#define	lx_elf_props32			lx_elf_props64
+#define	_rd_get_dyns32			_rd_get_dyns64
+#define	_rd_get_ehdr32			_rd_get_ehdr64
+#define	uint32_t			uint64_t
+#define	Elf32_Dyn			Elf64_Dyn
+#define	Elf32_Ehdr			Elf64_Ehdr
+#define	Elf32_Phdr			Elf64_Phdr
+#define	Elf32_Sym			Elf64_Sym
+#endif /* _ELF64 */
+#endif /* _LP64 */
+
+/* Included from usr/src/cmd/sgs/librtld_db/common */
+#include <_rtld_db.h>
+
+typedef struct lx_rd {
+	rd_agent_t		*lr_rap;
+	struct ps_prochandle	*lr_php;	/* proc handle pointer */
+	uint32_t		lr_rdebug;	/* address of lx r_debug */
+	uint32_t		lr_exec;	/* base address of executable */
+} lx_rd_t;
+
+typedef struct lx_link_map {
+	uint32_t lxm_addr;	/* Base address shared object is loaded at.  */
+	uint32_t lxm_name;	/* Absolute file name object was found in.  */
+	uint32_t lxm_ld;	/* Dynamic section of the shared object.  */
+	uint32_t lxm_next;	/* Chain of loaded objects.  */
+} lx_link_map_t;
+
+typedef struct lx_r_debug {
+	int r_version;		/* Version number for this protocol.  */
+	uint32_t	r_map;	/* Head of the chain of loaded objects. */
+
+	/*
+	 * This is the address of a function internal to the run-time linker,
+	 * that will always be called when the linker begins to map in a
+	 * library or unmap it, and again when the mapping change is complete.
+	 * The debugger can set a breakpoint at this address if it wants to
+	 * notice shared object mapping changes.
+	 */
+	uint32_t	r_brk;
+	r_state_e	r_state; /* defined the same way between lx/solaris */
+	uint32_t	r_ldbase; /* Base address the linker is loaded at. */
+} lx_r_debug_t;
+
+static uint32_t
+lx_ldb_getauxval32(struct ps_prochandle *php, int type)
+{
+	const auxv_t		*auxvp = NULL;
+
+	if (ps_pauxv(php, &auxvp) != PS_OK)
+		return ((uint32_t)-1);
+
+	while (auxvp->a_type != AT_NULL) {
+		if (auxvp->a_type == type)
+			return ((uint32_t)(uintptr_t)auxvp->a_un.a_ptr);
+		auxvp++;
+	}
+	return ((uint32_t)-1);
+}
+
+/*
+ * A key difference between the linux linker and ours' is that the linux
+ * linker adds the base address of segments to certain values in the
+ * segments' ELF header. As an example, look at the address of the
+ * DT_HASH hash table in a Solaris section - it is a relative address
+ * which locates the start of the hash table, relative to the beginning
+ * of the ELF file. However, when the linux linker loads a section, it
+ * modifies the in-memory ELF image by changing address of the hash
+ * table to be an absolute address. This is only done for libraries - not for
+ * executables.
+ *
+ * Solaris tools expect the relative address to remain relative, so
+ * here we will modify the in-memory ELF image so that it once again
+ * contains relative addresses.
+ *
+ * To accomplish this, we walk through all sections in the target.
+ * Linux sections are identified by pointing to the linux linker or libc in the
+ * DT_NEEDED section. For all matching sections, we subtract the segment
+ * base address to get back to relative addresses.
+ */
+static rd_err_e
+lx_ldb_get_dyns32(rd_helper_data_t rhd,
+    psaddr_t addr, void **dynpp, size_t *dynpp_sz)
+{
+	lx_rd_t			*lx_rd = (lx_rd_t *)rhd;
+	rd_agent_t		*rap = lx_rd->lr_rap;
+	Elf32_Ehdr		ehdr;
+	Elf32_Dyn		*dynp = NULL;
+	size_t			dynp_sz;
+	uint_t			ndyns;
+	int			i;
+
+	ps_plog("lx_ldb_get_dyns: invoked for object at 0x%p", addr);
+
+	/* Read in a copy of the ehdr */
+	if (_rd_get_ehdr32(rap, addr, &ehdr, NULL) != RD_OK) {
+		ps_plog("lx_ldb_get_dyns: _rd_get_ehdr() failed");
+		return (RD_ERR);
+	}
+
+	/* read out the PT_DYNAMIC elements for this object */
+	if (_rd_get_dyns32(rap, addr, &dynp, &dynp_sz) != RD_OK) {
+		ps_plog("lx_ldb_get_dyns: _rd_get_dyns() failed");
+		return (RD_ERR);
+	}
+
+	/*
+	 * From here on out if we encounter an error we'll just return
+	 * success and pass back the unmolested dynamic elements that
+	 * we've already obtained.
+	 */
+	if (dynpp != NULL)
+		*dynpp = dynp;
+	if (dynpp_sz != NULL)
+		*dynpp_sz = dynp_sz;
+	ndyns = dynp_sz / sizeof (Elf32_Dyn);
+
+	/* If this isn't a dynamic object, there's nothing left todo */
+	if (ehdr.e_type != ET_DYN) {
+		ps_plog("lx_ldb_get_dyns: done: not a shared object");
+		return (RD_OK);
+	}
+
+	/*
+	 * Before we blindly start changing dynamic section addresses
+	 * we need to figure out if the current object that we're looking
+	 * at is a linux object or a solaris object.  To do this first
+	 * we need to find the string tab dynamic section element.
+	 */
+	for (i = 0; i < ndyns; i++) {
+		if (dynp[i].d_tag == DT_STRTAB)
+			break;
+	}
+	if (i == ndyns) {
+		ps_plog("lx_ldb_get_dyns: "
+		    "failed to find string tab in the dynamic section");
+		return (RD_OK);
+	}
+
+	/*
+	 * Check if the strtab value looks like an offset or an address.
+	 * It's an offset if the value is less then the base address that
+	 * the object is loaded at, or if the value is less than the offset
+	 * of the section headers in the same elf object.  This check isn't
+	 * perfect, but in practice it's good enough.
+	 */
+	if ((dynp[i].d_un.d_ptr < addr) ||
+	    (dynp[i].d_un.d_ptr < ehdr.e_shoff)) {
+		ps_plog("lx_ldb_get_dyns: "
+		    "doesn't appear to be an lx object");
+		return (RD_OK);
+	}
+
+	/*
+	 * This seems to be a a linux object, so we'll patch up the dynamic
+	 * section addresses
+	 */
+	ps_plog("lx_ldb_get_dyns: "
+	    "patching up lx object dynamic section addresses");
+	for (i = 0; i < ndyns; i++) {
+		switch (dynp[i].d_tag) {
+		case DT_PLTGOT:
+		case DT_HASH:
+		case DT_STRTAB:
+		case DT_SYMTAB:
+		case DT_RELA:
+		case DT_REL:
+		case DT_DEBUG:
+		case DT_JMPREL:
+		case DT_VERSYM:
+			if (dynp[i].d_un.d_val > addr) {
+				dynp[i].d_un.d_ptr -= addr;
+			}
+			break;
+		default:
+			break;
+		}
+	}
+	return (RD_OK);
+}
+
+static void
+lx_ldb_fini32(rd_helper_data_t rhd)
+{
+	lx_rd_t *lx_rd = (lx_rd_t *)rhd;
+	ps_plog("lx_ldb_fini: cleaning up lx helper");
+	free(lx_rd);
+}
+
+/*
+ * The linux linker has an r_debug structure somewhere in its data section that
+ * contains the address of the head of the link map list. To find this, we will
+ * use the DT_DEBUG token in the executable's dynamic section. The linux linker
+ * wrote the address of its r_debug structure to the DT_DEBUG dynamic entry. We
+ * get the address of the executable's program headers from the
+ * AT_SUN_BRAND_LX_PHDR aux vector entry. From there, we calculate the
+ * address of the Elf header, and from there we can easily get to the DT_DEBUG
+ * entry.
+ */
+static rd_helper_data_t
+lx_ldb_init32(rd_agent_t *rap, struct ps_prochandle *php)
+{
+	lx_rd_t		*lx_rd;
+	uint32_t	addr, phdr_addr, dyn_addr;
+	uint32_t	symtab, strtab, offs;
+	uint32_t	vaddr, memsz;
+	caddr_t		mem;
+	Elf32_Dyn	*dyn;
+	Elf32_Phdr	phdr, *ph, *dph, *phdrs;
+	Elf32_Ehdr	ehdr;
+	Elf32_Sym	*sym;
+	int		i, dyn_count;
+
+	lx_rd = calloc(sizeof (lx_rd_t), 1);
+	if (lx_rd == NULL) {
+		ps_plog("lx_ldb_init: cannot allocate memory");
+		return (NULL);
+	}
+	lx_rd->lr_rap = rap;
+	lx_rd->lr_php = php;
+
+	phdr_addr = lx_ldb_getauxval32(php, AT_SUN_BRAND_LX_PHDR);
+	if (phdr_addr == (uint32_t)-1) {
+		ps_plog("lx_ldb_init: no LX_PHDR found in aux vector");
+		return (NULL);
+	}
+	ps_plog("lx_ldb_init: found LX_PHDR auxv phdr at: 0x%p",
+	    phdr_addr);
+
+	if (ps_pread(php, phdr_addr, &phdr, sizeof (phdr)) != PS_OK) {
+		ps_plog("lx_ldb_init: couldn't read phdr at 0x%p",
+		    phdr_addr);
+		free(lx_rd);
+		return (NULL);
+	}
+
+	/* The ELF header should be before the program header in memory */
+	lx_rd->lr_exec = addr = phdr_addr - phdr.p_offset;
+	if (ps_pread(php, addr, &ehdr, sizeof (ehdr)) != PS_OK) {
+		ps_plog("lx_ldb_init: couldn't read ehdr at 0x%p",
+		    lx_rd->lr_exec);
+		free(lx_rd);
+		return (NULL);
+	}
+	ps_plog("lx_ldb_init: read ehdr at: 0x%p", addr);
+	ps_plog("lx_ldb_init: ehdr t %d ent 0x%p poff 0x%p psize %d pnum %d",
+	    ehdr.e_type, ehdr.e_entry, ehdr.e_phoff, ehdr.e_phentsize,
+	    ehdr.e_phnum);
+
+	if ((phdrs = malloc(ehdr.e_phnum * ehdr.e_phentsize)) == NULL) {
+		ps_plog("lx_ldb_init: couldn't alloc phdrs memory");
+		free(lx_rd);
+		return (NULL);
+	}
+
+	if (ps_pread(php, phdr_addr, phdrs, ehdr.e_phnum * ehdr.e_phentsize) !=
+	    PS_OK) {
+		ps_plog("lx_ldb_init: couldn't read phdrs at 0x%p",
+		    phdr_addr);
+		free(lx_rd);
+		free(phdrs);
+		return (NULL);
+	}
+
+	/* program headers */
+	ps_plog("lx_ldb_init: read %d phdrs at: 0x%p",
+	    ehdr.e_phnum, phdr_addr);
+
+	for (i = 0, ph = phdrs; i < ehdr.e_phnum; i++,
+	    /*LINTED */
+	    ph = (Elf32_Phdr *)((char *)ph + ehdr.e_phentsize)) {
+		ps_plog("lx_ldb_init: ph[%d] 0x%p type %d", i,
+		    (phdr_addr + ((char *)ph - (char *)phdrs)), ph->p_type);
+		if (ph->p_type == PT_DYNAMIC)
+			break;
+	}
+	if (i == ehdr.e_phnum) {
+		ps_plog("lx_ldb_init: no PT_DYNAMIC in executable");
+		free(lx_rd);
+		free(phdrs);
+		return (NULL);
+	}
+	ps_plog("lx_ldb_init: found PT_DYNAMIC phdr[%d] at: 0x%p",
+	    i, (phdr_addr + ((char *)ph - (char *)phdrs)));
+	ps_plog("lx_ldb_init: ph t 0x%x f 0x%x o 0x%p v 0x%p s %d",
+	    ph->p_type, ph->p_flags, ph->p_offset, ph->p_vaddr, ph->p_filesz);
+
+	if ((dyn = malloc(ph->p_filesz)) == NULL) {
+		ps_plog("lx_ldb_init: couldn't alloc for PT_DYNAMIC");
+		free(lx_rd);
+		free(phdrs);
+		return (NULL);
+	}
+
+	/*
+	 * Unclear why the dyn_addr works sometimes with one value and
+	 * sometimes for the other, so we handle both cases.
+	 */
+
+	dyn_count = ph->p_filesz / sizeof (Elf32_Dyn);
+	ps_plog("lx_ldb_init: dyn_count %d %d", dyn_count, sizeof (Elf32_Dyn));
+	dyn_addr = addr + ph->p_vaddr;
+	ps_plog("lx_ldb_init: dyn_addr 0x%p 0x%x = 0x%p",
+	    addr, ph->p_offset, dyn_addr);
+	if (ps_pread(php, dyn_addr, dyn, ph->p_filesz) != PS_OK) {
+		ps_plog("lx_ldb_init: couldn't read dynamic at 0x%p, "
+		    "trying dyn_addr 0x%p",
+		    dyn_addr, ph->p_vaddr);
+
+		dyn_addr = ph->p_vaddr;
+		if (ps_pread(php, dyn_addr, dyn, ph->p_filesz) != PS_OK) {
+			ps_plog("lx_ldb_init: couldn't read dynamic at 0x%p",
+			    dyn_addr);
+
+			free(lx_rd);
+			free(phdrs);
+			free(dyn);
+			return (NULL);
+		}
+	}
+	ps_plog("lx_ldb_init: read %d dynamic headers at: 0x%p",
+	    dyn_count, dyn_addr);
+
+	for (i = 0; i < dyn_count; i++) {
+		if (dyn[i].d_tag == DT_DEBUG) {
+			lx_rd->lr_rdebug = dyn[i].d_un.d_ptr;
+			break;
+		}
+	}
+	free(phdrs);
+	free(dyn);
+
+	if (lx_rd->lr_rdebug != 0) {
+		ps_plog("lx_ldb_init: found DT_DEBUG: 0x%p", lx_rd->lr_rdebug);
+		return ((rd_helper_data_t)lx_rd);
+	}
+
+	ps_plog("lx_ldb_init: no DT_DEBUG found in exe; looking for r_debug");
+
+	/*
+	 * If we didn't find DT_DEBUG, we're going to employ the same fallback
+	 * as gdb:  pawing through the dynamic linker's symbol table looking
+	 * for the r_debug symbol.
+	 */
+	addr = lx_ldb_getauxval32(php, AT_SUN_BRAND_LX_INTERP);
+
+	if (addr == (uint32_t)-1) {
+		ps_plog("lx_ldb_init: no interpreter; failing");
+		free(lx_rd);
+		return (NULL);
+	}
+
+	ps_plog("lx_ldb_init: reading interp ehdr at 0x%p", addr);
+
+	if (ps_pread(php, addr, &ehdr, sizeof (ehdr)) != PS_OK) {
+		ps_plog("lx_ldb_init: couldn't read interp ehdr at 0x%p", addr);
+		free(lx_rd);
+		return (NULL);
+	}
+
+	if (ehdr.e_type != ET_DYN) {
+		ps_plog("lx_ldb_init: interp ehdr not of type ET_DYN");
+		free(lx_rd);
+		return (NULL);
+	}
+
+	phdr_addr = addr + ehdr.e_phoff;
+
+	if ((phdrs = malloc(ehdr.e_phnum * ehdr.e_phentsize)) == NULL) {
+		ps_plog("lx_ldb_init: couldn't alloc interp phdrs memory");
+		free(lx_rd);
+		return (NULL);
+	}
+
+	if (ps_pread(php, phdr_addr, phdrs,
+	    ehdr.e_phnum * ehdr.e_phentsize) != PS_OK) {
+		ps_plog("lx_ldb_init: couldn't read interp phdrs at 0x%p",
+		    phdr_addr);
+		free(lx_rd);
+		free(phdrs);
+		return (NULL);
+	}
+
+	ps_plog("lx_ldb_init: read %d interp phdrs at: 0x%p",
+	    ehdr.e_phnum, phdr_addr);
+
+	vaddr = (uint32_t)-1;
+	memsz = 0;
+
+	for (i = 0, ph = phdrs, dph = NULL; i < ehdr.e_phnum; i++,
+	    /*LINTED */
+	    ph = (Elf32_Phdr *)((char *)ph + ehdr.e_phentsize)) {
+		/*
+		 * Keep track of our lowest PT_LOAD address, as this segment
+		 * contains the DT_SYMTAB and DT_STRTAB.
+		 */
+		if (ph->p_type == PT_LOAD && ph->p_vaddr < vaddr) {
+			vaddr = ph->p_vaddr;
+			memsz = ph->p_memsz;
+		}
+
+		if (ph->p_type == PT_DYNAMIC)
+			dph = ph;
+	}
+
+	if (vaddr == (uint32_t)-1 || memsz == 0) {
+		ps_plog("lx_ldb_init: no PT_LOAD section in interp");
+		free(lx_rd);
+		free(phdrs);
+		return (NULL);
+	}
+
+	ps_plog("lx_ldb_init: found interp PT_LOAD to be %d bytes at 0x%p",
+	    memsz, vaddr);
+
+	if ((ph = dph) == NULL) {
+		ps_plog("lx_ldb_init: no PT_DYNAMIC in interp");
+		free(lx_rd);
+		free(phdrs);
+		return (NULL);
+	}
+
+	ps_plog("lx_ldb_init: found interp PT_DYNAMIC phdr[%d] at: 0x%p",
+	    i, (phdr_addr + ((char *)ph - (char *)phdrs)));
+
+	if ((dyn = malloc(ph->p_filesz)) == NULL) {
+		ps_plog("lx_ldb_init: couldn't alloc for interp PT_DYNAMIC");
+		free(lx_rd);
+		free(phdrs);
+		return (NULL);
+	}
+
+	dyn_addr = addr + ph->p_offset;
+	dyn_count = ph->p_filesz / sizeof (Elf32_Dyn);
+
+	if (ps_pread(php, dyn_addr, dyn, ph->p_filesz) != PS_OK) {
+		ps_plog("lx_ldb_init: couldn't read interp dynamic at 0x%p",
+		    dyn_addr);
+		free(lx_rd);
+		free(phdrs);
+		free(dyn);
+		return (NULL);
+	}
+
+	free(phdrs);
+
+	ps_plog("lx_ldb_init: read %d interp dynamic headers at: 0x%p",
+	    dyn_count, dyn_addr);
+
+	/*
+	 * As noted in lx_ldb_get_dyns32(), in Linux, the PT_DYNAMIC table
+	 * is adjusted to represent absolute addresses instead of offsets.
+	 * This is not true for the interpreter, however -- where the values
+	 * will be represented as offsets from the lowest PT_LOAD p_vaddr.
+	 */
+	symtab = strtab = (uint32_t)-1;
+
+	for (i = 0; i < dyn_count; i++) {
+		if (dyn[i].d_tag == DT_STRTAB)
+			strtab = dyn[i].d_un.d_ptr - vaddr;
+
+		if (dyn[i].d_tag == DT_SYMTAB)
+			symtab = dyn[i].d_un.d_ptr - vaddr;
+	}
+
+	free(dyn);
+
+	if (strtab == (uint32_t)-1 || strtab > memsz) {
+		ps_plog("lx_ldb_init: didn't find valid interp strtab");
+		free(lx_rd);
+		return (NULL);
+	}
+
+	if (symtab == (uint32_t)-1 || symtab > memsz) {
+		ps_plog("lx_ldb_init: didn't find valid interp symtab");
+		free(lx_rd);
+		return (NULL);
+	}
+
+	ps_plog("lx_ldb_init: strtab is 0x%p, symtab is 0x%p",
+	    addr + strtab, addr + symtab);
+
+	if ((mem = malloc(memsz)) == NULL) {
+		ps_plog("lx_ldb_init: couldn't allocate interp "
+		    "buffer of 0x%p bytes", memsz);
+		free(lx_rd);
+		return (NULL);
+	}
+
+	if (ps_pread(php, addr, mem, memsz) != PS_OK) {
+		ps_plog("lx_ldb_init: couldn't read interp at 0x%p", addr);
+		free(lx_rd);
+		free(mem);
+		return (NULL);
+	}
+
+	/*
+	 * We make an assumption that is made elsewhere in the Linux linker:
+	 * that the DT_SYMTAB immediately precedes the DT_STRTAB.
+	 */
+	for (offs = symtab; offs < strtab; offs += sizeof (Elf32_Sym)) {
+		uint32_t p;
+
+		/* LINTED - "offs" increments always in sizeof (Elf32_Sym). */
+		sym = (Elf32_Sym *)&mem[offs];
+
+		if (sym->st_name > memsz) {
+			ps_plog("lx_ldb_init: invalid st_name at sym 0x%p",
+			    addr + offs);
+			continue;
+		}
+
+		p = sym->st_name;
+
+		if (strtab + p > memsz) {
+			ps_plog("lx_ldb_init: invalid symbol address 0x%p, "
+			    "memsz 0x%p", strtab + p, memsz);
+			continue;
+		}
+
+		if (mem[strtab + p] == '\0')
+			continue;
+
+		/* Sometimes we're pointing into the middle of a symbol? */
+		while ((strtab + p) > 0 && (strtab + p) < memsz &&
+		    mem[strtab + p] != '\0')
+			p--;
+		p++;
+
+		ps_plog("lx_ldb_init: interp symbol (0x%p) %s",
+		    strtab + p, &mem[strtab + p]);
+
+		if (strcmp(&mem[strtab + p], "_r_debug") == 0)
+			break;
+	}
+
+	if (offs >= strtab) {
+		ps_plog("lx_ldb_init: no _r_debug found in interpreter");
+		free(mem);
+		free(lx_rd);
+		return (NULL);
+	}
+
+	lx_rd->lr_rdebug = (sym->st_value - vaddr) + addr;
+	ps_plog("lx_ldb_init: found _r_debug at 0x%p", lx_rd->lr_rdebug);
+	free(mem);
+
+	return ((rd_helper_data_t)lx_rd);
+}
+
+/*
+ * Given the address of an ELF object in the target, return its size and
+ * the proper link map ID.
+ */
+static size_t
+lx_elf_props32(struct ps_prochandle *php, uint32_t addr, psaddr_t *data_addr)
+{
+	Elf32_Ehdr	ehdr;
+	Elf32_Phdr	*phdrs, *ph;
+	int		i;
+	uint32_t	min = (uint32_t)-1;
+	uint32_t	max = 0;
+	size_t		sz = NULL;
+
+	if (ps_pread(php, addr, &ehdr, sizeof (ehdr)) != PS_OK) {
+		ps_plog("lx_elf_props: Couldn't read ELF header at 0x%p",
+		    addr);
+		return (0);
+	}
+
+	if ((phdrs = malloc(ehdr.e_phnum * ehdr.e_phentsize)) == NULL)
+		return (0);
+
+	if (ps_pread(php, addr + ehdr.e_phoff, phdrs, ehdr.e_phnum *
+	    ehdr.e_phentsize) != PS_OK) {
+		ps_plog("lx_elf_props: Couldn't read program headers at 0x%p",
+		    addr + ehdr.e_phoff);
+		return (0);
+	}
+
+	for (i = 0, ph = phdrs; i < ehdr.e_phnum; i++,
+	    /*LINTED */
+	    ph = (Elf32_Phdr *)((char *)ph + ehdr.e_phentsize)) {
+
+		if (ph->p_type != PT_LOAD)
+			continue;
+
+		if ((ph->p_flags & (PF_W | PF_R)) == (PF_W | PF_R)) {
+			*data_addr = ph->p_vaddr;
+			if (ehdr.e_type == ET_DYN)
+				*data_addr += addr;
+			if (*data_addr & (ph->p_align - 1))
+				*data_addr = *data_addr & (~(ph->p_align -1));
+		}
+
+		if (ph->p_vaddr < min)
+			min = ph->p_vaddr;
+
+		if (ph->p_vaddr > max) {
+			max = ph->p_vaddr;
+			sz = ph->p_memsz + max - min;
+			if (sz & (ph->p_align - 1))
+				sz = (sz & (~(ph->p_align - 1))) + ph->p_align;
+		}
+	}
+
+	free(phdrs);
+	return (sz);
+}
+
+static int
+lx_ldb_loadobj_iter32(rd_helper_data_t rhd, rl_iter_f *cb, void *client_data)
+{
+	lx_rd_t			*lx_rd = (lx_rd_t *)rhd;
+	struct ps_prochandle	*php = lx_rd->lr_php;
+	lx_r_debug_t		r_debug;
+	lx_link_map_t		map;
+	uint32_t		p = NULL;
+	int			rc;
+	rd_loadobj_t		exec;
+
+	if ((rc = ps_pread(php, (psaddr_t)lx_rd->lr_rdebug, &r_debug,
+	    sizeof (r_debug))) != PS_OK) {
+		ps_plog("lx_ldb_loadobj_iter: "
+		    "Couldn't read linux r_debug at 0x%p", lx_rd->lr_rdebug);
+		return (rc);
+	}
+
+	p = r_debug.r_map;
+
+	/*
+	 * The first item on the link map list is for the executable, but it
+	 * doesn't give us any useful information about it. We need to
+	 * synthesize a rd_loadobj_t for the client.
+	 *
+	 * Linux doesn't give us the executable name, so we'll get it from
+	 * the AT_EXECNAME entry instead.
+	 */
+	if ((rc = ps_pread(php, (psaddr_t)p, &map, sizeof (map))) != PS_OK) {
+		ps_plog("lx_ldb_loadobj_iter: "
+		    "Couldn't read linux link map at 0x%p", p);
+		return (rc);
+	}
+
+	bzero(&exec, sizeof (exec));
+	exec.rl_base = lx_rd->lr_exec;
+	exec.rl_dynamic = map.lxm_ld;
+	exec.rl_nameaddr = lx_ldb_getauxval32(php, AT_SUN_EXECNAME);
+	exec.rl_lmident = LM_ID_BASE;
+
+	exec.rl_bend = exec.rl_base +
+	    lx_elf_props32(php, lx_rd->lr_exec, &exec.rl_data_base);
+
+	if ((*cb)(&exec, client_data) == 0) {
+		ps_plog("lx_ldb_loadobj_iter: "
+		    "client callb failed for executable");
+		return (PS_ERR);
+	}
+	ps_plog("lx_ldb_loadobj_iter: exec base 0x%p dyn 0x%p",
+	    exec.rl_base, exec.rl_dynamic);
+
+	for (p = map.lxm_next; p != NULL; p = map.lxm_next) {
+		rd_loadobj_t	obj;
+
+		if ((rc = ps_pread(php, (psaddr_t)p, &map, sizeof (map))) !=
+		    PS_OK) {
+			ps_plog("lx_ldb_loadobj_iter: "
+			    "Couldn't read lk map at %p", p);
+			return (rc);
+		}
+
+		/*
+		 * The linux link map has less information than the Solaris one.
+		 * We need to go fetch the missing information from the ELF
+		 * headers.
+		 */
+
+		obj.rl_nameaddr = (psaddr_t)map.lxm_name;
+		obj.rl_base = map.lxm_addr;
+		obj.rl_refnameaddr = (psaddr_t)map.lxm_name;
+		obj.rl_plt_base = NULL;
+		obj.rl_plt_size = 0;
+		obj.rl_lmident = LM_ID_BASE;
+
+		ps_plog("lx_ldb_loadobj_iter: map base 0x%p 0x%p",
+		    obj.rl_base, obj.rl_nameaddr);
+
+		/*
+		 * Ugh - we have to walk the ELF stuff, find the PT_LOAD
+		 * sections, and calculate the end of the file's mappings
+		 * ourselves.
+		 */
+
+		obj.rl_bend = map.lxm_addr +
+		    lx_elf_props32(php, map.lxm_addr, &obj.rl_data_base);
+		obj.rl_padstart = obj.rl_base;
+		obj.rl_padend = obj.rl_bend;
+		obj.rl_dynamic = map.lxm_ld;
+		obj.rl_tlsmodid = 0;
+
+		ps_plog("lx_ldb_loadobj_iter: 0x%p to 0x%p",
+		    obj.rl_base, obj.rl_bend);
+
+		if ((*cb)(&obj, client_data) == 0) {
+			ps_plog("lx_ldb_loadobj_iter: "
+			    "Client callback failed on %s", map.lxm_name);
+			return (rc);
+		}
+	}
+	return (RD_OK);
+}
+
+/*
+ * Librtld_db plugin linkage struct.
+ *
+ * When we get loaded by librtld_db, it will look for the symbol below
+ * to find our plugin entry points.
+ */
+rd_helper_ops_t RTLD_DB_BRAND_OPS = {
+	LM_ID_BRAND,
+	lx_ldb_init32,
+	lx_ldb_fini32,
+	lx_ldb_loadobj_iter32,
+	lx_ldb_get_dyns32
+};
--- /dev/null	Wed Oct  5 00:31:46 2016
+++ new/usr/src/lib/brand/lx/librtld_db/common/mapfile-vers	Wed Oct  5 00:31:46 2016
@@ -0,0 +1,58 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING:  STOP NOW.  DO NOT MODIFY THIS FILE.
+# Object versioning must comply with the rules detailed in
+#
+#	usr/src/lib/README.mapfiles
+#
+# You should not be making modifications here until you've read the most current
+# copy of that file. If you need help, contact a gatekeeper for guidance.
+#
+# MAPFILE HEADER END
+#
+
+{
+	global:
+		rtld_db_brand_ops32;
+	local:
+		*;
+};
+
+#Externally defined symbols
+{
+    global:
+	ps_pauxv =		NODIRECT PARENT;
+	ps_pdmodel =		NODIRECT PARENT;
+	ps_pglobal_lookup =	NODIRECT PARENT;
+	ps_pglobal_sym =	NODIRECT PARENT;
+	ps_plog =		NODIRECT PARENT;
+	ps_pread =		NODIRECT PARENT;
+	ps_pwrite =		NODIRECT PARENT;
+};
--- /dev/null	Wed Oct  5 00:31:46 2016
+++ new/usr/src/lib/brand/lx/librtld_db/i386/Makefile	Wed Oct  5 00:31:46 2016
@@ -0,0 +1,33 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#ident	"%Z%%M%	%I%	%E% SMI"
+#
+
+include ../Makefile.com
+
+CLOBBERFILES =	$(ROOTLIBDIR)/$(DYNLIB)
+
+install: all $(ROOTLIBS)
--- /dev/null	Wed Oct  5 00:31:46 2016
+++ new/usr/src/lib/brand/lx/lx_brand/Makefile	Wed Oct  5 00:31:46 2016
@@ -0,0 +1,49 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+# Copyright 2014 Joyent, Inc.  All rights reserved.
+#
+
+include ../../../Makefile.lib
+
+default:	all
+
+SUBDIRS=	$(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+all :=          TARGET= all
+clean :=        TARGET= clean
+clobber :=      TARGET= clobber
+install :=      TARGET= install
+lint :=         TARGET= lint
+_msg :=		TARGET= _msg
+
+.KEEP_STATE:
+
+all install clean clobber lint _msg:	$(SUBDIRS)
+
+$(SUBDIRS): FRC
+	@cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
--- /dev/null	Wed Oct  5 00:31:46 2016
+++ new/usr/src/lib/brand/lx/lx_brand/Makefile.com	Wed Oct  5 00:31:46 2016
@@ -0,0 +1,103 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+# Copyright 2016 Joyent, Inc.
+#
+
+LX_CMN  =	$(SRC)/common/brand/lx
+
+LIBRARY =	lx_brand.a
+VERS	=	.1
+COBJS	=	aio.o			\
+		capabilities.o		\
+		clock.o			\
+		clone.o			\
+		debug.o			\
+		dir.o			\
+		file.o			\
+		fcntl.o			\
+		fork.o			\
+		lx_brand.o		\
+		mem.o			\
+		misc.o			\
+		module.o		\
+		mount.o			\
+		mount_nfs.o		\
+		priority.o		\
+		ptrace.o		\
+		sched.o			\
+		sendfile.o		\
+		signal.o		\
+		stack.o			\
+		statfs.o		\
+		sysctl.o		\
+		sysv_ipc.o		\
+		time.o			\
+		truncate.o
+
+CMNOBJS =	lx_auxv.o	\
+		lx_errno.o	\
+		lx_signum.o
+ASOBJS	=	lx_handler.o lx_crt.o
+OBJECTS	=	$(CMNOBJS) $(COBJS) $(ASOBJS)
+
+USDT_PROVIDERS =	lx_provider.d
+
+include ../../Makefile.lx
+include ../../../../Makefile.lib
+
+CSRCS   =	$(COBJS:%o=../common/%c) $(CMNOBJS:%o=$(LX_CMN)/%c)
+ASSRCS  =	$(ASOBJS:%o=$(ISASRCDIR)/%s)
+SRCS    =	$(CSRCS) $(ASSRCS)
+
+SRCDIR =	../common
+UTSBASE	=	../../../../../uts
+
+LIBS =		$(DYNLIB)
+LDLIBS +=	-lc -lsocket -lmapmalloc -lrpcsvc -lnsl
+DYNFLAGS +=	$(DYNFLAGS_$(CLASS))
+DYNFLAGS +=	$(BLOCAL) $(ZNOVERSION) -Wl,-e_start -M../common/mapfile
+CFLAGS +=	$(CCVERBOSE)
+CPPFLAGS +=	-D_REENTRANT -I. -I../ -I$(UTSBASE)/common/brand/lx -I$(LX_CMN)
+ASFLAGS =	-P $(ASFLAGS_$(CURTYPE)) -D_ASM -I../	\
+			-I$(UTSBASE)/common/brand/lx
+
+C99MODE=	-xc99=%all
+C99LMODE=	-Xc99=%all
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+lint: lintcheck
+
+include ../../../../Makefile.targ
+include ../../../../Makefile.usdt
+
+pics/%.o: $(ISASRCDIR)/%.s
+	$(COMPILE.s) -o $@ $<
+	$(POST_PROCESS_O)
+
+pics/%.o: $(LX_CMN)/%.c
+	$(COMPILE.c) -o $@ $<
+	$(POST_PROCESS_O)
--- /dev/null	Wed Oct  5 00:31:47 2016
+++ new/usr/src/lib/brand/lx/lx_brand/amd64/Makefile	Wed Oct  5 00:31:47 2016
@@ -0,0 +1,42 @@
+#
+# 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 2006 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+# Copyright 2015 Joyent, Inc.
+#
+#
+# lib/brand/lx/amd64/Makefile
+
+ISASRCDIR=.
+
+ASFLAGS	+= -P -D_ASM
+
+include ../Makefile.com
+include $(SRC)/lib/Makefile.lib.64
+
+DYNFLAGS +=	-Wl,-I/native/lib/64/ld.so.1
+CPPFLAGS +=	-D_SYSCALL32
+
+POFILE=		lx_brand.po
+MSGFILES=	$(CSRCS)
+
+ASSYMDEP_OBJS = lx_handler.o
+
+install: all $(ROOTLIBS64)
+
+$(POFILE): $(MSGFILES)
+	$(BUILDPO.msgfiles)
+
+_msg: $(MSGDOMAINPOFILE)
+
+include $(SRC)/Makefile.msg.targ
--- /dev/null	Wed Oct  5 00:31:47 2016
+++ new/usr/src/lib/brand/lx/lx_brand/amd64/lx_crt.s	Wed Oct  5 00:31:47 2016
@@ -0,0 +1,62 @@
+/*
+ * 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 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2014 Joyent, Inc.  All rights reserved.
+ */
+
+#include <sys/asm_linkage.h>
+
+#if defined(lint)
+
+void
+_start(void)
+{
+}
+
+#else	/* lint */
+
+	/*
+	 * C language startup routine for the lx brand shared library.
+	 *
+	 * That routine expects to be called with the following arguments:
+	 *	brand_init(int argc, char *argv[], char *envp[])
+	 *
+	 * There are no arguments explicitly passed to this entry point,
+	 * routine, but we do know how our initial stack has been setup by
+	 * the kernel.  The stack format is documented in:
+	 * 	usr/src/cmd/sgs/rtld/amd64/boot.s
+	 *
+	 * So this routine will troll through the stack to setup the argument
+	 * values for the common brand library startup routine and then invoke
+	 * it.  This routine is modeled after the default crt1.s`_start()
+	 * routines.
+	 */
+	ENTRY_NP(_start)
+	pushq	$0			/ Build a stack frame. retpc = NULL
+	pushq	$0			/ fp = NULL
+	movq	%rsp, %rbp		/ first stack frame
+
+	/*
+	 * Calculate the location of the envp array by adding the size of
+	 * the argv array to the start of the argv array.
+	 */
+	movq	16(%rbp), %rdi		/ argc in %rdi (1st param)
+	leaq	24(%rbp), %rsi		/ &argv[0] in %rsi (2nd param)
+	leaq	32(%rbp,%rdi,8), %rdx	/ envp in %rdx (3rd param)
+	call	lx_init
+
+	/* lx_init will never return. */
+	/*NOTREACHED*/
+	SET_SIZE(_start)
+#endif	/* lint */
--- /dev/null	Wed Oct  5 00:31:47 2016
+++ new/usr/src/lib/brand/lx/lx_brand/amd64/lx_handler.s	Wed Oct  5 00:31:47 2016
@@ -0,0 +1,53 @@
+/*
+ * 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 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
+ */
+
+#include <sys/asm_linkage.h>
+#include <sys/regset.h>
+#include <sys/segments.h>
+#include <sys/syscall.h>
+#include <sys/lx_brand.h>
+
+#if defined(_ASM)
+#include <sys/lx_signal.h>
+#include <sys/lx_syscall.h>
+#endif	/* _ASM */
+
+/* 64-bit signal syscall numbers */
+#define	LX_SYS_rt_sigreturn	15
+
+#if defined(lint)
+
+#include <sys/types.h>
+#include <sys/regset.h>
+#include <sys/signal.h>
+
+void
+lx_rt_sigreturn_tramp(void)
+{}
+
+#else	/* lint */
+
+	/*
+	 * Trampoline code is called by the return at the end of a Linux
+	 * signal handler to return control to the interrupted application
+	 * via the lx_rt_sigreturn() syscall.
+	 */
+	ENTRY_NP(lx_rt_sigreturn_tramp)
+	movq	$LX_SYS_rt_sigreturn, %rax
+	syscall
+	SET_SIZE(lx_rt_sigreturn_tramp)
+
+#endif	/* lint */
--- /dev/null	Wed Oct  5 00:31:47 2016
+++ new/usr/src/lib/brand/lx/lx_brand/common/aio.c	Wed Oct  5 00:31:47 2016
@@ -0,0 +1,558 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/mman.h>
+#include <thread.h>
+#include <synch.h>
+#include <port.h>
+#include <aio.h>
+#include <assert.h>
+#include <errno.h>
+#include <limits.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <sys/lx_types.h>
+#include <sys/lx_debug.h>
+#include <sys/lx_syscall.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_aio.h>
+
+/*
+ * We implement the Linux asynchronous I/O system calls by using the POSIX
+ * asynchronous I/O facilities together with event port notification.  This
+ * approach allows us to broadly approximate Linux semantics, but see
+ * lx_io_cancel() for some limitations.
+ *
+ * NOTE:
+ * The Linux implementation of the io_* syscalls is not exposed via glibc.
+ * These syscalls are documented to use an aio_context_t for the context
+ * parameter. On Linux this is a ulong_t. On Linux the contexts live in the
+ * kernel address space and are looked up using the aio_context_t parameter.
+ * The Linux libaio interface uses a different type for the context_t parameter.
+ *
+ * Our implementation assumes the lx_aio_context_t can be treated as a
+ * pointer. This works fortuitously because a ulong_t is the same size as a
+ * pointer. Our implementation maps the contexts into the program's address
+ * space so the aio_context_t we pass back and forth will be valid as a
+ * pointer for the program. This is similar to the native aio implementation.
+ */
+
+typedef struct lx_aiocb {
+	struct aiocb	lxaiocb_cb;		/* POSIX AIO control block */
+	struct lx_aiocb *lxaiocb_next;		/* next outstanding/free I/O */
+	struct lx_aiocb	*lxaiocb_prev;		/* prev outstanding I/O */
+	uintptr_t	lxaiocb_iocbp;		/* pointer to lx_iocb_t */
+	uintptr_t	lxaiocb_data;		/* data payload */
+} lx_aiocb_t;
+
+typedef struct lx_aio_ctxt {
+	mutex_t		lxaio_lock;		/* lock protecting context */
+	boolean_t	lxaio_destroying;	/* boolean: being destroyed */
+	cond_t		lxaio_destroyer;	/* destroyer's condvar */
+	int		lxaio_waiters;		/* number of waiters */
+	size_t		lxaio_size;		/* total size of mapping */
+	int		lxaio_port;		/* port for completion */
+	lx_aiocb_t	*lxaio_outstanding;	/* outstanding I/O */
+	lx_aiocb_t	*lxaio_free;		/* free I/O control blocks */
+	int		lxaio_nevents;		/* max number of events */
+} lx_aio_ctxt_t;
+
+int lx_aio_max_nr = 65536;
+
+long
+lx_io_setup(unsigned int nr_events, lx_aio_context_t *cidp)
+{
+	lx_aio_ctxt_t *ctx;
+	intptr_t tp;
+	lx_aiocb_t *lxcbs;
+	uintptr_t check;
+	size_t size;
+	int i;
+
+	if (uucopy(cidp, &check, sizeof (cidp)) != 0)
+		return (-EFAULT);
+
+	if (check != NULL || nr_events == 0 || nr_events > lx_aio_max_nr)
+		return (-EINVAL);
+
+	/*
+	 * We're saved from complexity in no small measure by the fact that the
+	 * cap on the number of concurrent events must be specified a priori;
+	 * we use that to determine the amount of memory we need and mmap() it
+	 * upfront.
+	 */
+	size = sizeof (lx_aio_ctxt_t) + nr_events * sizeof (lx_aiocb_t);
+
+	if ((tp = (intptr_t)mmap(0, size, PROT_READ | PROT_WRITE,
+	    MAP_PRIVATE | MAP_ANON, -1, 0)) == -1) {
+		return (-ENOMEM);
+	}
+	ctx = (lx_aio_ctxt_t *)tp;
+
+	ctx->lxaio_size = size;
+	ctx->lxaio_nevents = nr_events;
+
+	if ((ctx->lxaio_port = port_create()) == -1) {
+		(void) munmap((caddr_t)ctx, ctx->lxaio_size);
+		return (-EAGAIN);
+	}
+
+	(void) mutex_init(&ctx->lxaio_lock, USYNC_THREAD|LOCK_ERRORCHECK, NULL);
+
+	/*
+	 * Link up the free list.
+	 */
+	lxcbs = (lx_aiocb_t *)((uintptr_t)ctx + sizeof (lx_aio_ctxt_t));
+
+	for (i = 0; i < nr_events - 1; i++)
+		lxcbs[i].lxaiocb_next = &lxcbs[i + 1];
+
+	ctx->lxaio_free = &lxcbs[0];
+
+	if (uucopy(&ctx, cidp, sizeof (cidp)) != 0) {
+		(void) close(ctx->lxaio_port);
+		(void) munmap((caddr_t)ctx, ctx->lxaio_size);
+		return (-EFAULT);
+	}
+
+	return (0);
+}
+
+long
+lx_io_submit(lx_aio_context_t cid, long nr, uintptr_t **bpp)
+{
+	int processed = 0, err = 0, i;
+	port_notify_t notify;
+	lx_aiocb_t *lxcb;
+	lx_iocb_t **iocbpp, iocb, *iocbp = &iocb;
+	struct aiocb *aiocb;
+	lx_aio_ctxt_t *ctx = (lx_aio_ctxt_t *)cid;
+
+	if (nr <= 0 || ctx == NULL)
+		return (-EINVAL);
+
+	if ((iocbpp = (lx_iocb_t **)malloc(nr * sizeof (uintptr_t))) == NULL)
+		return (-EAGAIN);
+
+	if (uucopy(bpp, iocbpp, nr * sizeof (uintptr_t)) != 0) {
+		free(iocbpp);
+		return (-EFAULT);
+	}
+
+	mutex_enter(&ctx->lxaio_lock);
+
+	for (i = 0; i < nr; i++) {
+		if ((lxcb = ctx->lxaio_free) == NULL) {
+			err = EAGAIN;
+			break;
+		}
+
+		if (uucopy(iocbpp[i], &iocb, sizeof (iocb)) != 0) {
+			err = EFAULT;
+			break;
+		}
+
+		lxcb->lxaiocb_iocbp = (uintptr_t)iocbpp[i];
+		lxcb->lxaiocb_data = iocbp->lxiocb_data;
+
+		/*
+		 * We don't currently support eventfd-based notification.
+		 */
+		if (iocbp->lxiocb_flags & LX_IOCB_FLAG_RESFD) {
+			err = ENOSYS;
+			break;
+		}
+
+		notify.portnfy_port = ctx->lxaio_port;
+		notify.portnfy_user = lxcb;
+
+		aiocb = &lxcb->lxaiocb_cb;
+		aiocb->aio_fildes = iocbp->lxiocb_fd;
+		aiocb->aio_sigevent.sigev_notify = SIGEV_PORT;
+		aiocb->aio_sigevent.sigev_value.sival_ptr = &notify;
+
+		switch (iocbp->lxiocb_op) {
+		case LX_IOCB_CMD_FSYNC:
+		case LX_IOCB_CMD_FDSYNC:
+			err = aio_fsync(iocbp->lxiocb_op == LX_IOCB_CMD_FSYNC ?
+			    O_SYNC : O_DSYNC, aiocb);
+			break;
+
+		case LX_IOCB_CMD_PREAD:
+		case LX_IOCB_CMD_PWRITE:
+			aiocb->aio_offset = iocbp->lxiocb_offset;
+
+			if (aiocb->aio_nbytes > LONG_MAX) {
+				err = EINVAL;
+				break;
+			}
+
+			aiocb->aio_nbytes = iocbp->lxiocb_nbytes;
+
+			if ((uintptr_t)iocbp->lxiocb_buf > ULONG_MAX) {
+				err = EINVAL;
+				break;
+			}
+
+			aiocb->aio_buf = (void *)(uintptr_t)iocbp->lxiocb_buf;
+			aiocb->aio_reqprio = 0;
+
+			if (iocbp->lxiocb_op == LX_IOCB_CMD_PREAD) {
+				err = aio_read(aiocb);
+			} else {
+				err = aio_write(aiocb);
+			}
+
+			break;
+
+		case LX_IOCB_CMD_NOOP:
+			/*
+			 * Yet another whodunit in Adventure Playground: why
+			 * does Linux define an operation -- IOCB_CMD_NOOP --
+			 * for which it always returns EINVAL?!  And what
+			 * could a "no-op" possibly mean for asynchronous I/O
+			 * anyway?! Do nothing... later?!
+			 */
+			err = EINVAL;
+			break;
+
+		case LX_IOCB_CMD_PREADV:
+		case LX_IOCB_CMD_PWRITEV:
+			/*
+			 * We don't support asynchronous preadv and pwritev
+			 * (an asynchronous scatter/gather being a somewhat odd
+			 * notion to begin with); we return EINVAL in this
+			 * case, which the caller should be able to deal with.
+			 */
+			err = EINVAL;
+			break;
+
+		default:
+			err = EINVAL;
+			break;
+		}
+
+		if (err == -1)
+			err = errno;
+
+		if (err != 0)
+			break;
+
+		/*
+		 * We successfully enqueued I/O.  Take our control block off
+		 * of the free list and transition it to our list of
+		 * outstanding I/O.
+		 */
+		ctx->lxaio_free = lxcb->lxaiocb_next;
+		lxcb->lxaiocb_next = ctx->lxaio_outstanding;
+
+		if (ctx->lxaio_outstanding != NULL)
+			ctx->lxaio_outstanding->lxaiocb_prev = lxcb;
+
+		ctx->lxaio_outstanding = lxcb;
+		processed++;
+	}
+
+	mutex_exit(&ctx->lxaio_lock);
+
+	free(iocbpp);
+	if (processed == 0)
+		return (-err);
+
+	return (processed);
+}
+
+long
+lx_io_getevents(lx_aio_context_t cid, long min_nr, long nr,
+    lx_io_event_t *events, struct timespec *timeout)
+{
+	port_event_t *list;
+	lx_io_event_t *out;
+	unsigned int nget = min_nr;
+	int rval, i, err;
+	uint32_t max = nr;
+	lx_aio_ctxt_t *ctx = (lx_aio_ctxt_t *)cid;
+
+	if (nr > ctx->lxaio_nevents)
+		return (-EINVAL);
+
+	/*
+	 * We can't return ENOMEM from this syscall so EINTR is the closest
+	 * we can come.
+	 */
+	if ((list = malloc(nr * sizeof (port_event_t))) == NULL)
+		return (-EINTR);
+
+	/*
+	 * Grab the lock associated with the context to bump the number of
+	 * waiters.  This is needed in case this context is destroyed while
+	 * we're still waiting on it.
+	 */
+	mutex_enter(&ctx->lxaio_lock);
+
+	if (ctx->lxaio_destroying) {
+		mutex_exit(&ctx->lxaio_lock);
+		free(list);
+		return (-EINVAL);
+	}
+
+	ctx->lxaio_waiters++;
+	mutex_exit(&ctx->lxaio_lock);
+
+	rval = port_getn(ctx->lxaio_port, list, max, &nget, timeout);
+	err = errno;
+
+	mutex_enter(&ctx->lxaio_lock);
+
+	assert(ctx->lxaio_waiters > 0);
+	ctx->lxaio_waiters--;
+
+	if ((rval == -1 && err != ETIME) || nget == 0 ||
+	    (nget == 1 && list[0].portev_source == PORT_SOURCE_ALERT)) {
+		/*
+		 * If we're being destroyed, kick our waiter and clear out with
+		 * EINVAL -- this is effectively an application-level race.
+		 */
+		if (ctx->lxaio_destroying) {
+			(void) cond_signal(&ctx->lxaio_destroyer);
+			err = EINVAL;
+		}
+
+		mutex_exit(&ctx->lxaio_lock);
+
+		free(list);
+		return (nget == 0 ? 0 : -err);
+	}
+
+	if ((out = malloc(nget * sizeof (lx_io_event_t))) == NULL) {
+		mutex_exit(&ctx->lxaio_lock);
+		free(list);
+		return (-EINTR);
+	}
+
+	/*
+	 * For each returned event, translate it into the Linux event in our
+	 * stack-based buffer.  As we're doing this, we also free the lxcb by
+	 * moving it from the outstanding list to the free list.
+	 */
+	for (i = 0; i < nget; i++) {
+		port_event_t *pe = &list[i];
+		lx_io_event_t *lxe = &out[i];
+		struct aiocb *aiocb;
+		lx_aiocb_t *lxcb;
+
+		lxcb = pe->portev_user;
+		aiocb = (struct aiocb *)pe->portev_object;
+
+		assert(pe->portev_source == PORT_SOURCE_AIO);
+		assert(aiocb == &lxcb->lxaiocb_cb);
+
+		lxe->lxioe_data = lxcb->lxaiocb_data;
+		lxe->lxioe_object = lxcb->lxaiocb_iocbp;
+		lxe->lxioe_res = aio_return(aiocb);
+		lxe->lxioe_res2 = 0;
+
+		if (lxcb->lxaiocb_next != NULL)
+			lxcb->lxaiocb_next->lxaiocb_prev = lxcb->lxaiocb_prev;
+
+		if (lxcb->lxaiocb_prev != NULL) {
+			lxcb->lxaiocb_prev->lxaiocb_next = lxcb->lxaiocb_next;
+		} else {
+			assert(ctx->lxaio_outstanding == lxcb);
+			ctx->lxaio_outstanding = lxcb->lxaiocb_next;
+		}
+
+		lxcb->lxaiocb_prev = NULL;
+		lxcb->lxaiocb_next = ctx->lxaio_free;
+		ctx->lxaio_free = lxcb;
+	}
+
+	free(list);
+
+	/*
+	 * Perform one final check for a shutdown -- it's possible that we
+	 * raced with the port transitioning into alert mode, in which case we
+	 * have a blocked destroyer that we need to kick.  (Note that we do
+	 * this after having properly cleaned up the completed I/O.)
+	 */
+	if (ctx->lxaio_destroying) {
+		(void) cond_signal(&ctx->lxaio_destroyer);
+		mutex_exit(&ctx->lxaio_lock);
+		free(out);
+		return (-EINVAL);
+	}
+
+	mutex_exit(&ctx->lxaio_lock);
+
+	if (uucopy(out, events, nget * sizeof (lx_io_event_t)) != 0) {
+		free(out);
+		return (-EFAULT);
+	}
+
+	free(out);
+	return (nget);
+}
+
+/*
+ * Cancellation is unfortunately problematic for us as the POSIX semantics for
+ * AIO cancellation differ slightly from the Linux semantics: on Linux,
+ * io_cancel() regrettably does not use the same mechanism for event
+ * consumption (that is, as an event retrievable via io_getevents()), but
+ * rather returns the cancellation event directly from io_cancel().  This is
+ * in contrast to POSIX AIO cancellation, which does not actually alter the
+ * notification mechanism:  the cancellation is still received via its
+ * specified notification (i.e., an event port or signal).  The unfortunate
+ * Linux semantics leave us with several (suboptimal) choices:
+ *
+ * (1) Cancel the I/O via aio_cancel(), and then somehow attempt to block on
+ *     the asynchronous cancellation notification without otherwise disturbing
+ *     other events that may be pending.
+ *
+ * (2) Cancel the I/O via aio_cancel() but ignore (and later, discard) the
+ *     asynchronous cancellation notification.
+ *
+ * (3) Explicitly fail to cancel any asynchronous I/O by having io_cancel()
+ *     always return EAGAIN.
+ *
+ * While the third option is the least satisfying from an engineering
+ * perspective, it is also entirely within the rights of the interface (which
+ * may return EAGAIN to merely denote that the specified I/O "was not
+ * canceled") and has the added advantage of being entirely honest.  (This is
+ * in stark contrast to the first two options, each of which tries to tell
+ * small lies that seem to sure to end in elaborate webs of deceit.)  Honesty
+ * is the best policy; after checking that the specified I/O is outstanding,
+ * we fail with EAGAIN.
+ */
+/*ARGSUSED*/
+long
+lx_io_cancel(lx_aio_context_t cid, lx_iocb_t *iocbp, lx_io_event_t *result)
+{
+	lx_iocb_t iocb;
+	lx_aiocb_t *lxcb;
+	lx_aio_ctxt_t *ctx = (lx_aio_ctxt_t *)cid;
+
+	if (uucopy(iocbp, &iocb, sizeof (lx_iocb_t)) != 0)
+		return (-EFAULT);
+
+	mutex_enter(&ctx->lxaio_lock);
+
+	if (ctx->lxaio_destroying) {
+		mutex_exit(&ctx->lxaio_lock);
+		return (-EINVAL);
+	}
+
+	for (lxcb = ctx->lxaio_outstanding; lxcb != NULL &&
+	    lxcb->lxaiocb_iocbp != (uintptr_t)iocbp; lxcb = lxcb->lxaiocb_next)
+		continue;
+
+	mutex_exit(&ctx->lxaio_lock);
+
+	if (lxcb == NULL)
+		return (-EINVAL);
+
+	/*
+	 * Congratulations on your hard-won EAGAIN!
+	 */
+	return (-EAGAIN);
+}
+
+/*
+ * As is often the case, the destruction case makes everything a lot more
+ * complicated.  In this case, io_destroy() is defined to block on the
+ * completion of all outstanding operations.  To effect this, we throw the
+ * event port into the rarely-used alert mode -- invented long ago for just
+ * this purpose -- thereby kicking any waiters out of their port_get().
+ */
+long
+lx_io_destroy(lx_aio_context_t cid)
+{
+	lx_aiocb_t *lxcb;
+	unsigned int nget = 0, i;
+	int port;
+	lx_aio_ctxt_t *ctx = (lx_aio_ctxt_t *)cid;
+
+	port = ctx->lxaio_port;
+	mutex_enter(&ctx->lxaio_lock);
+
+	if (ctx->lxaio_destroying) {
+		mutex_exit(&ctx->lxaio_lock);
+		return (-EINVAL);
+	}
+
+	ctx->lxaio_destroying = B_TRUE;
+
+	if (ctx->lxaio_waiters) {
+		/*
+		 * If we have waiters, put the port into alert mode.
+		 */
+		(void) port_alert(port, PORT_ALERT_SET, B_TRUE, NULL);
+
+		while (ctx->lxaio_waiters) {
+			(void) cond_wait(&ctx->lxaio_destroyer,
+			    &ctx->lxaio_lock);
+		}
+
+		/*
+		 * Transition the port out of alert mode:  we will need to
+		 * block on the port ourselves for any outstanding I/O.
+		 */
+		(void) port_alert(port, PORT_ALERT_SET, B_FALSE, NULL);
+	}
+
+	/*
+	 * We have no waiters and we never will again -- we can be assured
+	 * that our list of outstanding I/Os is now completely static and it's
+	 * now safe to iterate over our outstanding I/Os and aio_cancel() them.
+	 */
+	for (lxcb = ctx->lxaio_outstanding; lxcb != NULL;
+	    lxcb = lxcb->lxaiocb_next) {
+		struct aiocb *aiocb = &lxcb->lxaiocb_cb;
+
+		/*
+		 * Surely a new bureaucratic low even for POSIX that we must
+		 * specify both the file descriptor and the structure that
+		 * must contain the file desctiptor...
+		 */
+		(void) aio_cancel(aiocb->aio_fildes, aiocb);
+		nget++;
+	}
+
+	/*
+	 * Drain one at a time using port_get (vs. port_getn) so that we don't
+	 * have to malloc a port_event list, which might fail.
+	 */
+	for (i = 0; i < nget; i++) {
+		port_event_t pe;
+		int rval;
+
+		do {
+			rval = port_get(port, &pe, NULL);
+		} while (rval == -1 && errno == EINTR);
+
+		assert(rval == 0);
+	}
+
+	/*
+	 * I/Os are either cancelled or completed.  We can safely close our
+	 * port and nuke the mapping that contains our context.
+	 */
+	(void) close(ctx->lxaio_port);
+	(void) munmap((caddr_t)ctx, ctx->lxaio_size);
+
+	return (0);
+}
--- /dev/null	Wed Oct  5 00:31:47 2016
+++ new/usr/src/lib/brand/lx/lx_brand/common/capabilities.c	Wed Oct  5 00:31:47 2016
@@ -0,0 +1,516 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+/*
+ * LX Brand emulation of capget/capset syscalls
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/debug.h>
+#include <sys/lx_types.h>
+#include <sys/lx_syscall.h>
+#include <sys/syscall.h>
+#include <alloca.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/lx_misc.h>
+#include <priv.h>
+
+typedef struct {
+	uint32_t version;
+	int pid;
+} lx_cap_user_header_t;
+
+typedef struct {
+	uint32_t effective;
+	uint32_t permitted;
+	uint32_t inheritable;
+} lx_cap_user_data_t;
+
+typedef struct {
+	priv_set_t *p_effective;
+	priv_set_t *p_permitted;
+	priv_set_t *p_inheritable;
+} lx_cap_privs_t;
+
+#define	LX_CAP_UPDATE_PERMITTED		0x1
+#define	LX_CAP_UPDATE_EFFECTIVE		0x2
+#define	LX_CAP_UPDATE_INHERITABLE	0x4
+
+
+#define	LX_CAP_MAXLEN	2
+
+typedef struct {
+	uint32_t effective[LX_CAP_MAXLEN];
+	uint32_t permitted[LX_CAP_MAXLEN];
+	uint32_t inheritable[LX_CAP_MAXLEN];
+} lx_cap_data_t;
+
+#define	LX_CAP_VERSION_1	0x19980330
+#define	LX_CAP_VERSION_2	0x20071026	/* deprecated by Linux */
+#define	LX_CAP_VERSION_3	0x20080522
+
+#define	LX_CAP_SETPCAP	8
+
+/*
+ * Even though we lack mappings for capabilities higher than LX_CAP_MAX_VALID,
+ * it's valuable to test all the way out to the end of the second field. This
+ * ensures that new capabilities we lack support for are not silently accepted.
+ */
+#define	LX_CAP_MAX_CHECK		63
+
+#define	LX_CAP_CAPISSET(id, cap) \
+	(((id < 32) && (((0x1 << id) & cap[0]) != 0)) || \
+	((id >= 32) && (((0x1 << (id - 32) & cap[1]) != 0))))
+
+#define	LX_CAP_CAPSET(id, cap) \
+	if (id < 32) { cap[0] |= (0x1 << id); } \
+	else { cap[1] |= (0x1 << (id - 32)); }
+
+static const char *lx_cap_map_chown[] = {
+	PRIV_FILE_CHOWN,
+	PRIV_FILE_CHOWN_SELF,
+	NULL
+};
+static const char *lx_cap_map_dac_override[] = {
+	PRIV_FILE_DAC_READ,
+	PRIV_FILE_DAC_WRITE,
+	PRIV_FILE_DAC_EXECUTE,
+	NULL
+};
+static const char *lx_cap_map_dac_read_search[] = {
+	PRIV_FILE_DAC_SEARCH,
+	PRIV_FILE_DAC_READ,
+	NULL
+};
+static const char *lx_cap_map_fowner[] = { PRIV_FILE_OWNER, NULL };
+static const char *lx_cap_map_fsetid[] = { PRIV_FILE_SETID, NULL };
+static const char *lx_cap_map_kill[] = { PRIV_PROC_OWNER, NULL };
+/*
+ * One way that Linux capabilities(7) differs from Illumos privileges(5) is
+ * that it distinguishes between setuid and setgroups rights.  This will be a
+ * problem if an lx-branded process requests to drop only CAP_SETUID but not
+ * CAP_SETGID.
+ *
+ * In that case, CAP_SETUID will be maintained.
+ */
+static const char *lx_cap_map_setgid[] = { PRIV_PROC_SETID, NULL };
+static const char *lx_cap_map_setuid[] = { PRIV_PROC_SETID, NULL };
+static const char *lx_cap_map_linux_immutable[] = { PRIV_FILE_FLAG_SET, NULL };
+static const char *lx_cap_map_bind_service[] = { PRIV_NET_PRIVADDR, NULL };
+static const char *lx_cap_map_net_admin[] = {
+	PRIV_SYS_IP_CONFIG,
+	NULL
+	/*
+	 * It would probably make sense to include PRIV_SYS_DL_CONFIG, but that
+	 * privilege is not extended to non-global zones by default.  A more
+	 * sophisticated capabilities translation layer could make it optional.
+	 */
+};
+static const char *lx_cap_map_net_raw[] = {
+	PRIV_NET_RAWACCESS,
+	PRIV_NET_ICMPACCESS,
+	NULL
+};
+static const char *lx_cap_map_ipc_lock[] = { PRIV_PROC_LOCK_MEMORY, NULL };
+static const char *lx_cap_map_ipc_owner[] = {
+	PRIV_IPC_DAC_READ,
+	PRIV_IPC_DAC_WRITE,
+	PRIV_IPC_OWNER,
+	NULL
+};
+static const char *lx_cap_map_sys_chroot[] = { PRIV_PROC_CHROOT, NULL };
+static const char *lx_cap_map_sys_admin[] = {
+	PRIV_SYS_MOUNT,
+	PRIV_SYS_ADMIN,
+	NULL
+};
+static const char *lx_cap_map_sys_nice[] = { PRIV_PROC_PRIOUP, NULL };
+static const char *lx_cap_map_sys_resource[] = { PRIV_SYS_RESOURCE, NULL };
+static const char *lx_cap_map_audit_write[] = { PRIV_PROC_AUDIT, NULL };
+static const char *lx_cap_map_audit_control[] = { PRIV_SYS_AUDIT, NULL };
+
+/*
+ * Mapping of Linux capabilities -> Illumos privileges
+ * The ID definitions can be found in the Linux sources here:
+ * include/uapi/linux/capability.h
+ *
+ * Order is critical.
+ */
+static const char ** lx_cap_mapping[LX_CAP_MAX_VALID + 1] = {
+	lx_cap_map_chown,		/* CAP_CHOWN */
+	lx_cap_map_dac_override,	/* CAP_DAC_OVERRIDE */
+	lx_cap_map_dac_read_search,	/* CAP_DAC_READ_SEARCH */
+	lx_cap_map_fowner,		/* CAP_FOWNER */
+	lx_cap_map_fsetid,		/* CAP_FSETID */
+	lx_cap_map_kill,		/* CAP_KILL */
+	lx_cap_map_setgid,		/* CAP_SETGID */
+	lx_cap_map_setuid,		/* CAP_SETUID */
+	NULL,				/* CAP_SETPCAP */
+	lx_cap_map_linux_immutable,	/* CAP_LINUX_IMMUTABLE */
+	lx_cap_map_bind_service,	/* CAP_BIND_SERVICE */
+	NULL,				/* CAP_BROADCAST */
+	lx_cap_map_net_admin,		/* CAP_NET_ADMIN */
+	lx_cap_map_net_raw,		/* CAP_NET_RAW */
+	lx_cap_map_ipc_lock,		/* CAP_IPC_LOCK */
+	lx_cap_map_ipc_owner,		/* CAP_IPC_OWNER */
+	NULL,				/* CAP_MODULE */
+	NULL,				/* CAP_RAWIO */
+	lx_cap_map_sys_chroot,		/* CAP_SYS_CHROOT */
+	NULL,				/* CAP_PTRACE */
+	NULL,				/* CAP_PACCT */
+	lx_cap_map_sys_admin,		/* CAP_SYS_ADMIN */
+	NULL,				/* CAP_BOOT */
+	lx_cap_map_sys_nice,		/* CAP_SYS_NICE */
+	lx_cap_map_sys_resource,	/* CAP_SYS_RESOURCE */
+	NULL,				/* CAP_SYS_TIME */
+	NULL,				/* CAP_SYS_TTY_CONFIG */
+	NULL,				/* CAP_MKNOD */
+	NULL,				/* CAP_LEASE */
+	lx_cap_map_audit_write,		/* CAP_AUDIT_WRITE */
+	lx_cap_map_audit_control,	/* CAP_AUDIT_CONTROL */
+	NULL,				/* CAP_SETFCAP */
+	NULL,				/* CAP_MAC_OVERRIDE */
+	NULL,				/* CAP_MAC_ADMIN */
+	NULL,				/* CAP_SYSLOG */
+	NULL,				/* CAP_WAKE_ALARM */
+	NULL				/* CAP_BLOCK_SUSPEND */
+};
+
+/* track priv_set_t size, set on entry to lx_capset/lx_capget */
+static unsigned int lx_cap_priv_size = 0;
+
+/* safely allocate priv_set_t triplet on the stack */
+#define	LX_CAP_ALLOC_PRIVS(ptr)	\
+	{ \
+		ptr = SAFE_ALLOCA(sizeof (lx_cap_privs_t) + \
+		    (3 * lx_cap_priv_size)); \
+		if (ptr != NULL) { \
+			ptr->p_effective = (priv_set_t *) \
+			    ((caddr_t)ptr + sizeof (lx_cap_privs_t)); \
+			ptr->p_permitted = (priv_set_t *) \
+			    ((caddr_t)ptr + sizeof (lx_cap_privs_t) + \
+			    lx_cap_priv_size);\
+			ptr->p_inheritable = (priv_set_t *) \
+			    ((caddr_t)ptr + sizeof (lx_cap_privs_t) + \
+			    2 * lx_cap_priv_size); \
+		} \
+	}
+
+static long
+lx_cap_update_priv(priv_set_t *priv, const uint32_t cap[])
+{
+	int i, j;
+	boolean_t cap_set;
+	boolean_t priv_set;
+	boolean_t updated = B_FALSE;
+	for (i = 0; i <= LX_CAP_MAX_CHECK; i++) {
+		cap_set = LX_CAP_CAPISSET(i, cap);
+		if (lx_cap_mapping[i] == NULL || i > LX_CAP_MAX_VALID) {
+			/* don't allow setting unsupported caps */
+			if (cap_set) {
+				/*
+				 * CAP_SETPCAP is a special capability, with
+				 * varying behavior, that can be used to
+				 * control if the process can change other
+				 * process's capabilities, or to control moving
+				 * capabilities between sets. For now we ignore
+				 * this if its passed in.
+				 */
+				if (i == LX_CAP_SETPCAP) {
+					continue;
+				}
+				lx_unsupported("set unsupported capability %d",
+				    i);
+				return (-1);
+			} else {
+				continue;
+			}
+		}
+		for (j = 0; lx_cap_mapping[i][j] != NULL; j++) {
+			priv_set = priv_ismember(priv, lx_cap_mapping[i][j]);
+			if (priv_set && !cap_set) {
+				VERIFY0(priv_delset(priv,
+				    lx_cap_mapping[i][j]));
+				updated = B_TRUE;
+			} else if (!priv_set && cap_set) {
+				VERIFY0(priv_addset(priv,
+				    lx_cap_mapping[i][j]));
+				updated = B_TRUE;
+			}
+		}
+	}
+	if (updated)
+		return (1);
+	else
+		return (0);
+}
+
+static long
+lx_cap_to_priv(lx_cap_data_t *cap, lx_cap_privs_t *priv)
+{
+	long changes = 0;
+	long result;
+
+	result = lx_cap_update_priv(priv->p_permitted, cap->permitted);
+	if (result < 0)
+		return (-1);
+	else if (result > 0)
+		changes |= LX_CAP_UPDATE_PERMITTED;
+
+	result = lx_cap_update_priv(priv->p_effective, cap->effective);
+	if (result < 0)
+		return (-1);
+	else if (result > 0)
+		changes |= LX_CAP_UPDATE_EFFECTIVE;
+
+	result = lx_cap_update_priv(priv->p_inheritable, cap->inheritable);
+	if (result < 0)
+		return (-1);
+	else if (result > 0)
+		changes |= LX_CAP_UPDATE_INHERITABLE;
+
+	return (changes);
+}
+
+static void
+lx_cap_from_priv(const priv_set_t *priv, uint32_t cap[])
+{
+	int i, j;
+	boolean_t valid;
+	(void) memset(cap, '\0', sizeof (uint32_t) * LX_CAP_MAXLEN);
+	for (i = 0; i <= LX_CAP_MAX_VALID; i++) {
+		if (lx_cap_mapping[i] == NULL) {
+			continue;
+		}
+		valid = B_TRUE;
+		for (j = 0; lx_cap_mapping[i][j] != NULL; j++) {
+			if (!priv_ismember(priv,
+			    lx_cap_mapping[i][j])) {
+				valid = B_FALSE;
+			}
+		}
+		if (valid) {
+			LX_CAP_CAPSET(i, cap);
+		}
+	}
+}
+
+static long
+lx_cap_read_cap(const lx_cap_user_header_t *uhp, const lx_cap_user_data_t *udp,
+    lx_cap_data_t *cd)
+{
+	lx_cap_user_header_t uh;
+	lx_cap_user_data_t ud_buf;
+	int cap_count;
+	int i;
+
+	if (uucopy(uhp, &uh, sizeof (uh)) != 0)
+		return (-errno);
+
+	switch (uh.version) {
+	case LX_CAP_VERSION_1:
+		cap_count = 1;
+		break;
+	case LX_CAP_VERSION_2:
+	case LX_CAP_VERSION_3:
+		cap_count = 2;
+		break;
+	default:
+		return (-EINVAL);
+	}
+
+	/* Only allow capset on calling process */
+	if (uh.pid != 0 && uh.pid != getpid())
+		return (-EPERM);
+
+	/* zero the struct in case cap_count < 2 */
+	(void) memset(cd, '\0', sizeof (lx_cap_data_t));
+
+	for (i = 0; i < cap_count; i++) {
+		if (uucopy(udp + i, &ud_buf, sizeof (ud_buf)) != 0)
+			return (-errno);
+		cd->permitted[i] = ud_buf.permitted;
+		cd->effective[i] = ud_buf.effective;
+		cd->inheritable[i] = ud_buf.inheritable;
+	}
+	return (0);
+}
+
+long
+lx_capget(uintptr_t p1, uintptr_t p2)
+{
+	const priv_impl_info_t *impl;
+	lx_cap_user_header_t *uhp = (lx_cap_user_header_t *)p1;
+	lx_cap_user_data_t *udp = (lx_cap_user_data_t *)p2;
+	lx_cap_user_header_t uh;
+	lx_cap_privs_t *privs;
+	lx_cap_data_t cd_result;
+	lx_cap_user_data_t cd_buf;
+	int cap_count;
+	int i;
+
+	if (lx_cap_priv_size == 0) {
+		impl = getprivimplinfo();
+		lx_cap_priv_size = sizeof (priv_chunk_t) * impl->priv_setsize;
+	}
+
+	if (uucopy(uhp, &uh, sizeof (uh)) != 0)
+		return (-errno);
+
+	switch (uh.version) {
+	case LX_CAP_VERSION_1:
+		cap_count = 1;
+		break;
+	case LX_CAP_VERSION_2:
+	case LX_CAP_VERSION_3:
+		cap_count = 2;
+		break;
+	default:
+		/*
+		 * As per the man page: call will fail with EINVAL and set the
+		 * version field of the header to the kernel preferred version
+		 * when an unsupported version value is provided.
+		 */
+		uh.version = LX_CAP_VERSION_3;
+		if (uucopy(&uh, uhp, sizeof (uh)) != 0)
+			return (-errno);
+		return (-EINVAL);
+	}
+
+	/*
+	 * Only allow capget on the calling process.
+	 * If a pid is specified, lie about being able to locate it.
+	 */
+	if (uh.pid > 0 && uh.pid != getpid())
+		return (-ESRCH);
+	if (uh.pid < 0)
+		return (-EINVAL);
+
+	LX_CAP_ALLOC_PRIVS(privs)
+
+	if (privs == NULL)
+		return (-ENOMEM);
+
+	if (getppriv(PRIV_PERMITTED, privs->p_permitted) != 0)
+		return (-errno);
+	if (getppriv(PRIV_EFFECTIVE, privs->p_effective) != 0)
+		return (-errno);
+	if (getppriv(PRIV_INHERITABLE, privs->p_inheritable) != 0)
+		return (-errno);
+
+	lx_cap_from_priv(privs->p_permitted, cd_result.permitted);
+	lx_cap_from_priv(privs->p_effective, cd_result.effective);
+	lx_cap_from_priv(privs->p_inheritable, cd_result.inheritable);
+
+	/* convert to output format */
+	for (i = 0; i < cap_count; i++) {
+		cd_buf.effective = cd_result.effective[i];
+		cd_buf.permitted = cd_result.permitted[i];
+		cd_buf.inheritable = cd_result.inheritable[i];
+		if (uucopy(&cd_buf, udp + i, sizeof (cd_buf)) != 0)
+			return (-errno);
+	}
+
+	return (0);
+}
+
+long
+lx_capset(uintptr_t p1, uintptr_t p2)
+{
+	const priv_impl_info_t *impl;
+	lx_cap_data_t cd;
+	lx_cap_privs_t *privs;
+	long result;
+
+	if (lx_cap_priv_size == 0) {
+		impl = getprivimplinfo();
+		lx_cap_priv_size = sizeof (priv_chunk_t) * impl->priv_setsize;
+	}
+
+	/* verify header and read in desired capabilities */
+	result = lx_cap_read_cap((lx_cap_user_header_t *)p1,
+	    (lx_cap_user_data_t *)p2, &cd);
+	if (result != 0)
+		return (result);
+
+	LX_CAP_ALLOC_PRIVS(privs)
+
+	if (privs == NULL)
+		return (-ENOMEM);
+
+	/* fetch current privs to compare against */
+	if (getppriv(PRIV_PERMITTED, privs->p_permitted) != 0)
+		return (-errno);
+	if (getppriv(PRIV_EFFECTIVE, privs->p_effective) != 0)
+		return (-errno);
+	if (getppriv(PRIV_INHERITABLE, privs->p_inheritable) != 0)
+		return (-errno);
+
+
+	result = lx_cap_to_priv(&cd, privs);
+	if (result < 0)
+		return (-EPERM);
+
+	/* report success if no changes needed */
+	if (result == 0)
+		return (0);
+
+	/* Ensure the effective/inheritable caps aren't > permitted */
+	if (!priv_issubset(privs->p_effective, privs->p_permitted) ||
+	    !priv_issubset(privs->p_inheritable, privs->p_permitted))
+		return (-EPERM);
+
+	/*
+	 * Here is where things become racy.  Linux updates all three
+	 * capability sets simultaneously in the capset syscall.  In order to
+	 * emulate capabilities via privileges, three setppriv operations are
+	 * required in sequence.  If one or two should fail, there is not a
+	 * mechanism to convey the incomplete operation to the caller.
+	 *
+	 * We do two things to make this less risky:
+	 * 1. Verify that both the desired effective and inheritable
+	 *    sets are subsets of the desired permitted set.
+	 * 2. Perform the setppriv of the permitted set first.
+	 *
+	 * Should the setppriv(permitted) fail, we can safely bail out with an
+	 * error.  If it succeeds, the setppriv of effective and inheritable
+	 * are likely to succeed given that they've been verified legal.
+	 *
+	 * If the partial error does happen, we'll be forced to report failure
+	 * even though the privileges were altered.
+	 */
+
+	if ((result & LX_CAP_UPDATE_PERMITTED) != 0) {
+		/* failure here is totally safe */
+		if (setppriv(PRIV_SET, PRIV_PERMITTED, privs->p_permitted) != 0)
+			return (-errno);
+	}
+	if ((result & LX_CAP_UPDATE_EFFECTIVE) != 0) {
+		/* failure here is a bummer */
+		if (setppriv(PRIV_SET, PRIV_EFFECTIVE, privs->p_effective) != 0)
+			return (-errno);
+	}
+	if ((result & LX_CAP_UPDATE_EFFECTIVE) != 0) {
+		/* failure here is a major bummer */
+		if (setppriv(PRIV_SET, PRIV_INHERITABLE,
+		    privs->p_inheritable) != 0)
+			return (-errno);
+	}
+
+	return (0);
+}
--- /dev/null	Wed Oct  5 00:31:47 2016
+++ new/usr/src/lib/brand/lx/lx_brand/common/clock.c	Wed Oct  5 00:31:47 2016
@@ -0,0 +1,344 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/resource.h>
+#include <sys/syscall.h>
+#include <sys/timerfd.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_syscall.h>
+#include <lx_signum.h>
+
+/*
+ * Translating from the Linux clock types to the illumos types is a bit of a
+ * mess.
+ *
+ * Linux uses different values for it clock identifiers, so we have to do basic
+ * translations between the two.  Thankfully, both Linux and illumos implement
+ * the same POSIX SUSv3 clock types, so the semantics should be identical.
+ *
+ * However, CLOCK_REALTIME and CLOCK_HIGHRES (CLOCK_MONOTONIC) are the only two
+ * clock backends currently implemented on illumos. Functions in the kernel
+ * that use the CLOCK_BACKEND macro will return an error for any clock type
+ * that does not exist in the clock_backend array. These functions are
+ * clock_settime, clock_gettime, clock_getres and timer_create.
+ *
+ * For reference, the kernel's clock_backend array looks like this:
+ *
+ * clock_backend[CLOCK_MAX] (6 entries)
+ *    0	__CLOCK_REALTIME0		valid ptr. (obs. same as CLOCK_REALTIME)
+ *    1	CLOCK_VIRTUAL			NULL
+ *    2	CLOCK_THREAD_CPUTIME_ID		NULL
+ *    3	CLOCK_REALTIME			valid ptr.
+ *    4	CLOCK_MONOTONIC (CLOCK_HIGHRES)	valid ptr.
+ *    5	CLOCK_PROCESS_CPUTIME_ID	NULL
+ */
+
+#define	CLOCK_RT_SLOT	0
+
+#define	LX_CLOCK_REALTIME	0
+#define	LX_CLOCK_MONOTONIC	1
+
+/*
+ * Limits for a minimum interval are enforced when creating timers from the
+ * CLOCK_HIGHRES source. Values below this minimum will be clamped if the
+ * process lacks the proc_clock_highres privilege.
+ */
+static int ltos_clock[] = {
+	CLOCK_REALTIME,			/* LX_CLOCK_REALTIME */
+	CLOCK_HIGHRES,			/* LX_CLOCK_MONOTONIC */
+	CLOCK_PROCESS_CPUTIME_ID,	/* LX_CLOCK_PROCESS_CPUTIME_ID */
+	CLOCK_THREAD_CPUTIME_ID,	/* LX_CLOCK_THREAD_CPUTIME_ID */
+	CLOCK_HIGHRES,			/* LX_CLOCK_MONOTONIC_RAW */
+	CLOCK_REALTIME,			/* LX_CLOCK_REALTIME_COARSE */
+	CLOCK_HIGHRES			/* LX_CLOCK_MONOTONIC_COARSE */
+};
+
+#define	LX_CLOCK_MAX	(sizeof (ltos_clock) / sizeof (ltos_clock[0]))
+
+#define	LX_SIGEV_PAD_SIZE	((64 - \
+	(sizeof (int) * 2 + sizeof (union sigval))) / sizeof (int))
+
+typedef struct {
+	union sigval	lx_sigev_value;	/* same layout for both */
+	int		lx_sigev_signo;
+	int		lx_sigev_notify;
+	union {
+		int	lx_pad[LX_SIGEV_PAD_SIZE];
+		int	lx_tid;
+		struct {
+			void (*lx_notify_function)(union sigval);
+			void *lx_notify_attribute;
+		} lx_sigev_thread;
+	} lx_sigev_un;
+} lx_sigevent_t;
+
+/* sigevent sigev_notify conversion table */
+static int ltos_sigev[] = {
+	SIGEV_SIGNAL,
+	SIGEV_NONE,
+	SIGEV_THREAD,
+	0,		/* Linux skips event 3 */
+	SIGEV_THREAD	/* Linux SIGEV_THREAD_ID -- see lx_sigev_thread_id() */
+};
+
+#define	LX_SIGEV_MAX		(sizeof (ltos_sigev) / sizeof (ltos_sigev[0]))
+#define	LX_SIGEV_THREAD_ID	4
+
+long
+lx_clock_nanosleep(int clock, int flags, struct timespec *rqtp,
+    struct timespec *rmtp)
+{
+	int ret = 0;
+	int err;
+	struct timespec rqt, rmt;
+
+	if (clock < 0 || clock >= LX_CLOCK_MAX)
+		return (-EINVAL);
+
+	if (uucopy(rqtp, &rqt, sizeof (struct timespec)) < 0)
+		return (-EFAULT);
+
+	/* the TIMER_RELTIME and TIMER_ABSTIME flags are the same on Linux */
+	if ((err = clock_nanosleep(ltos_clock[clock], flags, &rqt, &rmt))
+	    != 0) {
+		if (err != EINTR)
+			return (-err);
+		ret = -EINTR;
+		/*
+		 * We fall through in case we have to pass back the remaining
+		 * time.
+		 */
+	}
+
+	/*
+	 * Only copy values to rmtp if the timer is TIMER_RELTIME and rmtp is
+	 * non-NULL.
+	 */
+	if (((flags & TIMER_RELTIME) == TIMER_RELTIME) && (rmtp != NULL) &&
+	    (uucopy(&rmt, rmtp, sizeof (struct timespec)) < 0))
+		return (-EFAULT);
+
+	return (ret);
+}
+
+/*ARGSUSED*/
+long
+lx_adjtimex(void *tp)
+{
+	return (-EPERM);
+}
+
+/*
+ * Notification function for use with native SIGEV_THREAD in order to
+ * emulate Linux SIGEV_THREAD_ID. Native SIGEV_THREAD is used as the
+ * timer mechanism and B_SIGEV_THREAD_ID performs the actual event
+ * delivery to the appropriate lx tid.
+ */
+static void
+lx_sigev_thread_id(union sigval sival)
+{
+	lx_sigevent_t *lev = (lx_sigevent_t *)sival.sival_ptr;
+	(void) syscall(SYS_brand, B_SIGEV_THREAD_ID, lev->lx_sigev_un.lx_tid,
+	    lev->lx_sigev_signo, lev->lx_sigev_value.sival_ptr);
+	free(lev);
+}
+
+
+/*
+ * The illumos timer_create man page says it accepts the following clocks:
+ *   CLOCK_REALTIME (3)	wall clock
+ *   CLOCK_VIRTUAL (1)	user CPU usage clock - No Backend
+ *   CLOCK_PROF (2)	user and system CPU usage clock - No Backend
+ *   CLOCK_HIGHRES (4)	non-adjustable, high-resolution clock
+ * However, in reality the illumos timer_create only accepts CLOCK_REALTIME
+ * and CLOCK_HIGHRES.
+ *
+ * Linux has complicated support for clock IDs. For example, the
+ * clock_getcpuclockid() function can return a negative clock_id. See the Linux
+ * source and the comment in include/linux/posix-timers.h (above CLOCKFD) which
+ * describes clock file descriptors and shows how they map to a virt. or sched.
+ * clock ID. A process can pass one of these negative IDs to timer_create so we
+ * need to convert it and we currently only allow CLOCK_PROCESS_CPUTIME_ID
+ * against the current process as the input.
+ */
+long
+lx_timer_create(int clock, struct sigevent *lx_sevp, timer_t *tid)
+{
+	lx_sigevent_t lev;
+	struct sigevent sev;
+
+	if (clock < 0) {
+		if (clock != 0xfffffffe)
+			return (-EINVAL);
+		clock = CLOCK_RT_SLOT;	/* force our use of CLOCK_REALTIME */
+	}
+
+	if (clock >= LX_CLOCK_MAX)
+		return (-EINVAL);
+
+	/* We have to convert the Linux sigevent layout to the illumos layout */
+	if (uucopy(lx_sevp, &lev, sizeof (lev)) < 0)
+		return (-EFAULT);
+
+	if (lev.lx_sigev_notify < 0 || lev.lx_sigev_notify > LX_SIGEV_MAX)
+		return (-EINVAL);
+
+	sev.sigev_notify = ltos_sigev[lev.lx_sigev_notify];
+	sev.sigev_signo = lx_ltos_signo(lev.lx_sigev_signo, 0);
+	sev.sigev_value = lev.lx_sigev_value;
+
+	/*
+	 * The signal number is meaningless in SIGEV_NONE, Linux
+	 * accepts any value. We convert invalid signals to 0 so other
+	 * parts of lx signal handling don't break.
+	 */
+	if ((sev.sigev_notify != SIGEV_NONE) && (sev.sigev_signo == 0))
+		return (-EINVAL);
+
+	/*
+	 * Assume all Linux libc implementations map SIGEV_THREAD to
+	 * SIGEV_THREAD_ID and ignore passed-in attributes.
+	 */
+	sev.sigev_notify_attributes = NULL;
+
+	if (lev.lx_sigev_notify == LX_SIGEV_THREAD_ID) {
+		pid_t caller_pid = getpid();
+		pid_t target_pid;
+		lwpid_t ignore;
+		lx_sigevent_t *lev_copy;
+
+		if (lx_lpid_to_spair(lev.lx_sigev_un.lx_tid,
+		    &target_pid, &ignore) != 0)
+			return (-EINVAL);
+
+		/*
+		 * The caller of SIGEV_THREAD_ID must be in the same
+		 * process as the target thread.
+		 */
+		if (caller_pid != target_pid)
+			return (-EINVAL);
+
+		/*
+		 * Pass the original lx sigevent_t to the native
+		 * notify function so that it may pass it to the lx
+		 * helper thread. It is the responsibility of
+		 * lx_sigev_thread_id() to free lev_copy after the
+		 * information is relayed to lx.
+		 *
+		 * If the calling process is forked without an exec
+		 * after this copy but before the timer fires then
+		 * lev_copy will leak in the child. This is acceptable
+		 * given the rarity of this event, the miniscule
+		 * amount leaked, and the fact that the memory is
+		 * reclaimed when the proc dies. It is firmly in the
+		 * land of "good enough".
+		 */
+		lev_copy = malloc(sizeof (lx_sigevent_t));
+		if (lev_copy == NULL)
+			return (-ENOMEM);
+
+		if (uucopy(&lev, lev_copy, sizeof (lx_sigevent_t)) < 0) {
+			free(lev_copy);
+			return (-EFAULT);
+		}
+
+		sev.sigev_notify_function = lx_sigev_thread_id;
+		sev.sigev_value.sival_ptr = lev_copy;
+	}
+
+	return ((timer_create(ltos_clock[clock], &sev, tid) < 0) ? -errno : 0);
+}
+
+long
+lx_timer_settime(timer_t tid, int flags, struct itimerspec *new_val,
+    struct itimerspec *old_val)
+{
+	return ((timer_settime(tid, flags, new_val, old_val) < 0) ? -errno : 0);
+}
+
+long
+lx_timer_gettime(timer_t tid, struct itimerspec *val)
+{
+	return ((timer_gettime(tid, val) < 0) ? -errno : 0);
+}
+
+long
+lx_timer_getoverrun(timer_t tid)
+{
+	int val;
+
+	val = timer_getoverrun(tid);
+	return ((val < 0) ? -errno : val);
+}
+
+long
+lx_timer_delete(timer_t tid)
+{
+	return ((timer_delete(tid) < 0) ? -errno : 0);
+}
+
+long
+lx_timerfd_create(int clockid, int flags)
+{
+	int r;
+
+	/* These are the only two valid values. LTP tests for this. */
+	if (clockid != LX_CLOCK_REALTIME && clockid != LX_CLOCK_MONOTONIC)
+		return (-EINVAL);
+
+	r = timerfd_create(ltos_clock[clockid], flags);
+	/*
+	 * As with the eventfd case, we return a slightly less jarring
+	 * error condition if we cannot open /dev/timerfd.
+	 */
+	if (r == -1 && errno == ENOENT)
+		return (-ENOTSUP);
+
+	return (r == -1 ? -errno : r);
+}
+
+long
+lx_timerfd_settime(int fd, int flags, const struct itimerspec *value,
+    struct itimerspec *ovalue)
+{
+	int r = timerfd_settime(fd, flags, value, ovalue);
+
+	return (r == -1 ? -errno : r);
+}
+
+long
+lx_timerfd_gettime(int fd, struct itimerspec *value)
+{
+	int r = timerfd_gettime(fd, value);
+
+	return (r == -1 ? -errno : r);
+}
--- /dev/null	Wed Oct  5 00:31:47 2016
+++ new/usr/src/lib/brand/lx/lx_brand/common/clone.c	Wed Oct  5 00:31:47 2016
@@ -0,0 +1,703 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+#include <ucontext.h>
+#include <thread.h>
+#include <strings.h>
+#include <libintl.h>
+#include <sys/regset.h>
+#include <sys/syscall.h>
+#include <sys/inttypes.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/segments.h>
+#include <signal.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_types.h>
+#include <sys/lx_signal.h>
+#include <sys/lx_syscall.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_debug.h>
+#include <sys/lx_thread.h>
+#include <sys/fork.h>
+#include <sys/mman.h>
+#include <sys/debug.h>
+#include <lx_syscall.h>
+
+
+#define	SHARED_AS	\
+	(LX_CLONE_VM | LX_CLONE_FS | LX_CLONE_FILES | LX_CLONE_SIGHAND	\
+	    | LX_CLONE_THREAD)
+#define	CLONE_VFORK (LX_CLONE_VM | LX_CLONE_VFORK)
+#define	CLONE_TD (LX_CLONE_THREAD|LX_CLONE_DETACH)
+
+#define	IS_FORK(f)	(((f) & SHARED_AS) == 0)
+#define	IS_VFORK(f)	(((f) & CLONE_VFORK) == CLONE_VFORK)
+
+/*
+ * This is dicey.  This seems to be an internal glibc structure, and not
+ * part of any external interface.  Thus, it is subject to change without
+ * notice.  FWIW, clone(2) itself seems to be an internal (or at least
+ * unstable) interface, since strace(1) shows it differently than the man
+ * page.
+ */
+struct lx_desc
+{
+	uint32_t entry_number;
+	uint32_t base_addr;
+	uint32_t limit;
+	uint32_t seg_32bit:1;
+	uint32_t contents:2;
+	uint32_t read_exec_only:1;
+	uint32_t limit_in_pages:1;
+	uint32_t seg_not_present:1;
+	uint32_t useable:1;
+	uint32_t empty:25;
+};
+
+struct clone_state {
+	void		*c_retaddr;	/* instr after clone()'s int80 */
+	int		c_flags;	/* flags to clone(2) */
+	int 		c_sig;		/* signal to send on thread exit */
+	void 		*c_stk;		/* %esp of new thread */
+	void 		*c_ptidp;
+	struct lx_desc	*c_ldtinfo;	/* thread-specific segment */
+	void		*c_ctidp;
+	ucontext_t	c_uc;		/* original register state/sigmask */
+	lx_affmask_t	c_affmask;	/* CPU affinity mask */
+	volatile int	*c_clone_res;	/* pid/error returned to cloner */
+	int		c_ptrace_event;	/* ptrace(2) event for child stop */
+	void		*c_ntv_stk;	/* native stack for this thread */
+	size_t		c_ntv_stk_sz;	/* native stack size */
+	lx_tsd_t	*c_lx_tsd;	/* tsd area for thread */
+};
+
+long
+lx_exit(uintptr_t p1)
+{
+	int		status = (int)p1;
+	lx_tsd_t	*lx_tsd = lx_get_tsd();
+
+	/*
+	 * If we are a vfork(2)ed child, we need to exit as quickly and
+	 * cleanly as possible to avoid corrupting our parent.
+	 */
+	if (lx_tsd->lxtsd_is_vforked != 0) {
+		_exit(status);
+	}
+
+	lx_tsd->lxtsd_exit = LX_ET_EXIT;
+	lx_tsd->lxtsd_exit_status = status;
+
+	lx_ptrace_stop_if_option(LX_PTRACE_O_TRACEEXIT, B_FALSE,
+	    (ulong_t)status, NULL);
+
+	/*
+	 * This thread is exiting.  Restore the state of the thread to
+	 * what it was before we started running linux code.
+	 */
+	(void) setcontext(&lx_tsd->lxtsd_exit_context);
+
+	/*
+	 * If we returned from the setcontext(2), something is very wrong.
+	 */
+	lx_err_fatal("exit: unable to set exit context: %s", strerror(errno));
+
+	/*NOTREACHED*/
+	return (0);
+}
+
+long
+lx_group_exit(uintptr_t p1)
+{
+	int		status = (int)p1;
+	lx_tsd_t	*lx_tsd = lx_get_tsd();
+
+	/*
+	 * If we are a vfork(2)ed child, we need to exit as quickly and
+	 * cleanly as possible to avoid corrupting our parent.
+	 */
+	if (lx_tsd->lxtsd_is_vforked != 0) {
+		_exit(status);
+	}
+
+	lx_tsd->lxtsd_exit = LX_ET_EXIT_GROUP;
+	lx_tsd->lxtsd_exit_status = status;
+
+	/*
+	 * This thread is exiting.  Restore the state of the thread to
+	 * what it was before we started running linux code.
+	 */
+	(void) setcontext(&lx_tsd->lxtsd_exit_context);
+
+	/*
+	 * If we returned from the setcontext(2), something is very wrong.
+	 */
+	lx_err_fatal("group_exit: unable to set exit context: %s",
+	    strerror(errno));
+
+	/*NOTREACHED*/
+	return (0);
+}
+
+static void *
+clone_start(void *arg)
+{
+	int rval;
+	struct clone_state *cs = (struct clone_state *)arg;
+	lx_tsd_t *lxtsd;
+
+	/*
+	 * Let the kernel finish setting up all the needed state for this
+	 * new thread.
+	 *
+	 * We already created the thread using the thr_create(3C) library
+	 * call, so most of the work required to emulate lx_clone(2) has
+	 * been done by the time we get to this point.
+	 */
+	lx_debug("\tre-vectoring to lx kernel module to complete lx_clone()");
+	lx_debug("\tB_HELPER_CLONE(0x%x, 0x%p, 0x%p, 0x%p)",
+	    cs->c_flags, cs->c_ptidp, cs->c_ldtinfo, cs->c_ctidp);
+
+	rval = syscall(SYS_brand, B_HELPER_CLONE, cs->c_flags, cs->c_ptidp,
+	    cs->c_ldtinfo, cs->c_ctidp);
+
+	/*
+	 * At this point the parent is waiting for cs->c_clone_res to go
+	 * non-zero to indicate the thread has been cloned.  The value set
+	 * in cs->c_clone_res will be used for the return value from
+	 * clone().
+	 */
+	if (rval < 0) {
+		*(cs->c_clone_res) = -errno;
+		lx_debug("\tkernel clone failed, errno %d\n", errno);
+		free(cs->c_lx_tsd);
+		free(cs);
+		return (NULL);
+	}
+
+	if (lx_sched_setaffinity(0, sizeof (cs->c_affmask),
+	    (uintptr_t)&cs->c_affmask) != 0) {
+		*(cs->c_clone_res) = -errno;
+
+		lx_err_fatal("Unable to set affinity mask in child thread: %s",
+		    strerror(errno));
+	}
+
+	/*
+	 * Initialize the thread specific data for this thread.
+	 */
+	lxtsd = cs->c_lx_tsd;
+	lx_init_tsd(lxtsd);
+	lxtsd->lxtsd_clone_state = cs;
+
+	/*
+	 * Install the emulation stack for this thread.  Register the
+	 * thread-specific data structure with the stack list so that it may be
+	 * freed at thread exit or fork(2).
+	 */
+	lx_install_stack(cs->c_ntv_stk, cs->c_ntv_stk_sz, lxtsd);
+
+	/*
+	 * Let the parent know that the clone has (effectively) been
+	 * completed.
+	 */
+	*(cs->c_clone_res) = rval;
+
+	/*
+	 * We want to load the general registers from this context, restore the
+	 * original signal mask, and switch to the BRAND stack.  The original
+	 * signal mask was saved to the context by lx_clone().
+	 */
+	cs->c_uc.uc_flags = UC_CPU | UC_SIGMASK;
+	cs->c_uc.uc_brand_data[0] = (void *)LX_UC_STACK_BRAND;
+
+	/*
+	 * New threads will not link into the existing context chain.
+	 */
+	cs->c_uc.uc_link = NULL;
+
+	/*
+	 * Set stack pointer and entry point for new thread:
+	 */
+	LX_REG(&cs->c_uc, REG_SP) = (uintptr_t)cs->c_stk;
+	LX_REG(&cs->c_uc, REG_PC) = (uintptr_t)cs->c_retaddr;
+
+	/*
+	 * Return 0 to the child:
+	 */
+	LX_REG(&cs->c_uc, REG_R0) = (uintptr_t)0;
+
+	/*
+	 * Fire the ptrace(2) event stop in the new thread:
+	 */
+	lx_ptrace_stop_if_option(cs->c_ptrace_event, B_TRUE, 0, &cs->c_uc);
+
+	/*
+	 * Jump to the Linux process.  This call cannot return.
+	 */
+	lx_jump_to_linux(&cs->c_uc);
+	/* NOTREACHED */
+}
+
+/*
+ * The way Linux handles stopping for FORK vs. CLONE does not map exactly to
+ * which syscall was used. Instead, it has to do with which signal is set in
+ * the low byte of the clone flag. The only time the CLONE event is emitted is
+ * if the clone signal (the low byte of the flags argument) is set to something
+ * other than SIGCHLD (see the Linux src in kernel/fork.c do_fork() for the
+ * actual code).
+ */
+static int
+ptrace_clone_event(int flags)
+{
+	if (flags & LX_CLONE_VFORK)
+		return (LX_PTRACE_O_TRACEVFORK);
+
+	if ((flags & LX_CSIGNAL) != LX_SIGCHLD)
+		return (LX_PTRACE_O_TRACECLONE);
+
+	return (LX_PTRACE_O_TRACEFORK);
+}
+
+/*
+ * See glibc sysdeps/unix/sysv/linux/x86_64/clone.S code for x64 argument order
+ * and the Linux kernel/fork.c code for the various ways arguments can be passed
+ * to the clone syscall (CONFIG_CLONE_BACKWARDS, et al).
+ */
+long
+lx_clone(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4, uintptr_t p5)
+{
+	struct clone_state *cs;
+	int flags = (int)p1;
+	void *cldstk = (void *)p2;
+	void *ptidp = (void *)p3;
+#if defined(_LP64)
+	void *ctidp = (void *)p4;
+	struct lx_desc *ldtinfo = (void *)p5;
+#else /* is 32bit */
+	struct lx_desc *ldtinfo = (void *)p4;
+	void *ctidp = (void *)p5;
+#endif
+	thread_t tid;
+	volatile int clone_res;
+	int sig;
+	int rval;
+	int pid;
+	ucontext_t *ucp;
+	sigset_t sigmask, osigmask;
+	int fork_flags = 0;
+	int ptrace_event;
+	lx_tsd_t *lx_tsd = lx_get_tsd();
+
+	if (flags & LX_CLONE_SETTLS) {
+		lx_debug("lx_clone(flags=0x%x stk=0x%p ptidp=0x%p ldt=0x%p "
+		    "ctidp=0x%p", flags, cldstk, ptidp, ldtinfo, ctidp);
+	} else {
+		lx_debug("lx_clone(flags=0x%x stk=0x%p ptidp=0x%p)",
+		    flags, cldstk, ptidp);
+	}
+
+	/*
+	 * Only supported for pid 0 on Linux after version 2.3.21, and
+	 * apparently not at all since 2.5.16.
+	 */
+	if (flags & LX_CLONE_PID)
+		return (-EINVAL);
+
+	/*
+	 * CLONE_THREAD requires CLONE_SIGHAND.
+	 *
+	 * CLONE_THREAD and CLONE_DETACHED must both be either set or cleared
+	 * in kernel 2.4 and prior.
+	 * In kernel 2.6 (and later) CLONE_DETACHED was dropped completely, so
+	 * we no longer have this requirement.
+	 */
+
+	if (flags & CLONE_TD) {
+		if (!(flags & LX_CLONE_SIGHAND))
+			return (-EINVAL);
+		if (strncmp(lx_release, "2.4", 3) == 0 &&
+		    (flags & CLONE_TD) != CLONE_TD)
+			return (-EINVAL);
+	}
+
+	ucp = lx_syscall_regs();
+
+	/* test if pointer passed by user are writable */
+	if (flags & LX_CLONE_PARENT_SETTID) {
+		if (uucopy(ptidp, &pid, sizeof (int)) != 0)
+			return (-EFAULT);
+		if (uucopy(&pid, ptidp, sizeof (int)) != 0)
+			return (-EFAULT);
+	}
+	if (flags & LX_CLONE_CHILD_SETTID) {
+		if (uucopy(ctidp, &pid, sizeof (int)) != 0)
+			return (-EFAULT);
+		if (uucopy(&pid, ctidp, sizeof (int)) != 0)
+			return (-EFAULT);
+	}
+
+	ptrace_event = ptrace_clone_event(flags);
+
+	/*
+	 * Inform the in-kernel ptrace(2) subsystem that we are about to
+	 * emulate a fork(2), vfork(2) or clone(2) system call.
+	 */
+	lx_ptrace_clone_begin(ptrace_event, !!(flags & LX_CLONE_PTRACE));
+
+	/*
+	 * Handle a fork(2) operation here.  If this is not a fork, a new
+	 * thread will be created after this block.
+	 */
+	if (IS_FORK(flags) || IS_VFORK(flags)) {
+		if (flags & LX_CLONE_PARENT) {
+			lx_unsupported("clone(2) only supports CLONE_PARENT "
+			    "for threads.\n");
+			return (-ENOTSUP);
+		}
+
+		if ((flags & LX_CSIGNAL) == 0)
+			fork_flags |= FORK_NOSIGCHLD;
+
+		/*
+		 * Suspend signal delivery, run the stack management prefork
+		 * handler and perform the actual fork(2) operation.
+		 */
+		_sigoff();
+		lx_stack_prefork();
+		if (flags & LX_CLONE_VFORK) {
+			lx_sighandlers_t saved;
+
+			/*
+			 * Because we keep our signal disposition at user-land
+			 * (and in memory), we must prevent it from being
+			 * clobbered should our vforked child change the
+			 * disposition (e.g., via sigaction()) before releasing
+			 * the address space.  We preserve our disposition by
+			 * taking a snapshot of it before the vfork and
+			 * restoring it afterwards -- which we can get away
+			 * with because we know that we aren't executing
+			 * concurrently with our child.
+			 */
+			lx_sighandlers_save(&saved);
+			lx_tsd->lxtsd_is_vforked++;
+			rval = vforkx(fork_flags);
+			if (rval != 0) {
+				lx_tsd->lxtsd_is_vforked--;
+				lx_sighandlers_restore(&saved);
+			}
+		} else {
+			rval = forkx(fork_flags);
+		}
+
+		/*
+		 * The parent process returns through the regular system call
+		 * path here.
+		 */
+		if (rval != 0) {
+			/*
+			 * Run the stack management postfork handler in the
+			 * parent.  In the CLONE_VFORK case, where it only
+			 * needs to be performed once due to the shared address
+			 * space, it is critical that this step is performed in
+			 * the parent and not the child.  The latter can result
+			 * in un-woken threads blocked on lx_stack_list_lock.
+			 */
+			lx_stack_postfork();
+
+			/*
+			 * Since we've already forked, we can't do much if
+			 * uucopy fails, so we just ignore failure. Failure is
+			 * unlikely since we've tested the memory before we did
+			 * the fork.
+			 */
+			if (rval > 0 && (flags & LX_CLONE_PARENT_SETTID)) {
+				(void) uucopy(&rval, ptidp, sizeof (int));
+			}
+
+			if (rval > 0) {
+				lx_ptrace_stop_if_option(ptrace_event, B_FALSE,
+				    (ulong_t)rval, NULL);
+			}
+
+			/*
+			 * Re-enable signal delivery in the parent process.
+			 */
+			_sigon();
+
+			return ((rval < 0) ? -errno : rval);
+		}
+
+		/*
+		 * The rest of this block runs only within the new child
+		 * process.
+		 */
+
+		if (!IS_VFORK(flags)) {
+			/*
+			 * For non-vfork children run the stack management
+			 * postfork handler.
+			 */
+			lx_stack_postfork();
+
+			/*
+			 * We must free the stacks and thread-specific data
+			 * objects for every thread except the one duplicated
+			 * from the parent by forkx().
+			 */
+			lx_free_other_stacks();
+		}
+
+		if (rval == 0 && (flags & LX_CLONE_CHILD_SETTID)) {
+			/*
+			 * lx_getpid should not fail, and if it does, there's
+			 * not much we can do about it since we've already
+			 * forked, so on failure, we just don't copy the
+			 * memory.
+			 */
+			pid = syscall(SYS_brand, B_GETPID);
+			if (pid >= 0)
+				(void) uucopy(&pid, ctidp, sizeof (int));
+		}
+
+		/*
+		 * Set up additional data in the lx_proc_data structure as
+		 * necessary.
+		 */
+		if ((rval = syscall(SYS_brand, B_HELPER_CLONE, flags, ptidp,
+		    ldtinfo, ctidp)) < 0) {
+			return (rval);
+		}
+
+		if (IS_VFORK(flags)) {
+			ucontext_t vforkuc;
+
+			/*
+			 * The vfork(2) interface is somewhat less than ideal.
+			 * The unfortunate notion of borrowing the address
+			 * space of the parent process requires us to jump
+			 * through several hoops to prevent corrupting parent
+			 * emulation state.
+			 *
+			 * When returning in the child, we make a copy of the
+			 * system call return context and discard three pages
+			 * of the native stack.  Returning normally would
+			 * clobber the native stack frame in which the brand
+			 * library in the parent process is presently waiting.
+			 *
+			 * The calling program is expected to correctly use
+			 * this dusty, underspecified relic.  Neglecting to
+			 * immediately call execve(2) or exit(2) is not
+			 * cricket; this stack space will be permanently lost,
+			 * not to mention myriad other undefined behaviour.
+			 */
+			bcopy(ucp, &vforkuc, sizeof (vforkuc));
+			vforkuc.uc_brand_data[1] =
+			    (caddr_t)vforkuc.uc_brand_data[1] -
+			    LX_NATIVE_STACK_VFORK_GAP;
+			vforkuc.uc_link = NULL;
+
+			lx_debug("\tvfork native stack sp %p",
+			    vforkuc.uc_brand_data[1]);
+
+			/*
+			 * If provided, the child needs its new stack set up.
+			 */
+			if (cldstk != 0) {
+				lx_debug("\tvfork cldstk %p", cldstk);
+				LX_REG(&vforkuc, REG_SP) = (uintptr_t)cldstk;
+			}
+
+			/*
+			 * Stop for ptrace if required.
+			 */
+			lx_ptrace_stop_if_option(ptrace_event, B_TRUE, 0, NULL);
+
+			/*
+			 * Return to the child via the specially constructed
+			 * vfork(2) context.
+			 */
+			LX_EMULATE_RETURN(&vforkuc, LX_SYS_clone, 0, 0);
+			(void) syscall(SYS_brand, B_EMULATION_DONE, &vforkuc,
+			    LX_SYS_clone, 0, 0);
+
+			assert(0);
+		}
+
+		/*
+		 * If provided, the child needs its new stack set up.
+		 */
+		if (cldstk != 0) {
+			lx_debug("\tcldstk %p", cldstk);
+			LX_REG(ucp, REG_SP) = (uintptr_t)cldstk;
+		}
+
+		/*
+		 * Stop for ptrace if required.
+		 */
+		lx_ptrace_stop_if_option(ptrace_event, B_TRUE, 0, NULL);
+
+		/*
+		 * Re-enable signal delivery in the child process.
+		 */
+		_sigon();
+
+		/*
+		 * The child process returns via the regular emulated system
+		 * call path:
+		 */
+		return (0);
+	}
+
+	/*
+	 * We have very restricted support.... only exactly these flags are
+	 * supported
+	 */
+	if (((flags & SHARED_AS) != SHARED_AS)) {
+		lx_unsupported("clone(2) requires that all or none of "
+		    "CLONE_VM/FS/FILES/THREAD/SIGHAND be set. (flags:0x%08X)\n",
+		    flags);
+		return (-ENOTSUP);
+	}
+
+	if (cldstk == NULL) {
+		lx_unsupported("clone(2) requires the caller to allocate the "
+		    "child's stack.\n");
+		return (-ENOTSUP);
+	}
+
+	/*
+	 * If we want a signal-on-exit, ensure that the signal is valid.
+	 */
+	if ((sig = ltos_signo[flags & LX_CSIGNAL]) == -1) {
+		lx_unsupported("clone(2) passed unsupported signal: %d", sig);
+		return (-ENOTSUP);
+	}
+
+	/*
+	 * Initialise the state structure we pass as an argument to the new
+	 * thread:
+	 */
+	if ((cs = malloc(sizeof (*cs))) == NULL) {
+		lx_debug("could not allocate clone_state: %s", strerror(errno));
+		return (-ENOMEM);
+	}
+	cs->c_flags = flags;
+	cs->c_sig = sig;
+	cs->c_stk = cldstk;
+	cs->c_ptidp = ptidp;
+	cs->c_ldtinfo = ldtinfo;
+	cs->c_ctidp = ctidp;
+	cs->c_clone_res = &clone_res;
+	cs->c_ptrace_event = ptrace_event;
+	/*
+	 * We want the new thread to return directly to the call site for
+	 * the system call.
+	 */
+	cs->c_retaddr = (void *)LX_REG(ucp, REG_PC);
+	/*
+	 * Copy the saved context for the clone(2) system call so that the
+	 * new thread may use it to initialise registers.
+	 */
+	bcopy(ucp, &cs->c_uc, sizeof (cs->c_uc));
+	if ((cs->c_lx_tsd = malloc(sizeof (*cs->c_lx_tsd))) == NULL) {
+		free(cs);
+		return (-ENOMEM);
+	}
+
+	if (lx_sched_getaffinity(0, sizeof (cs->c_affmask),
+	    (uintptr_t)&cs->c_affmask) == -1) {
+		lx_err_fatal("Unable to get affinity mask for parent "
+		    "thread: %s", strerror(errno));
+	}
+
+	clone_res = 0;
+
+	/*
+	 * Block all signals because the thread we create won't be able to
+	 * properly handle them until it's fully set up.
+	 */
+	VERIFY0(sigfillset(&sigmask));
+	if (sigprocmask(SIG_BLOCK, &sigmask, &osigmask) < 0) {
+		lx_debug("lx_clone sigprocmask() failed: %s", strerror(errno));
+		free(cs->c_lx_tsd);
+		free(cs);
+		return (-errno);
+	}
+	cs->c_uc.uc_sigmask = osigmask;
+
+	/*
+	 * Allocate the native stack for this new thread now, so that we
+	 * can return failure gracefully as ENOMEM.
+	 */
+	if (lx_alloc_stack(&cs->c_ntv_stk, &cs->c_ntv_stk_sz) != 0) {
+		free(cs->c_lx_tsd);
+		free(cs);
+		return (-ENOMEM);
+	}
+
+	rval = thr_create(NULL, NULL, clone_start, cs, THR_DETACHED, &tid);
+
+	/*
+	 * If the thread did not start, free the resources we allocated:
+	 */
+	if (rval != 0) {
+		(void) munmap(cs->c_ntv_stk, cs->c_ntv_stk_sz);
+		free(cs->c_lx_tsd);
+		free(cs);
+	}
+
+	/*
+	 * Release any pending signals
+	 */
+	(void) sigprocmask(SIG_SETMASK, &osigmask, NULL);
+
+	/*
+	 * Wait for the child to be created and have its tid assigned.
+	 */
+	if (rval == 0) {
+		while (clone_res == 0)
+			;
+
+		rval = clone_res;
+		lx_ptrace_stop_if_option(ptrace_event, B_FALSE, (ulong_t)rval,
+		    NULL);
+
+		return (rval);
+	} else {
+		/*
+		 * Return the error from thr_create(3C).
+		 */
+		return (-rval);
+	}
+}
--- /dev/null	Wed Oct  5 00:31:47 2016
+++ new/usr/src/lib/brand/lx/lx_brand/common/debug.c	Wed Oct  5 00:31:47 2016
@@ -0,0 +1,171 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <thread.h>
+#include <unistd.h>
+
+#include <sys/modctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <sys/lx_brand.h>
+#include <sys/lx_debug.h>
+#include <sys/lx_misc.h>
+
+/* internal debugging state */
+static const char	*lx_debug_path = NULL;	/* debug output file path */
+static char		lx_debug_path_buf[MAXPATHLEN];
+
+int		lx_dtrace_lazyload = 1;		/* patchable; see below */
+
+void
+lx_debug_init(boolean_t do_dtrace, boolean_t dbg_enable, const char *dbg_file)
+{
+	/*
+	 * Our DTrace USDT provider is loaded in our .init section, which is
+	 * not run by our e_entry ELF entry point (_start, which calls into
+	 * lx_init()).  We exploit this to only actually load our USDT provider
+	 * if LX_DTRACE is set, assuring that we don't compromise fork()
+	 * performance in the (common) case that DTrace of lx_brand.so.1 itself
+	 * isn't enabled or desired. (As with all USDT providers, it can always
+	 * be loaded by explicitly specifying the full provider name).  Note
+	 * that we also allow this behavior to be set via a manual override,
+	 * lx_dtrace_lazyload -- allowing for USDT probes to be automatically
+	 * provided in situations where setting an environment variable is
+	 * tedious or otherwise impossible.
+	 */
+	if (do_dtrace || !lx_dtrace_lazyload) {
+		extern void _init(void);
+		_init();
+	}
+
+	if (!dbg_enable)
+		return;
+
+	/*
+	 * It's OK to use this value without any locking, as all callers can
+	 * use the return value to decide whether extra work should be done
+	 * before calling lx_debug().
+	 *
+	 * If debugging is disabled after a routine calls this function it
+	 * doesn't really matter as lx_debug() will see debugging is disabled
+	 * and will not output anything.
+	 */
+	lx_debug_enabled = 1;
+
+	/* check if there's a debug log file specified */
+	lx_debug_path = dbg_file;
+	if (lx_debug_path == NULL) {
+		/* send all debugging output to /dev/tty */
+		lx_debug_path = "/dev/tty";
+	}
+
+	(void) strlcpy(lx_debug_path_buf, lx_debug_path,
+	    sizeof (lx_debug_path_buf));
+	lx_debug_path = lx_debug_path_buf;
+
+	lx_debug("lx_debug: debugging output ENABLED to path: \"%s\"",
+	    lx_debug_path);
+}
+
+void
+lx_debug(const char *msg, ...)
+{
+	va_list		ap;
+	char		*buf;
+	int		rv, fd, n;
+	int		errno_backup;
+	int		size = LX_MSG_MAXLEN + 1;
+
+	if (lx_debug_enabled == 0 && !LX_DEBUG_ENABLED())
+		return;
+
+	/*
+	 * If debugging is not enabled, we do not wish to have a large stack
+	 * footprint.  The buffer allocation is thus done conditionally,
+	 * rather than as regular automatic storage.
+	 */
+	if ((buf = SAFE_ALLOCA(size)) == NULL)
+		return;
+
+	errno_backup = errno;
+
+	/* prefix the message with pid/tid */
+	if ((n = snprintf(buf, size, "%u/%u: ", getpid(), thr_self())) == -1) {
+		errno = errno_backup;
+		return;
+	}
+
+	/* format the message */
+	va_start(ap, msg);
+	rv = vsnprintf(&buf[n], size - n, msg, ap);
+	va_end(ap);
+	if (rv == -1) {
+		errno = errno_backup;
+		return;
+	}
+
+	/* add a carrige return if there isn't one already */
+	if ((buf[strlen(buf) - 1] != '\n') &&
+	    (strlcat(buf, "\n", size) >= size)) {
+		errno = errno_backup;
+		return;
+	}
+
+	LX_DEBUG(buf);
+
+	if (!lx_debug_enabled)
+		return;
+
+	/*
+	 * Open the debugging output file.  note that we don't protect
+	 * ourselves against exec or fork1 here.  if an mt process were
+	 * to exec/fork1 while we're doing this they'd end up with an
+	 * extra open desciptor in their fd space.  a'well.  shouldn't
+	 * really matter.
+	 */
+	if ((fd = open(lx_debug_path,
+	    O_WRONLY|O_APPEND|O_CREAT|O_NDELAY|O_NOCTTY, 0666)) == -1) {
+		return;
+	}
+	(void) fchmod(fd, 0666);
+
+	/* we retry in case of EINTR */
+	do {
+		rv = write(fd, buf, strlen(buf));
+	} while ((rv == -1) && (errno == EINTR));
+	(void) fsync(fd);
+
+	(void) close(fd);
+	errno = errno_backup;
+}
--- /dev/null	Wed Oct  5 00:31:47 2016
+++ new/usr/src/lib/brand/lx/lx_brand/common/dir.c	Wed Oct  5 00:31:47 2016
@@ -0,0 +1,82 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
+ */
+
+#include <string.h>
+#include <stddef.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/dirent.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_debug.h>
+#include <sys/lx_syscall.h>
+
+#define	LX_NAMEMAX	256
+
+struct lx_old_dirent {
+	long		d_ino;  /* not l_ino_t */
+	long		d_off;
+	ushort_t	d_reclen;
+	char 		d_name[LX_NAMEMAX];
+};
+
+/*
+ * Read in one dirent structure from fd into dirp.
+ * p3 (count) is ignored.
+ */
+/*ARGSUSED*/
+long
+lx_readdir(uintptr_t p1, uintptr_t p2, uintptr_t p3)
+{
+	int fd = (int)p1;
+	struct lx_old_dirent *dirp = (struct lx_old_dirent *)p2;
+	uint_t count = sizeof (struct lx_old_dirent);
+	int rc = 0;
+	struct lx_old_dirent _ld;
+	struct dirent *sd = (struct dirent *)&_ld;
+
+	/*
+	 * The return value from getdents is not applicable, as
+	 * it might have squeezed more than one dirent in the buffer
+	 * we provided.
+	 *
+	 * getdents() will deal with the case of dirp == NULL
+	 */
+	if ((rc = getdents(fd, sd, count)) < 0)
+		return (-errno);
+
+	/*
+	 * Set rc 1 (pass), or 0 (end of directory).
+	 */
+	rc = (sd->d_reclen == 0) ? 0 : 1;
+
+	if (uucopy(sd, dirp, count) != 0)
+		return (-errno);
+
+	return (rc);
+}
--- /dev/null	Wed Oct  5 00:31:47 2016
+++ new/usr/src/lib/brand/lx/lx_brand/common/fcntl.c	Wed Oct  5 00:31:47 2016
@@ -0,0 +1,88 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/filio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stropts.h>
+#include <libintl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <sys/lx_fcntl.h>
+#include <sys/lx_debug.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_syscall.h>
+
+/*
+ * flock() applies or removes an advisory lock on the file
+ * associated with the file descriptor fd.
+ *
+ * operation is: LX_LOCK_SH, LX_LOCK_EX, LX_LOCK_UN, LX_LOCK_NB
+ */
+long
+lx_flock(uintptr_t p1, uintptr_t p2)
+{
+	int			fd = (int)p1;
+	int			operation = (int)p2;
+	struct flock		fl;
+	int			cmd;
+	int			ret;
+
+	if (operation & LX_LOCK_NB) {
+		cmd = F_FLOCK;
+		operation &= ~LX_LOCK_NB; /* turn off this bit */
+	} else {
+		cmd = F_FLOCKW;
+	}
+
+	switch (operation) {
+		case LX_LOCK_UN:
+			fl.l_type = F_UNLCK;
+			break;
+		case LX_LOCK_SH:
+			fl.l_type = F_RDLCK;
+			break;
+		case LX_LOCK_EX:
+			fl.l_type = F_WRLCK;
+			break;
+		default:
+			return (-EINVAL);
+	}
+
+	fl.l_whence = 0;
+	fl.l_start = 0;
+	fl.l_len = 0;
+	fl.l_sysid = 0;
+	fl.l_pid = 0;
+
+	ret = fcntl(fd, cmd, &fl);
+
+	return ((ret == -1) ? -errno : ret);
+}
--- /dev/null	Wed Oct  5 00:31:47 2016
+++ new/usr/src/lib/brand/lx/lx_brand/common/file.c	Wed Oct  5 00:31:47 2016
@@ -0,0 +1,347 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#include <sys/fstyp.h>
+#include <sys/fsid.h>
+
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/vnode.h>
+#include <fcntl.h>
+#include <string.h>
+#include <utime.h>
+#include <atomic.h>
+#include <sys/syscall.h>
+
+#include <sys/lx_syscall.h>
+#include <sys/lx_types.h>
+#include <sys/lx_debug.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_fcntl.h>
+
+#define	LX_UTIME_NOW	((1l << 30) - 1l)
+#define	LX_UTIME_OMIT	((1l << 30) - 2l)
+
+static int
+install_checkpath(uintptr_t p1)
+{
+	int saved_errno = errno;
+	char path[MAXPATHLEN];
+
+	/*
+	 * The "dev" RPM package wants to modify /dev/pts, but /dev/pts is a
+	 * lofs mounted copy of /native/dev/pts, so that won't work.
+	 *
+	 * Instead, if we're trying to modify /dev/pts from install mode, just
+	 * act as if it succeded.
+	 */
+	if (uucopystr((void *)p1, path, MAXPATHLEN) == -1)
+		return (-errno);
+
+	if (strcmp(path, "/dev/pts") == 0)
+		return (0);
+
+	errno = saved_errno;
+	return (-errno);
+}
+
+/*
+ * Miscellaneous file-related system calls.
+ */
+
+/*
+ * fsync() and fdatasync() - On Illumos, these calls translate into a common
+ * fdsync() syscall with a different parameter. fsync is handled in the
+ * fsync wrapper.
+ */
+long
+lx_fsync(uintptr_t fd)
+{
+	int fildes = (int)fd;
+	struct stat64 statbuf;
+
+	if ((fstat64(fildes, &statbuf) == 0) &&
+	    (S_ISCHR(statbuf.st_mode) || S_ISFIFO(statbuf.st_mode)))
+		return (-EINVAL);
+
+	return (fsync((int)fd) ? -errno : 0);
+}
+
+long
+lx_fdatasync(uintptr_t fd)
+{
+	int fildes = (int)fd;
+	struct stat64 statbuf;
+
+	if ((fstat64(fildes, &statbuf) == 0) &&
+	    (S_ISCHR(statbuf.st_mode) || S_ISFIFO(statbuf.st_mode)))
+		return (-EINVAL);
+
+	return (fdatasync((int)fd) ? -errno : 0);
+}
+
+long
+lx_utime(uintptr_t p1, uintptr_t p2)
+{
+	int ret;
+
+	ret = utime((const char *)p1, (const struct utimbuf *)p2);
+
+	if (ret < 0) {
+		/*
+		 * If utime() failed and we're in install mode, return success
+		 * if the the reason we failed was because the source file
+		 * didn't actually exist or if we're trying to modify /dev/pts.
+		 */
+		if ((lx_install != 0) &&
+		    ((errno == ENOENT) || (install_checkpath(p1) == 0)))
+			return (0);
+
+		return (-errno);
+	}
+
+	return (0);
+}
+
+long
+lx_rmdir(uintptr_t p1)
+{
+	int r;
+	char *nm = (char *)p1;
+
+	r = rmdir(nm);
+	if (r < 0) {
+		int terr = errno;
+
+		/*
+		 * On both Illumos and Linux rmdir returns EINVAL if the last
+		 * component of the path is '.', but on Illumos we also return
+		 * this errno if we're trying to remove the CWD. Unfortunately,
+		 * at least the LTP test suite assumes that it can rmdir the
+		 * CWD, so we need handle this. We try to get out of the
+		 * directory we're trying to remove.
+		 */
+		if (terr == EINVAL) {
+			int l;
+
+			l = strlen(nm);
+			if (l >= 2 && !(nm[l - 2] == '/' && nm[l - 1] == '.')) {
+				if (chdir("..") == 0 && rmdir(nm) == 0) {
+					return (0);
+				}
+			}
+		}
+
+		return ((terr == EEXIST) ? -ENOTEMPTY : -terr);
+	}
+	return (0);
+}
+
+/*
+ * Exactly the same as Illumos' sysfs(2), except Linux numbers their fs indices
+ * starting at 0, and Illumos starts at 1.
+ */
+long
+lx_sysfs(uintptr_t p1, uintptr_t p2, uintptr_t p3)
+{
+	int option = (int)p1;
+	int res;
+
+	/*
+	 * Linux actually doesn't have #defines for these; their sysfs(2)
+	 * man page literally defines the "option" field as being 1, 2 or 3,
+	 * corresponding to Solaris' GETFSIND, GETFSTYP and GETNFSTYP,
+	 * respectively.
+	 */
+	switch (option) {
+		case 1:
+			if ((res = sysfs(GETFSIND, (const char *)p2)) < 0)
+				return (-errno);
+
+			return (res - 1);
+
+		case 2:
+			if ((res = sysfs(GETFSTYP, (int)p2 + 1,
+			    (char *)p3)) < 0)
+				return (-errno);
+
+			return (0);
+
+		case 3:
+			if ((res = sysfs(GETNFSTYP)) < 0)
+				return (-errno);
+
+			return (res);
+
+		default:
+			break;
+	}
+
+	return (-EINVAL);
+}
+
+long
+lx_futimesat(uintptr_t p1, uintptr_t p2, uintptr_t p3)
+{
+	int atfd = (int)p1;
+	char *path = (char *)p2;
+	struct timeval *times = (struct timeval *)p3;
+
+	if (atfd == LX_AT_FDCWD)
+		atfd = AT_FDCWD;
+
+	return (futimesat(atfd, path, times) ? -errno : 0);
+}
+
+/*
+ * From the utimensat man page:
+ * On Linux, futimens() is a library function implemented on top of the
+ * utimensat() system call. To support this, the Linux utimensat() system
+ * call implements a nonstandard feature: if pathname is NULL, then the
+ * call modifies the timestamps of the file referred to by the file
+ * descriptor dirfd (which may refer to any type of file). Using this
+ * feature, the call futimens(fd, times) is implemented as:
+ *
+ *     utimensat(fd, NULL, times, 0);
+ *
+ * Some of the returns fail here. Linux allows the time to be modified if:
+ *
+ *   the caller must have write access to the file
+ * or
+ *   the caller's effective user ID must match the owner of the file
+ * or
+ *   the caller must have appropriate privileges
+ *
+ * We behave differently. We fail with EPERM if:
+ *
+ *   the calling process's euid has write access to the file but does not match
+ *   the owner of the file and the calling process does not have the
+ *   appropriate privileges
+ *
+ * This causes some of the LTP utimensat tests to fail because they expect an
+ * unprivileged process can update the time on a file it can write but does not
+ * own. There are also other LTP failures when the test uses attributes
+ * (e.g. chattr a+) and expects a failure, but we succeed.
+ */
+long
+lx_utimensat(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4)
+{
+	int fd = (int)p1;
+	const char *path = (const char *)p2;
+	const timespec_t *times = (const timespec_t *)p3;
+	timespec_t ts[2];
+	int flag = (int)p4;
+
+	if (times != NULL) {
+		if (uucopy((void *)p3, ts, sizeof (ts)) == -1)
+			return (-errno);
+
+		if (ts[0].tv_nsec == LX_UTIME_NOW)
+			ts[0].tv_nsec = UTIME_NOW;
+		if (ts[1].tv_nsec == LX_UTIME_NOW)
+			ts[1].tv_nsec = UTIME_NOW;
+
+		if (ts[0].tv_nsec == LX_UTIME_OMIT)
+			ts[0].tv_nsec = UTIME_OMIT;
+		if (ts[1].tv_nsec == LX_UTIME_OMIT)
+			ts[1].tv_nsec = UTIME_OMIT;
+
+		times = (const timespec_t *)ts;
+	}
+
+	if (flag == LX_AT_SYMLINK_NOFOLLOW)
+		flag = AT_SYMLINK_NOFOLLOW;
+
+	if (fd == LX_AT_FDCWD)
+		fd = AT_FDCWD;
+
+	if (path == NULL) {
+		return (futimens(fd, times) ? -errno : 0);
+	} else {
+		return (utimensat(fd, path, times, flag) ? -errno : 0);
+	}
+}
+
+/*
+ * Constructs an absolute path string in buf from the path of fd and the
+ * relative path string pointed to by "p1". This is required for emulating
+ * *at() system calls.
+ * Example:
+ *    If the path of fd is "/foo/bar" and path is "etc" the string returned is
+ *    "/foo/bar/etc", if the fd is a file fd then it fails with ENOTDIR.
+ *    If path is absolute then no modifcations are made to it when copied.
+ */
+static int
+getpathat(int fd, uintptr_t p1, char *outbuf, size_t outbuf_size)
+{
+	char pathbuf[MAXPATHLEN];
+	char fdpathbuf[MAXPATHLEN];
+	char *fdpath;
+	struct stat64 statbuf;
+
+	if (uucopystr((void *)p1, pathbuf, MAXPATHLEN) == -1)
+		return (-errno);
+
+	/* If the path is absolute then we can early out */
+	if ((pathbuf[0] == '/') || (fd == LX_AT_FDCWD)) {
+		(void) strlcpy(outbuf, pathbuf, outbuf_size);
+		return (0);
+	}
+
+	fdpath = lx_fd_to_path(fd, fdpathbuf, sizeof (fdpathbuf));
+	if (fdpath == NULL)
+		return (-EBADF);
+
+	if ((fstat64(fd, &statbuf) < 0))
+		return (-EBADF);
+
+	if (!S_ISDIR(statbuf.st_mode))
+		return (-ENOTDIR);
+
+	if (snprintf(outbuf, outbuf_size, "%s/%s", fdpath, pathbuf) >
+	    (outbuf_size-1))
+		return (-ENAMETOOLONG);
+
+	return (0);
+}
+
+long
+lx_mknodat(uintptr_t ext1, uintptr_t p1, uintptr_t p2, uintptr_t p3)
+{
+	int atfd = (int)ext1;
+	char pathbuf[MAXPATHLEN];
+	int ret;
+
+	ret = getpathat(atfd, p1, pathbuf, sizeof (pathbuf));
+	if (ret < 0)
+		return (ret);
+
+	return (lx_mknod((uintptr_t)pathbuf, p2, p3));
+}
--- /dev/null	Wed Oct  5 00:31:47 2016
+++ new/usr/src/lib/brand/lx/lx_brand/common/fork.c	Wed Oct  5 00:31:47 2016
@@ -0,0 +1,184 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.  All rights reserved.
+ */
+
+#include <errno.h>
+#include <unistd.h>
+#include <sys/fork.h>
+#include <sys/syscall.h>
+#include <sys/debug.h>
+#include <strings.h>
+#include <sys/lx_debug.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_syscall.h>
+
+/*
+ * fork() and vfork()
+ *
+ * These cannot be pass thru system calls because we need libc to do its own
+ * initialization or else bad things will happen (i.e. ending up with a bad
+ * schedctl page).  On Linux, there is no such thing as forkall(), so we use
+ * fork1() here.
+ */
+
+long
+lx_fork(void)
+{
+	int ret;
+
+	/*
+	 * Inform the in-kernel ptrace(2) subsystem that we are about to
+	 * emulate fork(2).
+	 */
+	lx_ptrace_clone_begin(LX_PTRACE_O_TRACEFORK, B_FALSE);
+
+	/*
+	 * Suspend signal delivery, run the stack management prefork handler
+	 * and perform the fork operation.
+	 */
+	_sigoff();
+	lx_stack_prefork();
+	ret = fork1();
+	lx_stack_postfork();
+
+	switch (ret) {
+	case -1:
+		_sigon();
+		return (-errno);
+
+	case 0:
+		/*
+		 * Returning in the new child.  We must free the stacks and
+		 * thread-specific data objects for the threads we did not
+		 * duplicate; i.e. every other thread.
+		 */
+		lx_free_other_stacks();
+
+		lx_ptrace_stop_if_option(LX_PTRACE_O_TRACEFORK, B_TRUE, 0,
+		    NULL);
+
+		/*
+		 * Re-enable signal delivery in the child and return to the
+		 * new process.
+		 */
+		_sigon();
+		return (0);
+
+	default:
+		lx_ptrace_stop_if_option(LX_PTRACE_O_TRACEFORK, B_FALSE,
+		    (ulong_t)ret, NULL);
+
+		/*
+		 * Re-enable signal delivery in the parent and return from
+		 * the emulated system call.
+		 */
+		_sigon();
+		return (ret);
+	}
+}
+
+long
+lx_vfork(void)
+{
+	int ret;
+	lx_sighandlers_t saved;
+	ucontext_t vforkuc;
+	ucontext_t *ucp;
+	lx_tsd_t *lx_tsd = lx_get_tsd();
+
+	ucp = lx_syscall_regs();
+
+	/*
+	 * Inform the in-kernel ptrace(2) subsystem that we are about to
+	 * emulate vfork(2).
+	 */
+	lx_ptrace_clone_begin(LX_PTRACE_O_TRACEVFORK, B_FALSE);
+
+	/*
+	 * Suspend signal delivery, run the stack management prefork handler
+	 * and perform the vfork operation. We use the same approach as in
+	 * lx_clone for signal handling and child return across vfork. See
+	 * the comments in lx_clone for more detail.
+	 */
+
+	_sigoff();
+	lx_stack_prefork();
+	lx_sighandlers_save(&saved);
+	lx_tsd->lxtsd_is_vforked++;
+	ret = vfork();
+	if (ret != 0) {
+		/* parent/error */
+		lx_tsd->lxtsd_is_vforked--;
+		lx_sighandlers_restore(&saved);
+	}
+
+	switch (ret) {
+	case -1:
+		lx_stack_postfork();
+		_sigon();
+		return (-errno);
+
+	case 0:
+		/*
+		 * child
+		 * Unlike the regular fork case where the child also calls
+		 * lx_stack_postfork(), we only do that in the parent once it
+		 * resumes execution. This is required there to wake any other
+		 * threads in that process that are  blocked on the lock taken
+		 * in lx_stack_prefork().
+		 */
+		bcopy(ucp, &vforkuc, sizeof (vforkuc));
+		vforkuc.uc_brand_data[1] = (caddr_t)vforkuc.uc_brand_data[1] -
+		    LX_NATIVE_STACK_VFORK_GAP;
+		vforkuc.uc_link = NULL;
+
+		lx_debug("\tvfork native stack sp %p",
+		    vforkuc.uc_brand_data[1]);
+
+		/* Stop for ptrace if required. */
+		lx_ptrace_stop_if_option(LX_PTRACE_O_TRACEVFORK, B_TRUE, 0,
+		    NULL);
+
+		/*
+		 * Return to the child via the specially constructed vfork(2)
+		 * context.
+		 */
+		LX_EMULATE_RETURN(&vforkuc, LX_SYS_vfork, 0, 0);
+		(void) syscall(SYS_brand, B_EMULATION_DONE, &vforkuc,
+		    LX_SYS_vfork, 0, 0);
+
+		VERIFY(0);
+		return (0);
+
+	default:
+		/* parent - child should have exited or exec-ed by now */
+		lx_stack_postfork();
+		lx_ptrace_stop_if_option(LX_PTRACE_O_TRACEVFORK, B_FALSE,
+		    (ulong_t)ret, NULL);
+		_sigon();
+		return (ret);
+	}
+}
--- /dev/null	Wed Oct  5 00:31:47 2016
+++ new/usr/src/lib/brand/lx/lx_brand/common/lx_brand.c	Wed Oct  5 00:31:47 2016
@@ -0,0 +1,1705 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/syscall.h>
+#include <sys/utsname.h>
+#include <sys/inttypes.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/fstyp.h>
+#include <sys/fsid.h>
+#include <sys/systm.h>
+#include <sys/auxv.h>
+#include <sys/frame.h>
+#include <zone.h>
+#include <sys/brand.h>
+#include <sys/epoll.h>
+#include <sys/stack.h>
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+#include <errno.h>
+#include <syslog.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <synch.h>
+#include <libelf.h>
+#include <libgen.h>
+#include <pthread.h>
+#include <utime.h>
+#include <dirent.h>
+#include <ucontext.h>
+#include <libintl.h>
+#include <locale.h>
+
+#include <sys/lx_misc.h>
+#include <sys/lx_debug.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_types.h>
+#include <sys/lx_statfs.h>
+#include <sys/lx_signal.h>
+#include <sys/lx_syscall.h>
+#include <sys/lx_thread.h>
+#include <sys/lx_aio.h>
+#include <lx_auxv.h>
+
+/*
+ * There is a block comment in "uts/common/brand/lx/os/lx_brand.c" that
+ * describes the functioning of the LX brand in some detail.
+ *
+ * *** Setting errno
+ *
+ * This emulation library is loaded onto a seperate link map from the
+ * application whose address space we're running in. The Linux libc errno is
+ * independent of our native libc errno. To pass back an error the emulation
+ * function should return -errno back to the Linux caller.
+ */
+
+char lx_release[LX_KERN_RELEASE_MAX];
+char lx_cmd_name[MAXNAMLEN];
+boolean_t lx_no_abort_handler = B_FALSE;
+
+/*
+ * Map a linux locale ending string to the solaris equivalent.
+ */
+struct lx_locale_ending {
+	const char	*linux_end;	/* linux ending string */
+	const char	*solaris_end;	/* to transform with this string */
+	int		le_size;	/* linux ending string length */
+	int		se_size;	/* solaris ending string length */
+};
+
+#define	l2s_locale(lname, sname) \
+	{(lname), (sname), sizeof ((lname)) - 1, sizeof ((sname)) - 1}
+
+#define	MAXLOCALENAMELEN	30
+#if !defined(TEXT_DOMAIN)		/* should be defined by cc -D */
+#define	TEXT_DOMAIN	"SYS_TEST"	/* Use this only if it wasn't */
+#endif
+
+/*
+ * Most syscalls return an int but some return something else, typically a
+ * ssize_t. This can be either an int or a long, depending on if we're compiled
+ * for 32-bit or 64-bit. To correctly propagate the -errno return code in the
+ * 64-bit case, we declare all emulation wrappers will return a long. Thus,
+ * when we save the return value into the %eax or %rax register and return to
+ * Linux, we will have the right size value in both the 32 and 64 bit cases.
+ */
+
+typedef long (*lx_syscall_handler_t)();
+
+static lx_syscall_handler_t lx_handlers[LX_NSYSCALLS + 1];
+
+static uintptr_t stack_size;
+
+#if defined(_LP64)
+long lx_fsb;
+long lx_fs;
+#endif
+int lx_install = 0;		/* install mode enabled if non-zero */
+int lx_verbose = 0;		/* verbose mode enabled if non-zero */
+int lx_debug_enabled = 0;	/* debugging output enabled if non-zero */
+
+pid_t zoneinit_pid;		/* zone init PID */
+
+thread_key_t lx_tsd_key;
+
+int
+uucopy_unsafe(const void *src, void *dst, size_t n)
+{
+	bcopy(src, dst, n);
+	return (0);
+}
+
+int
+uucopystr_unsafe(const void *src, void *dst, size_t n)
+{
+	(void) strncpy((char *)src, dst, n);
+	return (0);
+}
+
+static void
+i_lx_msg(int fd, char *msg, va_list ap)
+{
+	int	i;
+	char	buf[LX_MSG_MAXLEN];
+
+	/* LINTED [possible expansion issues] */
+	i = vsnprintf(buf, sizeof (buf), msg, ap);
+	buf[LX_MSG_MAXLEN - 1] = '\0';
+	if (i == -1)
+		return;
+
+	/* if debugging is enabled, send this message to debug output */
+	if (LX_DEBUG_ISENABLED)
+		lx_debug(buf);
+
+	if (fd == 2) {
+		/*
+		 * We let the user choose whether or not to see these
+		 * messages on the console.
+		 */
+		if (lx_verbose == 0)
+			return;
+	}
+
+	/* we retry in case of EINTR */
+	do {
+		i = write(fd, buf, strlen(buf));
+	} while ((i == -1) && (errno == EINTR));
+}
+
+/*PRINTFLIKE1*/
+void
+lx_err(char *msg, ...)
+{
+	va_list	ap;
+
+	assert(msg != NULL);
+
+	va_start(ap, msg);
+	i_lx_msg(STDERR_FILENO, msg, ap);
+	va_end(ap);
+}
+
+/*
+ * This is just a non-zero exit value which also isn't one that would allow
+ * us to easily detect if a branded process exited because of a recursive
+ * fatal error.
+ */
+#define	LX_ERR_FATAL	42
+
+/*
+ * Our own custom version of abort(), this routine will be used in place
+ * of the one located in libc.  The primary difference is that this version
+ * will first reset the signal handler for SIGABRT to SIG_DFL, ensuring the
+ * SIGABRT sent causes us to dump core and is not caught by a user program.
+ */
+void
+abort(void)
+{
+	static int aborting = 0;
+
+	struct sigaction sa;
+	sigset_t sigmask;
+
+	/* watch out for recursive calls to this function */
+	if (aborting != 0)
+		exit(LX_ERR_FATAL);
+
+	aborting = 1;
+
+	/*
+	 * Block all signals here to avoid taking any signals while exiting
+	 * in an effort to avoid any strange user interaction with our death.
+	 */
+	(void) sigfillset(&sigmask);
+	(void) sigprocmask(SIG_BLOCK, &sigmask, NULL);
+
+	/*
+	 * Our own version of abort(3C) that we know will never call
+	 * a user-installed SIGABRT handler first.  We WANT to die.
+	 *
+	 * Do this by resetting the handler to SIG_DFL, and releasing any
+	 * held SIGABRTs.
+	 *
+	 * If no SIGABRTs are pending, send ourselves one.
+	 *
+	 * The while loop is a bit of overkill, but abort(3C) does it to
+	 * assure it never returns so we will as well.
+	 */
+	(void) sigemptyset(&sa.sa_mask);
+	sa.sa_sigaction = SIG_DFL;
+	sa.sa_flags = 0;
+
+	for (;;) {
+		(void) sigaction(SIGABRT, &sa, NULL);
+		(void) sigrelse(SIGABRT);
+		(void) thr_kill(thr_self(), SIGABRT);
+	}
+
+	/*NOTREACHED*/
+}
+
+/*PRINTFLIKE1*/
+void
+lx_msg(char *msg, ...)
+{
+	va_list	ap;
+
+	assert(msg != NULL);
+	va_start(ap, msg);
+	i_lx_msg(STDOUT_FILENO, msg, ap);
+	va_end(ap);
+}
+
+/*PRINTFLIKE1*/
+void
+lx_err_fatal(char *msg, ...)
+{
+	va_list	ap;
+
+	assert(msg != NULL);
+
+	va_start(ap, msg);
+	i_lx_msg(STDERR_FILENO, msg, ap);
+	va_end(ap);
+	abort();
+}
+
+/*
+ * See if it is safe to alloca() sz bytes.  Return 1 for yes, 0 for no.
+ * We can't be certain we won't blow the stack since we don't know where it
+ * starts, but since the stack is only two pages we know any allocation bigger
+ * than that will blow the stack. Fortunately most allocations are small (e.g.
+ * 128 bytes).
+ */
+int
+lx_check_alloca(size_t sz)
+{
+	uintptr_t sp = (uintptr_t)&sz;
+	uintptr_t end = sp - sz;
+
+	return ((end < sp) && (sz < stack_size));
+}
+
+/*PRINTFLIKE1*/
+void
+lx_unsupported(char *msg, ...)
+{
+	va_list	ap;
+	char dmsg[256];
+	int lastc;
+
+	assert(msg != NULL);
+
+	/* make a brand call so we can easily dtrace unsupported actions */
+	va_start(ap, msg);
+	(void) vsnprintf(dmsg, sizeof (dmsg), msg, ap);
+	dmsg[255] = '\0';
+	lastc = strlen(dmsg) - 1;
+	if (dmsg[lastc] == '\n')
+		dmsg[lastc] = '\0';
+	(void) syscall(SYS_brand, B_UNSUPPORTED, dmsg);
+	va_end(ap);
+
+	/* send the msg to the error stream */
+	va_start(ap, msg);
+	i_lx_msg(STDERR_FILENO, msg, ap);
+	va_end(ap);
+}
+
+int lx_init(int argc, char *argv[], char *envp[]);
+
+lx_tsd_t *
+lx_get_tsd(void)
+{
+	int ret;
+	lx_tsd_t *lx_tsd;
+
+	if ((ret = thr_getspecific(lx_tsd_key, (void **)&lx_tsd)) != 0) {
+		lx_err_fatal("lx_get_tsd: unable to read "
+		    "thread-specific data: %s", strerror(ret));
+	}
+
+	assert(lx_tsd != 0);
+
+	return (lx_tsd);
+}
+
+/*
+ * This function is called from the kernel like a signal handler.  Each
+ * function call is a request to provide emulation for a system call that, on
+ * illumos, is implemented in userland.  The system call number selection and
+ * argument parsing have already been done by the kernel.
+ */
+void
+lx_emulate(ucontext_t *ucp, int syscall_num, uintptr_t *args)
+{
+	long emu_ret;
+	int emu_errno = 0;
+
+	LX_EMULATE_ENTER(ucp, syscall_num, args);
+	lx_debug("lx_emulate(%p, %d, [%p, %p, %p, %p, %p, %p])\n",
+	    ucp, syscall_num, args[0], args[1], args[2], args[3], args[4],
+	    args[5]);
+
+	/*
+	 * The kernel should have saved us a context that will not restore the
+	 * previous signal mask.  Some emulated system calls alter the signal
+	 * mask; restoring it after the emulation would cancel that out.
+	 */
+	assert(!(ucp->uc_flags & UC_SIGMASK));
+
+	/*
+	 * The kernel ensures that the syscall_num is sane; Use it as is.
+	 */
+	assert(syscall_num >= 0);
+	assert(syscall_num < (sizeof (lx_handlers) / sizeof (lx_handlers[0])));
+	if (lx_handlers[syscall_num] == NULL) {
+		lx_err_fatal("lx_emulate: kernel sent us a call we cannot "
+		    "emulate (%d)", syscall_num);
+	}
+
+	/*
+	 * Call our handler function:
+	 */
+	emu_ret = lx_handlers[syscall_num](args[0], args[1], args[2], args[3],
+	    args[4], args[5]);
+
+	/*
+	 * If the return value is between -1 and -4095 then it's an errno.
+	 * The kernel will translate it to the Linux equivalent for us.
+	 */
+	if (emu_ret < 0 && emu_ret > -4096) {
+		emu_errno = (int)-emu_ret;
+	}
+
+	/*
+	 * Return to the context we were passed
+	 */
+	LX_EMULATE_RETURN(ucp, syscall_num, emu_ret, emu_errno);
+	lx_debug("\tlx_emulate(%d) done (ret %ld / 0x%p ; errno %d)",
+	    syscall_num, emu_ret, emu_ret, emu_errno);
+	(void) syscall(SYS_brand, B_EMULATION_DONE, ucp, syscall_num, emu_ret,
+	    emu_errno);
+
+	assert(!"cannot be returned here");
+}
+
+static void
+lx_close_fh(FILE *file)
+{
+	int fd, fd_new;
+
+	if (file == NULL)
+		return;
+
+	if ((fd = fileno(file)) < 0)
+		return;
+
+	fd_new = dup(fd);
+	if (fd_new == -1)
+		return;
+
+	(void) fclose(file);
+	(void) dup2(fd_new, fd);
+	(void) close(fd_new);
+}
+
+
+extern int set_l10n_alternate_root(char *path);
+
+/*
+ * Initialize the thread specific data for this thread.
+ */
+void
+lx_init_tsd(lx_tsd_t *lxtsd)
+{
+	int err;
+
+	bzero(lxtsd, sizeof (*lxtsd));
+	lxtsd->lxtsd_exit = LX_ET_NONE;
+
+	/*
+	 * The Linux alternate signal stack is initially disabled:
+	 */
+	lxtsd->lxtsd_sigaltstack.ss_flags = LX_SS_DISABLE;
+
+	/*
+	 * Create a per-thread exit context from the current register and
+	 * native/brand stack state.  Replace the saved program counter value
+	 * with the address of lx_exit_common(); we wish to revector there when
+	 * the thread or process is exiting.
+	 */
+	if (getcontext(&lxtsd->lxtsd_exit_context) != 0) {
+		lx_err_fatal("Unable to initialize thread-specific exit "
+		    "context: %s", strerror(errno));
+	}
+	LX_REG(&lxtsd->lxtsd_exit_context, REG_PC) = (uintptr_t)lx_exit_common;
+
+	/*
+	 * Align the stack pointer and clear the frame pointer.
+	 */
+	LX_REG(&lxtsd->lxtsd_exit_context, REG_FP) = 0;
+	LX_REG(&lxtsd->lxtsd_exit_context, REG_SP) &= ~(STACK_ALIGN - 1UL);
+#if defined(_LP64)
+#if (STACK_ENTRY_ALIGN != 8) && (STACK_ALIGN != 16)
+#error "lx_init_tsd: unexpected STACK_[ENTRY_]ALIGN values"
+#endif
+	/*
+	 * The AMD64 ABI requires that, on entry to a function, the stack
+	 * pointer must be 8-byte aligned, but _not_ 16-byte aligned.  When
+	 * the frame pointer is pushed, the alignment will then be correct.
+	 */
+	LX_REG(&lxtsd->lxtsd_exit_context, REG_SP) -= STACK_ENTRY_ALIGN;
+#endif
+
+	/*
+	 * Block all signals in the exit context to avoid taking any signals
+	 * (to the degree possible) while exiting.
+	 */
+	(void) sigfillset(&lxtsd->lxtsd_exit_context.uc_sigmask);
+
+	if ((err = thr_setspecific(lx_tsd_key, lxtsd)) != 0) {
+		lx_err_fatal("Unable to initialize thread-specific data: %s",
+		    strerror(err));
+	}
+}
+
+void
+lx_jump_to_linux(ucontext_t *ucp)
+{
+	extern void setcontext_sigmask(ucontext_t *);
+
+	/*
+	 * Call into this private libc interface to allow us to use only the
+	 * signal mask handling part of a regular setcontext() operation.
+	 */
+	setcontext_sigmask(ucp);
+
+	if (syscall(SYS_brand, B_JUMP_TO_LINUX, ucp) != 0) {
+		lx_err_fatal("B_JUMP_TO_LINUX failed: %s", strerror(errno));
+	}
+
+	/*
+	 * This system call should not return.
+	 */
+	abort();
+}
+
+static void
+lx_start(uintptr_t sp, uintptr_t entry)
+{
+	ucontext_t jump_uc;
+
+	if (getcontext(&jump_uc) != 0) {
+		lx_err_fatal("Unable to getcontext for program start: %s",
+		    strerror(errno));
+	}
+
+	/*
+	 * We want to load the general registers from this
+	 * context, and switch to the BRAND stack.
+	 */
+	jump_uc.uc_flags = UC_CPU;
+	jump_uc.uc_brand_data[0] = (void *)LX_UC_STACK_BRAND;
+
+	LX_REG(&jump_uc, REG_FP) = NULL;
+	LX_REG(&jump_uc, REG_SP) = sp;
+	LX_REG(&jump_uc, REG_PC) = entry;
+
+	/*
+	 * The AMD64 ABI states that at process entry, %rdx contains "a
+	 * function pointer that the application should register with
+	 * atexit()".  This behavior has been observed in statically linked
+	 * i386 programs as well.  As a precaution, all of the registers are
+	 * zeroed prior to initial execution.
+	 */
+#if defined(_LP64)
+	LX_REG(&jump_uc, REG_RAX) = NULL;
+	LX_REG(&jump_uc, REG_RCX) = NULL;
+	LX_REG(&jump_uc, REG_RDX) = NULL;
+	LX_REG(&jump_uc, REG_RBX) = NULL;
+	LX_REG(&jump_uc, REG_RBP) = NULL;
+	LX_REG(&jump_uc, REG_RSI) = NULL;
+	LX_REG(&jump_uc, REG_RDI) = NULL;
+	LX_REG(&jump_uc, REG_R8) = NULL;
+	LX_REG(&jump_uc, REG_R9) = NULL;
+	LX_REG(&jump_uc, REG_R10) = NULL;
+	LX_REG(&jump_uc, REG_R11) = NULL;
+	LX_REG(&jump_uc, REG_R12) = NULL;
+	LX_REG(&jump_uc, REG_R13) = NULL;
+	LX_REG(&jump_uc, REG_R14) = NULL;
+	LX_REG(&jump_uc, REG_R15) = NULL;
+#else
+	LX_REG(&jump_uc, EAX) = NULL;
+	LX_REG(&jump_uc, ECX) = NULL;
+	LX_REG(&jump_uc, EDX) = NULL;
+	LX_REG(&jump_uc, EBX) = NULL;
+	LX_REG(&jump_uc, EBP) = NULL;
+	LX_REG(&jump_uc, ESI) = NULL;
+	LX_REG(&jump_uc, EDI) = NULL;
+#endif /* defined(_LP64) */
+
+	lx_debug("starting Linux program sp %p ldentry %p", sp, entry);
+	lx_jump_to_linux(&jump_uc);
+}
+
+enum lx_env_setting {
+	LXES_INSTALL = 0,
+	LXES_VERBOSE,
+	LXES_DTRACE,
+	LXES_DEBUG,
+	LXES_DEBUG_FILE,
+	LXES_NO_ABORT_HANDLER,
+	LXES_RELEASE,
+	LXES_VERSION,
+	LXES_STRICT,
+	LXES_LIMIT
+};
+
+static void
+lx_parse_env(char *envp[], char *settings[])
+{
+	int i, j;
+	char *env;
+
+	typedef struct lx_env_entry {
+		char *lee_name;
+		int lee_len;
+		int lee_index;
+	} lx_env_entry_t;
+#define	LX_ENV_ENTRY(name, idx) { name, (sizeof (name)) - 1, idx }
+	static const lx_env_entry_t lx_env_entries[] = {
+		LX_ENV_ENTRY("LX_INSTALL", LXES_INSTALL),
+		LX_ENV_ENTRY("LX_VERBOSE", LXES_VERBOSE),
+		LX_ENV_ENTRY("LX_DTRACE", LXES_DTRACE),
+		LX_ENV_ENTRY("LX_DEBUG", LXES_DEBUG),
+		LX_ENV_ENTRY("LX_DEBUG_FILE", LXES_DEBUG_FILE),
+		LX_ENV_ENTRY("LX_NO_ABORT_HANDLER", LXES_NO_ABORT_HANDLER),
+		LX_ENV_ENTRY("LX_RELEASE", LXES_RELEASE),
+		LX_ENV_ENTRY("LX_VERSION", LXES_VERSION),
+		LX_ENV_ENTRY("LX_STRICT", LXES_STRICT)
+	};
+#define	LX_ENV_ENTRY_COUNT	\
+	(sizeof (lx_env_entries) / sizeof (lx_env_entries[0]))
+
+	for (i = 0; (env = envp[i]) != NULL; i++) {
+		if (env[0] != 'L' || env[1] != 'X' || env[2] != '_')
+			continue;
+		for (j = 0; j < LX_ENV_ENTRY_COUNT; j++) {
+			const lx_env_entry_t *lee = &lx_env_entries[j];
+
+			if (strncmp(env, lee->lee_name, lee->lee_len) != 0 ||
+			    env[lee->lee_len] != '=')
+				continue;
+			settings[lee->lee_index] = &env[lee->lee_len + 1];
+			break;
+		}
+	}
+}
+
+/*ARGSUSED*/
+int
+lx_init(int argc, char *argv[], char *envp[])
+{
+	auxv_t		*ap, *oap;
+	long		*p;
+	int		err;
+	lx_elf_data_t	edp;
+	lx_brand_registration_t reg;
+	lx_tsd_t	*lxtsd;
+	char		*lx_settings[LXES_LIMIT];
+
+	bzero(&reg, sizeof (reg));
+	stack_size = 2 * sysconf(_SC_PAGESIZE);
+
+	/*
+	 * We need to shutdown all libc stdio.  libc stdio normally goes to
+	 * file descriptors, but since we're actually part of a linux
+	 * process we don't own these file descriptors and we can't make
+	 * any assumptions about their state.
+	 */
+	lx_close_fh(stdin);
+	lx_close_fh(stdout);
+	lx_close_fh(stderr);
+
+	/*
+	 * Parse LX-related settings out of the environment array.
+	 * This is done manually instead of utilizing libc's getenv() to avoid
+	 * triggering any env-cleaning routines which are present.
+	 */
+	bzero(lx_settings, sizeof (lx_settings));
+	lx_parse_env(envp, lx_settings);
+
+	/*
+	 * Setting LX_NO_ABORT_HANDLER in the environment will prevent the
+	 * emulated Linux program from modifying the signal handling
+	 * disposition for SIGSEGV or SIGABRT.  It is useful for debugging
+	 * programs which fall over themselves to prevent useful core files
+	 * being generated.
+	 */
+	lx_no_abort_handler = (lx_settings[LXES_NO_ABORT_HANDLER] != NULL);
+
+	lx_debug_init(lx_settings[LXES_DTRACE] != NULL,
+	    lx_settings[LXES_DEBUG] != NULL,
+	    lx_settings[LXES_DEBUG_FILE]);
+
+	if (lx_settings[LXES_RELEASE] == NULL) {
+		if (zone_getattr(getzoneid(), LX_ATTR_KERN_RELEASE,
+		    lx_release, sizeof (lx_release)) <= 0)
+			(void) strlcpy(lx_release, "2.4.21",
+			    LX_KERN_RELEASE_MAX);
+	} else {
+		(void) strlcpy(lx_release, lx_settings[LXES_RELEASE],
+		    LX_KERN_RELEASE_MAX);
+	}
+
+	if (lx_settings[LXES_RELEASE] != NULL ||
+	    lx_settings[LXES_VERSION] != NULL) {
+		if (syscall(SYS_brand, B_OVERRIDE_KERN_VER,
+		    lx_settings[LXES_RELEASE],
+		    lx_settings[LXES_VERSION]) != 0) {
+			lx_debug("failed to override kernel release/version");
+		}
+	}
+	lx_debug("lx_release: %s\n", lx_release);
+
+
+	/*
+	 * Should we kill an application that attempts an unimplemented
+	 * system call?
+	 */
+	if (lx_settings[LXES_STRICT] != NULL) {
+		reg.lxbr_flags |= LX_PROC_STRICT_MODE;
+		lx_debug("STRICT mode enabled.\n");
+	}
+
+	/*
+	 * Are we in install mode?
+	 */
+	if (lx_settings[LXES_INSTALL] != NULL) {
+		reg.lxbr_flags |= LX_PROC_INSTALL_MODE;
+		lx_install = 1;
+		lx_debug("INSTALL mode enabled.\n");
+	}
+
+	(void) strlcpy(lx_cmd_name, basename(argv[0]), sizeof (lx_cmd_name));
+	lx_debug("executing linux process: %s", argv[0]);
+	lx_debug("branding myself and setting handler to 0x%p",
+	    (void *)lx_emulate);
+
+	reg.lxbr_version = LX_VERSION;
+	reg.lxbr_handler = (void *)&lx_emulate;
+
+	/*
+	 * Register the address of the user-space handler with the lx brand
+	 * module. As a side-effect this leaves the thread in native syscall
+	 * mode so that it's ok to continue to make syscalls during setup. We
+	 * need to switch to Linux mode at the end of initialization.
+	 */
+	if (syscall(SYS_brand, B_REGISTER, &reg))
+		lx_err_fatal("failed to brand the process");
+
+	/* Look up the PID that serves as init for this zone */
+	if ((err = lx_lpid_to_spid(1, &zoneinit_pid)) < 0)
+		lx_err_fatal("Unable to find PID for zone init process: %s",
+		    strerror(err));
+
+	/*
+	 * Upload data about the lx executable from the kernel.
+	 */
+	if (syscall(SYS_brand, B_ELFDATA, (void *)&edp))
+		lx_err_fatal("failed to get required ELF data from the kernel");
+
+	if (lx_statfs_init() != 0)
+		lx_err_fatal("failed to setup the statfs translator");
+
+	/*
+	 * Find the aux vector on the stack.
+	 */
+	p = (long *)envp;
+	while (*p != NULL)
+		p++;
+
+	/*
+	 * Now 'p' points at the NULL word immediately following the environ
+	 * pointers.  The list of auxv entries _should_ immediately follow.
+	 * If anything (such as the native linker or libc) has removed entries
+	 * from the environment array, extra NULLs will be present.
+	 *
+	 * The brand library takes care to avoid such behavior (via the
+	 * lx_parse_env routine above) but a belt-and-suspenders approach is
+	 * taken for safety.
+	 *
+	 * The address following the NULL spacer is recorded as the target for
+	 * auxv translation and any addition NULLs following it are skipped
+	 * until the first auxv entry is located.
+	 */
+	p++;
+	oap = (auxv_t *)p;
+	while (*p == NULL)
+		p++;
+	ap = (auxv_t *)p;
+
+	/*
+	 * Translate auxv entries to Linux equivalents.
+	 */
+	for (; ap->a_type != AT_NULL; ap++) {
+		if (lx_auxv_stol(ap, oap, &edp) == 0) {
+			/*
+			 * Copy only auxv entries which Linux programs will
+			 * understand. Other entries will be skipped.
+			 */
+			oap++;
+		}
+	}
+
+	/* NULL out skipped entries */
+	if (oap < ap) {
+		bzero(oap, (uintptr_t)ap - (uintptr_t)oap);
+	}
+
+	/* Setup signal handler information. */
+	if (lx_siginit()) {
+		lx_err_fatal("failed to initialize lx signals for the "
+		    "branded process");
+	}
+
+	/* Setup thread-specific data area for managing linux threads. */
+	if ((err = thr_keycreate(&lx_tsd_key, NULL)) != 0) {
+		lx_err_fatal("thr_keycreate(lx_tsd_key) failed: %s",
+		    strerror(err));
+	}
+
+	lx_debug("thr_keycreate created lx_tsd_key (%d)", lx_tsd_key);
+
+	/*
+	 * Initialize the thread specific data for this thread.
+	 */
+	if ((lxtsd = malloc(sizeof (*lxtsd))) == NULL) {
+		lx_err_fatal("failed to allocate tsd for main thread: %s",
+		    strerror(errno));
+	}
+	lx_debug("lx tsd allocated @ %p", lxtsd);
+	lx_init_tsd(lxtsd);
+
+	/*
+	 * Allocate the brand emulation stack for the main process thread.
+	 * Register the thread-specific data structure with the stack list so
+	 * that it may be freed at thread exit or fork(2).
+	 */
+	lx_install_stack(NULL, 0, lxtsd);
+
+	/*
+	 * The brand linker expects the stack pointer to point to
+	 * "argc", which is just before &argv[0].
+	 */
+	lx_start((uintptr_t)argv - sizeof (void *), edp.ed_ldentry);
+
+	/*NOTREACHED*/
+	abort();
+	return (0);
+}
+
+/*
+ * We "return" to this function via a context hand-crafted by
+ * "lx_init_tsd()"; see that function for more detail.
+ *
+ * NOTE: Our call frame is on the main thread stack, not the alternate native
+ * stack -- it is safe to release the latter here.  The frame does not have a
+ * valid return address, so this function MUST NOT return.
+ */
+void
+lx_exit_common(void)
+{
+	lx_tsd_t *lxtsd = lx_get_tsd();
+	int ev = (0xff & lxtsd->lxtsd_exit_status);
+
+	switch (lxtsd->lxtsd_exit) {
+	case LX_ET_EXIT:
+		lx_debug("lx_exit_common(LX_ET_EXIT, %d, %d)\n", thr_self(),
+		    ev);
+
+		if (thr_self() == 1) {
+			/*
+			 * Modern versions of glibc will call the exit_group
+			 * syscall when exit(3) is called, but if the primary
+			 * thread explicitly invokes the exit syscall we now
+			 * need to exit with the proper value.
+			 */
+			exit(ev);
+		} else {
+			/*
+			 * If the thread is exiting, but not the entire process,
+			 * we must free the stack we allocated for usermode
+			 * emulation. This is safe to do here because the
+			 * setcontext() put us back on the BRAND stack for this
+			 * process.  This function also frees the
+			 * thread-specific data object for this thread.
+			 */
+			lx_free_stack();
+
+			/*
+			 * The native thread return value is never seen so we
+			 * pass NULL.
+			 */
+			thr_exit(NULL);
+		}
+		break;
+
+	case LX_ET_EXIT_GROUP:
+		lx_debug("lx_exit_common(LX_ET_EXIT_GROUP, %d)\n", ev);
+		exit(ev);
+		break;
+
+	default:
+		abort();
+	}
+
+	abort();
+}
+
+const ucontext_t *
+lx_find_brand_uc(void)
+{
+	ucontext_t *ucp = NULL;
+
+	/*
+	 * Ask for the current emulation (or signal handling) ucontext_t...
+	 */
+	assert(syscall(SYS_brand, B_GET_CURRENT_CONTEXT, &ucp) == 0);
+
+	for (;;) {
+		uintptr_t flags;
+
+		lx_debug("lx_find_brand_uc: inspect ucp %p...\n", ucp);
+		assert(ucp != NULL);
+
+		flags = (uintptr_t)ucp->uc_brand_data[0];
+
+		if (flags & LX_UC_STACK_BRAND) {
+			lx_debug("lx_find_brand_uc: ucp %p\n", ucp);
+
+			return (ucp);
+		}
+
+		lx_debug("lx_find_brand_uc: skip non-BRAND ucp %p\n", ucp);
+
+		/*
+		 * Walk up the context chain to find the most recently stored
+		 * brand register state.
+		 */
+		ucp = ucp->uc_link;
+	}
+}
+
+uintptr_t
+lx_find_brand_sp(void)
+{
+	const ucontext_t *ucp = lx_find_brand_uc();
+	uintptr_t sp = LX_REG(ucp, REG_SP);
+
+	lx_debug("lx_find_brand_sp: ucp %p sp %p\n", ucp, sp);
+
+	return (sp);
+}
+
+ucontext_t *
+lx_syscall_regs(void)
+{
+	ucontext_t *ucp = NULL;
+	uintptr_t flags;
+
+	/*
+	 * Ask for the current emulation (or signal handling) ucontext_t...
+	 */
+	assert(syscall(SYS_brand, B_GET_CURRENT_CONTEXT, &ucp) == 0);
+	assert(ucp != NULL);
+
+	/*
+	 * Use of the lx_syscall_regs() function implies that the topmost (i.e.
+	 * current) context is for a system call emulation request from the
+	 * kernel, rather than a signal handling frame.
+	 */
+	flags = (uintptr_t)ucp->uc_brand_data[0];
+	assert(flags & LX_UC_FRAME_IS_SYSCALL);
+
+	lx_debug("lx_syscall_regs: ucp %p\n", ucp);
+
+	return (ucp);
+}
+
+int
+lx_lpid_to_spair(pid_t lpid, pid_t *spid, lwpid_t *slwp)
+{
+	pid_t pid;
+	lwpid_t tid;
+
+	if (lpid == 0) {
+		pid = getpid();
+		tid = thr_self();
+	} else {
+		if (syscall(SYS_brand, B_LPID_TO_SPAIR, lpid, &pid, &tid) < 0)
+			return (-errno);
+
+		/*
+		 * If the returned pid is -1, that indicates we tried to
+		 * look up the PID for init, but that process no longer
+		 * exists.
+		 */
+		if (pid == -1)
+			return (-ESRCH);
+	}
+
+	if (uucopy(&pid, spid, sizeof (pid_t)) != 0)
+		return (-errno);
+
+	if (uucopy(&tid, slwp, sizeof (lwpid_t)) != 0)
+		return (-errno);
+
+	return (0);
+}
+
+int
+lx_lpid_to_spid(pid_t lpid, pid_t *spid)
+{
+	lwpid_t slwp;
+
+	return (lx_lpid_to_spair(lpid, spid, &slwp));
+}
+
+char *
+lx_fd_to_path(int fd, char *buf, int buf_size)
+{
+	char	path_proc[MAXPATHLEN];
+	pid_t	pid;
+	int	n;
+
+	assert((buf != NULL) && (buf_size >= 0));
+
+	if (fd < 0)
+		return (NULL);
+
+	if ((pid = getpid()) == -1)
+		return (NULL);
+
+	(void) snprintf(path_proc, MAXPATHLEN,
+	    "/native/proc/%d/path/%d", pid, fd);
+
+	if ((n = readlink(path_proc, buf, buf_size - 1)) == -1)
+		return (NULL);
+	buf[n] = '\0';
+
+	return (buf);
+}
+
+#if defined(_LP64)
+/* The following is the 64-bit syscall table */
+
+static lx_syscall_handler_t lx_handlers[] = {
+	NULL,				/*   0: read */
+	NULL,				/*   1: write */
+	NULL,				/*   2: open */
+	lx_close,			/*   3: close */
+	NULL,				/*   4: stat */
+	NULL,				/*   5: fstat */
+	NULL,				/*   6: lstat */
+	NULL,				/*   7: poll */
+	NULL,				/*   8: lseek */
+	lx_mmap,			/*   9: mmap */
+	lx_mprotect,			/*  10: mprotect */
+	NULL,				/*  11: munmap */
+	NULL,				/*  12: brk */
+	lx_rt_sigaction,		/*  13: rt_sigaction */
+	lx_rt_sigprocmask,		/*  14: rt_sigprocmask */
+	lx_rt_sigreturn,		/*  15: rt_sigreturn */
+	NULL,				/*  16: ioctl */
+	NULL,				/*  17: pread64 */
+	NULL,				/*  18: pwrite64 */
+	NULL,				/*  19: readv */
+	NULL,				/*  20: writev */
+	NULL,				/*  21: access */
+	NULL,				/*  22: pipe */
+	NULL,				/*  23: select */
+	NULL,				/*  24: sched_yield */
+	lx_remap,			/*  25: mremap */
+	lx_msync,			/*  26: msync */
+	NULL,				/*  27: mincore */
+	lx_madvise,			/*  28: madvise */
+	lx_shmget,			/*  29: shmget */
+	lx_shmat,			/*  30: shmat */
+	lx_shmctl,			/*  31: shmctl */
+	NULL,				/*  32: dup */
+	NULL,				/*  33: dup2 */
+	NULL,				/*  34: pause */
+	NULL,				/*  35: nanosleep */
+	NULL,				/*  36: getitimer */
+	NULL,				/*  37: alarm */
+	lx_setitimer,			/*  38: setitimer */
+	NULL,				/*  39: getpid */
+	lx_sendfile64,			/*  40: sendfile */
+	NULL,				/*  41: socket */
+	NULL,				/*  42: connect */
+	NULL,				/*  43: accept */
+	NULL,				/*  44: sendto */
+	NULL,				/*  45: recvfrom */
+	NULL,				/*  46: sendmsg */
+	NULL,				/*  47: recvmsg */
+	NULL,				/*  48: shutdown */
+	NULL,				/*  49: bind */
+	NULL,				/*  50: listen */
+	NULL,				/*  51: getsockname */
+	NULL,				/*  52: getpeername */
+	NULL,				/*  53: socketpair */
+	NULL,				/*  54: setsockopt */
+	NULL,				/*  55: getsockopt */
+	lx_clone,			/*  56: clone */
+	lx_fork,			/*  57: fork */
+	lx_vfork,			/*  58: vfork */
+	lx_execve,			/*  59: execve */
+	lx_exit,			/*  60: exit */
+	NULL,				/*  61: wait4 */
+	NULL,				/*  62: kill */
+	NULL,				/*  63: uname */
+	lx_semget,			/*  64: semget */
+	lx_semop,			/*  65: semop */
+	lx_semctl,			/*  66: semctl */
+	lx_shmdt,			/*  67: shmdt */
+	lx_msgget,			/*  68: msgget */
+	lx_msgsnd,			/*  69: msgsnd */
+	lx_msgrcv,			/*  70: msgrcv */
+	lx_msgctl,			/*  71: msgctl */
+	NULL,				/*  72: fcntl */
+	lx_flock,			/*  73: flock */
+	lx_fsync,			/*  74: fsync */
+	lx_fdatasync,			/*  75: fdatasync */
+	lx_truncate,			/*  76: truncate */
+	lx_ftruncate,			/*  77: ftruncate */
+	NULL,				/*  78: getdents */
+	NULL,				/*  79: getcwd */
+	NULL,				/*  80: chdir */
+	NULL,				/*  81: fchdir */
+	NULL,				/*  82: rename */
+	NULL,				/*  83: mkdir */
+	lx_rmdir,			/*  84: rmdir */
+	NULL,				/*  85: creat */
+	NULL,				/*  86: link */
+	NULL,				/*  87: unlink */
+	NULL,				/*  88: symlink */
+	NULL,				/*  89: readlink */
+	NULL,				/*  90: chmod */
+	NULL,				/*  91: fchmod */
+	NULL,				/*  92: chown */
+	NULL,				/*  93: fchown */
+	NULL,				/*  94: lchown */
+	NULL,				/*  95: umask */
+	NULL,				/*  96: gettimeofday */
+	NULL,				/*  97: getrlimit */
+	NULL,				/*  98: getrusage */
+	NULL,				/*  99: sysinfo */
+	lx_times,			/* 100: times */
+	NULL,				/* 101: ptrace */
+	NULL,				/* 102: getuid */
+	NULL,				/* 103: syslog */
+	NULL,				/* 104: getgid */
+	NULL,				/* 105: setuid */
+	NULL,				/* 106: setgid */
+	NULL,				/* 107: geteuid */
+	NULL,				/* 108: getegid */
+	NULL,				/* 109: setpgid */
+	NULL,				/* 110: getppid */
+	NULL,				/* 111: getpgrp */
+	NULL,				/* 112: setsid */
+	NULL,				/* 113: setreuid */
+	NULL,				/* 114: setregid */
+	lx_getgroups,			/* 115: getgroups */
+	lx_setgroups,			/* 116: setgroups */
+	NULL,				/* 117: setresuid */
+	NULL,				/* 118: getresuid */
+	NULL,				/* 119: setresgid */
+	NULL,				/* 120: getresgid */
+	NULL,				/* 121: getpgid */
+	NULL,				/* 122: setfsuid */
+	NULL,				/* 123: setfsgid */
+	NULL,				/* 124: getsid */
+	lx_capget,			/* 125: capget */
+	lx_capset,			/* 126: capset */
+	lx_rt_sigpending,		/* 127: rt_sigpending */
+	lx_rt_sigtimedwait,		/* 128: rt_sigtimedwait */
+	lx_rt_sigqueueinfo,		/* 129: rt_sigqueueinfo */
+	lx_rt_sigsuspend,		/* 130: rt_sigsuspend */
+	lx_sigaltstack,			/* 131: sigaltstack */
+	lx_utime,			/* 132: utime */
+	lx_mknod,			/* 133: mknod */
+	NULL,				/* 134: uselib */
+	NULL,				/* 135: personality */
+	NULL,				/* 136: ustat */
+	lx_statfs,			/* 137: statfs */
+	lx_fstatfs,			/* 138: fstatfs */
+	lx_sysfs,			/* 139: sysfs */
+	lx_getpriority,			/* 140: getpriority */
+	lx_setpriority,			/* 141: setpriority */
+	lx_sched_setparam,		/* 142: sched_setparam */
+	lx_sched_getparam,		/* 143: sched_getparam */
+	lx_sched_setscheduler,		/* 144: sched_setscheduler */
+	lx_sched_getscheduler,		/* 145: sched_getscheduler */
+	lx_sched_get_priority_max,	/* 146: sched_get_priority_max */
+	lx_sched_get_priority_min,	/* 147: sched_get_priority_min */
+	lx_sched_rr_get_interval,	/* 148: sched_rr_get_interval */
+	lx_mlock,			/* 149: mlock */
+	lx_munlock,			/* 150: munlock */
+	lx_mlockall,			/* 151: mlockall */
+	lx_munlockall,			/* 152: munlockall */
+	NULL,				/* 153: vhangup */
+	NULL,				/* 154: modify_ldt */
+	NULL,				/* 155: pivot_root */
+	lx_sysctl,			/* 156: sysctl */
+	NULL,				/* 157: prctl */
+	NULL,				/* 158: arch_prctl */
+	lx_adjtimex,			/* 159: adjtimex */
+	NULL,				/* 160: setrlimit */
+	NULL,				/* 161: chroot */
+	NULL,				/* 162: sync */
+	NULL,				/* 163: acct */
+	lx_settimeofday,		/* 164: settimeofday */
+	lx_mount,			/* 165: mount */
+	NULL,				/* 166: umount2 */
+	NULL,				/* 167: swapon */
+	NULL,				/* 168: swapoff */
+	NULL,				/* 169: reboot */
+	NULL,				/* 170: sethostname */
+	NULL,				/* 171: setdomainname */
+	NULL,				/* 172: iopl */
+	NULL,				/* 173: ioperm */
+	NULL,				/* 174: create_module */
+	NULL,				/* 175: init_module */
+	NULL,				/* 176: delete_module */
+	NULL,				/* 177: get_kernel_syms */
+	lx_query_module,		/* 178: query_module */
+	NULL,				/* 179: quotactl */
+	NULL,				/* 180: nfsservctl */
+	NULL,				/* 181: getpmsg */
+	NULL,				/* 182: putpmsg */
+	NULL,				/* 183: afs_syscall */
+	NULL,				/* 184: tux */
+	NULL,				/* 185: security */
+	NULL,				/* 186: gettid */
+	NULL,				/* 187: readahead */
+	NULL,				/* 188: setxattr */
+	NULL,				/* 189: lsetxattr */
+	NULL,				/* 190: fsetxattr */
+	NULL,				/* 191: getxattr */
+	NULL,				/* 192: lgetxattr */
+	NULL,				/* 193: fgetxattr */
+	NULL,				/* 194: listxattr */
+	NULL,				/* 195: llistxattr */
+	NULL,				/* 196: flistxattr */
+	NULL,				/* 197: removexattr */
+	NULL,				/* 198: lremovexattr */
+	NULL,				/* 199: fremovexattr */
+	NULL,				/* 200: tkill */
+	NULL,				/* 201: time */
+	NULL,				/* 202: futex */
+	lx_sched_setaffinity,		/* 203: sched_setaffinity */
+	lx_sched_getaffinity,		/* 204: sched_getaffinity */
+	NULL,				/* 205: set_thread_area */
+	lx_io_setup,			/* 206: io_setup */
+	lx_io_destroy,			/* 207: io_destroy */
+	lx_io_getevents,		/* 208: io_getevents */
+	lx_io_submit,			/* 209: io_submit */
+	lx_io_cancel,			/* 210: io_cancel */
+	NULL,				/* 211: get_thread_area */
+	NULL,				/* 212: lookup_dcookie */
+	NULL,				/* 213: epoll_create */
+	NULL,				/* 214: epoll_ctl_old */
+	NULL,				/* 215: epoll_wait_old */
+	NULL,				/* 216: remap_file_pages */
+	NULL,				/* 217: getdents64 */
+	NULL,				/* 218: set_tid_address */
+	NULL,				/* 219: restart_syscall */
+	lx_semtimedop,			/* 220: semtimedop */
+	NULL,				/* 221: fadvise64 */
+	lx_timer_create,		/* 222: timer_create */
+	lx_timer_settime,		/* 223: timer_settime */
+	lx_timer_gettime,		/* 224: timer_gettime */
+	lx_timer_getoverrun,		/* 225: timer_getoverrun */
+	lx_timer_delete,		/* 226: timer_delete */
+	NULL,				/* 227: clock_settime */
+	NULL,				/* 228: clock_gettime */
+	NULL,				/* 229: clock_getres */
+	lx_clock_nanosleep,		/* 230: clock_nanosleep */
+	lx_group_exit,			/* 231: exit_group */
+	NULL,				/* 232: epoll_wait */
+	NULL,				/* 233: epoll_ctl */
+	NULL,				/* 234: tgkill */
+	lx_utimes,			/* 235: utimes */
+	NULL,				/* 236: vserver */
+	NULL,				/* 237: mbind */
+	NULL,				/* 238: set_mempolicy */
+	NULL,				/* 239: get_mempolicy */
+	NULL,				/* 240: mq_open */
+	NULL,				/* 241: mq_unlink */
+	NULL,				/* 242: mq_timedsend */
+	NULL,				/* 243: mq_timedreceive */
+	NULL,				/* 244: mq_notify */
+	NULL,				/* 245: mq_getsetattr */
+	NULL,				/* 246: kexec_load */
+	NULL,				/* 247: waitid */
+	NULL,				/* 248: add_key */
+	NULL,				/* 249: request_key */
+	NULL,				/* 250: keyctl */
+	NULL,				/* 251: ioprio_set */
+	NULL,				/* 252: ioprio_get */
+	lx_inotify_init,		/* 253: inotify_init */
+	lx_inotify_add_watch,		/* 254: inotify_add_watch */
+	lx_inotify_rm_watch,		/* 255: inotify_rm_watch */
+	NULL,				/* 256: migrate_pages */
+	NULL,				/* 257: openat */
+	NULL,				/* 258: mkdirat */
+	lx_mknodat,			/* 259: mknodat */
+	NULL,				/* 260: fchownat */
+	lx_futimesat,			/* 261: futimesat */
+	NULL,				/* 262: fstatat64 */
+	NULL,				/* 263: unlinkat */
+	NULL,				/* 264: renameat */
+	NULL,				/* 265: linkat */
+	NULL,				/* 266: symlinkat */
+	NULL,				/* 267: readlinkat */
+	NULL,				/* 268: fchmodat */
+	NULL,				/* 269: faccessat */
+	NULL,				/* 270: pselect6 */
+	NULL,				/* 271: ppoll */
+	NULL,				/* 272: unshare */
+	NULL,				/* 273: set_robust_list */
+	NULL,				/* 274: get_robust_list */
+	NULL,				/* 275: splice */
+	NULL,				/* 276: tee */
+	NULL,				/* 277: sync_file_range */
+	NULL,				/* 278: vmsplice */
+	NULL,				/* 279: move_pages */
+	lx_utimensat,			/* 280: utimensat */
+	NULL,				/* 281: epoll_pwait */
+	lx_signalfd,			/* 282: signalfd */
+	lx_timerfd_create,		/* 283: timerfd_create */
+	lx_eventfd,			/* 284: eventfd */
+	NULL,				/* 285: fallocate */
+	lx_timerfd_settime,		/* 286: timerfd_settime */
+	lx_timerfd_gettime,		/* 287: timerfd_gettime */
+	NULL,				/* 288: accept4 */
+	lx_signalfd4,			/* 289: signalfd4 */
+	lx_eventfd2,			/* 290: eventfd2 */
+	NULL,				/* 291: epoll_create1 */
+	NULL,				/* 292: dup3 */
+	NULL,				/* 293: pipe2 */
+	lx_inotify_init1,		/* 294: inotify_init1 */
+	NULL,				/* 295: preadv */
+	NULL,				/* 296: pwritev */
+	lx_rt_tgsigqueueinfo,		/* 297: rt_tgsigqueueinfo */
+	NULL,				/* 298: perf_event_open */
+	NULL,				/* 299: recvmmsg */
+	NULL,				/* 300: fanotify_init */
+	NULL,				/* 301: fanotify_mark */
+	NULL,				/* 302: prlimit64 */
+	NULL,				/* 303: name_to_handle_at */
+	NULL,				/* 304: open_by_handle_at */
+	NULL,				/* 305: clock_adjtime */
+	NULL,				/* 306: syncfs */
+	NULL,				/* 307: sendmmsg */
+	NULL,				/* 309: setns */
+	NULL,				/* 309: getcpu */
+	NULL,				/* 310: process_vm_readv */
+	NULL,				/* 311: process_vm_writev */
+	NULL,				/* 312: kcmp */
+	NULL,				/* 313: finit_module */
+	NULL,				/* 314: sched_setattr */
+	NULL,				/* 315: sched_getattr */
+	NULL,				/* 316: renameat2 */
+	NULL,				/* 317: seccomp */
+	NULL,				/* 318: getrandom */
+	NULL,				/* 319: memfd_create */
+	NULL,				/* 320: kexec_file_load */
+	NULL,				/* 321: bpf */
+	NULL,				/* 322: execveat */
+
+	/* XXX TBD gap then x32 syscalls from 512 - 544 */
+};
+
+#else
+/* The following is the 32-bit syscall table */
+
+static lx_syscall_handler_t lx_handlers[] = {
+	NULL,				/*   0: nosys */
+	lx_exit,			/*   1: exit */
+	lx_fork,			/*   2: fork */
+	NULL,				/*   3: read */
+	NULL,				/*   4: write */
+	NULL,				/*   5: open */
+	lx_close,			/*   6: close */
+	NULL,				/*   7: waitpid */
+	NULL,				/*   8: creat */
+	NULL,				/*   9: link */
+	NULL,				/*  10: unlink */
+	lx_execve,			/*  11: execve */
+	NULL,				/*  12: chdir */
+	NULL,				/*  13: time */
+	lx_mknod,			/*  14: mknod */
+	NULL,				/*  15: chmod */
+	NULL,				/*  16: lchown16 */
+	NULL,				/*  17: break */
+	NULL,				/*  18: stat */
+	NULL,				/*  19: lseek */
+	NULL,				/*  20: getpid */
+	lx_mount,			/*  21: mount */
+	NULL,				/*  22: umount */
+	NULL,				/*  23: setuid16 */
+	NULL,				/*  24: getuid16 */
+	NULL,				/*  25: stime */
+	NULL,				/*  26: ptrace */
+	NULL,				/*  27: alarm */
+	NULL,				/*  28: fstat */
+	NULL,				/*  29: pause */
+	lx_utime,			/*  30: utime */
+	NULL,				/*  31: stty */
+	NULL,				/*  32: gtty */
+	NULL,				/*  33: access */
+	NULL,				/*  34: nice */
+	NULL,				/*  35: ftime */
+	NULL,				/*  36: sync */
+	NULL,				/*  37: kill */
+	NULL,				/*  38: rename */
+	NULL,				/*  39: mkdir */
+	lx_rmdir,			/*  40: rmdir */
+	NULL,				/*  41: dup */
+	NULL,				/*  42: pipe */
+	lx_times,			/*  43: times */
+	NULL,				/*  44: prof */
+	NULL,				/*  45: brk */
+	NULL,				/*  46: setgid16 */
+	NULL,				/*  47: getgid16 */
+	lx_signal,			/*  48: signal */
+	NULL,				/*  49: geteuid16 */
+	NULL,				/*  50: getegid16 */
+	NULL,				/*  51: acct */
+	NULL,				/*  52: umount2 */
+	NULL,				/*  53: lock */
+	NULL,				/*  54: ioctl */
+	NULL,				/*  55: fcntl */
+	NULL,				/*  56: mpx */
+	NULL,				/*  57: setpgid */
+	NULL,				/*  58: ulimit */
+	NULL,				/*  59: olduname */
+	NULL,				/*  60: umask */
+	NULL,				/*  61: chroot */
+	NULL,				/*  62: ustat */
+	NULL,				/*  63: dup2 */
+	NULL,				/*  64: getppid */
+	NULL,				/*  65: getpgrp */
+	NULL,				/*  66: setsid */
+	lx_sigaction,			/*  67: sigaction */
+	NULL,				/*  68: sgetmask */
+	NULL,				/*  69: ssetmask */
+	NULL,				/*  70: setreuid16 */
+	NULL,				/*  71: setregid16 */
+	lx_sigsuspend,			/*  72: sigsuspend */
+	lx_sigpending,			/*  73: sigpending */
+	NULL,				/*  74: sethostname */
+	NULL,				/*  75: setrlimit */
+	NULL,				/*  76: getrlimit */
+	NULL,				/*  77: getrusage */
+	NULL,				/*  78: gettimeofday */
+	lx_settimeofday,		/*  79: settimeofday */
+	lx_getgroups16,			/*  80: getgroups16 */
+	lx_setgroups16,			/*  81: setgroups16 */
+	NULL,				/*  82: select */
+	NULL,				/*  83: symlink */
+	NULL,				/*  84: oldlstat */
+	NULL,				/*  85: readlink */
+	NULL,				/*  86: uselib */
+	NULL,				/*  87: swapon */
+	NULL,				/*  88: reboot */
+	lx_readdir,			/*  89: readdir */
+	lx_mmap,			/*  90: mmap */
+	NULL,				/*  91: munmap */
+	lx_truncate,			/*  92: truncate */
+	lx_ftruncate,			/*  93: ftruncate */
+	NULL,				/*  94: fchmod */
+	NULL,				/*  95: fchown16 */
+	lx_getpriority,			/*  96: getpriority */
+	lx_setpriority,			/*  97: setpriority */
+	NULL,				/*  98: profil */
+	lx_statfs,			/*  99: statfs */
+	lx_fstatfs,			/* 100: fstatfs */
+	NULL,				/* 101: ioperm */
+	NULL,				/* 102: socketcall */
+	NULL,				/* 103: syslog */
+	lx_setitimer,			/* 104: setitimer */
+	NULL,				/* 105: getitimer */
+	NULL,				/* 106: stat */
+	NULL,				/* 107: lstat */
+	NULL,				/* 108: fstat */
+	NULL,				/* 109: uname */
+	NULL,				/* 110: oldiopl */
+	NULL,				/* 111: vhangup */
+	NULL,				/* 112: idle */
+	NULL,				/* 113: vm86old */
+	NULL,				/* 114: wait4 */
+	NULL,				/* 115: swapoff */
+	NULL,				/* 116: sysinfo */
+	lx_ipc,				/* 117: ipc */
+	lx_fsync,			/* 118: fsync */
+	lx_sigreturn,			/* 119: sigreturn */
+	lx_clone,			/* 120: clone */
+	NULL,				/* 121: setdomainname */
+	NULL,				/* 122: uname */
+	NULL,				/* 123: modify_ldt */
+	lx_adjtimex,			/* 124: adjtimex */
+	lx_mprotect,			/* 125: mprotect */
+	lx_sigprocmask,			/* 126: sigprocmask */
+	NULL,				/* 127: create_module */
+	NULL,				/* 128: init_module */
+	NULL,				/* 129: delete_module */
+	NULL,				/* 130: get_kernel_syms */
+	NULL,				/* 131: quotactl */
+	NULL,				/* 132: getpgid */
+	NULL,				/* 133: fchdir */
+	NULL,				/* 134: bdflush */
+	lx_sysfs,			/* 135: sysfs */
+	NULL,				/* 136: personality */
+	NULL,				/* 137: afs_syscall */
+	NULL,				/* 138: setfsuid16 */
+	NULL,				/* 139: setfsgid16 */
+	NULL,				/* 140: llseek */
+	NULL,				/* 141: getdents */
+	NULL,				/* 142: select */
+	lx_flock,			/* 143: flock */
+	lx_msync,			/* 144: msync */
+	NULL,				/* 145: readv */
+	NULL,				/* 146: writev */
+	NULL,				/* 147: getsid */
+	lx_fdatasync,			/* 148: fdatasync */
+	lx_sysctl,			/* 149: sysctl */
+	lx_mlock,			/* 150: mlock */
+	lx_munlock,			/* 151: munlock */
+	lx_mlockall,			/* 152: mlockall */
+	lx_munlockall,			/* 153: munlockall */
+	lx_sched_setparam,		/* 154: sched_setparam */
+	lx_sched_getparam,		/* 155: sched_getparam */
+	lx_sched_setscheduler,		/* 156: sched_setscheduler */
+	lx_sched_getscheduler,		/* 157: sched_getscheduler */
+	NULL,				/* 158: sched_yield */
+	lx_sched_get_priority_max,	/* 159: sched_get_priority_max */
+	lx_sched_get_priority_min,	/* 160: sched_get_priority_min */
+	lx_sched_rr_get_interval,	/* 161: sched_rr_get_interval */
+	NULL,				/* 162: nanosleep */
+	lx_remap,			/* 163: mremap */
+	NULL,				/* 164: setresuid16 */
+	NULL,				/* 165: getresuid16 */
+	NULL,				/* 166: vm86 */
+	lx_query_module,		/* 167: query_module */
+	NULL,				/* 168: poll */
+	NULL,				/* 169: nfsservctl */
+	NULL,				/* 170: setresgid16 */
+	NULL,				/* 171: getresgid16 */
+	NULL,				/* 172: prctl */
+	lx_rt_sigreturn,		/* 173: rt_sigreturn */
+	lx_rt_sigaction,		/* 174: rt_sigaction */
+	lx_rt_sigprocmask,		/* 175: rt_sigprocmask */
+	lx_rt_sigpending,		/* 176: rt_sigpending */
+	lx_rt_sigtimedwait,		/* 177: rt_sigtimedwait */
+	lx_rt_sigqueueinfo,		/* 178: rt_sigqueueinfo */
+	lx_rt_sigsuspend,		/* 179: rt_sigsuspend */
+	NULL,				/* 180: pread64 */
+	NULL,				/* 181: pwrite64 */
+	NULL,				/* 182: chown16 */
+	NULL,				/* 183: getcwd */
+	lx_capget,			/* 184: capget */
+	lx_capset,			/* 185: capset */
+	lx_sigaltstack,			/* 186: sigaltstack */
+	lx_sendfile,			/* 187: sendfile */
+	NULL,				/* 188: getpmsg */
+	NULL,				/* 189: putpmsg */
+	lx_vfork,			/* 190: vfork */
+	NULL,				/* 191: getrlimit */
+	lx_mmap2,			/* 192: mmap2 */
+	lx_truncate64,			/* 193: truncate64 */
+	lx_ftruncate64,			/* 194: ftruncate64 */
+	NULL,				/* 195: stat64 */
+	NULL,				/* 196: lstat64 */
+	NULL,				/* 197: fstat64 */
+	NULL,				/* 198: lchown */
+	NULL,				/* 199: getuid */
+	NULL,				/* 200: getgid */
+	NULL,				/* 201: geteuid */
+	NULL,				/* 202: getegid */
+	NULL,				/* 203: setreuid */
+	NULL,				/* 204: setregid */
+	lx_getgroups,			/* 205: getgroups */
+	lx_setgroups,			/* 206: setgroups */
+	NULL,				/* 207: fchown */
+	NULL,				/* 208: setresuid */
+	NULL,				/* 209: getresuid */
+	NULL,				/* 210: setresgid */
+	NULL,				/* 211: getresgid */
+	NULL,				/* 212: chown */
+	NULL,				/* 213: setuid */
+	NULL,				/* 214: setgid */
+	NULL,				/* 215: setfsuid */
+	NULL,				/* 216: setfsgid */
+	NULL,				/* 217: pivot_root */
+	NULL,				/* 218: mincore */
+	lx_madvise,			/* 219: madvise */
+	NULL,				/* 220: getdents64 */
+	NULL,				/* 221: fcntl64 */
+	NULL,				/* 222: tux */
+	NULL,				/* 223: security */
+	NULL,				/* 224: gettid */
+	NULL,				/* 225: readahead */
+	NULL,				/* 226: setxattr */
+	NULL,				/* 227: lsetxattr */
+	NULL,				/* 228: fsetxattr */
+	NULL,				/* 229: getxattr */
+	NULL,				/* 230: lgetxattr */
+	NULL,				/* 231: fgetxattr */
+	NULL,				/* 232: listxattr */
+	NULL,				/* 233: llistxattr */
+	NULL,				/* 234: flistxattr */
+	NULL,				/* 235: removexattr */
+	NULL,				/* 236: lremovexattr */
+	NULL,				/* 237: fremovexattr */
+	NULL,				/* 238: tkill */
+	lx_sendfile64,			/* 239: sendfile64 */
+	NULL,				/* 240: futex */
+	lx_sched_setaffinity,		/* 241: sched_setaffinity */
+	lx_sched_getaffinity,		/* 242: sched_getaffinity */
+	NULL,				/* 243: set_thread_area */
+	NULL,				/* 244: get_thread_area */
+	lx_io_setup,			/* 245: io_setup */
+	lx_io_destroy,			/* 246: io_destroy */
+	lx_io_getevents,		/* 247: io_getevents */
+	lx_io_submit,			/* 248: io_submit */
+	lx_io_cancel,			/* 249: io_cancel */
+	NULL,				/* 250: fadvise64 */
+	NULL,				/* 251: nosys */
+	lx_group_exit,			/* 252: group_exit */
+	NULL,				/* 253: lookup_dcookie */
+	NULL,				/* 254: epoll_create */
+	NULL,				/* 255: epoll_ctl */
+	NULL,				/* 256: epoll_wait */
+	NULL,				/* 257: remap_file_pages */
+	NULL,				/* 258: set_tid_address */
+	lx_timer_create,		/* 259: timer_create */
+	lx_timer_settime,		/* 260: timer_settime */
+	lx_timer_gettime,		/* 261: timer_gettime */
+	lx_timer_getoverrun,		/* 262: timer_getoverrun */
+	lx_timer_delete,		/* 263: timer_delete */
+	NULL,				/* 264: clock_settime */
+	NULL,				/* 265: clock_gettime */
+	NULL,				/* 266: clock_getres */
+	lx_clock_nanosleep,		/* 267: clock_nanosleep */
+	lx_statfs64,			/* 268: statfs64 */
+	lx_fstatfs64,			/* 269: fstatfs64 */
+	NULL,				/* 270: tgkill */
+	lx_utimes,			/* 271: utimes */
+	NULL,			/* 272: fadvise64_64 */
+	NULL,				/* 273: vserver */
+	NULL,				/* 274: mbind */
+	NULL,				/* 275: get_mempolicy */
+	NULL,				/* 276: set_mempolicy */
+	NULL,				/* 277: mq_open */
+	NULL,				/* 278: mq_unlink */
+	NULL,				/* 279: mq_timedsend */
+	NULL,				/* 280: mq_timedreceive */
+	NULL,				/* 281: mq_notify */
+	NULL,				/* 282: mq_getsetattr */
+	NULL,				/* 283: kexec_load */
+	NULL,				/* 284: waitid */
+	NULL,				/* 285: sys_setaltroot */
+	NULL,				/* 286: add_key */
+	NULL,				/* 287: request_key */
+	NULL,				/* 288: keyctl */
+	NULL,				/* 289: ioprio_set */
+	NULL,				/* 290: ioprio_get */
+	lx_inotify_init,		/* 291: inotify_init */
+	lx_inotify_add_watch,		/* 292: inotify_add_watch */
+	lx_inotify_rm_watch,		/* 293: inotify_rm_watch */
+	NULL,				/* 294: migrate_pages */
+	NULL,				/* 295: openat */
+	NULL,				/* 296: mkdirat */
+	lx_mknodat,			/* 297: mknodat */
+	NULL,				/* 298: fchownat */
+	lx_futimesat,			/* 299: futimesat */
+	NULL,				/* 300: fstatat64 */
+	NULL,				/* 301: unlinkat */
+	NULL,				/* 302: renameat */
+	NULL,				/* 303: linkat */
+	NULL,				/* 304: symlinkat */
+	NULL,				/* 305: readlinkat */
+	NULL,				/* 306: fchmodat */
+	NULL,				/* 307: faccessat */
+	NULL,				/* 308: pselect6 */
+	NULL,				/* 309: ppoll */
+	NULL,				/* 310: unshare */
+	NULL,				/* 311: set_robust_list */
+	NULL,				/* 312: get_robust_list */
+	NULL,				/* 313: splice */
+	NULL,				/* 314: sync_file_range */
+	NULL,				/* 315: tee */
+	NULL,				/* 316: vmsplice */
+	NULL,				/* 317: move_pages */
+	NULL,				/* 318: getcpu */
+	NULL,				/* 319: epoll_pwait */
+	lx_utimensat,			/* 320: utimensat */
+	lx_signalfd,			/* 321: signalfd */
+	lx_timerfd_create,		/* 322: timerfd_create */
+	lx_eventfd,			/* 323: eventfd */
+	NULL,				/* 324: fallocate */
+	lx_timerfd_settime,		/* 325: timerfd_settime */
+	lx_timerfd_gettime,		/* 326: timerfd_gettime */
+	lx_signalfd4,			/* 327: signalfd4 */
+	lx_eventfd2,			/* 328: eventfd2 */
+	NULL,				/* 329: epoll_create1 */
+	NULL,				/* 330: dup3 */
+	NULL,				/* 331: pipe2 */
+	lx_inotify_init1,		/* 332: inotify_init1 */
+	NULL,				/* 333: preadv */
+	NULL,				/* 334: pwritev */
+	lx_rt_tgsigqueueinfo,		/* 335: rt_tgsigqueueinfo */
+	NULL,				/* 336: perf_event_open */
+	NULL,				/* 337: recvmmsg */
+	NULL,				/* 338: fanotify_init */
+	NULL,				/* 339: fanotify_mark */
+	NULL,				/* 340: prlimit64 */
+	NULL,				/* 341: name_to_handle_at */
+	NULL,				/* 342: open_by_handle_at */
+	NULL,				/* 343: clock_adjtime */
+	NULL,				/* 344: syncfs */
+	NULL,				/* 345: sendmmsg */
+	NULL,				/* 346: setns */
+	NULL,				/* 347: process_vm_readv */
+	NULL,				/* 348: process_vm_writev */
+	NULL,				/* 349: kcmp */
+	NULL,				/* 350: finit_module */
+	NULL,				/* 351: sched_setattr */
+	NULL,				/* 352: sched_getattr */
+	NULL,				/* 353: renameat2 */
+	NULL,				/* 354: seccomp */
+	NULL,				/* 355: getrandom */
+	NULL,				/* 356: memfd_create */
+	NULL,				/* 357: bpf */
+	NULL,				/* 358: execveat */
+};
+#endif
--- /dev/null	Wed Oct  5 00:31:47 2016
+++ new/usr/src/lib/brand/lx/lx_brand/common/lx_provider.d	Wed Oct  5 00:31:47 2016
@@ -0,0 +1,39 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+provider lx {
+	probe debug(char *buf);
+	probe sigdeliver(int sig, void *lx_sigaction, void *lx_sigstack);
+	probe sigreturn(void *lx_ucontext, void *ucontext, uintptr_t sp);
+
+	probe signal__delivery__frame__create(void *lx_sigdeliver_frame);
+	probe signal__delivery__frame__found(void *lx_sigdeliver_frame);
+	probe signal__delivery__frame__corrupt(void *lx_sigdeliver_frame);
+
+	probe signal__post__handler(uintptr_t old_sp, uintptr_t new_sp);
+
+	probe signal__altstack__enable(uintptr_t alt_sp);
+	probe signal__altstack__disable();
+
+	probe emulate__enter(void *ucp, int syscall_num, uintptr_t *args);
+	probe emulate__return(void *ucp, int syscall_num, uintptr_t ret,
+	    uintptr_t errn);
+};
+
+#pragma D attributes Evolving/Evolving/ISA provider lx provider
+#pragma D attributes Private/Private/Unknown provider lx module
+#pragma D attributes Private/Private/Unknown provider lx function
+#pragma D attributes Private/Private/ISA provider lx name
+#pragma D attributes Private/Private/ISA provider lx args
--- /dev/null	Wed Oct  5 00:31:47 2016
+++ new/usr/src/lib/brand/lx/lx_brand/common/mapfile	Wed Oct  5 00:31:47 2016
@@ -0,0 +1,47 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING:  STOP NOW.  DO NOT MODIFY THIS FILE.
+# Object versioning must comply with the rules detailed in
+#
+#	usr/src/lib/README.mapfiles
+#
+# You should not be making modifications here until you've read the most current
+# copy of that file. If you need help, contact a gatekeeper for guidance.
+#
+# MAPFILE HEADER END
+#
+
+#
+# Scope everything local -- our .init section is our only public interface.
+#
+{
+	local:
+		*;
+};
--- /dev/null	Wed Oct  5 00:31:47 2016
+++ new/usr/src/lib/brand/lx/lx_brand/common/mapfile-vers	Wed Oct  5 00:31:47 2016
@@ -0,0 +1,47 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING:  STOP NOW.  DO NOT MODIFY THIS FILE.
+# Object versioning must comply with the rules detailed in
+#
+#	usr/src/lib/README.mapfiles
+#
+# You should not be making modifications here until you've read the most current
+# copy of that file. If you need help, contact a gatekeeper for guidance.
+#
+# MAPFILE HEADER END
+#
+
+#
+# Scope everything local -- our .init section is our only public interface.
+#
+{
+	local:
+		*;
+};
--- /dev/null	Wed Oct  5 00:31:47 2016
+++ new/usr/src/lib/brand/lx/lx_brand/common/mem.c	Wed Oct  5 00:31:47 2016
@@ -0,0 +1,814 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <procfs.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/lx_debug.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_syscall.h>
+
+/*
+ * There are two forms of mmap, mmap() and mmap2().  The only difference is that
+ * the final argument to mmap2() specifies the number of pages, not bytes.
+ * Linux has a number of additional flags, but they are all deprecated.  We also
+ * ignore the MAP_GROWSDOWN flag, which has no equivalent on Solaris.
+ *
+ * The Linux mmap() returns ENOMEM in some cases where Solaris returns
+ * EOVERFLOW, so we translate the errno as necessary.
+ */
+
+int pagesize;	/* needed for mmap2() */
+
+#define	LX_MAP_ANONYMOUS	0x00020
+#define	LX_MAP_LOCKED		0x02000
+#define	LX_MAP_NORESERVE	0x04000
+#define	LX_MAP_32BIT		0x00040
+
+#define	LX_MADV_REMOVE		9
+#define	LX_MADV_DONTFORK	10
+#define	LX_MADV_DOFORK		11
+#define	LX_MADV_MERGEABLE	12
+#define	LX_MADV_UNMERGEABLE	13
+#define	LX_MADV_HUGEPAGE	14
+#define	LX_MADV_NOHUGEPAGE	15
+#define	LX_MADV_DONTDUMP	16
+#define	LX_MADV_DODUMP		17
+
+#define	TWO_GB			0x80000000
+
+static void lx_remap_anoncache_invalidate(uintptr_t, size_t);
+
+static int
+ltos_mmap_flags(int flags)
+{
+	int new_flags;
+
+	new_flags = flags & (MAP_TYPE | MAP_FIXED);
+
+	if (flags & LX_MAP_ANONYMOUS)
+		new_flags |= MAP_ANONYMOUS;
+	if (flags & LX_MAP_NORESERVE)
+		new_flags |= MAP_NORESERVE;
+
+#if defined(_LP64)
+	if (flags & LX_MAP_32BIT)
+		new_flags |= MAP_32BIT;
+#endif
+
+	return (new_flags);
+}
+
+static void *
+mmap_common(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4,
+    uintptr_t p5, off64_t p6)
+{
+	void *addr = (void *)p1;
+	size_t len = p2;
+	int prot = p3;
+	int flags = p4;
+	int fd = p5;
+	off64_t off = p6;
+	void *ret;
+
+	if (LX_DEBUG_ISENABLED) {
+		char *path, path_buf[MAXPATHLEN];
+
+		path = lx_fd_to_path(fd, path_buf, sizeof (path_buf));
+		if (path == NULL)
+			path = "?";
+
+		lx_debug("\tmmap_common(): fd = %d - %s", fd, path);
+	}
+
+	/*
+	 * Under Linux, the file descriptor is ignored when mapping zfod
+	 * anonymous memory,  On Solaris, we want the fd set to -1 for the
+	 * same functionality.
+	 */
+	if (flags & LX_MAP_ANONYMOUS)
+		fd = -1;
+
+	/*
+	 * We refuse, as a matter of principle, to overcommit memory.
+	 * Unfortunately, several bits of important and popular software expect
+	 * to be able to pre-allocate large amounts of virtual memory but then
+	 * probably never use it.  One particularly bad example of this
+	 * practice is golang.
+	 *
+	 * In the interest of running software, unsafe or not, we fudge
+	 * something vaguely similar to overcommit by permanently enabling
+	 * MAP_NORESERVE unless MAP_LOCKED was requested:
+	 */
+	if (!(flags & LX_MAP_LOCKED)) {
+		flags |= LX_MAP_NORESERVE;
+	}
+
+	/*
+	 * This is totally insane. The NOTES section in the linux mmap(2) man
+	 * page claims that on some architectures, read protection may
+	 * automatically include exec protection. It has been observed on a
+	 * native linux system that the /proc/<pid>/maps file does indeed
+	 * show that segments mmap'd from userland (such as libraries mapped in
+	 * by the dynamic linker) all have exec the permission set, even for
+	 * data segments.
+	 *
+	 * This insanity is tempered by the fact that the behavior is disabled
+	 * for ELF binaries bearing a PT_GNU_STACK header which lacks PF_X
+	 * (which most do).  Such a header will clear the READ_IMPLIES_EXEC
+	 * flag from the process personality.
+	 */
+	if (prot & PROT_READ) {
+		unsigned int personality;
+
+		personality = syscall(SYS_brand, B_GET_PERSONALITY);
+		if ((personality & LX_PER_READ_IMPLIES_EXEC) != 0) {
+			prot |= PROT_EXEC;
+		}
+	}
+
+	ret = mmap64(addr, len, prot, ltos_mmap_flags(flags), fd, off);
+
+	if (ret == MAP_FAILED)
+		return ((void *)(long)(errno == EOVERFLOW ? -ENOMEM : -errno));
+
+	if (flags & LX_MAP_LOCKED)
+		(void) mlock(ret, len);
+
+	/*
+	 * We have a new mapping; invalidate any cached anonymous regions that
+	 * overlap(ped) with it.
+	 */
+	lx_remap_anoncache_invalidate((uintptr_t)ret, len);
+
+	return (ret);
+}
+
+long
+lx_mmap(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4,
+    uintptr_t p5, uintptr_t p6)
+{
+	return ((ssize_t)mmap_common(p1, p2, p3, p4, p5, (off64_t)p6));
+}
+
+long
+lx_mmap2(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4,
+    uintptr_t p5, uintptr_t p6)
+{
+	if (pagesize == 0)
+		pagesize = sysconf(_SC_PAGESIZE);
+
+	return ((ssize_t)mmap_common(p1, p2, p3, p4, p5,
+	    (off64_t)p6 * pagesize));
+}
+
+
+/*
+ * The locking family of system calls, as well as msync(), are identical.  On
+ * Solaris, they are layered on top of the memcntl syscall, so they cannot be
+ * pass-thru.
+ */
+long
+lx_mlock(uintptr_t addr, uintptr_t len)
+{
+	uintptr_t addr1 = addr & PAGEMASK;
+	uintptr_t len1 = len + (addr & PAGEOFFSET);
+
+	return (mlock((void *)addr1, (size_t)len1) ? -errno : 0);
+}
+
+long
+lx_mlockall(uintptr_t flags)
+{
+	return (mlockall(flags) ? -errno : 0);
+}
+
+long
+lx_munlock(uintptr_t addr, uintptr_t len)
+{
+	uintptr_t addr1 = addr & PAGEMASK;
+	uintptr_t len1 = len + (addr & PAGEOFFSET);
+
+	return (munlock((void *)addr1, (size_t)len1) ? -errno : 0);
+}
+
+long
+lx_munlockall(void)
+{
+	return (munlockall() ? -errno : 0);
+}
+
+long
+lx_msync(uintptr_t addr, uintptr_t len, uintptr_t flags)
+{
+	return (msync((void *)addr, (size_t)len, flags) ? -errno : 0);
+}
+
+/*
+ * Illumos and Linux overlap on the basic flags, and are disjoint on the rest.
+ * Linux also allows the length to be zero, while Illumos does not.
+ */
+long
+lx_madvise(uintptr_t start, uintptr_t len, uintptr_t advice)
+{
+	int ret;
+
+	if (len == 0)
+		return (0);
+
+	/* approximately similar */
+	if (advice == LX_MADV_REMOVE)
+		advice = MADV_FREE;
+
+	switch (advice) {
+	case MADV_NORMAL:
+	case MADV_RANDOM:
+	case MADV_SEQUENTIAL:
+	case MADV_WILLNEED:
+	case MADV_FREE:
+	case MADV_DONTNEED:
+		if (advice == MADV_DONTNEED) {
+			/*
+			 * On Linux, MADV_DONTNEED implies an immediate purge
+			 * of the specified region.  This is spuriously
+			 * different from (nearly) every other Unix, having
+			 * apparently been done to mimic the semantics on
+			 * Digital Unix (!).  This is bad enough (MADV_FREE
+			 * both has better semantics and results in better
+			 * performance), but it gets worse:  Linux applications
+			 * (and notably, jemalloc) have managed to depend on
+			 * the busted semantics of MADV_DONTNEED on Linux.  We
+			 * implement these semantics via MADV_PURGE -- and
+			 * we translate our advice accordingly.
+			 */
+			advice = MADV_PURGE;
+		}
+
+		ret = madvise((void *)start, len, advice);
+		if (ret == -1) {
+			if (errno == EBUSY) {
+				if (advice != MADV_PURGE)
+					return (-EINVAL);
+
+				/*
+				 * If we got an EBUSY from a MADV_PURGE, we
+				 * will now try again with a MADV_DONTNEED:
+				 * there are conditions (namely, with locked
+				 * mappings that haven't yet been faulted in)
+				 * where MADV_PURGE will fail but MADV_DONTNEED
+				 * will succeed.  If this succeeds, we'll call
+				 * the operation successful; if not, we'll kick
+				 * back EINVAL.
+				 */
+				advice = MADV_DONTNEED;
+
+				if (madvise((void *)start, len, advice) == 0)
+					return (0);
+
+				return (-EINVAL);
+			}
+
+			return (-errno);
+		} else {
+			return (0);
+		}
+
+	/* harmless to pretend these work */
+	case LX_MADV_DONTFORK:
+	case LX_MADV_DOFORK:
+	case LX_MADV_HUGEPAGE:
+	case LX_MADV_NOHUGEPAGE:
+	case LX_MADV_DONTDUMP:
+	case LX_MADV_DODUMP:
+		return (0);
+
+	/* we'll return an error for the rest of the Linux flags */
+	default:
+		return (-EINVAL);
+	}
+}
+
+/*
+ * mprotect() is identical except that we ignore the Linux flags PROT_GROWSDOWN
+ * and PROT_GROWSUP, which have no equivalent on Solaris.
+ */
+#define	LX_PROT_GROWSDOWN	0x01000000
+#define	LX_PROT_GROWSUP		0x02000000
+
+long
+lx_mprotect(uintptr_t start, uintptr_t len, uintptr_t prot)
+{
+	prot &= ~(LX_PROT_GROWSUP | LX_PROT_GROWSDOWN);
+
+	return (mprotect((void *)start, len, prot) ? -errno : 0);
+}
+
+#define	LX_MREMAP_MAYMOVE	1	/* mapping can be moved */
+#define	LX_MREMAP_FIXED		2	/* address is fixed */
+
+/*
+ * Unfortunately, the Linux mremap() manpage contains a statement that is, at
+ * best, grossly oversimplified: that mremap() "can be used to implement a
+ * very efficient realloc(3)."  To the degree this is true at all, it is only
+ * true narrowly (namely, when large buffers are being expanded but can't be
+ * expanded in place due to virtual address space restrictions) -- but
+ * apparently, someone took this very literally, because variants of glibc
+ * appear to simply implement realloc() in terms of mremap().  This is
+ * unfortunate because absent intelligent usage, it forces realloc() to have
+ * an unncessary interaction with the VM system for small expansions -- and if
+ * realloc() is itself abused (e.g., if a consumer repeatedly expands and
+ * contracts the same memory buffer), the net result can be less efficient
+ * than a much more naive realloc() implementation.  And if native Linux is
+ * suboptimal in this case, we are deeply pathological, having not
+ * historically supported mremap() for anonymous mappings at all.  To make
+ * this at least palatable, we not only support remap for anonymous mappings
+ * (see lx_remap_anon(), below), we also cache the metadata associated with
+ * these mappings to save both the reads from /proc and the libmapmalloc
+ * alloc/free.  We implement the anonymous metadata cache with
+ * lx_remap_anoncache, an LRU cache of prmap_t's that correspond to anonymous
+ * segments that have been resized.
+ */
+#define	LX_REMAP_ANONCACHE_NENTRIES	4
+
+static prmap_t	lx_remap_anoncache[LX_REMAP_ANONCACHE_NENTRIES];
+static int	lx_remap_anoncache_nentries = LX_REMAP_ANONCACHE_NENTRIES;
+static offset_t	lx_remap_anoncache_generation;
+static mutex_t	lx_remap_anoncache_lock = ERRORCHECKMUTEX;
+
+static void
+lx_remap_anoncache_invalidate(uintptr_t addr, size_t size)
+{
+	int i;
+
+	if (lx_remap_anoncache_generation == 0)
+		return;
+
+	mutex_enter(&lx_remap_anoncache_lock);
+
+	for (i = 0; i < LX_REMAP_ANONCACHE_NENTRIES; i++) {
+		prmap_t *map = &lx_remap_anoncache[i];
+
+		/*
+		 * If the ranges overlap at all, we zap it by clearing the
+		 * pr_vaddr.
+		 */
+		if (addr < map->pr_vaddr + map->pr_size &&
+		    map->pr_vaddr < addr + size) {
+			map->pr_vaddr = 0;
+		}
+	}
+
+	mutex_exit(&lx_remap_anoncache_lock);
+}
+
+static void
+lx_remap_anoncache_evict(prmap_t *map)
+{
+	if (map >= &lx_remap_anoncache[0] &&
+	    map < &lx_remap_anoncache[LX_REMAP_ANONCACHE_NENTRIES]) {
+		/*
+		 * We're already in the cache; we just need to zap our pr_vaddr
+		 * to indicate that this has been evicted.
+		 */
+		map->pr_vaddr = 0;
+	} else {
+		/*
+		 * We need to invalidate this by address and size.
+		 */
+		lx_remap_anoncache_invalidate(map->pr_vaddr, map->pr_size);
+	}
+}
+
+static void
+lx_remap_anoncache_load(prmap_t *map, size_t size)
+{
+	offset_t oldest = 0;
+	prmap_t *evict = NULL;
+	int i;
+
+	if (map >= &lx_remap_anoncache[0] &&
+	    map < &lx_remap_anoncache[LX_REMAP_ANONCACHE_NENTRIES]) {
+		/*
+		 * We're already in the cache -- we just need to update
+		 * our LRU field (pr_offset) to reflect the hit.
+		 */
+		map->pr_offset = lx_remap_anoncache_generation++;
+		map->pr_size = size;
+		return;
+	}
+
+	mutex_enter(&lx_remap_anoncache_lock);
+
+	for (i = 0; i < lx_remap_anoncache_nentries; i++) {
+		if (lx_remap_anoncache[i].pr_vaddr == 0) {
+			evict = &lx_remap_anoncache[i];
+			break;
+		}
+
+		if (oldest == 0 || lx_remap_anoncache[i].pr_offset < oldest) {
+			oldest = lx_remap_anoncache[i].pr_offset;
+			evict = &lx_remap_anoncache[i];
+		}
+	}
+
+	if (evict != NULL) {
+		*evict = *map;
+		evict->pr_offset = lx_remap_anoncache_generation++;
+		evict->pr_size = size;
+	}
+
+	mutex_exit(&lx_remap_anoncache_lock);
+}
+
+/*
+ * As part of lx_remap() (see below) and to accommodate heavy realloc() use
+ * cases (see the discussion of the lx_remap_anoncache, above), we allow
+ * anonymous segments to be "remapped" in that we are willing to truncate them
+ * or append to them (as much as that's allowed by virtual address space
+ * usage).  If we fall out of these cases, we take the more expensive option
+ * of actually copying the data to a new segment -- but we locate the address
+ * in a portion of the address space that should give us plenty of VA space to
+ * expand.
+ */
+static long
+lx_remap_anon(prmap_t *map, prmap_t *maps, int nmap,
+    uintptr_t new_size, uintptr_t flags, uintptr_t new_address)
+{
+	int mflags = MAP_ANON;
+	int prot = 0, i;
+	void *addr, *hint = NULL;
+
+	/*
+	 * If our new size is less than our old size and we're either not
+	 * being ordered to move it or the address we're being ordered to
+	 * move it to is our current address, we can just act as Procrustes
+	 * and chop off anything larger than the new size.
+	 */
+	if (new_size < map->pr_size && (!(flags & LX_MREMAP_FIXED) ||
+	    new_address == map->pr_vaddr)) {
+		if (munmap((void *)(map->pr_vaddr + new_size),
+		    map->pr_size - new_size) != 0) {
+			return (-EINVAL);
+		}
+
+		lx_remap_anoncache_load(map, new_size);
+
+		return (map->pr_vaddr);
+	}
+
+	if (map->pr_mflags & (MA_SHM | MA_ISM))
+		return (-EINVAL);
+
+	if (map->pr_mflags & MA_WRITE)
+		prot |= PROT_WRITE;
+
+	if (map->pr_mflags & MA_READ)
+		prot |= PROT_READ;
+
+	if (map->pr_mflags & MA_EXEC)
+		prot |= PROT_EXEC;
+
+	mflags |= (map->pr_mflags & MA_SHARED) ? MAP_SHARED : MAP_PRIVATE;
+
+	if (map->pr_mflags & MA_NORESERVE)
+		mflags |= MAP_NORESERVE;
+
+	/*
+	 * If we're not being told where to move it, or the address matches
+	 * where we already are, let's try to expand our mapping in place
+	 * by adding a fixed mapping after it.
+	 */
+	if (!(flags & LX_MREMAP_FIXED) || new_address == map->pr_vaddr) {
+		addr = mmap((void *)(map->pr_vaddr + map->pr_size),
+		    new_size - map->pr_size, prot, mflags, -1, 0);
+
+		if (addr == (void *)-1)
+			return (-EINVAL);
+
+		if (addr == (void *)(map->pr_vaddr + map->pr_size)) {
+			lx_remap_anoncache_load(map, new_size);
+			return (map->pr_vaddr);
+		}
+
+		/*
+		 * Our advisory address was not followed -- which, as a
+		 * practical matter, means that the range conflicted with an
+		 * extant mapping.  Unmap wherever we landed, and drop into
+		 * the relocation case.
+		 */
+		(void) munmap(addr, new_size - map->pr_size);
+	}
+
+	lx_remap_anoncache_evict(map);
+
+	/*
+	 * If we're here, we actually need to move this mapping -- so if we
+	 * can't move it, we're done.
+	 */
+	if (!(flags & LX_MREMAP_MAYMOVE))
+		return (-ENOMEM);
+
+	/*
+	 * If this is a shared private mapping, we can't remap it.
+	 */
+	if (map->pr_mflags & MA_SHARED)
+		return (-EINVAL);
+
+	if (new_address != NULL && (flags & LX_MREMAP_FIXED)) {
+		mflags |= MAP_FIXED;
+		hint = (void *)new_address;
+	} else {
+		/*
+		 * We're going to start at the bottom of the address space;
+		 * once we hit an address above 2G, we'll treat that as the
+		 * bottom of the top of the address space, and set our address
+		 * hint below that.  To give ourselves plenty of room for
+		 * further mremap() expansion, we'll multiply our new size by
+		 * 16 and leave that much room between our lowest high address
+		 * and our hint.
+		 */
+		for (i = 0; i < nmap; i++) {
+			if (maps[i].pr_vaddr < TWO_GB)
+				continue;
+
+			hint = (void *)(maps[i].pr_vaddr - (new_size << 4UL));
+			break;
+		}
+	}
+
+	if ((addr = mmap(hint, new_size, prot, mflags, -1, 0)) == (void *)-1)
+		return (-errno);
+
+	bcopy((void *)map->pr_vaddr, addr, map->pr_size);
+	(void) munmap((void *)map->pr_vaddr, map->pr_size);
+
+	return ((long)addr);
+}
+
+/*
+ * We don't have a native mremap() (and nor do we particularly want one), so
+ * we emulate it strictly in user-land.  The idea is simple: we just want to
+ * mmap() the underlying object with the new size and rip down the old mapping.
+ * However, this is problematic because we don't actually have the file
+ * descriptor that corresponds to the resized mapping (and indeed, the mapped
+ * file may not exist in any file system name space).  So to get a file
+ * descriptor, we find the (or rather, a) path to the mapped object via its
+ * entry in /proc/self/path and attempt to open it.  Assuming that this
+ * succeeds, we then mmap() it and rip down the original mapping.  There are
+ * clearly many reasons why this might fail; absent a more apt errno (e.g.,
+ * ENOMEM in some cases), we return EINVAL to denote these cases.
+ */
+long
+lx_remap(uintptr_t old_address, uintptr_t old_size,
+    uintptr_t new_size, uintptr_t flags, uintptr_t new_address)
+{
+	int prot = 0, oflags, mflags = 0, len, fd = -1, i, nmap;
+	prmap_t *map = NULL, *maps;
+	long rval;
+	char path[256], buf[MAXPATHLEN + 1];
+	struct stat st;
+	ssize_t n;
+	static uintptr_t pagesize = 0;
+
+	if (pagesize == 0)
+		pagesize = sysconf(_SC_PAGESIZE);
+
+	if ((flags & (LX_MREMAP_MAYMOVE | LX_MREMAP_FIXED)) == LX_MREMAP_FIXED)
+		return (-EINVAL);
+
+	if (old_address & (pagesize - 1))
+		return (-EINVAL);
+
+	if (new_size == 0)
+		return (-EINVAL);
+
+	if ((flags & LX_MREMAP_FIXED) && (new_address & (pagesize - 1)))
+		return (-EINVAL);
+
+	if (new_size == old_size && !(flags & LX_MREMAP_FIXED))
+		return (old_address);
+
+	/*
+	 * First consult the anoncache; if we find the segment there, we'll
+	 * drop straight into lx_remap_anon() and save ourself the pain of
+	 * the /proc reads.
+	 */
+	mutex_enter(&lx_remap_anoncache_lock);
+
+	for (i = 0; i < lx_remap_anoncache_nentries; i++) {
+		map = &lx_remap_anoncache[i];
+
+		if (map->pr_vaddr != old_address)
+			continue;
+
+		if (map->pr_size != old_size)
+			continue;
+
+		if (lx_remap_anon(map, NULL,
+		    0, new_size, 0, new_address) == old_address) {
+			mutex_exit(&lx_remap_anoncache_lock);
+			return (old_address);
+		}
+
+		break;
+	}
+
+	mutex_exit(&lx_remap_anoncache_lock);
+
+	/*
+	 * We need to search the mappings to find our specified mapping.  Note
+	 * that to perform this search, we use /proc/self/rmap instead of
+	 * /proc/self/map.  This is to accommodate the case where an mmap()'d
+	 * and then ftruncate()'d file is being mremap()'d:  rmap will report
+	 * the size of the mapping (which we need to validate old_size) where
+	 * map will report the smaller of the size of the mapping and the
+	 * size of the object.  (The "r" in "rmap" denotes "reserved".)
+	 */
+	if ((fd = open("/native/proc/self/rmap", O_RDONLY)) == -1 ||
+	    fstat(fd, &st) != 0) {
+		if (fd >= 0) {
+			(void) close(fd);
+		}
+		return (-EINVAL);
+	}
+
+	/*
+	 * Determine the number of mappings we need to read and allocate
+	 * a buffer:
+	 */
+	nmap = st.st_size / sizeof (prmap_t);
+	if ((maps = malloc((nmap + 1) * sizeof (prmap_t))) == NULL) {
+		(void) close(fd);
+		return (-EINVAL);
+	}
+
+	/*
+	 * Read mappings from the kernel and determine how many complete
+	 * mappings were read:
+	 */
+	if ((n = read(fd, maps, (nmap + 1) * sizeof (prmap_t))) < 0) {
+		lx_debug("\rread of /proc/self/map failed: %s",
+		    strerror(errno));
+		(void) close(fd);
+		rval = -EINVAL;
+		goto out;
+	}
+	(void) close(fd);
+
+	nmap = n / sizeof (prmap_t);
+	lx_debug("\tfound %d mappings", nmap);
+
+	/*
+	 * Check if any mappings match our arguments:
+	 */
+	for (i = 0; i < nmap; i++) {
+		if (maps[i].pr_vaddr == old_address &&
+		    maps[i].pr_size == old_size) {
+			map = &maps[i];
+			break;
+		}
+
+		if (maps[i].pr_vaddr <= old_address &&
+		    old_address + old_size < maps[i].pr_vaddr +
+		    maps[i].pr_size) {
+			/*
+			 * We have a mismatch, but our specified range is
+			 * a subset of the actual segment; this is EINVAL.
+			 */
+			rval = -EINVAL;
+			goto out;
+		}
+	}
+
+	if (i == nmap) {
+		lx_debug("\tno matching mapping found");
+		rval = -EFAULT;
+		goto out;
+	}
+
+	if (map->pr_mflags & (MA_ISM | MA_SHM)) {
+		/*
+		 * If this is either ISM or System V shared memory, we're not
+		 * going to remap it.
+		 */
+		rval = -EINVAL;
+		goto out;
+	}
+
+	oflags = (map->pr_mflags & MA_WRITE) ? O_RDWR : O_RDONLY;
+
+	if (map->pr_mflags & MA_ANON) {
+		/*
+		 * This is an anonymous mapping -- which is the one case in
+		 * which we perform something that approaches a true remap.
+		 */
+		rval = lx_remap_anon(map, maps, nmap,
+		    new_size, flags, new_address);
+		goto out;
+	}
+
+	if (!(flags & LX_MREMAP_MAYMOVE)) {
+		/*
+		 * If we're not allowed to move this mapping, we're going to
+		 * act as if we can't expand it.
+		 */
+		rval = -ENOMEM;
+		goto out;
+	}
+
+	if (!(map->pr_mflags & MA_SHARED)) {
+		/*
+		 * If this is a private mapping, we're not going to remap it.
+		 */
+		rval = -EINVAL;
+		goto out;
+	}
+
+	(void) snprintf(path, sizeof (path),
+	    "/native/proc/self/path/%s", map->pr_mapname);
+
+	if ((len = readlink(path, buf, sizeof (buf))) == -1 ||
+	    len == sizeof (buf)) {
+		/*
+		 * If we failed to read the link, the path might not exist.
+		 */
+		rval = -EINVAL;
+		goto out;
+	}
+
+	buf[len] = '\0';
+
+	if ((fd = open(buf, oflags)) == -1) {
+		/*
+		 * If we failed to open the object, it may be because it's
+		 * not named (i.e., it's anonymous) or because we somehow
+		 * don't have permissions.  Either way, we're going to kick
+		 * it back with EINVAL.
+		 */
+		rval = -EINVAL;
+		goto out;
+	}
+
+	if (map->pr_mflags & MA_WRITE)
+		prot |= PROT_WRITE;
+
+	if (map->pr_mflags & MA_READ)
+		prot |= PROT_READ;
+
+	if (map->pr_mflags & MA_EXEC)
+		prot |= PROT_EXEC;
+
+	mflags = MAP_SHARED;
+
+	if (new_address != NULL && (flags & LX_MREMAP_FIXED)) {
+		mflags |= MAP_FIXED;
+	} else {
+		new_address = NULL;
+	}
+
+	rval = (long)mmap((void *)new_address, new_size,
+	    prot, mflags, fd, map->pr_offset);
+	(void) close(fd);
+
+	if ((void *)rval == MAP_FAILED) {
+		rval = -ENOMEM;
+		goto out;
+	}
+
+	/*
+	 * Our mapping succeeded; we're now going to rip down the old mapping.
+	 */
+	(void) munmap((void *)old_address, old_size);
+out:
+	free(maps);
+	return (rval);
+}
--- /dev/null	Wed Oct  5 00:31:47 2016
+++ new/usr/src/lib/brand/lx/lx_brand/common/misc.c	Wed Oct  5 00:31:47 2016
@@ -0,0 +1,391 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <priv.h>
+#include <strings.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/sysmacros.h>
+#include <sys/systeminfo.h>
+#include <sys/types.h>
+#include <sys/shm.h>
+#include <sys/socket.h>
+#include <sys/inotify.h>
+#include <sys/eventfd.h>
+#include <sys/lx_debug.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_syscall.h>
+#include <sys/lx_fcntl.h>
+
+/*
+ * {get,set}groups16() - Handle the conversion between 16-bit Linux gids and
+ * 32-bit illumos gids.
+ */
+long
+lx_getgroups16(uintptr_t p1, uintptr_t p2)
+{
+	int count = (int)p1;
+	lx_gid16_t *grouplist = (lx_gid16_t *)p2;
+	gid_t *grouplist32;
+	int ret;
+	int i;
+
+	if (count < 0)
+		return (-EINVAL);
+
+	grouplist32 = malloc(count * sizeof (gid_t));
+	if (grouplist32 == NULL && count > 0) {
+		free(grouplist32);
+		return (-ENOMEM);
+	}
+	if ((ret = getgroups(count, grouplist32)) < 0) {
+		free(grouplist32);
+		return (-errno);
+	}
+
+	/* we must not modify the list if the incoming count was 0 */
+	if (count > 0) {
+		for (i = 0; i < ret; i++)
+			grouplist[i] = LX_GID32_TO_GID16(grouplist32[i]);
+	}
+
+	free(grouplist32);
+	return (ret);
+}
+
+long
+lx_setgroups16(uintptr_t p1, uintptr_t p2)
+{
+	long rv;
+	int count = (int)p1;
+	lx_gid16_t *grouplist = NULL;
+	gid_t *grouplist32 = NULL;
+	int i;
+
+	if ((grouplist = malloc(count * sizeof (lx_gid16_t))) == NULL) {
+		return (-ENOMEM);
+	}
+	if (uucopy((void *)p2, grouplist, count * sizeof (lx_gid16_t)) != 0) {
+		free(grouplist);
+		return (-EFAULT);
+	}
+
+	grouplist32 = malloc(count * sizeof (gid_t));
+	if (grouplist32 == NULL) {
+		free(grouplist);
+		return (-ENOMEM);
+	}
+	for (i = 0; i < count; i++)
+		grouplist32[i] = LX_GID16_TO_GID32(grouplist[i]);
+
+	/* order matters here to get the correct errno back */
+	if (count > NGROUPS_MAX_DEFAULT) {
+		free(grouplist);
+		free(grouplist32);
+		return (-EINVAL);
+	}
+
+	rv = setgroups(count, grouplist32);
+
+	free(grouplist);
+	free(grouplist32);
+
+	return (rv != 0 ? -errno : 0);
+}
+
+/*
+ * mknod() - Since we don't have the SYS_CONFIG privilege within a zone, the
+ * only mode we have to support is S_IFIFO.  We also have to distinguish between
+ * an invalid type and insufficient privileges.
+ */
+#define	LX_S_IFMT	0170000
+#define	LX_S_IFDIR	0040000
+#define	LX_S_IFCHR	0020000
+#define	LX_S_IFBLK	0060000
+#define	LX_S_IFREG	0100000
+#define	LX_S_IFIFO	0010000
+#define	LX_S_IFLNK	0120000
+#define	LX_S_IFSOCK	0140000
+
+/*ARGSUSED*/
+long
+lx_mknod(uintptr_t p1, uintptr_t p2, uintptr_t p3)
+{
+	char *path = (char *)p1;
+	lx_dev_t lx_dev = (lx_dev_t)p3;
+	struct sockaddr_un sockaddr;
+	struct stat statbuf;
+	mode_t mode, type;
+	dev_t dev;
+	int fd;
+
+	type = ((mode_t)p2 & LX_S_IFMT);
+	mode = ((mode_t)p2 & 07777);
+
+	switch (type) {
+	case 0:
+	case LX_S_IFREG:
+		/* create a regular file */
+		if (stat(path, &statbuf) == 0)
+			return (-EEXIST);
+
+		if (errno != ENOENT)
+			return (-errno);
+
+		if ((fd = creat(path, mode)) < 0)
+			return (-errno);
+
+		(void) close(fd);
+		return (0);
+
+	case LX_S_IFSOCK:
+		/*
+		 * Create a UNIX domain socket.
+		 *
+		 * Most programmers aren't even aware you can do this.
+		 *
+		 * Note you can also do this via illumos' mknod(2), but
+		 * Linux allows anyone who can create a UNIX domain
+		 * socket via bind(2) to create one via mknod(2);
+		 * illumos requires the caller to be privileged.
+		 */
+		if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
+			return (-errno);
+
+		if (stat(path, &statbuf) == 0)
+			return (-EEXIST);
+
+		if (errno != ENOENT)
+			return (-errno);
+
+		if (uucopy(path, &sockaddr.sun_path,
+		    sizeof (sockaddr.sun_path)) < 0)
+			return (-errno);
+
+		/* assure NULL termination of sockaddr.sun_path */
+		sockaddr.sun_path[sizeof (sockaddr.sun_path) - 1] = '\0';
+		sockaddr.sun_family = AF_UNIX;
+
+		if (bind(fd, (struct sockaddr *)&sockaddr,
+		    strlen(sockaddr.sun_path) +
+		    sizeof (sockaddr.sun_family)) < 0)
+			return (-errno);
+
+		(void) close(fd);
+		return (0);
+
+	case LX_S_IFIFO:
+		dev = 0;
+		break;
+
+	case LX_S_IFCHR:
+	case LX_S_IFBLK:
+		/*
+		 * The "dev" RPM package wants to create all possible Linux
+		 * device nodes, so just report its mknod()s as having
+		 * succeeded if we're in install mode.
+		 */
+		if (lx_install != 0) {
+			lx_debug("lx_mknod: install mode spoofed creation of "
+			    "Linux device [%lld, %lld]\n",
+			    LX_GETMAJOR(lx_dev), LX_GETMINOR(lx_dev));
+
+			return (0);
+		}
+
+		dev = makedevice(LX_GETMAJOR(lx_dev), LX_GETMINOR(lx_dev));
+		break;
+
+	default:
+		return (-EINVAL);
+	}
+
+	return (mknod(path, mode | type, dev) ? -errno : 0);
+}
+
+long
+lx_execve(uintptr_t p1, uintptr_t p2, uintptr_t p3)
+{
+	char *filename = (char *)p1;
+	char **argv = (char **)p2;
+	char **envp = (char **)p3;
+	char *nullist[] = { NULL };
+
+	if (argv == NULL)
+		argv = nullist;
+
+	/*
+	 * Emulate PR_SET_KEEPCAPS which is reset on execve. If this is not done
+	 * the emulated capabilities could be reduced more than expected.
+	 */
+	(void) setpflags(PRIV_AWARE_RESET, 1);
+
+	/* This is a normal exec call. */
+	(void) execve(filename, argv, envp);
+
+	return (-errno);
+}
+
+long
+lx_setgroups(uintptr_t p1, uintptr_t p2)
+{
+	int ng = (int)p1;
+	gid_t *glist = NULL;
+	int i, r;
+
+	lx_debug("\tlx_setgroups(%d, 0x%p", ng, p2);
+
+	if (ng > 0) {
+		if ((glist = (gid_t *)malloc(ng * sizeof (gid_t))) == NULL)
+			return (-ENOMEM);
+
+		if (uucopy((void *)p2, glist, ng * sizeof (gid_t)) != 0) {
+			free(glist);
+			return (-errno);
+		}
+
+		/*
+		 * Linux doesn't check the validity of the group IDs, but
+		 * illumos does. Change any invalid group IDs to a known, valid
+		 * value (yuck).
+		 */
+		for (i = 0; i < ng; i++) {
+			if (glist[i] > MAXUID)
+				glist[i] = MAXUID;
+		}
+	}
+
+	/* order matters here to get the correct errno back */
+	if (ng > NGROUPS_MAX_DEFAULT) {
+		free(glist);
+		return (-EINVAL);
+	}
+
+	r = syscall(SYS_brand, B_HELPER_SETGROUPS, ng, glist);
+
+	free(glist);
+	return ((r == -1) ? -errno : r);
+}
+
+long
+lx_close(int fildes)
+{
+	int r;
+
+	r = close(fildes);
+	return ((r == -1) ? -errno : r);
+}
+
+long
+lx_getgroups(int gidsetsize, gid_t *grouplist)
+{
+	int r;
+
+	r = getgroups(gidsetsize, grouplist);
+	return ((r == -1) ? -errno : r);
+}
+
+long
+lx_inotify_add_watch(int fd, const char *pathname, uint32_t mask)
+{
+	int r;
+
+	r = inotify_add_watch(fd, pathname, mask);
+	return ((r == -1) ? -errno : r);
+}
+
+long
+lx_inotify_init(void)
+{
+	int r;
+
+	r = inotify_init();
+	return ((r == -1) ? -errno : r);
+}
+
+long
+lx_inotify_init1(int flags)
+{
+	int r;
+
+	r = inotify_init1(flags);
+	return ((r == -1) ? -errno : r);
+}
+
+long
+lx_inotify_rm_watch(int fd, int wd)
+{
+	int r;
+
+	r = inotify_rm_watch(fd, wd);
+	return ((r == -1) ? -errno : r);
+}
+
+long
+lx_shmdt(char *shmaddr)
+{
+	int r;
+
+	r = shmdt(shmaddr);
+	return ((r == -1) ? -errno : r);
+}
+
+long
+lx_utimes(const char *path, const struct timeval times[2])
+{
+	int r;
+
+	r = utimes(path, times);
+	return ((r == -1) ? -errno : r);
+}
+
+long
+lx_eventfd(unsigned int initval)
+{
+	return (lx_eventfd2(initval, 0));
+}
+
+long
+lx_eventfd2(unsigned int initval, int flags)
+{
+	int r = eventfd(initval, flags);
+
+	/*
+	 * eventfd(3C) may fail with ENOENT if /dev/eventfd is not available.
+	 * It is less jarring to Linux programs to tell them that the system
+	 * call is not supported than to report an error number they are not
+	 * expecting.
+	 */
+	if (r == -1 && errno == ENOENT)
+		return (-ENOTSUP);
+
+	return (r == -1 ? -errno : r);
+}
--- /dev/null	Wed Oct  5 00:31:48 2016
+++ new/usr/src/lib/brand/lx/lx_brand/common/module.c	Wed Oct  5 00:31:48 2016
@@ -0,0 +1,90 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2014 Joyent, Inc.  All rights reserved.
+ */
+
+/*
+ * We don't support Linux modules, but we have to emulate enough of the system
+ * calls to show that we don't have any modules installed.
+ */
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_syscall.h>
+
+/*
+ * For query_module(), we provide an empty list of modules, and return ENOENT
+ * on any request for a specific module.
+ */
+#define	LX_QM_MODULES	1
+#define	LX_QM_DEPS	2
+#define	LX_QM_REFS	3
+#define	LX_QM_SYMBOLS	4
+#define	LX_QM_INFO	5
+
+/*ARGSUSED*/
+long
+lx_query_module(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4,
+    uintptr_t p5)
+{
+	/*
+	 * parameter p1 is the 'name' argument.
+	 */
+	int which = (int)p2;
+	char *buf = (char *)p3;
+	size_t bufsize = (size_t)p4;
+	size_t *ret = (size_t *)p5;
+
+	switch (which) {
+	case 0:
+		/*
+		 * Special case: always return 0
+		 */
+		return (0);
+
+	case LX_QM_MODULES:
+		/*
+		 * Generate an empty list of modules.
+		 */
+		if (bufsize && buf)
+			buf[0] = '\0';
+		if (ret)
+			*ret = 0;
+		return (0);
+
+	case LX_QM_DEPS:
+	case LX_QM_REFS:
+	case LX_QM_SYMBOLS:
+	case LX_QM_INFO:
+		/*
+		 * Any requests for specific module information return ENOENT.
+		 */
+		return (-ENOENT);
+
+	default:
+		return (-EINVAL);
+	}
+}
--- /dev/null	Wed Oct  5 00:31:48 2016
+++ new/usr/src/lib/brand/lx/lx_brand/common/mount.c	Wed Oct  5 00:31:48 2016
@@ -0,0 +1,540 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <strings.h>
+#include <nfs/mount.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <sys/lx_debug.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_syscall.h>
+#include <sys/lx_mount.h>
+
+/*
+ * support definitions
+ */
+union fh_buffer {
+	struct nfs_fid	fh2;
+	struct nfs_fh3	fh3;
+	char		fh_data[NFS3_FHSIZE + 2];
+};
+
+static int
+i_add_option(char *option, char *buf, size_t buf_size)
+{
+	char *fmt_str = NULL;
+
+	assert((option != NULL) && (strlen(option) > 0));
+	assert((buf != NULL) && (buf_size > 0));
+
+	if (buf[0] == '\0') {
+		fmt_str = "%s";
+	} else {
+		fmt_str = ",%s";
+	}
+
+	buf_size -= strlen(buf);
+	buf += strlen(buf);
+
+	/*LINTED*/
+	if (snprintf(buf, buf_size, fmt_str, option) > (buf_size - 1))
+		return (-EOVERFLOW);
+	return (0);
+}
+
+static int
+i_add_option_int(char *option, int val, char *buf, size_t buf_size)
+{
+	char *fmt_str = NULL;
+
+	assert((option != NULL) && (strlen(option) > 0));
+	assert((buf != NULL) && (buf_size > 0));
+
+	if (buf[0] == '\0') {
+		fmt_str = "%s=%d";
+	} else {
+		fmt_str = ",%s=%d";
+	}
+
+	buf_size -= strlen(buf);
+	buf += strlen(buf);
+
+	/*LINTED*/
+	if (snprintf(buf, buf_size, fmt_str, option, val) > (buf_size - 1))
+		return (-EOVERFLOW);
+	return (0);
+}
+
+static int
+i_make_nfs_args(lx_nfs_mount_data_t *lx_nmd, struct nfs_args *nfs_args,
+    struct netbuf *nfs_args_addr, struct knetconfig *nfs_args_knconf,
+    union fh_buffer *nfs_args_fh, struct sec_data *nfs_args_secdata,
+    char *fstype, char *options, int options_size)
+{
+	struct stat	statbuf;
+	int		i, rv, use_tcp;
+
+	/* Sanity check the incomming Linux request. */
+	if ((lx_nmd->nmd_rsize < 0) || (lx_nmd->nmd_wsize < 0) ||
+	    (lx_nmd->nmd_timeo < 0) || (lx_nmd->nmd_retrans < 0) ||
+	    (lx_nmd->nmd_acregmin < 0) || (lx_nmd->nmd_acregmax < 0) ||
+	    (lx_nmd->nmd_acdirmax < 0)) {
+		return (-EINVAL);
+	}
+
+	/*
+	 * Additional sanity checks of incomming request.
+	 *
+	 * Some of the sanity checks below should probably return
+	 * EINVAL (or some other error code) instead or ENOTSUP,
+	 * but without experiminting on Linux to see how it
+	 * deals with certain strange values there is no way
+	 * to really know what we should return, hence we return
+	 * ENOTSUP to tell us that eventually if we see some
+	 * application hitting the problem we can go to a real
+	 * Linux system, figure out how it deals with the situation
+	 * and update our code to handle it in the same fashion.
+	 */
+	if (lx_nmd->nmd_version != 4) {
+		lx_unsupported("unsupported nfs mount request, "
+		    "unrecognized NFS mount structure: %d\n",
+		    lx_nmd->nmd_version);
+		return (-ENOTSUP);
+	}
+	if ((lx_nmd->nmd_flags & ~LX_NFS_MOUNT_SUPPORTED) != 0) {
+		lx_unsupported("unsupported nfs mount request, "
+		    "flags: 0x%x\n", lx_nmd->nmd_flags);
+		return (-ENOTSUP);
+	}
+	if (lx_nmd->nmd_addr.sin_family != AF_INET) {
+		lx_unsupported("unsupported nfs mount request, "
+		    "transport address family: 0x%x\n",
+		    lx_nmd->nmd_addr.sin_family);
+		return (-ENOTSUP);
+	}
+	for (i = 0; i < LX_NMD_MAXHOSTNAMELEN; i++) {
+		if (lx_nmd->nmd_hostname[i] == '\0')
+			break;
+	}
+	if (i == 0) {
+		lx_unsupported("unsupported nfs mount request, "
+		    "no hostname specified\n");
+		return (-ENOTSUP);
+	}
+	if (i == LX_NMD_MAXHOSTNAMELEN) {
+		lx_unsupported("unsupported nfs mount request, "
+		    "hostname not terminated\n");
+		return (-ENOTSUP);
+	}
+	if (lx_nmd->nmd_namlen < i) {
+		lx_unsupported("unsupported nfs mount request, "
+		    "invalid namlen value: 0x%x\n", lx_nmd->nmd_namlen);
+		return (-ENOTSUP);
+	}
+	if (lx_nmd->nmd_bsize != 0) {
+		lx_unsupported("unsupported nfs mount request, "
+		    "bsize value: 0x%x\n", lx_nmd->nmd_bsize);
+		return (-ENOTSUP);
+	}
+
+	/* Initialize and clear the output structure pointers passed in. */
+	bzero(nfs_args, sizeof (*nfs_args));
+	bzero(nfs_args_addr, sizeof (*nfs_args_addr));
+	bzero(nfs_args_knconf, sizeof (*nfs_args_knconf));
+	bzero(nfs_args_fh, sizeof (*nfs_args_fh));
+	bzero(nfs_args_secdata, sizeof (*nfs_args_secdata));
+	nfs_args->addr = nfs_args_addr;
+	nfs_args->knconf = nfs_args_knconf;
+	nfs_args->fh = (caddr_t)nfs_args_fh;
+	nfs_args->nfs_ext_u.nfs_extB.secdata = nfs_args_secdata;
+
+	/* Check if we're using tcp. */
+	use_tcp = (lx_nmd->nmd_flags & LX_NFS_MOUNT_TCP) ? 1 : 0;
+
+	/*
+	 * These seem to be the default flags used by Solaris for v2 and v3
+	 * nfs mounts.
+	 *
+	 * Don't bother with NFSMNT_TRYRDMA since we always specify a
+	 * transport (either udp or tcp).
+	 */
+	nfs_args->flags = NFSMNT_NEWARGS | NFSMNT_KNCONF | NFSMNT_INT |
+	    NFSMNT_HOSTNAME;
+
+	/* Translate some Linux mount flags into Solaris mount flags. */
+	if (lx_nmd->nmd_flags & LX_NFS_MOUNT_SOFT)
+		nfs_args->flags |= NFSMNT_SOFT;
+	if (lx_nmd->nmd_flags & LX_NFS_MOUNT_INTR)
+		nfs_args->flags |= NFSMNT_INT;
+	if (lx_nmd->nmd_flags & LX_NFS_MOUNT_POSIX)
+		nfs_args->flags |= NFSMNT_POSIX;
+	if (lx_nmd->nmd_flags & LX_NFS_MOUNT_NOCTO)
+		nfs_args->flags |= NFSMNT_NOCTO;
+	if (lx_nmd->nmd_flags & LX_NFS_MOUNT_NOAC)
+		nfs_args->flags |= NFSMNT_NOAC;
+	if (lx_nmd->nmd_flags & LX_NFS_MOUNT_NONLM)
+		nfs_args->flags |= NFSMNT_LLOCK;
+
+	if ((lx_nmd->nmd_flags & LX_NFS_MOUNT_VER3) != 0) {
+		(void) strcpy(fstype, "nfs3");
+		if ((rv = i_add_option_int("vers", 3,
+		    options, options_size)) != 0)
+			return (rv);
+
+		if (lx_nmd->nmd_root.lx_fh3_length >
+		    sizeof (nfs_args_fh->fh3.fh3_u.data)) {
+			lx_unsupported("unsupported nfs mount request, "
+			    "nfs file handle length: 0x%x\n",
+			    lx_nmd->nmd_root.lx_fh3_length);
+			return (-ENOTSUP);
+		}
+
+		/* Set the v3 file handle info. */
+		nfs_args_fh->fh3.fh3_length = lx_nmd->nmd_root.lx_fh3_length;
+		bcopy(&lx_nmd->nmd_root.lx_fh3_data,
+		    nfs_args_fh->fh3.fh3_u.data,
+		    lx_nmd->nmd_root.lx_fh3_length);
+	} else {
+		/*
+		 * Assume nfs v2.  Note that this could also be a v1
+		 * mount request but there doesn't seem to be any difference
+		 * in the parameters passed to the Linux mount system
+		 * call for v1 or v2 mounts so there is no way of really
+		 * knowing.
+		 */
+		(void) strcpy(fstype, "nfs");
+		if ((rv = i_add_option_int("vers", 2,
+		    options, options_size)) != 0)
+			return (rv);
+
+		/* Solaris seems to add this flag when using v2. */
+		nfs_args->flags |= NFSMNT_SECDEFAULT;
+
+		/* Set the v2 file handle info. */
+		bcopy(&lx_nmd->nmd_old_root,
+		    nfs_args_fh, sizeof (nfs_args_fh->fh2));
+	}
+
+	/*
+	 * We can't use getnetconfig() here because there is no netconfig
+	 * database in linux.
+	 */
+	nfs_args_knconf->knc_protofmly = "inet";
+	if (use_tcp) {
+		/*
+		 * TCP uses NC_TPI_COTS_ORD semantics.
+		 * See /etc/netconfig.
+		 */
+		nfs_args_knconf->knc_semantics = NC_TPI_COTS_ORD;
+		nfs_args_knconf->knc_proto = "tcp";
+		if ((rv = i_add_option("proto=tcp",
+		    options, options_size)) != 0)
+			return (rv);
+		if (stat("/dev/tcp", &statbuf) != 0)
+			return (-errno);
+		nfs_args_knconf->knc_rdev = statbuf.st_rdev;
+	} else {
+		/*
+		 * Assume UDP.  UDP uses NC_TPI_CLTS semantics.
+		 * See /etc/netconfig.
+		 */
+		nfs_args_knconf->knc_semantics = NC_TPI_CLTS;
+		nfs_args_knconf->knc_proto = "udp";
+		if ((rv = i_add_option("proto=udp",
+		    options, options_size)) != 0)
+			return (rv);
+		if (stat("/dev/udp", &statbuf) != 0)
+			return (-errno);
+		nfs_args_knconf->knc_rdev = statbuf.st_rdev;
+	}
+
+	/* Set the server address. */
+	nfs_args_addr->maxlen = nfs_args_addr->len =
+	    sizeof (struct sockaddr_in);
+	nfs_args_addr->buf = (char *)&lx_nmd->nmd_addr;
+
+	/* Set the server hostname string. */
+	nfs_args->hostname = lx_nmd->nmd_hostname;
+
+	/* Translate Linux nfs mount parameters into Solaris mount options. */
+	if (lx_nmd->nmd_rsize != LX_NMD_DEFAULT_RSIZE) {
+		if ((rv = i_add_option_int("rsize", lx_nmd->nmd_rsize,
+		    options, options_size)) != 0)
+			return (rv);
+		nfs_args->rsize = lx_nmd->nmd_rsize;
+		nfs_args->flags |= NFSMNT_RSIZE;
+	}
+	if (lx_nmd->nmd_wsize != LX_NMD_DEFAULT_WSIZE) {
+		if ((rv = i_add_option_int("wsize", lx_nmd->nmd_wsize,
+		    options, options_size)) != 0)
+			return (rv);
+		nfs_args->wsize = lx_nmd->nmd_wsize;
+		nfs_args->flags |= NFSMNT_WSIZE;
+	}
+	if ((rv = i_add_option_int("timeo", lx_nmd->nmd_timeo,
+	    options, options_size)) != 0)
+		return (rv);
+	nfs_args->timeo = lx_nmd->nmd_timeo;
+	nfs_args->flags |= NFSMNT_TIMEO;
+	if ((rv = i_add_option_int("retrans", lx_nmd->nmd_retrans,
+	    options, options_size)) != 0)
+		return (rv);
+	nfs_args->retrans = lx_nmd->nmd_retrans;
+	nfs_args->flags |= NFSMNT_RETRANS;
+	if ((rv = i_add_option_int("acregmin", lx_nmd->nmd_acregmin,
+	    options, options_size)) != 0)
+		return (rv);
+	nfs_args->acregmin = lx_nmd->nmd_acregmin;
+	nfs_args->flags |= NFSMNT_ACREGMIN;
+	if ((rv = i_add_option_int("acregmax", lx_nmd->nmd_acregmax,
+	    options, options_size)) != 0)
+		return (rv);
+	nfs_args->acregmax = lx_nmd->nmd_acregmax;
+	nfs_args->flags |= NFSMNT_ACREGMAX;
+	if ((rv = i_add_option_int("acdirmin", lx_nmd->nmd_acdirmin,
+	    options, options_size)) != 0)
+		return (rv);
+	nfs_args->acdirmin = lx_nmd->nmd_acdirmin;
+	nfs_args->flags |= NFSMNT_ACDIRMIN;
+	if ((rv = i_add_option_int("acdirmax", lx_nmd->nmd_acdirmax,
+	    options, options_size)) != 0)
+		return (rv);
+	nfs_args->acdirmax = lx_nmd->nmd_acdirmax;
+	nfs_args->flags |= NFSMNT_ACDIRMAX;
+
+	/* We only support nfs with a security type of AUTH_SYS. */
+	nfs_args->nfs_args_ext = NFS_ARGS_EXTB;
+	nfs_args_secdata->secmod = AUTH_SYS;
+	nfs_args_secdata->rpcflavor = AUTH_SYS;
+	nfs_args_secdata->flags = 0;
+	nfs_args_secdata->uid = 0;
+	nfs_args_secdata->data = NULL;
+	nfs_args->nfs_ext_u.nfs_extB.next = NULL;
+
+	/*
+	 * The Linux nfs mount command seems to pass an open socket fd
+	 * to the kernel during the mount system call.  We don't need
+	 * this fd on Solaris so just close it.
+	 */
+	(void) close(lx_nmd->nmd_fd);
+
+	return (0);
+}
+
+/*
+ * The user-level mount(2) code is only used to support NFS mounts. All other
+ * fstypes are handled in-kernel.
+ */
+long
+lx_mount(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4,
+    uintptr_t p5)
+{
+	/* Linux input arguments. */
+	const char		*sourcep = (const char *)p1;
+	const char		*targetp = (const char *)p2;
+	const char		*fstypep = (const char *)p3;
+	unsigned int		flags = (unsigned int)p4;
+	const void		*datap = (const void *)p5;
+
+	char			source[MAXPATHLEN + LX_NMD_MAXHOSTNAMELEN + 1];
+	char			target[MAXPATHLEN];
+	char			fstype[8], options[MAX_MNTOPT_STR];
+	int			sflags, rv;
+	long			res;
+
+	lx_nfs_mount_data_t	lx_nmd;
+	struct nfs_args		nfs_args;
+	struct netbuf 		nfs_args_addr;
+	struct knetconfig	nfs_args_knconf;
+	union fh_buffer		nfs_args_fh;
+	struct sec_data		nfs_args_secdata;
+	char			*sdataptr = NULL;
+	int			sdatalen = 0;
+	int			vers;
+
+	/* Initialize illumos mount arguments. */
+	sflags = MS_OPTIONSTR;
+	options[0] = '\0';
+	sdatalen = 0;
+
+	/* Copy in parameters that are always present. */
+	rv = uucopystr((void *)sourcep, &source, sizeof (source));
+	if ((rv == -1) || (rv == sizeof (source)))
+		return (-EFAULT);
+
+	rv = uucopystr((void *)targetp, &target, sizeof (target));
+	if ((rv == -1) || (rv == sizeof (target)))
+		return (-EFAULT);
+
+	rv = uucopystr((void *)fstypep, &fstype, sizeof (fstype));
+	if ((rv == -1) || (rv == sizeof (fstype)))
+		return (-EFAULT);
+
+	lx_debug("\tlinux mount source: %s", source);
+	lx_debug("\tlinux mount target: %s", target);
+	lx_debug("\tlinux mount fstype: %s", fstype);
+
+	/* The in-kernel mount code should only call us for an NFS mount. */
+	assert(strcmp(fstype, "nfs") == 0);
+
+	/*
+	 * While SunOS is picky about mount(2) target paths being absolute,
+	 * Linux is not so strict.  In order to facilitate this looser
+	 * requirement, the cwd is prepended to non-absolute target paths.
+	 */
+	if (target[0] != '/') {
+		char *cpath, *buf = NULL;
+		int len;
+
+		if ((cpath = getcwd(NULL, MAXPATHLEN)) == NULL) {
+			return (-ENOMEM);
+		}
+		len = asprintf(&buf, "%s/%s", cpath, target);
+		free(cpath);
+		if (len < 0) {
+			return (-ENOMEM);
+		} else if (len >= MAXPATHLEN) {
+			free(buf);
+			return (-ENAMETOOLONG);
+		}
+		(void) strlcpy(target, buf, sizeof (target));
+		free(buf);
+	}
+
+	/* Make sure we support the requested mount flags. */
+	if ((flags & ~LX_MS_SUPPORTED) != 0) {
+		lx_unsupported("unsupported mount flags: 0x%x", flags);
+		return (-ENOTSUP);
+	}
+
+	/*
+	 * Copy in Linux mount options. Note that for older Linux kernels
+	 * (pre 2.6.23) the mount options pointer (which normally points to a
+	 * string) points to a structure which is populated by the user-level
+	 * code after it has done the preliminary RPCs (similar to how our NFS
+	 * mount cmd works). For newer kernels the options pointer is just a
+	 * string of options. We're unlikely to actually emulate a kernel that
+	 * uses the old style but support is kept and handled in
+	 * i_make_nfs_args(). The new style handling is implemented in
+	 * lx_nfs_mount(). The user-level mount caller is in charge of
+	 * determining the format in which it passes the data parameter.
+	 */
+	if (datap == NULL)
+		return (-EINVAL);
+	if (uucopy((void *)datap, &vers, sizeof (int)) < 0)
+		return (-errno);
+
+	/*
+	 * As described above, the data parameter might be a versioned lx_nmd
+	 * structure or (most likely on a modern distribution) it is a string.
+	 */
+	if (vers < 1 || vers > 6) {
+		/*
+		 * Handle the modern style with options as a string, make the
+		 * preliminary RPC calls and do the native mount all within
+		 * lx_nfs_mount().
+		 */
+		if (uucopystr((void *)datap, options, sizeof (options)) < 0)
+			return (-errno);
+		return (lx_nfs_mount(source, target, fstype, flags, options));
+	}
+
+	/*
+	 * This is an old style NFS mount call and we only support v4.
+	 */
+	if (vers != 4) {
+		lx_unsupported("unsupported nfs mount request version: %d\n",
+		    vers);
+		return (-ENOTSUP);
+	}
+
+	if (uucopy((void *)datap, &lx_nmd, sizeof (lx_nmd)) < 0)
+		return (-errno);
+
+	/*
+	 * For illumos NFS mounts, the kernel expects a special structure, but
+	 * a pointer to this structure is passed in via an extra parameter
+	 * (sdataptr below.)
+	 */
+	if ((rv = i_make_nfs_args(&lx_nmd, &nfs_args, &nfs_args_addr,
+	    &nfs_args_knconf, &nfs_args_fh, &nfs_args_secdata, fstype, options,
+	    sizeof (options))) != 0)
+		return (rv);
+
+	/*
+	 * For the following old-style NFS mount we need to tell the mount
+	 * system call to expect extra parameters.
+	 */
+	sflags |= MS_DATA;
+	sdataptr = (char *)&nfs_args;
+	sdatalen = sizeof (nfs_args);
+
+	/* Linux seems to always allow overlay mounts */
+	sflags |= MS_OVERLAY;
+
+	/* Convert some Linux flags to illumos flags. */
+	if (flags & LX_MS_RDONLY)
+		sflags |= MS_RDONLY;
+	if (flags & LX_MS_NOSUID)
+		sflags |= MS_NOSUID;
+	if (flags & LX_MS_REMOUNT)
+		sflags |= MS_REMOUNT;
+
+	/*
+	 * Convert some Linux flags to illumos option strings.
+	 */
+	if (flags & LX_MS_STRICTATIME) {
+		/*
+		 * The "strictatime" mount option ensures that none of the
+		 * weaker atime-related mode options are in effect.
+		 */
+		flags &= ~(LX_MS_RELATIME | LX_MS_NOATIME);
+	}
+	if ((flags & LX_MS_NODEV) &&
+	    ((rv = i_add_option("nodev", options, sizeof (options))) != 0))
+		return (rv);
+	if ((flags & LX_MS_NOEXEC) &&
+	    ((rv = i_add_option("noexec", options, sizeof (options))) != 0))
+		return (rv);
+	if ((flags & LX_MS_NOATIME) &&
+	    ((rv = i_add_option("noatime", options, sizeof (options))) != 0))
+		return (rv);
+
+	lx_debug("\tsolaris mount fstype: %s", fstype);
+	lx_debug("\tsolaris mount options: \"%s\"", options);
+
+	res = mount(source, target, sflags, fstype, sdataptr, sdatalen,
+	    options, sizeof (options));
+
+	return ((res == 0) ? 0 : -errno);
+}
--- /dev/null	Wed Oct  5 00:31:48 2016
+++ new/usr/src/lib/brand/lx/lx_brand/common/mount_nfs.c	Wed Oct  5 00:31:48 2016
@@ -0,0 +1,2453 @@
+/*
+ * 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.
+ */
+
+/*
+ * NFS mount syscall support
+ *
+ * All of the Linux NFS mount RPC support is handled within the kernel whereas
+ * on Illumos the NFS mount command performs the initial RPC calls to contact
+ * the server's mountd, get the file handle, and negotiate security before
+ * making the actual 'mount' syscall. Thus we emulate the Linux in-kernel
+ * RPC behavior here using code that is partially based on the code from our
+ * user-level NFS mount command. This code also includes the nullproc RPC
+ * function calls
+ *
+ * In addition to the code described above we also have brand-specific code to
+ * convert the Linux mount arguments into our native format.
+ *
+ * Because libnsl (which we need to make RPCs) depends on the netconfig table
+ * (which won't exist inside an lx zone) we provide a built-in default
+ * netconfig table which we hook into libnsl via the brand callbacks. See
+ *	_nsl_brand_set_hooks(lx_nsl_set_sz_func, lx_get_ent_func)
+ * in lx_nfs_mount().
+ *
+ * Finally, in most of the functions below, when the code refers to the
+ * hostname we're really working with the IP addr that the Linux user-level
+ * mount command passed in to us.
+ */
+
+/*
+ * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
+/*	  All Rights Reserved  	*/
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#define	NFSCLIENT
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <rpc/rpc.h>
+#include <errno.h>
+#include <netdb.h>
+#include <sys/mount.h>
+#include <sys/mntent.h>
+#include <sys/mnttab.h>
+#include <nfs/nfs.h>
+#include <nfs/mount.h>
+#include <rpcsvc/mount.h>
+#include <sys/pathconf.h>
+#include <netdir.h>
+#include <netconfig.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <nfs/nfs_sec.h>
+#include <rpcsvc/daemon_utils.h>
+#include <rpcsvc/nfs4_prot.h>
+#include <limits.h>
+#include <nfs/nfssys.h>
+#include <strings.h>
+#include <assert.h>
+#include <sys/lx_mount.h>
+#include <sys/lx_misc.h>
+
+#ifndef	NFS_VERSMAX
+#define	NFS_VERSMAX	4
+#endif
+#ifndef	NFS_VERSMIN
+#define	NFS_VERSMIN	2
+#endif
+
+#define	RET_OK		0
+#define	RET_RETRY	32
+#define	RET_ERR		33
+#define	RET_PROTOUNSUPP	34
+#define	RET_MNTERR	1000
+#define	ERR_PROTO_NONE		0
+#define	ERR_PROTO_INVALID	901
+#define	ERR_PROTO_UNSUPP	902
+#define	ERR_NETPATH		903
+#define	ERR_NOHOST		904
+#define	ERR_RPCERROR		905
+
+typedef struct err_ret {
+	int error_type;
+	int error_value;
+} err_ret_t;
+
+#define	SET_ERR_RET(errst, etype, eval) \
+	{ \
+	(errst)->error_type = etype; \
+	(errst)->error_value = eval; \
+	}
+
+/*
+ * Built-in netconfig table.
+ */
+#define	N_NETCONF_ENTS	4
+static struct netconfig nca[N_NETCONF_ENTS] = {
+	{"udp6", NC_TPI_CLTS,     1, "inet6", "udp", "/dev/udp6", 0, NULL},
+	{"tcp6", NC_TPI_COTS_ORD, 1, "inet6", "tcp", "/dev/tcp6", 0, NULL},
+	{"udp",  NC_TPI_CLTS,     1, "inet",  "udp", "/dev/udp", 0, NULL},
+	{"tcp",  NC_TPI_COTS_ORD, 1, "inet",  "tcp", "/dev/tcp", 0, NULL}
+};
+
+/*
+ * Mapping table of Linux NFS mount options to the corresponding Illumos
+ * option. The nmo_argtyp field tells us how to handle the argument.
+ */
+typedef enum map_mount_opt_type {
+	MOUNT_OPT_INVALID	= 0,
+	MOUNT_OPT_PASTHRU	= 1,
+	MOUNT_OPT_IGNORE	= 2,
+	MOUNT_OPT_TOKEN		= 3,
+	MOUNT_OPT_HAS_ARG	= 4
+} map_mount_opt_type_t;
+
+typedef struct nfs_map_opt {
+	char			*nmo_lx_opt;
+	char			*nmo_il_opt;
+	map_mount_opt_type_t	nmo_argtyp;
+} nfs_map_opt_t;
+
+static nfs_map_opt_t nmo_tab[] = {
+	{"ac",		NULL,		MOUNT_OPT_IGNORE},
+	{"acdirmax",	NULL,		MOUNT_OPT_PASTHRU},
+	{"acdirmin",	NULL,		MOUNT_OPT_PASTHRU},
+	{"acl",		NULL,		MOUNT_OPT_INVALID},
+	{"acregmax",	NULL,		MOUNT_OPT_PASTHRU},
+	{"acregmin",	NULL,		MOUNT_OPT_PASTHRU},
+	{"actimeo",	NULL,		MOUNT_OPT_PASTHRU},
+	{"bg",		NULL,		MOUNT_OPT_PASTHRU},
+	{"cto",		NULL,		MOUNT_OPT_IGNORE},
+	{"fg",		NULL,		MOUNT_OPT_PASTHRU},
+	{"fsc",		NULL,		MOUNT_OPT_IGNORE},
+	{"hard",	NULL,		MOUNT_OPT_PASTHRU},
+	{"intr",	NULL,		MOUNT_OPT_PASTHRU},
+	{"lock",	NULL,		MOUNT_OPT_IGNORE},
+	{"lookupcache",	NULL,		MOUNT_OPT_INVALID},
+	{"local_lock=%s", NULL,		MOUNT_OPT_INVALID },
+	{"migration",	NULL,		MOUNT_OPT_INVALID},
+	{"minorversion", NULL,		MOUNT_OPT_INVALID},
+	{"mountaddr",	NULL,		MOUNT_OPT_INVALID},
+	{"mounthost",	NULL,		MOUNT_OPT_INVALID},
+	{"mountport",	NULL,		MOUNT_OPT_PASTHRU},
+	{"mountproto",	NULL,		MOUNT_OPT_PASTHRU},
+	{"mountvers",	NULL,		MOUNT_OPT_PASTHRU},
+	{"namlen",	NULL,		MOUNT_OPT_INVALID},
+	{"nfsvers",	NULL,		MOUNT_OPT_INVALID},
+	{"noac",	NULL,		MOUNT_OPT_PASTHRU},
+	{"noacl",	NULL,		MOUNT_OPT_INVALID},
+	{"nocto",	NULL,		MOUNT_OPT_PASTHRU},
+	{"nofsc",	NULL,		MOUNT_OPT_IGNORE},
+	{"nointr",	NULL,		MOUNT_OPT_PASTHRU},
+	{"nolock",	"llock",	MOUNT_OPT_TOKEN},
+	{"nomigration",	NULL,		MOUNT_OPT_INVALID},
+	{"noposix",	NULL,		MOUNT_OPT_IGNORE},
+	{"nordirplus",	NULL,		MOUNT_OPT_IGNORE},
+	{"noresvport",	NULL,		MOUNT_OPT_INVALID},
+	{"nosharecache", NULL,		MOUNT_OPT_IGNORE},
+	{"port",	NULL,		MOUNT_OPT_PASTHRU},
+	{"posix",	NULL,		MOUNT_OPT_PASTHRU},
+	{"proto",	NULL,		MOUNT_OPT_PASTHRU},
+	{"rdirplus",	NULL,		MOUNT_OPT_IGNORE},
+	{"rdma",	"proto=rdma",	MOUNT_OPT_TOKEN},
+	{"resvport",	NULL,		MOUNT_OPT_IGNORE},
+	{"retrans",	NULL,		MOUNT_OPT_PASTHRU},
+	{"retry",	NULL,		MOUNT_OPT_PASTHRU},
+	{"rsize",	NULL,		MOUNT_OPT_PASTHRU},
+	{"sec",		NULL,		MOUNT_OPT_PASTHRU},
+	{"sharecache",	NULL,		MOUNT_OPT_IGNORE},
+	{"sloppy",	NULL,		MOUNT_OPT_IGNORE},
+	{"soft",	NULL,		MOUNT_OPT_PASTHRU},
+	{"tcp",		"proto=tcp",	MOUNT_OPT_TOKEN},
+	{"timeo",	NULL,		MOUNT_OPT_PASTHRU},
+	{"udp",		"proto=udp",	MOUNT_OPT_TOKEN},
+	{"vers",	NULL,		MOUNT_OPT_PASTHRU},
+	{"wsize",	NULL,		MOUNT_OPT_PASTHRU},
+	{NULL,		NULL,		MOUNT_OPT_INVALID}
+};
+
+/*
+ * This struct is used to keep track of misc. variables which are set deep
+ * in one function then referenced someplace else. We pass this around to
+ * avoid the use of global variables as is done the the NFS mount command.
+ *
+ * The nfsvers variables control the NFS version number to be used.
+ *
+ * nmd_nfsvers defaults to 0 which means to use the highest number that
+ * both the client and the server support.  It can also be set to
+ * a particular value, either 2, 3, or 4 to indicate the version
+ * number of choice.  If the server (or the client) do not support
+ * the version indicated, then the mount attempt will be failed.
+ *
+ * nmd_nfsvers_to_use is the actual version number found to use.  It
+ * is determined in get_fh by pinging the various versions of the
+ * NFS service on the server to see which responds positively.
+ *
+ * nmd_nfsretry_vers is the version number set when we retry the mount
+ * command with the version decremented from nmd_nfsvers_to_use.
+ * nmd_nfsretry_vers is set from nmd_nfsvers_to_use when we retry the mount
+ * for errors other than RPC errors; it helps us know why we are
+ * retrying. It is an indication that the retry is due to non-RPC errors.
+ */
+typedef struct nfs_mnt_data {
+	int		nmd_bg;
+	int		nmd_posix;
+	int		nmd_retries;
+	ushort_t	nmd_nfs_port;
+	char		*nmd_nfs_proto;
+	ushort_t	nmd_mnt_port;
+	char		*nmd_mnt_proto;
+	char		*nmd_fstype;
+	seconfig_t	nmd_nfs_sec;
+	int		nmd_sec_opt;	/* any security option ? */
+	rpcvers_t	nmd_mnt_vers;
+	rpcvers_t	nmd_nfsvers;
+	rpcvers_t	nmd_nfsvers_to_use;
+	rpcvers_t	nmd_nfsretry_vers;
+} nfs_mnt_data_t;
+
+/* number of transports to try */
+#define	MNT_PREF_LISTLEN	2
+#define	FIRST_TRY		1
+#define	SECOND_TRY		2
+
+#define	BIGRETRY	10000
+
+/* maximum length of RPC header for NFS messages */
+#define	NFS_RPC_HDR	432
+
+#define	NFS_ARGS_EXTB_secdata(args, secdata) \
+	{ (args)->nfs_args_ext = NFS_ARGS_EXTB, \
+	(args)->nfs_ext_u.nfs_extB.secdata = secdata; }
+
+extern int __clnt_bindresvport(CLIENT *);
+
+static int retry(struct mnttab *, int, nfs_mnt_data_t *);
+static int set_args(int *, struct nfs_args *, char *, struct mnttab *,
+    nfs_mnt_data_t *);
+static int get_fh(struct nfs_args *, char *, char *, int *,
+	struct netconfig **, nfs_mnt_data_t *);
+static int make_secure(struct nfs_args *, char *, struct netconfig *,
+	rpcvers_t, nfs_mnt_data_t *);
+static int mount_nfs(struct mnttab *, int, err_ret_t *, nfs_mnt_data_t *);
+static int getaddr_nfs(struct nfs_args *, char *, struct netconfig **,
+	err_ret_t *, bool_t, nfs_mnt_data_t *);
+static struct netbuf *get_addr(char *, rpcprog_t, rpcvers_t,
+	struct netconfig **, char *, ushort_t, struct t_info *, err_ret_t *);
+static struct netbuf *get_the_addr(char *, rpcprog_t, rpcvers_t,
+	struct netconfig *, ushort_t, struct t_info *, err_ret_t *);
+
+static int lx_nsl_set_sz_func(void);
+static struct netconfig *lx_get_ent_func(int);
+
+/*
+ * These are the defaults (range) for the client when determining
+ * which NFS version to use when probing the server (see above).
+ * These will only be used when the vers mount option is not used.
+ */
+#define	vers_max_default NFS_VERSMAX_DEFAULT
+#define	vers_min_default NFS_VERSMIN_DEFAULT
+
+/*
+ * The wnl/WNL* definitions come from cmd/fs.d/nfs/mount/webnfs.h. We
+ * incorporate those here since the cmd src tree hierarchy is not built when
+ * we're compiling the lib portion of the src tree and since these definitions
+ * are a fundamental part of the protocol spec, there is no risk of these
+ * changing (i.e. we're just like the Linux kernel here, which has these
+ * built-in). We only need the bare minimum set of definitions to make RPCs to
+ * the NFS server to negotiate the mount.
+ */
+
+/* The timeout for our mount null proc pings is always 5 seconds. */
+static struct timeval TIMEOUT = { 5, 0 };
+#define	WNLPROC_NULL	0
+#define	WNLPROC3_NULL	0
+#define	WNLPROC4_NULL	0
+#define	WNL_FHSIZE	32
+
+static enum clnt_stat
+wnlproc_null_2(void *argp, void *clnt_res, CLIENT *clnt)
+{
+	return (clnt_call(clnt, WNLPROC_NULL, (xdrproc_t)xdr_void,
+	    (caddr_t)argp, (xdrproc_t)xdr_void, (caddr_t)clnt_res, TIMEOUT));
+}
+
+static enum clnt_stat
+wnlproc3_null_3(void *argp, void *clnt_res, CLIENT *clnt)
+{
+	return (clnt_call(clnt, WNLPROC3_NULL, (xdrproc_t)xdr_void,
+	    (caddr_t)argp, (xdrproc_t)xdr_void, (caddr_t)clnt_res, TIMEOUT));
+}
+
+static enum clnt_stat
+wnlproc4_null_4(void *argp, void *clnt_res, CLIENT *clnt)
+{
+	return (clnt_call(clnt, WNLPROC4_NULL, (xdrproc_t)xdr_void,
+	    (caddr_t)argp, (xdrproc_t)xdr_void, (caddr_t)clnt_res, TIMEOUT));
+}
+
+static void
+log_err(const char *fmt, ...)
+{
+	va_list ap;
+	char buf[128];
+	int fd;
+
+	va_start(ap, fmt);
+	(void) vsnprintf(buf, sizeof (buf), fmt, ap);
+	va_end(ap);
+
+	if ((fd = open("/dev/conslog", O_WRONLY)) != -1) {
+		(void) write(fd, buf, strlen(buf));
+		(void) close(fd);
+	}
+}
+
+static int
+i_add_option(char *option, char *buf, size_t buf_size)
+{
+	int len;
+	char *fmt_str = NULL;
+
+	if (buf[0] == '\0') {
+		fmt_str = "%s";
+	} else {
+		fmt_str = ",%s";
+	}
+
+	len = strlen(buf);
+	buf_size -= len;
+	buf += len;
+
+	/*LINTED*/
+	if (snprintf(buf, buf_size, fmt_str, option) > (buf_size - 1))
+		return (-EOVERFLOW);
+	return (0);
+}
+
+/*
+ * We can return a negative error value which is the errno we need to return to
+ * lx or we can return a positive error value which is primarily used to
+ * indicate retry (otherwise we map to -EINVAL in the caller). Returning 0
+ * indicates success.
+ */
+static int
+mount_nfs(struct mnttab *mntp, int mntflags, err_ret_t *retry_error,
+    nfs_mnt_data_t *nmdp)
+{
+	struct nfs_args *argp = NULL;
+	struct netconfig *nconf = NULL;
+	int vers = 0;
+	int r = 0;
+	char *colonp;
+	char *path;
+	char *host;
+	char spec_buf[MAXPATHLEN + LX_NMD_MAXHOSTNAMELEN + 1];
+
+	mntp->mnt_fstype = MNTTYPE_NFS;
+
+	(void) strlcpy(spec_buf, mntp->mnt_special, sizeof (spec_buf));
+	colonp = strchr(spec_buf, ':');
+	if (colonp == NULL)
+		return (-EINVAL);
+
+	*colonp = '\0';
+	host = spec_buf;
+	path = colonp + 1;
+
+	argp = (struct nfs_args *)malloc(sizeof (*argp));
+	if (argp == NULL)
+		return (-ENOMEM);
+
+	(void) memset(argp, 0, sizeof (*argp));
+	(void) memset(&nmdp->nmd_nfs_sec, 0, sizeof (seconfig_t));
+	nmdp->nmd_sec_opt = 0;
+
+	/* returns a negative errno */
+	if ((r = set_args(&mntflags, argp, host, mntp, nmdp)) != 0)
+		goto out;
+
+	/* returns a negative errno or positive EAGAIN for retry */
+	r = get_fh(argp, host, path, &vers, &nconf, nmdp);
+	if (r != 0) {
+		/* All attempts failed */
+		goto out;
+	}
+
+	/*
+	 * Call to get_fh() above may have obtained the netconfig info and NULL
+	 * proc'd the server via a call to getaddr_nfs. This would only be the
+	 * case with v4.
+	 */
+	if (!(argp->flags & NFSMNT_KNCONF)) {
+		/* returns a negative errno or positive EAGAIN for retry */
+		r = getaddr_nfs(argp, host, &nconf, retry_error, TRUE, nmdp);
+		if (r != 0) {
+			goto out;
+		}
+	}
+
+	if (make_secure(argp, host, nconf, vers, nmdp) < 0) {
+		r = -EAGAIN;
+		goto out;
+	}
+
+	mntflags |= MS_DATA | MS_OPTIONSTR;
+
+	r = mount(mntp->mnt_special, mntp->mnt_mountp, mntflags,
+	    nmdp->nmd_fstype, argp, sizeof (*argp), mntp->mnt_mntopts,
+	    MAX_MNTOPT_STR);
+	if (r != 0)
+		r = -errno;
+out:
+	if (nconf != NULL)
+		freenetconfigent(nconf);
+	if (argp->fh)
+		free(argp->fh);
+	if (argp->pathconf)
+		free(argp->pathconf);
+	if (argp->knconf)
+		free(argp->knconf);
+	if (argp->addr) {
+		free(argp->addr->buf);
+		free(argp->addr);
+	}
+	nfs_free_secdata(argp->nfs_ext_u.nfs_extB.secdata);
+	if (argp->syncaddr) {
+		free(argp->syncaddr->buf);
+		free(argp->syncaddr);
+	}
+	if (argp->netname)
+		free(argp->netname);
+	free(argp);
+
+	return (r);
+}
+
+/*
+ * These options were initially derived from uts/common/fs/nfs/nfs_dlinet.c
+ * but have been extended to add additional Linux options.
+ */
+static char *optlist[] = {
+#define	OPT_RO		0
+	MNTOPT_RO,
+#define	OPT_RW		1
+	MNTOPT_RW,
+#define	OPT_QUOTA	2
+	MNTOPT_QUOTA,
+#define	OPT_NOQUOTA	3
+	MNTOPT_NOQUOTA,
+#define	OPT_SOFT	4
+	MNTOPT_SOFT,
+#define	OPT_HARD	5
+	MNTOPT_HARD,
+#define	OPT_SUID	6
+	MNTOPT_SUID,
+#define	OPT_NOSUID	7
+	MNTOPT_NOSUID,
+#define	OPT_GRPID	8
+	MNTOPT_GRPID,
+#define	OPT_REMOUNT	9
+	MNTOPT_REMOUNT,
+#define	OPT_NOSUB	10
+	MNTOPT_NOSUB,
+#define	OPT_INTR	11
+	MNTOPT_INTR,
+#define	OPT_NOINTR	12
+	MNTOPT_NOINTR,
+#define	OPT_PORT	13
+	MNTOPT_PORT,
+#define	OPT_SECURE	14
+	MNTOPT_SECURE,
+#define	OPT_RSIZE	15
+	MNTOPT_RSIZE,
+#define	OPT_WSIZE	16
+	MNTOPT_WSIZE,
+#define	OPT_TIMEO	17
+	MNTOPT_TIMEO,
+#define	OPT_RETRANS	18
+	MNTOPT_RETRANS,
+#define	OPT_ACTIMEO	19
+	MNTOPT_ACTIMEO,
+#define	OPT_ACREGMIN	20
+	MNTOPT_ACREGMIN,
+#define	OPT_ACREGMAX	21
+	MNTOPT_ACREGMAX,
+#define	OPT_ACDIRMIN	22
+	MNTOPT_ACDIRMIN,
+#define	OPT_ACDIRMAX	23
+	MNTOPT_ACDIRMAX,
+#define	OPT_BG		24
+	MNTOPT_BG,
+#define	OPT_FG		25
+	MNTOPT_FG,
+#define	OPT_RETRY	26
+	MNTOPT_RETRY,
+#define	OPT_NOAC	27
+	MNTOPT_NOAC,
+#define	OPT_NOCTO	28
+	MNTOPT_NOCTO,
+#define	OPT_LLOCK	29
+	MNTOPT_LLOCK,
+#define	OPT_POSIX	30
+	MNTOPT_POSIX,
+#define	OPT_VERS	31
+	MNTOPT_VERS,
+#define	OPT_PROTO	32
+	MNTOPT_PROTO,
+#define	OPT_SEMISOFT	33
+	MNTOPT_SEMISOFT,
+#define	OPT_NOPRINT	34
+	MNTOPT_NOPRINT,
+#define	OPT_SEC		35
+	MNTOPT_SEC,
+#define	OPT_LARGEFILES	36
+	MNTOPT_LARGEFILES,
+#define	OPT_NOLARGEFILES 37
+	MNTOPT_NOLARGEFILES,
+#define	OPT_PUBLIC	38
+	MNTOPT_PUBLIC,
+#define	OPT_DIRECTIO	39
+	MNTOPT_FORCEDIRECTIO,
+#define	OPT_NODIRECTIO	40
+	MNTOPT_NOFORCEDIRECTIO,
+#define	OPT_XATTR	41
+	MNTOPT_XATTR,
+#define	OPT_NOXATTR	42
+	MNTOPT_NOXATTR,
+#define	OPT_DEVICES	43
+	MNTOPT_DEVICES,
+#define	OPT_NODEVICES	44
+	MNTOPT_NODEVICES,
+#define	OPT_SETUID	45
+	MNTOPT_SETUID,
+#define	OPT_NOSETUID	46
+	MNTOPT_NOSETUID,
+#define	OPT_EXEC	47
+	MNTOPT_EXEC,
+#define	OPT_NOEXEC	48
+	MNTOPT_NOEXEC,
+#define	OPT_MNT_VERS	49
+	"mountvers",
+#define	OPT_MNT_PORT	50
+	"mountport",
+#define	OPT_MNT_PROTO	51
+	"mountproto",
+
+	NULL
+};
+
+static int
+convert_int(int *val, char *str)
+{
+	long lval;
+
+	if (str == NULL || !isdigit(*str))
+		return (-1);
+
+	lval = strtol(str, &str, 10);
+	if (*str != '\0' || lval > INT_MAX)
+		return (-2);
+
+	*val = (int)lval;
+	return (0);
+}
+
+static int
+set_args(int *mntflags, struct nfs_args *args, char *fshost, struct mnttab *mnt,
+    nfs_mnt_data_t *nmdp)
+{
+	char *saveopt, *optstr, *opts, *newopts, *val;
+	int num;
+	int largefiles = 0;
+	int invalid = 0;
+	int attrpref = 0;
+	int optlen, oldlen;
+
+	args->flags = NFSMNT_INT;	/* default is "intr" */
+	args->flags |= NFSMNT_HOSTNAME;
+	args->flags |= NFSMNT_NEWARGS;	/* using extented nfs_args structure */
+	args->hostname = fshost;
+
+	optstr = opts = strdup(mnt->mnt_mntopts);
+	oldlen = strlen(mnt->mnt_mntopts);
+	/* sizeof (MNTOPT_XXX) includes one extra byte we may need for "," */
+	optlen = oldlen + sizeof (MNTOPT_XATTR) + 1;
+	if (optlen > MAX_MNTOPT_STR)
+		return (-EINVAL);
+
+	newopts = malloc(optlen);
+	if (opts == NULL || newopts == NULL) {
+		if (opts)
+			free(opts);
+		if (newopts)
+			free(newopts);
+		return (-EINVAL);
+	}
+	newopts[0] = '\0';
+
+	while (*opts) {
+		invalid = 0;
+		saveopt = opts;
+		switch (getsubopt(&opts, optlist, &val)) {
+		case OPT_RO:
+			*mntflags |= MS_RDONLY;
+			break;
+		case OPT_RW:
+			*mntflags &= ~(MS_RDONLY);
+			break;
+		case OPT_QUOTA:
+		case OPT_NOQUOTA:
+			break;
+		case OPT_SOFT:
+			args->flags |= NFSMNT_SOFT;
+			args->flags &= ~(NFSMNT_SEMISOFT);
+			break;
+		case OPT_SEMISOFT:
+			args->flags |= NFSMNT_SOFT;
+			args->flags |= NFSMNT_SEMISOFT;
+			break;
+		case OPT_HARD:
+			args->flags &= ~(NFSMNT_SOFT);
+			args->flags &= ~(NFSMNT_SEMISOFT);
+			break;
+		case OPT_SUID:
+			*mntflags &= ~(MS_NOSUID);
+			break;
+		case OPT_NOSUID:
+			*mntflags |= MS_NOSUID;
+			break;
+		case OPT_GRPID:
+			args->flags |= NFSMNT_GRPID;
+			break;
+		case OPT_REMOUNT:
+			*mntflags |= MS_REMOUNT;
+			break;
+		case OPT_INTR:
+			args->flags |= NFSMNT_INT;
+			break;
+		case OPT_NOINTR:
+			args->flags &= ~(NFSMNT_INT);
+			break;
+		case OPT_NOAC:
+			args->flags |= NFSMNT_NOAC;
+			break;
+		case OPT_PORT:
+			if (convert_int(&num, val) != 0)
+				goto badopt;
+			nmdp->nmd_nfs_port = num;
+			break;
+
+		case OPT_NOCTO:
+			args->flags |= NFSMNT_NOCTO;
+			break;
+
+		case OPT_RSIZE:
+			if (convert_int(&args->rsize, val) != 0)
+				goto badopt;
+			args->flags |= NFSMNT_RSIZE;
+			break;
+		case OPT_WSIZE:
+			if (convert_int(&args->wsize, val) != 0)
+				goto badopt;
+			args->flags |= NFSMNT_WSIZE;
+			break;
+		case OPT_TIMEO:
+			if (convert_int(&args->timeo, val) != 0)
+				goto badopt;
+			args->flags |= NFSMNT_TIMEO;
+			break;
+		case OPT_RETRANS:
+			if (convert_int(&args->retrans, val) != 0)
+				goto badopt;
+			args->flags |= NFSMNT_RETRANS;
+			break;
+		case OPT_ACTIMEO:
+			if (convert_int(&args->acregmax, val) != 0)
+				goto badopt;
+			args->acdirmin = args->acregmin = args->acdirmax
+			    = args->acregmax;
+			args->flags |= NFSMNT_ACDIRMAX;
+			args->flags |= NFSMNT_ACREGMAX;
+			args->flags |= NFSMNT_ACDIRMIN;
+			args->flags |= NFSMNT_ACREGMIN;
+			break;
+		case OPT_ACREGMIN:
+			if (convert_int(&args->acregmin, val) != 0)
+				goto badopt;
+			args->flags |= NFSMNT_ACREGMIN;
+			break;
+		case OPT_ACREGMAX:
+			if (convert_int(&args->acregmax, val) != 0)
+				goto badopt;
+			args->flags |= NFSMNT_ACREGMAX;
+			break;
+		case OPT_ACDIRMIN:
+			if (convert_int(&args->acdirmin, val) != 0)
+				goto badopt;
+			args->flags |= NFSMNT_ACDIRMIN;
+			break;
+		case OPT_ACDIRMAX:
+			if (convert_int(&args->acdirmax, val) != 0)
+				goto badopt;
+			args->flags |= NFSMNT_ACDIRMAX;
+			break;
+		case OPT_BG:
+			nmdp->nmd_bg++;
+			break;
+		case OPT_FG:
+			nmdp->nmd_bg = 0;
+			break;
+		case OPT_RETRY:
+			if (convert_int(&nmdp->nmd_retries, val) != 0)
+				goto badopt;
+			break;
+		case OPT_LLOCK:
+			args->flags |= NFSMNT_LLOCK;
+			break;
+		case OPT_POSIX:
+			nmdp->nmd_posix = 1;
+			break;
+		case OPT_VERS:
+			if (convert_int(&num, val) != 0)
+				goto badopt;
+			nmdp->nmd_nfsvers = (rpcvers_t)num;
+			break;
+		case OPT_PROTO:
+			if (val == NULL)
+				goto badopt;
+
+			nmdp->nmd_nfs_proto = (char *)malloc(strlen(val)+1);
+			if (!nmdp->nmd_nfs_proto)
+				return (-EINVAL);
+
+			(void) strncpy(nmdp->nmd_nfs_proto, val, strlen(val)+1);
+			break;
+
+		case OPT_NOPRINT:
+			args->flags |= NFSMNT_NOPRINT;
+			break;
+
+		case OPT_LARGEFILES:
+			largefiles = 1;
+			break;
+
+		case OPT_NOLARGEFILES:
+			free(optstr);
+			return (-EINVAL);
+
+		case OPT_SEC:
+			if (val == NULL)
+				return (-EINVAL);
+			/*
+			 * We initialize the nfs_sec struct as if we had the
+			 * basic /etc/nffssec.conf file.
+			 */
+			if (strcmp(val, "none") == 0) {
+				(void) strlcpy(nmdp->nmd_nfs_sec.sc_name,
+				    "none", MAX_NAME_LEN);
+				nmdp->nmd_nfs_sec.sc_nfsnum =
+				    nmdp->nmd_nfs_sec.sc_rpcnum = 0;
+			} else if (strcmp(val, "sys") == 0) {
+				(void) strlcpy(nmdp->nmd_nfs_sec.sc_name,
+				    "sys", MAX_NAME_LEN);
+				nmdp->nmd_nfs_sec.sc_nfsnum =
+				    nmdp->nmd_nfs_sec.sc_rpcnum = 1;
+			} else if (strcmp(val, "dh") == 0) {
+				(void) strlcpy(nmdp->nmd_nfs_sec.sc_name,
+				    "dh", MAX_NAME_LEN);
+				nmdp->nmd_nfs_sec.sc_nfsnum =
+				    nmdp->nmd_nfs_sec.sc_rpcnum = 3;
+			} else {
+				return (-EINVAL);
+			}
+			nmdp->nmd_sec_opt++;
+			break;
+
+		case OPT_DIRECTIO:
+			args->flags |= NFSMNT_DIRECTIO;
+			break;
+
+		case OPT_NODIRECTIO:
+			args->flags &= ~(NFSMNT_DIRECTIO);
+			break;
+
+		case OPT_XATTR:
+		case OPT_NOXATTR:
+			/*
+			 * VFS options; just need to get them into the
+			 * new mount option string and note we've seen them
+			 */
+			attrpref = 1;
+			break;
+
+		case OPT_MNT_VERS:
+			if (convert_int(&num, val) != 0)
+				goto badopt;
+			nmdp->nmd_mnt_vers = (rpcvers_t)num;
+			invalid = 1;	/* Invalid as a native option */
+			break;
+
+		case OPT_MNT_PORT:
+			if (convert_int(&num, val) != 0)
+				goto badopt;
+			nmdp->nmd_mnt_port = num;
+			invalid = 1;	/* Invalid as a native option */
+			break;
+
+		case OPT_MNT_PROTO:
+			if (val == NULL)
+				goto badopt;
+			nmdp->nmd_mnt_proto = strdup(val);
+			if (nmdp->nmd_mnt_proto == NULL)
+				return (-ENOMEM);
+			invalid = 1;	/* Invalid as a native option */
+			break;
+
+		default:
+			invalid = 1;
+			break;
+		}
+		if (!invalid) {
+			if (newopts[0] != '\0') {
+				(void) strlcat(newopts, ",", optlen);
+			}
+			(void) strlcat(newopts, saveopt, optlen);
+		}
+	}
+	/* Default is to turn extended attrs on */
+	if (!attrpref) {
+		if (newopts[0]) {
+			(void) strlcat(newopts, ",", optlen);
+		}
+		(void) strlcat(newopts, MNTOPT_XATTR, optlen);
+	}
+	(void) strlcpy(mnt->mnt_mntopts, newopts, oldlen);
+	free(newopts);
+	free(optstr);
+
+	/* ensure that only one secure mode is requested */
+	if (nmdp->nmd_sec_opt > 1)
+		return (-EINVAL);
+
+	/* ensure that the user isn't trying to get large files over V2 */
+	if (nmdp->nmd_nfsvers == NFS_VERSION && largefiles)
+		return (-EINVAL);
+
+	if (nmdp->nmd_nfsvers == NFS_V4 && nmdp->nmd_nfs_proto != NULL &&
+	    strncasecmp(nmdp->nmd_nfs_proto, NC_UDP, strlen(NC_UDP)) == 0)
+		return (-EINVAL);
+
+	return (0);
+
+badopt:
+	free(optstr);
+	return (-EINVAL);
+}
+
+/*
+ *  NFS project private API.
+ *
+ *  Free an sec_data structure.
+ *  Free the parts that nfs_clnt_secdata allocates.
+ */
+void
+nfs_free_secdata(sec_data_t *secdata)
+{
+	dh_k4_clntdata_t *dkdata;
+
+	if (!secdata)
+		return;
+
+	switch (secdata->rpcflavor) {
+		case AUTH_UNIX:
+		case AUTH_NONE:
+			break;
+
+		case AUTH_DES:
+			/* LINTED pointer alignment */
+			dkdata = (dh_k4_clntdata_t *)secdata->data;
+			if (dkdata) {
+				if (dkdata->netname)
+					free(dkdata->netname);
+				if (dkdata->syncaddr.buf)
+					free(dkdata->syncaddr.buf);
+				free(dkdata);
+			}
+			break;
+
+		default:
+			break;
+	}
+
+	free(secdata);
+}
+
+/*
+ *  Make an client side sec_data structure and fill in appropriate value
+ *  based on its rpc security flavor.
+ *
+ *  It is caller's responsibility to allocate space for seconfig_t,
+ *  and this routine will allocate space for the sec_data structure
+ *  and related data field.
+ *
+ *  Return the sec_data_t on success.
+ *  If fail, return NULL pointer.
+ */
+sec_data_t *
+nfs_clnt_secdata(seconfig_t *secp, char *hostname, struct knetconfig *knconf,
+    struct netbuf *syncaddr, int flags)
+{
+	char netname[MAXNETNAMELEN+1];
+	sec_data_t *secdata;
+	dh_k4_clntdata_t *dkdata;
+
+	secdata = malloc(sizeof (sec_data_t));
+	if (!secdata)
+		return (NULL);
+
+	(void) memset(secdata, 0, sizeof (sec_data_t));
+
+	secdata->secmod = secp->sc_nfsnum;
+	secdata->rpcflavor = secp->sc_rpcnum;
+	secdata->uid = secp->sc_uid;
+	secdata->flags = flags;
+
+	/*
+	 *  Now, fill in the information for client side secdata :
+	 *
+	 *  For AUTH_UNIX, AUTH_DES
+	 *  hostname can be in the form of
+	 *    nodename or
+	 *    nodename.domain
+	 */
+	switch (secp->sc_rpcnum) {
+		case AUTH_UNIX:
+		case AUTH_NONE:
+			secdata->data = NULL;
+			break;
+
+		case AUTH_DES:
+			if (!host2netname(netname, hostname, NULL))
+				goto err_out;
+
+			dkdata = malloc(sizeof (dh_k4_clntdata_t));
+			if (!dkdata)
+				goto err_out;
+
+			(void) memset((char *)dkdata, 0,
+			    sizeof (dh_k4_clntdata_t));
+			if ((dkdata->netname = strdup(netname)) == NULL)
+				goto err_out;
+
+			dkdata->netnamelen = strlen(netname);
+			dkdata->knconf = knconf;
+			dkdata->syncaddr = *syncaddr;
+			dkdata->syncaddr.buf = malloc(syncaddr->len);
+			if (dkdata->syncaddr.buf == NULL)
+				goto err_out;
+
+			(void) memcpy(dkdata->syncaddr.buf, syncaddr->buf,
+			    syncaddr->len);
+			secdata->data = (caddr_t)dkdata;
+			break;
+
+		default:
+			goto err_out;
+	}
+
+	return (secdata);
+
+err_out:
+	free(secdata);
+	return (NULL);
+}
+
+static int
+make_secure(struct nfs_args *args, char *hostname, struct netconfig *nconf,
+    rpcvers_t vers, nfs_mnt_data_t *nmdp)
+{
+	sec_data_t *secdata;
+	int flags;
+	struct netbuf *syncaddr = NULL;
+	struct nd_addrlist *retaddrs = NULL;
+	char netname[MAXNETNAMELEN+1];
+
+	/*
+	 * check to see if any secure mode is requested.
+	 * if not, use default security mode.
+	 */
+	if (!nmdp->nmd_sec_opt) {
+		/* AUTH_UNIX is the default. */
+		(void) strlcpy(nmdp->nmd_nfs_sec.sc_name, "sys", MAX_NAME_LEN);
+		nmdp->nmd_nfs_sec.sc_nfsnum = 1;
+		args->flags |= NFSMNT_SECDEFAULT;
+	}
+
+	/*
+	 * Get the network address for the time service on the server.
+	 * If an RPC based time service is not available then try the
+	 * IP time service.
+	 *
+	 * This is for AUTH_DH processing. We will also pass down syncaddr
+	 * and netname for NFS V4 even if AUTH_DH is not requested right now.
+	 * NFS V4 does security negotiation in the kernel via SECINFO.
+	 * This information might be needed later in the kernel.
+	 */
+	flags = 0;
+	syncaddr = NULL;
+
+	if (nmdp->nmd_nfs_sec.sc_rpcnum == AUTH_DH || vers == NFS_V4) {
+		/*
+		 * If using nfsv4, we will not contact the remote RPCBINDer,
+		 * since it is possibly behind a firewall.
+		 */
+		if (vers != NFS_V4)
+			syncaddr = get_the_addr(hostname, RPCBPROG, RPCBVERS,
+			    nconf, 0, NULL, NULL);
+
+		if (syncaddr != NULL) {
+			/* for flags in sec_data */
+			flags |= AUTH_F_RPCTIMESYNC;
+		} else {
+			/*
+			 * TBD:
+			 * For AUTH_DH (AUTH_DES) netdir_getbyname wants to
+			 * lookup the timeserver entry in the /etc/services
+			 * file (but our libnsl to do this won't work in Linux).
+			 * That entry is:
+			 *	timed    525/udp    timeserver
+			 * Since we haven't implemented the emulation for that
+			 * aspect of netdir_getbyname yet, we'll simply return
+			 * an error.
+			 */
+			struct nd_hostserv hs;
+			int error;
+
+			hs.h_host = hostname;
+			hs.h_serv = "timserver";
+
+			if (nmdp->nmd_nfs_sec.sc_rpcnum == AUTH_DH)
+				return (-1);
+
+			error = netdir_getbyname(nconf, &hs, &retaddrs);
+
+			if (error != ND_OK &&
+			    (nmdp->nmd_nfs_sec.sc_rpcnum == AUTH_DH))
+				return (-1);
+
+			if (error == ND_OK)
+				syncaddr = retaddrs->n_addrs;
+
+			/*
+			 * For NFS_V4 if AUTH_DH is negotiated later in the
+			 * kernel thru SECINFO, it will need syncaddr
+			 * and netname data.
+			 */
+			if (vers == NFS_V4 && syncaddr &&
+			    host2netname(netname, hostname, NULL)) {
+				args->syncaddr = malloc(sizeof (struct netbuf));
+				args->syncaddr->buf = malloc(syncaddr->len);
+				(void) memcpy(args->syncaddr->buf,
+				    syncaddr->buf, syncaddr->len);
+				args->syncaddr->len = syncaddr->len;
+				args->syncaddr->maxlen = syncaddr->maxlen;
+				args->netname = strdup(netname);
+				args->flags |= NFSMNT_SECURE;
+			}
+		}
+	}
+
+	/*
+	 * For the initial chosen flavor (any flavor defined in nfssec.conf),
+	 * the data will be stored in the sec_data structure via
+	 * nfs_clnt_secdata() and be passed to the kernel via nfs_args_*
+	 * extended data structure.
+	 */
+	if (!(secdata = nfs_clnt_secdata(&nmdp->nmd_nfs_sec, hostname,
+	    args->knconf, syncaddr, flags))) {
+		if (flags & AUTH_F_RPCTIMESYNC) {
+			free(syncaddr->buf);
+			free(syncaddr);
+		} else if (retaddrs)
+			netdir_free((void *)retaddrs, ND_ADDRLIST);
+		return (-1);
+	}
+
+	NFS_ARGS_EXTB_secdata(args, secdata);
+	if (flags & AUTH_F_RPCTIMESYNC) {
+		free(syncaddr->buf);
+		free(syncaddr);
+	} else if (retaddrs)
+		netdir_free((void *)retaddrs, ND_ADDRLIST);
+	return (0);
+}
+
+/*
+ * Get the network address on "hostname" for program "prog"
+ * with version "vers" by using the nconf configuration data
+ * passed in.
+ *
+ * If the address of a netconfig pointer is null then
+ * information is not sufficient and no netbuf will be returned.
+ *
+ * Finally, ping the null procedure of that service.
+ *
+ * A similar routine is also defined in ../../autofs/autod_nfs.c.
+ * This is a potential routine to move to ../lib for common usage.
+ */
+static struct netbuf *
+get_the_addr(char *hostname, rpcprog_t prog, rpcvers_t vers,
+    struct netconfig *nconf, ushort_t port, struct t_info *tinfo,
+    err_ret_t *error)
+{
+	struct netbuf *nb = NULL;
+	struct t_bind *tbind = NULL;
+	CLIENT *cl = NULL;
+	int fd = -1;
+	AUTH *ah = NULL;
+	AUTH *new_ah = NULL;
+	struct rpc_err r_err;
+	enum clnt_stat rc;
+
+	if (nconf == NULL)
+		return (NULL);
+
+	if ((fd = t_open(nconf->nc_device, O_RDWR, tinfo)) == -1)
+		goto done;
+
+	/* LINTED pointer alignment */
+	if ((tbind = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR)) == NULL)
+		goto done;
+
+	if (vers == NFS_V4) {
+		struct nd_hostserv hs;
+		struct nd_addrlist *retaddrs;
+		int retval;
+		hs.h_host = hostname;
+
+		/* NFS where vers==4 does not support UDP */
+		if (strncasecmp(nconf->nc_proto, NC_UDP,
+		    strlen(NC_UDP)) == 0) {
+			SET_ERR_RET(error, ERR_PROTO_UNSUPP, 0);
+			goto done;
+		}
+
+		if (port == 0)
+			hs.h_serv = "nfs";
+		else
+			hs.h_serv = NULL;
+
+		if ((retval = netdir_getbyname(nconf, &hs, &retaddrs))
+		    != ND_OK) {
+			/*
+			 * Carefully set the error value here. Want to signify
+			 * that the error was an unknown host.
+			 */
+			if (retval == ND_NOHOST) {
+				SET_ERR_RET(error, ERR_NOHOST, retval);
+			}
+
+			goto done;
+		}
+		(void) memcpy(tbind->addr.buf, retaddrs->n_addrs->buf,
+		    retaddrs->n_addrs->len);
+		tbind->addr.len = retaddrs->n_addrs->len;
+		netdir_free((void *)retaddrs, ND_ADDRLIST);
+		(void) netdir_options(nconf, ND_SET_RESERVEDPORT, fd, NULL);
+
+	} else {
+		if (rpcb_getaddr(prog, vers, nconf, &tbind->addr,
+		    hostname) == FALSE) {
+			goto done;
+		}
+	}
+
+	if (port) {
+		if (strcmp(nconf->nc_protofmly, NC_INET) == 0) {
+			/* LINTED alignment */
+			((struct sockaddr_in *)tbind->addr.buf)->sin_port
+			    = port;
+		} else if (strcmp(nconf->nc_protofmly, NC_INET6) == 0) {
+			/* LINTED alignment */
+			((struct sockaddr_in6 *)tbind->addr.buf)->sin6_port
+			    = port;
+		}
+
+	}
+
+	cl = clnt_tli_create(fd, nconf, &tbind->addr, prog, vers, 0, 0);
+	if (cl == NULL) {
+		/*
+		 * clnt_tli_create() returns either RPC_SYSTEMERROR,
+		 * RPC_UNKNOWNPROTO or RPC_TLIERROR. The RPC_TLIERROR translates
+		 * to "Misc. TLI error". This is not too helpful. Most likely
+		 * the connection to the remote server timed out, so this
+		 * error is at least less perplexing.
+		 * See: usr/src/cmd/rpcinfo/rpcinfo.c
+		 */
+		if (rpc_createerr.cf_stat == RPC_TLIERROR) {
+			SET_ERR_RET(error, ERR_RPCERROR, RPC_PMAPFAILURE);
+		} else {
+			SET_ERR_RET(error, ERR_RPCERROR, rpc_createerr.cf_stat);
+		}
+		goto done;
+	}
+
+	ah = authsys_create_default();
+	if (ah != NULL)
+		cl->cl_auth = ah;
+
+	/*
+	 * NULL procedures need not have an argument or
+	 * result param.
+	 */
+	if (vers == NFS_VERSION)
+		rc = wnlproc_null_2(NULL, NULL, cl);
+	else if (vers == NFS_V3)
+		rc = wnlproc3_null_3(NULL, NULL, cl);
+	else
+		rc = wnlproc4_null_4(NULL, NULL, cl);
+
+	if (rc != RPC_SUCCESS) {
+		clnt_geterr(cl, &r_err);
+		if (strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) {
+			switch (r_err.re_status) {
+			case RPC_TLIERROR:
+			case RPC_CANTRECV:
+			case RPC_CANTSEND:
+				r_err.re_status = RPC_PROGVERSMISMATCH;
+				break;
+			default:
+				break;
+			}
+		}
+		SET_ERR_RET(error, ERR_RPCERROR, r_err.re_status);
+		goto done;
+	}
+
+	/*
+	 * Make a copy of the netbuf to return
+	 */
+	nb = (struct netbuf *)malloc(sizeof (*nb));
+	if (nb == NULL)
+		goto done;
+
+	*nb = tbind->addr;
+	nb->buf = (char *)malloc(nb->maxlen);
+	if (nb->buf == NULL) {
+		free(nb);
+		nb = NULL;
+		goto done;
+	}
+	(void) memcpy(nb->buf, tbind->addr.buf, tbind->addr.len);
+
+done:
+	if (cl) {
+		if (ah != NULL) {
+			if (new_ah != NULL)
+				AUTH_DESTROY(ah);
+			AUTH_DESTROY(cl->cl_auth);
+			cl->cl_auth = NULL;
+		}
+		clnt_destroy(cl);
+		cl = NULL;
+	}
+	if (tbind) {
+		(void) t_free((char *)tbind, T_BIND);
+		tbind = NULL;
+	}
+	if (fd >= 0)
+		(void) t_close(fd);
+	return (nb);
+}
+
+static int
+check_nconf(struct netconfig *nconf, int nthtry, int *valid_proto)
+{
+	int	try_test = 0;
+	char	*proto = NULL;
+
+	if (nthtry == FIRST_TRY) {
+		try_test = ((nconf->nc_semantics == NC_TPI_COTS_ORD) ||
+		    (nconf->nc_semantics == NC_TPI_COTS));
+		proto = NC_TCP;
+	} else if (nthtry == SECOND_TRY) {
+		try_test = (nconf->nc_semantics == NC_TPI_CLTS);
+		proto = NC_UDP;
+	}
+
+	if (proto &&
+	    (strcmp(nconf->nc_protofmly, NC_INET) == 0 ||
+	    strcmp(nconf->nc_protofmly, NC_INET6) == 0) &&
+	    (strcmp(nconf->nc_proto, proto) == 0))
+		*valid_proto = TRUE;
+	else
+		*valid_proto = FALSE;
+
+	return (try_test);
+}
+
+static struct netconfig *
+netconfig_dup(struct netconfig *netconfigp)
+{
+	struct netconfig *nconf;
+
+	nconf = calloc(1, sizeof (struct netconfig));
+	if (nconf == NULL)
+		goto nomem;
+
+	if ((nconf->nc_netid = strdup(netconfigp->nc_netid)) == NULL)
+		goto nomem;
+
+	if ((nconf->nc_protofmly = strdup(netconfigp->nc_protofmly)) == NULL)
+		goto nomem;
+
+	if ((nconf->nc_proto = strdup(netconfigp->nc_proto)) == NULL)
+		goto nomem;
+
+	if ((nconf->nc_device = strdup(netconfigp->nc_device)) == NULL)
+		goto nomem;
+
+	nconf->nc_lookups = NULL;
+	nconf->nc_nlookups = netconfigp->nc_nlookups;
+	nconf->nc_flag = netconfigp->nc_flag;
+	nconf->nc_semantics = netconfigp->nc_semantics;
+	return (nconf);
+
+nomem:
+	if (nconf != NULL) {
+		free(nconf->nc_netid);
+		free(nconf->nc_protofmly);
+		free(nconf->nc_proto);
+		free(nconf->nc_device);
+		free(nconf);
+	}
+	return (NULL);
+}
+
+/*
+ * Get a network address on "hostname" for program "prog"
+ * with version "vers".  If the port number is specified (non zero)
+ * then try for a TCP/UDP transport and set the port number of the
+ * resulting IP address.
+ *
+ * If the address of a netconfig pointer was passed and
+ * if it's not null, use it as the netconfig otherwise
+ * assign the address of the netconfig that was used to
+ * establish contact with the service.
+ *
+ * "error" refers to a more descriptive term when get_addr fails
+ * and returns NULL: ERR_PROTO_NONE if no error introduced by
+ * -o proto option, ERR_NETPATH if error found in NETPATH
+ * environment variable, ERR_PROTO_INVALID if an unrecognized
+ * protocol is specified by user, and ERR_PROTO_UNSUPP for a
+ * recognized but invalid protocol (eg. ticlts, ticots, etc.).
+ * "error" is ignored if get_addr returns non-NULL result.
+ *
+ */
+static struct netbuf *
+get_addr(char *hostname, rpcprog_t prog, rpcvers_t vers,
+    struct netconfig **nconfp, char *proto, ushort_t port,
+    struct t_info *tinfo, err_ret_t *error)
+{
+	struct netbuf *nb = NULL;
+	struct netconfig *nconf = NULL;
+	int nci;
+	int nthtry = FIRST_TRY;
+	err_ret_t errsave_nohost, errsave_rpcerr;
+
+	SET_ERR_RET(&errsave_nohost, ERR_PROTO_NONE, 0);
+	SET_ERR_RET(&errsave_rpcerr, ERR_PROTO_NONE, 0);
+
+	SET_ERR_RET(error, ERR_PROTO_NONE, 0);
+
+	if (nconfp && *nconfp)
+		return (get_the_addr(hostname, prog, vers, *nconfp, port,
+		    tinfo, error));
+	/*
+	 * No nconf passed in.
+	 *
+	 * First search for COTS, second for CLTS unless proto
+	 * is specified.  When we retry, we reset the
+	 * netconfig list so that we would search the whole list
+	 * all over again.
+	 */
+
+	/*
+	 * If proto is specified, then only search for the match,
+	 * otherwise try COTS first, if failed, try CLTS.
+	 */
+	if (proto) {
+		/* no matching proto name */
+		SET_ERR_RET(error, ERR_PROTO_INVALID, 0);
+
+		for (nci = 0; nci < N_NETCONF_ENTS; nci++) {
+			nconf = &nca[nci];
+			if (strcmp(nconf->nc_netid, proto) != 0)
+				continue;
+
+			/* may be unsupported */
+			SET_ERR_RET(error, ERR_PROTO_UNSUPP, 0);
+
+			nb = get_the_addr(hostname, prog, vers, nconf, port,
+			    tinfo, error);
+			if (nb != NULL)
+				break;
+
+			/* nb is NULL - deal with errors */
+			if (error) {
+				if (error->error_type == ERR_NOHOST) {
+					SET_ERR_RET(&errsave_nohost,
+					    error->error_type,
+					    error->error_value);
+				}
+				if (error->error_type == ERR_RPCERROR) {
+					SET_ERR_RET(&errsave_rpcerr,
+					    error->error_type,
+					    error->error_value);
+				}
+			}
+
+			/* continue with same protocol selection */
+			continue;
+		} /* end of while */
+
+		if (nci >= N_NETCONF_ENTS)
+			goto done;
+
+		if (nb == NULL &&
+		    (nb = get_the_addr(hostname, prog, vers, nconf, port,
+		    tinfo, error)) == NULL)
+			goto done;
+	} else {
+retry:
+		SET_ERR_RET(error, ERR_NETPATH, 0);
+		for (nci = 0; nci < N_NETCONF_ENTS; nci++) {
+			int	valid_proto;
+
+			nconf = &nca[nci];
+			SET_ERR_RET(error, ERR_PROTO_NONE, 0);
+
+			if (check_nconf(nconf, nthtry, &valid_proto)) {
+				if (port != 0 && valid_proto != TRUE)
+					continue;
+
+				nb = get_the_addr(hostname, prog, vers, nconf,
+				    port, tinfo, error);
+				if (nb != NULL)
+					break;
+
+				/* nb is NULL - deal with errors */
+				if (error) {
+					if (error->error_type == ERR_NOHOST) {
+						SET_ERR_RET(&errsave_nohost,
+						    error->error_type,
+						    error->error_value);
+					}
+
+					if (error->error_type == ERR_RPCERROR) {
+						SET_ERR_RET(&errsave_rpcerr,
+						    error->error_type,
+						    error->error_value);
+					}
+				}
+
+				/*
+				 * Continue the same search path in the
+				 * netconfig db until no more matched
+				 * nconf.
+				 */
+			}
+		}
+
+		if (nci >= N_NETCONF_ENTS) {
+			if (++nthtry <= MNT_PREF_LISTLEN)
+				goto retry;
+			goto done;
+		}
+
+	}
+	SET_ERR_RET(error, ERR_PROTO_NONE, 0);
+
+	/*
+	 * Got nconf and nb. Now dup the netconfig structure
+	 * and return it thru nconfp.
+	 */
+	*nconfp = netconfig_dup(nconf);
+	if (*nconfp == NULL) {
+		free(nb);
+		nb = NULL;
+	}
+done:
+	if (nb == NULL) {
+		/*
+		 * Check the saved errors. The RPC error has
+		 * precedence over the no host error.
+		 */
+		if (errsave_nohost.error_type != ERR_PROTO_NONE) {
+			SET_ERR_RET(error, errsave_nohost.error_type,
+			    errsave_nohost.error_value);
+		}
+
+		if (errsave_rpcerr.error_type != ERR_PROTO_NONE) {
+			SET_ERR_RET(error, errsave_rpcerr.error_type,
+			    errsave_rpcerr.error_value);
+		}
+	}
+
+	return (nb);
+}
+
+static int
+lx_nsl_set_sz_func()
+{
+	return (N_NETCONF_ENTS);
+}
+
+static struct netconfig *
+lx_get_ent_func(int pos)
+{
+	struct netconfig *nconf;
+
+	if (pos < 0 || pos >= N_NETCONF_ENTS)
+		return (NULL);
+
+	nconf = &nca[pos];
+	return (nconf);
+}
+
+/*
+ * Roughly based on the NFSv4 try_failover_table but used here for generic
+ * errno translation.
+ */
+static int
+rpcerr2errno(int rpcerr)
+{
+	switch (rpcerr) {
+	case RPC_INTR:
+		return (EINTR);
+	case RPC_TIMEDOUT:
+		return (ETIMEDOUT);
+	case RPC_VERSMISMATCH:
+	case RPC_PROGVERSMISMATCH:
+	case RPC_PROGUNAVAIL:
+	case RPC_PROCUNAVAIL:
+	case RPC_PMAPFAILURE:
+	case RPC_PROGNOTREGISTERED:
+		return (EPROTONOSUPPORT);
+	case RPC_AUTHERROR:
+		return (EACCES);
+	case RPC_UNKNOWNPROTO:
+	case RPC_UNKNOWNHOST:
+		return (EHOSTUNREACH);
+	case RPC_CANTENCODEARGS:
+	case RPC_CANTDECODERES:
+	case RPC_CANTDECODEARGS:
+	case RPC_CANTSEND:
+	case RPC_CANTRECV:
+		return (ECOMM);
+	case RPC_SYSTEMERROR:
+		return (ENOSR);
+	default:
+		return (EIO);
+	}
+}
+
+static int
+err2errno(int err)
+{
+	assert(err != ERR_RPCERROR);
+	switch (err) {
+	case ERR_PROTO_NONE:
+	case ERR_PROTO_INVALID:
+	case ERR_PROTO_UNSUPP:
+		return (EPROTONOSUPPORT);
+	case ERR_NETPATH:
+		return (ENOSR);
+	case ERR_NOHOST:
+		return (EHOSTUNREACH);
+	default:
+		return (EIO);
+	}
+}
+
+/*
+ * Use our built-in netconfig table to lookup and construct a netconfig struct
+ * for the given netid.
+ */
+static struct netconfig *
+get_netconf(char *id)
+{
+	int i;
+	struct netconfig *nconf, *np;
+
+	if ((nconf = calloc(1, sizeof (struct netconfig))) == NULL)
+		return (NULL);
+
+	for (i = 0; i < N_NETCONF_ENTS; i++) {
+		np = &nca[i];
+		if (strcmp(np->nc_netid, id) != 0)
+			continue;
+
+		nconf->nc_semantics = np->nc_semantics;
+		if ((nconf->nc_netid = strdup(np->nc_netid)) == NULL)
+			goto out;
+		if ((nconf->nc_protofmly = strdup(np->nc_protofmly)) == NULL)
+			goto out;
+		if ((nconf->nc_proto = strdup(np->nc_proto)) == NULL)
+			goto out;
+		if ((nconf->nc_device = strdup(np->nc_device)) == NULL)
+			goto out;
+
+		return (nconf);
+	}
+
+out:
+	freenetconfigent(nconf);
+	return (NULL);
+}
+
+/*
+ * If the user provided a logical name for the NFS server, then the user-level
+ * mount command will have already resolved that name and passed it in using
+ * the 'addr' option (see convert_nfs_arg_str where we've already handled this).
+ * We construct a netbuf from that provided IP and a given port option.
+ *
+ * Note: this code may need to be revisited when we add IPv6 support.
+ */
+static struct netbuf *
+get_netbuf(struct netconfig *nconf, char *ip, ushort_t port)
+{
+	char buf[64];
+
+	assert(port != 0);
+	(void) snprintf(buf, sizeof (buf), "%s.%d.%d", ip,
+	    port >> 8 & 0xff, port & 0xff);
+	return (uaddr2taddr(nconf, buf));
+}
+
+/*
+ * get fhandle of remote path from server's mountd
+ *
+ * Return a positive EAGAIN if the caller should retry and a -EAGAIN to
+ * indicate a fatal (Linux-oriented) error condition. Return other negative
+ * errno values to indicate different fatal errors.
+ */
+static int
+get_fh(struct nfs_args *args, char *fshost, char *fspath, int *versp,
+    struct netconfig **nconfp, nfs_mnt_data_t *nmdp)
+{
+	struct fhstatus fhs;
+	struct mountres3 mountres3;
+	struct pathcnf p;
+	nfs_fh3 *fh3p;
+	struct timeval timeout = { 25, 0};
+	CLIENT *cl = NULL;
+	enum clnt_stat rpc_stat;
+	rpcvers_t outvers = 0;
+	rpcvers_t vers_to_try;
+	rpcvers_t vers_min = vers_min_default;
+	int count, i, *auths;
+
+	bzero(&fhs, sizeof (fhs));
+	bzero(&mountres3, sizeof (mountres3));
+	bzero(&p, sizeof (p));
+
+	switch (nmdp->nmd_nfsvers) {
+	case 2: /* version 2 specified try that only */
+		vers_to_try = MOUNTVERS_POSIX;
+		vers_min = MOUNTVERS;
+		break;
+	case 3: /* version 3 specified try that only */
+		vers_to_try = MOUNTVERS3;
+		vers_min = MOUNTVERS3;
+		break;
+	case 4: /* version 4 specified try that only */
+		/*
+		 * This assignment is in the wrong version sequence.
+		 * The above are MOUNT program and this is NFS
+		 * program.  However, it happens to work out since the
+		 * two don't collide for NFSv4.
+		 */
+		vers_to_try = NFS_V4;
+		vers_min = NFS_V4;
+		break;
+	default: /* no version specified, start with default */
+		/*
+		 * If the retry version is set, use that. This will
+		 * be set if the last mount attempt returned any other
+		 * besides an RPC error.
+		 */
+		if (nmdp->nmd_nfsretry_vers)
+			vers_to_try = nmdp->nmd_nfsretry_vers;
+		else {
+			vers_to_try = vers_max_default;
+			vers_min = vers_min_default;
+		}
+
+		break;
+	}
+
+	/*
+	 * In the case of version 4, just NULL proc the server since
+	 * there is no MOUNT program.  If this fails, then decrease
+	 * vers_to_try and continue on with regular MOUNT program
+	 * processing.
+	 */
+	if (vers_to_try == NFS_V4) {
+		int savevers = nmdp->nmd_nfsvers_to_use;
+		err_ret_t error;
+		int retval;
+		SET_ERR_RET(&error, ERR_PROTO_NONE, 0);
+
+		/* Let's hope for the best */
+		nmdp->nmd_nfsvers_to_use = NFS_V4;
+		retval = getaddr_nfs(args, fshost, nconfp, &error,
+		    vers_min == NFS_V4, nmdp);
+
+		if (retval == RET_OK) {
+			*versp = nmdp->nmd_nfsvers_to_use = NFS_V4;
+			nmdp->nmd_fstype = MNTTYPE_NFS4;
+			args->fh = strdup(fspath);
+			if (args->fh == NULL) {
+				*versp = nmdp->nmd_nfsvers_to_use = savevers;
+				return (-EAGAIN);
+			}
+			return (0);
+		}
+		nmdp->nmd_nfsvers_to_use = savevers;
+
+		if (retval == RET_ERR && error.error_type == ERR_RPCERROR &&
+		    error.error_value == RPC_PROGVERSMISMATCH &&
+		    nmdp->nmd_nfsvers != 0) {
+			/*
+			 * We had an explicit vers=N mount request which locked
+			 * us in to that version, however the server does not
+			 * support that version (and responded to tell us that).
+			 */
+			return (-EPROTONOSUPPORT);
+		}
+
+		vers_to_try--;
+		/* If no more versions to try, let the user know. */
+		if (vers_to_try < vers_min) {
+			if (error.error_value == 0)
+				return (-EPROTONOSUPPORT);
+			return (-rpcerr2errno(error.error_value));
+		}
+
+		/*
+		 * If we are here, there are more versions to try but
+		 * there has been an error of some sort.  If it is not
+		 * an RPC error (e.g. host unknown), we just stop and
+		 * return the error since the other versions would see
+		 * the same error as well.
+		 */
+		if (retval == RET_ERR && error.error_type != ERR_RPCERROR)
+			return (-err2errno(error.error_type));
+	}
+
+	if (nmdp->nmd_mnt_port != 0 && nmdp->nmd_mnt_proto != NULL) {
+		/* We have the necessary info to skip the call to rpcbind. */
+		struct netconfig *nconf;
+
+		outvers = nmdp->nmd_mnt_vers;
+
+		if ((nconf = get_netconf(nmdp->nmd_mnt_proto)) != NULL) {
+			struct netbuf *srvaddr;
+
+			srvaddr = get_netbuf(nconf, fshost, nmdp->nmd_mnt_port);
+			if (srvaddr != NULL) {
+				cl = clnt_tli_create(RPC_ANYFD, nconf, srvaddr,
+				    MOUNTPROG, outvers, 0, 0);
+				free(srvaddr->buf);
+				free(srvaddr);
+			}
+
+			freenetconfigent(nconf);
+		}
+	}
+
+	/*
+	 * If we cannot bypass the call to rpcbind then do the lookup of the
+	 * mountd via rpcbind now.
+	 */
+	if (cl == NULL) {
+		while ((cl = clnt_create_vers(fshost, MOUNTPROG, &outvers,
+		    vers_min, vers_to_try, NULL)) == NULL) {
+			if (rpc_createerr.cf_stat == RPC_UNKNOWNHOST)
+				return (-EAGAIN);
+
+			/*
+			 * We don't want to downgrade version on lost packets
+			 */
+			if ((rpc_createerr.cf_stat == RPC_TIMEDOUT) ||
+			    (rpc_createerr.cf_stat == RPC_PMAPFAILURE))
+				return (EAGAIN);
+
+			/*
+			 * Back off and try the previous version - patch to the
+			 * problem of version numbers not being contigous and
+			 * clnt_create_vers failing (SunOS4.1 clients & SGI
+			 * servers).
+			 * The problem happens with most non-Sun servers who
+			 * don't support mountd protocol #2. So, in case the
+			 * call fails, we re-try the call anyway.
+			 */
+			vers_to_try--;
+			if (vers_to_try < vers_min) {
+				if (rpc_createerr.cf_stat ==
+				    RPC_PROGVERSMISMATCH) {
+					return (-EAGAIN);
+				}
+
+				return (EAGAIN);
+			}
+		}
+	}
+
+	if (nmdp->nmd_posix && outvers < MOUNTVERS_POSIX) {
+		clnt_destroy(cl);
+		return (-EAGAIN);
+	}
+
+	if (__clnt_bindresvport(cl) < 0) {
+		clnt_destroy(cl);
+		return (EAGAIN);
+	}
+
+	if ((cl->cl_auth = authsys_create_default()) == NULL) {
+		clnt_destroy(cl);
+		return (EAGAIN);
+	}
+
+	switch (outvers) {
+	case MOUNTVERS:
+	case MOUNTVERS_POSIX:
+		*versp = nmdp->nmd_nfsvers_to_use = NFS_VERSION;
+		rpc_stat = clnt_call(cl, MOUNTPROC_MNT, xdr_dirpath,
+		    (caddr_t)&fspath, xdr_fhstatus, (caddr_t)&fhs, timeout);
+		if (rpc_stat != RPC_SUCCESS) {
+			log_err("%s:%s: server not responding %s\n",
+			    fshost, fspath, clnt_sperror(cl, ""));
+			clnt_destroy(cl);
+			return (EAGAIN);
+		}
+
+		if ((errno = fhs.fhs_status) != MNT_OK) {
+			clnt_destroy(cl);
+			return (-fhs.fhs_status);
+		}
+		args->fh = malloc(sizeof (fhs.fhstatus_u.fhs_fhandle));
+		if (args->fh == NULL)
+			return (-EAGAIN);
+
+		(void) memcpy((caddr_t)args->fh,
+		    (caddr_t)&fhs.fhstatus_u.fhs_fhandle,
+		    sizeof (fhs.fhstatus_u.fhs_fhandle));
+		if (!errno && nmdp->nmd_posix) {
+			rpc_stat = clnt_call(cl, MOUNTPROC_PATHCONF,
+			    xdr_dirpath, (caddr_t)&fspath, xdr_ppathcnf,
+			    (caddr_t)&p, timeout);
+			if (rpc_stat != RPC_SUCCESS) {
+				log_err("%s:%s: server not responding %s\n",
+				    fshost, fspath, clnt_sperror(cl, ""));
+				free(args->fh);
+				clnt_destroy(cl);
+				return (EAGAIN);
+			}
+			if (_PC_ISSET(_PC_ERROR, p.pc_mask)) {
+				free(args->fh);
+				clnt_destroy(cl);
+				return (-EAGAIN);
+			}
+			args->flags |= NFSMNT_POSIX;
+			args->pathconf = malloc(sizeof (p));
+			if (args->pathconf == NULL) {
+				free(args->fh);
+				clnt_destroy(cl);
+				return (-EAGAIN);
+			}
+			(void) memcpy((caddr_t)args->pathconf, (caddr_t)&p,
+			    sizeof (p));
+		}
+		break;
+
+	case MOUNTVERS3:
+		*versp = nmdp->nmd_nfsvers_to_use = NFS_V3;
+		rpc_stat = clnt_call(cl, MOUNTPROC_MNT, xdr_dirpath,
+		    (caddr_t)&fspath, xdr_mountres3, (caddr_t)&mountres3,
+		    timeout);
+		if (rpc_stat != RPC_SUCCESS) {
+			log_err("%s:%s: server not responding %s\n",
+			    fshost, fspath, clnt_sperror(cl, ""));
+			clnt_destroy(cl);
+			return (EAGAIN);
+		}
+
+		/*
+		 * Assume here that most of the MNT3ERR_*
+		 * codes map into E* errors. See the nfsstat enum for values.
+		 */
+		if ((errno = mountres3.fhs_status) != MNT_OK) {
+			clnt_destroy(cl);
+			return (-mountres3.fhs_status);
+		}
+
+		fh3p = (nfs_fh3 *)malloc(sizeof (*fh3p));
+		if (fh3p == NULL)
+			return (-EAGAIN);
+
+		fh3p->fh3_length =
+		    mountres3.mountres3_u.mountinfo.fhandle.fhandle3_len;
+		(void) memcpy(fh3p->fh3_u.data,
+		    mountres3.mountres3_u.mountinfo.fhandle.fhandle3_val,
+		    fh3p->fh3_length);
+		args->fh = (caddr_t)fh3p;
+		nmdp->nmd_fstype = MNTTYPE_NFS3;
+
+		/*
+		 * Check the security flavor to be used.
+		 *
+		 * If "secure" or "sec=flavor" is a mount
+		 * option, check if the server supports the "flavor".
+		 * If the server does not support the flavor, return
+		 * error.
+		 *
+		 * If no mount option is given then look for default auth
+		 * (default auth entry in /etc/nfssec.conf) in the auth list
+		 * returned from server. If default auth not found, then use
+		 * the first supported security flavor (by the client) in the
+		 * auth list returned from the server.
+		 *
+		 */
+		auths =
+		    mountres3.mountres3_u.mountinfo.auth_flavors
+		    .auth_flavors_val;
+		count =
+		    mountres3.mountres3_u.mountinfo.auth_flavors
+		    .auth_flavors_len;
+
+		if (count <= 0) {
+			clnt_destroy(cl);
+			return (-EAGAIN);
+		}
+
+		if (nmdp->nmd_sec_opt) {
+			for (i = 0; i < count; i++) {
+				if (auths[i] == nmdp->nmd_nfs_sec.sc_nfsnum)
+					break;
+			}
+			if (i == count)
+				goto autherr;
+		} else {
+			/* AUTH_UNIX is the default. */
+			(void) strlcpy(nmdp->nmd_nfs_sec.sc_name, "sys",
+			    MAX_NAME_LEN);
+			nmdp->nmd_nfs_sec.sc_nfsnum = 1;
+		}
+		break;
+	default:
+		clnt_destroy(cl);
+		return (-EAGAIN);
+	}
+
+	if (cl != NULL)
+		clnt_destroy(cl);
+	return (0);
+
+autherr:
+	clnt_destroy(cl);
+	return (-EAGAIN);
+}
+
+/*
+ * Fill in the address for the server's NFS service and fill in a knetconfig
+ * structure for the transport that the service is available on.
+ *
+ * Return a positive EAGAIN if the caller should retry and a -EAGAIN to
+ * indicate a fatal (Linux-oriented) error condition. Return other negative
+ * errno values to indicate different fatal errors.
+ */
+static int
+getaddr_nfs(struct nfs_args *args, char *fshost, struct netconfig **nconfp,
+    err_ret_t *error, bool_t print_rpcerror, nfs_mnt_data_t *nmdp)
+{
+	struct stat sb;
+	struct netconfig *nconf = NULL;
+	struct knetconfig *knconfp;
+	struct t_info tinfo;
+	err_ret_t addr_error;
+
+	SET_ERR_RET(error, ERR_PROTO_NONE, 0);
+	SET_ERR_RET(&addr_error, ERR_PROTO_NONE, 0);
+
+	if (nmdp->nmd_nfs_port != 0 && nmdp->nmd_nfs_proto != NULL) {
+		/* We have the necessary info to skip the call to rpcbind. */
+		nconf = get_netconf(nmdp->nmd_nfs_proto);
+		if (nconf != NULL) {
+			args->addr = get_netbuf(nconf, fshost,
+			    nmdp->nmd_nfs_port);
+
+			*nconfp = nconf;
+			tinfo.tsdu = 0;
+		}
+	}
+
+	if (args->addr == NULL) {
+		args->addr = get_addr(fshost, NFS_PROGRAM,
+		    nmdp->nmd_nfsvers_to_use, nconfp, nmdp->nmd_nfs_proto,
+		    htons(nmdp->nmd_nfs_port), &tinfo, &addr_error);
+	}
+
+	if (args->addr == NULL) {
+		switch (addr_error.error_type) {
+		case 0:
+			break;
+		case ERR_RPCERROR:
+			if (!print_rpcerror)
+				/* no error print at this time */
+				break;
+			log_err("%s NFS service not available %s\n", fshost,
+			    clnt_sperrno(addr_error.error_value));
+			break;
+		case ERR_NETPATH:
+			log_err("%s: Error in NETPATH.\n", fshost);
+			break;
+		case ERR_PROTO_INVALID:
+			log_err("%s: NFS service does not recognize "
+			    "protocol: %s.\n", fshost, nmdp->nmd_nfs_proto);
+			break;
+		case ERR_PROTO_UNSUPP:
+			if (nmdp->nmd_nfsvers ||
+			    nmdp->nmd_nfsvers_to_use == NFS_VERSMIN) {
+				log_err("%s: NFS service does"
+				    " not support protocol: %s.\n",
+				    fshost, nmdp->nmd_nfs_proto);
+			}
+			break;
+		case ERR_NOHOST:
+			log_err("%s: %s\n", fshost, "Unknown host");
+			break;
+		default:
+			/* case ERR_PROTO_NONE falls through */
+			log_err("%s: NFS service not responding\n", fshost);
+			break;
+		}
+
+		SET_ERR_RET(error,
+		    addr_error.error_type, addr_error.error_value);
+		if (addr_error.error_type == ERR_PROTO_NONE) {
+			return (EAGAIN);
+		} else if (addr_error.error_type == ERR_RPCERROR &&
+		    !IS_UNRECOVERABLE_RPC(addr_error.error_value)) {
+			return (EAGAIN);
+		} else if (nmdp->nmd_nfsvers == 0 &&
+		    addr_error.error_type == ERR_PROTO_UNSUPP &&
+		    nmdp->nmd_nfsvers_to_use != NFS_VERSMIN) {
+			/*
+			 * If no version is specified, and the error is due
+			 * to an unsupported transport, then decrement the
+			 * version and retry.
+			 */
+			return (EAGAIN);
+		} else if (addr_error.error_type != ERR_RPCERROR) {
+			return (-err2errno(addr_error.error_type));
+		} else {
+			return (-rpcerr2errno(addr_error.error_value));
+		}
+	}
+	nconf = *nconfp;
+
+	if (stat(nconf->nc_device, &sb) < 0)
+		return (-ENOSR);
+
+	knconfp = (struct knetconfig *)malloc(sizeof (*knconfp));
+	if (!knconfp)
+		return (-ENOMEM);
+
+	knconfp->knc_semantics = nconf->nc_semantics;
+	knconfp->knc_protofmly = nconf->nc_protofmly;
+	knconfp->knc_proto = nconf->nc_proto;
+	knconfp->knc_rdev = sb.st_rdev;
+	args->flags |= NFSMNT_KNCONF;
+	args->knconf = knconfp;
+
+	/* make sure we don't overload the transport */
+	if (tinfo.tsdu > 0 && tinfo.tsdu < NFS_MAXDATA + NFS_RPC_HDR) {
+		args->flags |= (NFSMNT_RSIZE | NFSMNT_WSIZE);
+		if (args->rsize == 0 || args->rsize > tinfo.tsdu - NFS_RPC_HDR)
+			args->rsize = tinfo.tsdu - NFS_RPC_HDR;
+		if (args->wsize == 0 || args->wsize > tinfo.tsdu - NFS_RPC_HDR)
+			args->wsize = tinfo.tsdu - NFS_RPC_HDR;
+	}
+
+	return (0);
+}
+
+static int
+retry(struct mnttab *mntp, int mntflags, nfs_mnt_data_t *nmdp)
+{
+	int delay = 5;
+	int count = nmdp->nmd_retries;
+	int r = -EAGAIN;
+	char *p;
+
+	if (nmdp->nmd_bg) {
+		if (fork() > 0)
+			return (0);
+	} else {
+		p = strchr(mntp->mnt_special, ':');
+		if (p != NULL)
+			*p = '\0';
+		log_err("%s: server not responding\n", mntp->mnt_special);
+		if (p != NULL)
+			*p = ':';
+	}
+
+	while (count--) {
+		err_ret_t retry_error;
+
+		if ((r = mount_nfs(mntp, mntflags, &retry_error, nmdp)) == 0)
+			return (0);
+
+		if (r != EAGAIN)
+			break;
+
+		if (count > 0) {
+			(void) sleep(delay);
+			delay *= 2;
+			if (delay > 120)
+				delay = 120;
+		}
+		p = strchr(mntp->mnt_special, ':');
+		if (p != NULL)
+			*p = '\0';
+		log_err("%s: server not responding\n", mntp->mnt_special);
+		if (p != NULL)
+			*p = ':';
+	}
+
+	if (!nmdp->nmd_nfsretry_vers)
+		log_err("giving up on: %s\n", mntp->mnt_mountp);
+
+	if (r > 0)
+		r = -EAGAIN;
+	return (r);
+}
+
+static int
+append_opt(char *optstr, int len, char *k, char *v)
+{
+	int i;
+
+	for (i = 0; nmo_tab[i].nmo_lx_opt != NULL; i++) {
+		if (strcmp(k, nmo_tab[i].nmo_lx_opt) == 0) {
+			switch (nmo_tab[i].nmo_argtyp) {
+			case MOUNT_OPT_INVALID:
+				lx_unsupported("invalid NFS mount option: %s",
+				    k);
+				return (-EINVAL);
+
+			case MOUNT_OPT_PASTHRU:
+				if (*optstr != '\0')
+					(void) strlcat(optstr, ",", len);
+				if (v == NULL) {
+					(void) strlcat(optstr, k, len);
+				} else {
+					(void) strlcat(optstr, k, len);
+					(void) strlcat(optstr, "=", len);
+					(void) strlcat(optstr, v, len);
+				}
+				break;
+
+			case MOUNT_OPT_IGNORE:
+				break;
+
+			case MOUNT_OPT_TOKEN:
+				if (*optstr != '\0')
+					(void) strlcat(optstr, ",", len);
+				(void) strlcat(optstr,
+				    nmo_tab[i].nmo_il_opt, len);
+				break;
+
+			case MOUNT_OPT_HAS_ARG:
+				if (*optstr != '\0')
+					(void) strlcat(optstr, ",", len);
+				(void) strlcat(optstr,
+				    nmo_tab[i].nmo_il_opt, len);
+				(void) strlcat(optstr, "=", len);
+				(void) strlcat(optstr, v, len);
+				break;
+			}
+			break;
+		}
+	}
+
+	return (0);
+}
+
+static int
+get_nfs_kv(char *vs, char **kp, char **vp)
+{
+	char *p;
+
+	p = strchr(vs, '=');
+	if (p == NULL) {
+		*kp = vs;
+		return (1);
+	}
+
+	*vp = p + 1;
+	*p = '\0';
+	*kp = vs;
+	return (0);
+}
+
+/*
+ * Convert the Linux-specific opt string into an Illumos opt string. We also
+ * fix up the special string (host:/path) to use the address that the
+ * user-level mount code has looked up. This overwrites both the srcp special
+ * string and the mntopts string.
+ *
+ * example input string, given 'nolock' as the only user-level option:
+ *     nolock,vers=4,addr=127.0.0.1,clientaddr=0.0.0.0
+ *
+ * opt string (all one line) from a Centos 6 distro given 'nolock,vers=3' as
+ * the explicit options:
+ *     nolock,addr=127.0.0.1,vers=3,proto=tcp,mountvers=3,mountproto=tcp,
+ *     mountport=1892
+ *
+ * This is an example emitted by the Ubuntu 14.04 automounter for an explicit
+ * v3 mount:
+ *	timeo=60,soft,intr,sloppy,addr=10.88.88.200,vers=3,proto=tcp,
+ *	mountvers=3,mountproto=tcp,mountport=63484
+ */
+static int
+convert_nfs_arg_str(char *srcp, char *mntopts)
+{
+	char *key, *val, *p;
+	char tmpbuf[MAX_MNTOPT_STR];
+	char *tbp = tmpbuf;
+	boolean_t no_sec = B_TRUE;
+
+	(void) strlcpy(tmpbuf, mntopts, sizeof (tmpbuf));
+	*mntopts = '\0';
+
+	while ((p = strsep(&tbp, ",")) != NULL) {
+		int tok;
+
+		tok = get_nfs_kv(p, &key, &val);
+
+		if (tok == 0) {
+			if (strcmp(key, "addr") == 0) {
+				/*
+				 * The Linux user-level code looked up the
+				 * address of the NFS server. We need to
+				 * substitute that into the special string.
+				 */
+				char *pp;
+				char spec[MAXPATHLEN + LX_NMD_MAXHOSTNAMELEN
+				    + 1];
+
+				(void) strlcpy(spec, srcp, sizeof (spec));
+				pp = strchr(spec, ':');
+				if (pp == NULL)
+					return (-EINVAL);
+
+				pp++;
+				(void) snprintf(srcp,
+				    MAXPATHLEN + LX_NMD_MAXHOSTNAMELEN + 1,
+				    "%s:%s", val, pp);
+			} else if (strcmp(key, "clientaddr") == 0) {
+				/*
+				 * Ignore, this is an artifact of the
+				 * user-level lx mount code.
+				 */
+				/* EMPTY */
+			} else if (strcmp(key, "vers") == 0) {
+				/*
+				 * This may be implicitly or explicitly passed.
+				 * Check for the versions we want to support.
+				 */
+				int r;
+				int v = atoi(val);
+
+				if (v != 3 && v != 4)
+					return (-EINVAL);
+
+				r = append_opt(mntopts, MAX_MNTOPT_STR,
+				    key, val);
+				if (r != 0)
+					return (r);
+
+			} else if (strcmp(key, "sec") == 0) {
+				no_sec = B_FALSE;
+			} else {
+				int r;
+
+				r = append_opt(mntopts, MAX_MNTOPT_STR,
+				    key, val);
+				if (r != 0)
+					return (r);
+			}
+		} else {
+			int r;
+
+			r = append_opt(mntopts, MAX_MNTOPT_STR, key, NULL);
+			if (r != 0)
+				return (r);
+		}
+	}
+
+	if (no_sec) {
+		/*
+		 * XXX Temporarily work around missing DES auth by defaulting
+		 * to sec=sys.
+		 */
+		int r;
+
+		r = append_opt(mntopts, MAX_MNTOPT_STR, "sec", "sys");
+		if (r != 0)
+			return (r);
+	}
+
+	return (0);
+}
+
+/* ARGSUSED2 */
+int
+lx_nfs_mount(char *srcp, char *mntp, char *fst, int lx_flags, char *opts)
+{
+	struct mnttab mnt;
+	int r;
+	int il_flags = 0;
+	err_ret_t retry_error;
+	nfs_mnt_data_t nmd;
+
+	_nsl_brand_set_hooks(lx_nsl_set_sz_func, lx_get_ent_func);
+
+	bzero(&nmd, sizeof (nmd));
+	nmd.nmd_retries = BIGRETRY;
+	nmd.nmd_fstype = MNTTYPE_NFS;
+
+	/*
+	 * This will modify the special string so that the hostname passed
+	 * in will be replaced with the host address that the user-land code
+	 * looked up. Thus the rest of the code down the mount_nfs path will
+	 * be working with that IP address in places were it references the
+	 * 'hostname'. This also converts the opts string so that we'll be
+	 * dealing with Illumos options after this.
+	 */
+	if ((r = convert_nfs_arg_str(srcp, opts)) < 0) {
+		return (r);
+	}
+
+
+	/* Linux seems to always allow overlay mounts */
+	il_flags |= MS_OVERLAY;
+
+	/* Convert some Linux flags to Illumos flags. */
+	if (lx_flags & LX_MS_RDONLY)
+		il_flags |= MS_RDONLY;
+	if (lx_flags & LX_MS_NOSUID)
+		il_flags |= MS_NOSUID;
+	if (lx_flags & LX_MS_REMOUNT)
+		il_flags |= MS_REMOUNT;
+
+	/*
+	 * Convert some Linux flags to Illumos option strings.
+	 */
+	if (lx_flags & LX_MS_STRICTATIME) {
+		/*
+		 * The "strictatime" mount option ensures that none of the
+		 * weaker atime-related mode options are in effect.
+		 */
+		lx_flags &= ~(LX_MS_RELATIME | LX_MS_NOATIME);
+	}
+	if ((lx_flags & LX_MS_NODEV) &&
+	    ((r = i_add_option("nodev", opts, MAX_MNTOPT_STR)) != 0))
+		return (r);
+	if ((lx_flags & LX_MS_NOEXEC) &&
+	    ((r = i_add_option("noexec", opts, MAX_MNTOPT_STR)) != 0))
+		return (r);
+	if ((lx_flags & LX_MS_NOATIME) &&
+	    ((r = i_add_option("noatime", opts, MAX_MNTOPT_STR)) != 0))
+		return (r);
+
+	mnt.mnt_special = srcp;
+	mnt.mnt_mountp = mntp;
+	mnt.mnt_mntopts = opts;
+
+	SET_ERR_RET(&retry_error, ERR_PROTO_NONE, 0);
+	r = mount_nfs(&mnt, il_flags, &retry_error, &nmd);
+
+	/* A negative errno return means we're done. */
+	if (r < 0)
+		return (r);
+
+	if (r == EAGAIN && nmd.nmd_retries) {
+		/*
+		 * Check the error code from the last mount attempt. If it was
+		 * an RPC error, then retry as is. Otherwise we retry with the
+		 * nmd_nfsretry_vers set. It is set by decrementing
+		 * nmd_nfsvers_to_use.
+		 */
+		if (retry_error.error_type != 0) {
+			if (retry_error.error_type != ERR_RPCERROR) {
+				nmd.nmd_nfsretry_vers =
+				    nmd.nmd_nfsvers_to_use =
+				    nmd.nmd_nfsvers_to_use - 1;
+				if (nmd.nmd_nfsretry_vers < NFS_VERSMIN)
+					return (-EAGAIN);
+			}
+		}
+
+		r = retry(&mnt, il_flags, &nmd);
+	}
+
+	/* Convert positve EAGAIN into a valid errno. */
+	if (r > 0)
+		return (-EAGAIN);
+
+	return (0);
+}
--- /dev/null	Wed Oct  5 00:31:48 2016
+++ new/usr/src/lib/brand/lx/lx_brand/common/priority.c	Wed Oct  5 00:31:48 2016
@@ -0,0 +1,117 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.  All rights reserved.
+ */
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/lx_debug.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_syscall.h>
+#include <sys/lx_types.h>
+#include <sys/resource.h>
+#include <sys/lx_misc.h>
+#include <sched.h>
+
+/*
+ * The Linux syscall returns priorities in the range (lowest) 40-1 (highest)
+ * and then glibc adjusts these to the range -20 - 19.
+ */
+long
+lx_getpriority(uintptr_t p1, uintptr_t p2)
+{
+	int	which = (int)p1;
+	id_t	who  = (id_t)p2;
+	int	ret;
+
+	/*
+	 * The only valid values for 'which' are positive integers, and unlike
+	 * Solaris, linux doesn't support anything past PRIO_USER.
+	 */
+	if (which < 0 || which > PRIO_USER)
+		return (-EINVAL);
+
+	lx_debug("\tgetpriority(%d, %d)", which, who);
+
+	errno = 0;
+
+	if ((which == PRIO_PROCESS) && (who == 1))
+		who = zoneinit_pid;
+
+	ret = getpriority(which, who);
+	if (ret == -1 && errno != 0) {
+		pid_t mypid = getpid();
+
+		if (which == PRIO_PROCESS &&
+		    (who == mypid || who == 0 || who == P_MYID) &&
+		    sched_getscheduler(mypid) == SCHED_RR) {
+			/*
+			 * The getpriority kernel handling will always return
+			 * an error if we're in the RT class. The zone itself
+			 * won't be able to put itself or any of its processes
+			 * into RT but if we put the whole zone into RT via
+			 * the scheduling-class property, then getpriority will
+			 * always fail. This breaks pam and prevents any login.
+			 * Just pretend to be the highest priority.
+			 */
+			return (1);
+		}
+
+		/*
+		 * Linux does not return EINVAL for invalid 'who' values, it
+		 * returns ESRCH instead. We already validated 'which' above.
+		 */
+		if (errno == EINVAL)
+			errno = ESRCH;
+		return (-errno);
+	}
+
+	/*
+	 * The return value of the getpriority syscall is biased by 20 to avoid
+	 * returning negative values when successful.
+	 */
+	return (20 - ret);
+}
+
+long
+lx_setpriority(uintptr_t p1, uintptr_t p2, uintptr_t p3)
+{
+	int which = (int)p1;
+	id_t who  = (id_t)p2;
+	int prio  = (int)p3;
+	int rval;
+
+	if (which > PRIO_USER)
+		return (-EINVAL);
+
+	lx_debug("\tsetpriority(%d, %d, %d)", which, who, prio);
+
+	if ((which == PRIO_PROCESS) && (who == 1))
+		who = zoneinit_pid;
+
+	rval = setpriority(which, who, prio);
+
+	return ((rval == -1) ? -errno : rval);
+}
--- /dev/null	Wed Oct  5 00:31:48 2016
+++ new/usr/src/lib/brand/lx/lx_brand/common/ptrace.c	Wed Oct  5 00:31:48 2016
@@ -0,0 +1,105 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.  All rights reserved.
+ */
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_debug.h>
+#include <sys/lx_syscall.h>
+#include <sys/lx_signal.h>
+#include <sys/lx_thread.h>
+#include <sys/lwp.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <procfs.h>
+#include <sys/frame.h>
+#include <strings.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <sys/auxv.h>
+#include <thread.h>
+#include <pthread.h>
+#include <synch.h>
+#include <elf.h>
+#include <ieeefp.h>
+#include <assert.h>
+#include <libintl.h>
+#include <lx_syscall.h>
+
+/*
+ * Much of the Linux ptrace(2) emulation is performed in the kernel, and there
+ * is a block comment in "lx_ptrace.c" that describes the facility in some
+ * detail.
+ */
+
+
+void
+lx_ptrace_stop_if_option(int option, boolean_t child, ulong_t msg,
+    ucontext_t *ucp)
+{
+	/*
+	 * We call into the kernel to see if we need to stop for specific
+	 * ptrace(2) events.
+	 */
+	lx_debug("lx_ptrace_stop_if_option(%d, %s, %lu, %p)", option,
+	    child ? "TRUE [child]" : "FALSE [parent]", msg, ucp);
+	if (ucp == NULL) {
+		ucp = (ucontext_t *)lx_find_brand_uc();
+		lx_debug("\tucp = %p", ucp);
+	}
+	if (syscall(SYS_brand, B_PTRACE_STOP_FOR_OPT, option, child, msg,
+	    ucp) != 0) {
+		if (errno != ESRCH) {
+			/*
+			 * This should _only_ fail if we are not traced, or do
+			 * not have this option set.
+			 */
+			lx_err_fatal("B_PTRACE_STOP_FOR_OPT failed: %s",
+			    strerror(errno));
+		}
+	}
+}
+
+/*
+ * Signal to the in-kernel ptrace(2) subsystem that the next native fork() or
+ * thr_create() is part of an emulated fork(2) or clone(2).  If PTRACE_CLONE
+ * was passed to clone(2), inherit_flag should be B_TRUE.
+ */
+void
+lx_ptrace_clone_begin(int option, boolean_t inherit_flag)
+{
+	lx_debug("lx_ptrace_clone_begin(%d, %sPTRACE_CLONE)", option,
+	    inherit_flag ? "" : "!");
+	if (syscall(SYS_brand, B_PTRACE_CLONE_BEGIN, option,
+	    inherit_flag) != 0) {
+		lx_err_fatal("B_PTRACE_CLONE_BEGIN failed: %s",
+		    strerror(errno));
+	}
+}
--- /dev/null	Wed Oct  5 00:31:48 2016
+++ new/usr/src/lib/brand/lx/lx_brand/common/sched.c	Wed Oct  5 00:31:48 2016
@@ -0,0 +1,622 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/cred_impl.h>
+#include <sys/ucred.h>
+#include <ucred.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <errno.h>
+#include <sched.h>
+#include <strings.h>
+#include <pthread.h>
+#include <time.h>
+#include <thread.h>
+#include <alloca.h>
+#include <unistd.h>
+#include <sys/syscall.h>
+#include <sys/lx_syscall.h>
+#include <sys/lx_debug.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_sched.h>
+
+/* Linux only has three valid policies, SCHED_FIFO, SCHED_RR and SCHED_OTHER */
+static int
+validate_policy(int policy)
+{
+	switch (policy) {
+		case LX_SCHED_FIFO:
+			return (SCHED_FIFO);
+
+		case LX_SCHED_RR:
+			return (SCHED_RR);
+
+		case LX_SCHED_OTHER:
+			return (SCHED_OTHER);
+
+		default:
+			lx_debug("validate_policy: illegal policy: %d", policy);
+			return (-EINVAL);
+	}
+}
+
+/*
+ * Check to see if we have the permissions to set scheduler parameters and
+ * policy, based on Linux' demand that such commands fail with errno set to
+ * EPERM if the current euid is not the euid or ruid of the process in
+ * question.
+ */
+static int
+check_schedperms(pid_t pid)
+{
+	size_t sz;
+	ucred_t *cr;
+	uid_t euid;
+
+	euid = geteuid();
+
+	if (pid == getpid()) {
+		/*
+		 * If we're the process to be checked, simply check the euid
+		 * against our ruid.
+		 */
+		if (euid != getuid())
+			return (-EPERM);
+
+		return (0);
+	}
+
+	/*
+	 * We allocate a ucred_t ourselves rather than call ucred_get(3C)
+	 * because ucred_get() calls malloc(3C), which the brand library cannot
+	 * use.  Because we allocate the space with SAFE_ALLOCA(), there's
+	 * no need to free it when we're done.
+	 */
+	sz = ucred_size();
+	cr = (ucred_t *)SAFE_ALLOCA(sz);
+
+	if (cr == NULL)
+		return (-ENOMEM);
+
+	/*
+	 * If we can't access the process' credentials, fail with errno EPERM
+	 * as the call would not have succeeded anyway.
+	 */
+	if (syscall(SYS_ucredsys, UCREDSYS_UCREDGET, pid, cr) != 0)
+		return ((errno == EACCES) ? -EPERM : -errno);
+
+	if ((euid != ucred_geteuid(cr)) && (euid != ucred_getruid(cr)))
+		return (-EPERM);
+
+	return (0);
+}
+
+static int
+ltos_sparam(int policy, struct lx_sched_param *lsp, struct sched_param *sp)
+{
+	struct lx_sched_param ls;
+	int smin = sched_get_priority_min(policy);
+	int smax = sched_get_priority_max(policy);
+
+	if (uucopy(lsp, &ls, sizeof (struct lx_sched_param)) != 0)
+		return (-errno);
+
+	bzero(sp, sizeof (struct sched_param));
+
+	/*
+	 * Linux has a fixed priority range, 0 - 99, which we need to convert to
+	 * Solaris's dynamic range. Linux considers lower numbers to be
+	 * higher priority, so we'll invert the priority within Solaris's range.
+	 *
+	 * The formula to convert between ranges is:
+	 *
+	 *	L * (smax - smin)
+	 * S =  -----------------  + smin
+	 *	  (lmax - lmin)
+	 *
+	 * where S is the Solaris equivalent of the linux priority L.
+	 *
+	 * To invert the priority, we use:
+	 * S' = smax - S + smin
+	 *
+	 * Together, these two formulas become:
+	 *
+	 *		L * (smax - smin)
+	 *   S = smax - -----------------  + 2smin
+	 *			99
+	 */
+	sp->sched_priority = smax -
+	    ((ls.lx_sched_prio * (smax - smin)) / LX_PRI_MAX) + 2*smin;
+
+	lx_debug("ltos_sparam: linux prio %d = Solaris prio %d "
+	    "(Solaris range %d,%d)\n", ls.lx_sched_prio, sp->sched_priority,
+	    smin, smax);
+
+	return (0);
+}
+
+static int
+stol_sparam(int policy, struct sched_param *sp, struct lx_sched_param *lsp)
+{
+	struct lx_sched_param ls;
+	int smin = sched_get_priority_min(policy);
+	int smax = sched_get_priority_max(policy);
+
+	if (policy == SCHED_OTHER) {
+		/*
+		 * In Linux, the only valid SCHED_OTHER scheduler priority is 0
+		 */
+		ls.lx_sched_prio = 0;
+	} else {
+		/*
+		 * Convert Solaris's dynamic, inverted priority range to the
+		 * fixed Linux range of 1 - 99.
+		 *
+		 * The formula is (see above):
+		 *
+		 *	(smax - s + 2smin) * 99
+		 *  l = -----------------------
+		 *		smax - smin
+		 */
+		ls.lx_sched_prio = ((smax - sp->sched_priority + 2*smin) *
+		    LX_PRI_MAX) / (smax - smin);
+	}
+
+	lx_debug("stol_sparam: policy %d: Solaris prio %d = linux prio %d "
+	    "(Solaris range %d,%d)\n", policy,
+	    sp->sched_priority, ls.lx_sched_prio, smin, smax);
+
+	return ((uucopy(&ls, lsp, sizeof (struct lx_sched_param)) != 0)
+	    ? -errno : 0);
+}
+
+#define	BITINDEX(ind)	(ind / (sizeof (uint_t) * 8))
+#define	BITSHIFT(ind)	(1 << (ind % (sizeof (uint_t) * 8)))
+
+/* ARGSUSED */
+long
+lx_sched_getaffinity(uintptr_t pid, uintptr_t len, uintptr_t maskp)
+{
+	int	sz;
+	uint_t	*lmask, *zmask;
+	int	i;
+
+	sz = syscall(SYS_brand, B_GET_AFFINITY_MASK, pid, len, maskp);
+	if (sz == -1)
+		return (-errno);
+
+	/*
+	 * If the target LWP hasn't ever had an affinity mask set, the kernel
+	 * will return a mask of all 0's. If that is the case we must build a
+	 * default mask that has all valid bits turned on.
+	 */
+	lmask = SAFE_ALLOCA(sz);
+	zmask = SAFE_ALLOCA(sz);
+	if (lmask == NULL || zmask == NULL)
+		return (-ENOMEM);
+
+	bzero(zmask, sz);
+
+	if (uucopy((void *)maskp, lmask, sz) != 0)
+		return (-EFAULT);
+
+	if (bcmp(lmask, zmask, sz) != 0)
+		return (sz);
+
+	for (i = 0; i < sz * 8; i++) {
+		if (p_online(i, P_STATUS) != -1) {
+			lmask[BITINDEX(i)] |= BITSHIFT(i);
+		}
+	}
+
+	if (uucopy(lmask, (void *)maskp, sz) != 0)
+		return (-EFAULT);
+
+	return (sz);
+}
+
+/* ARGSUSED */
+long
+lx_sched_setaffinity(uintptr_t pid, uintptr_t len, uintptr_t maskp)
+{
+	int		ret;
+	int		sz;
+	int		i;
+	int		found;
+	uint_t		*lmask;
+	pid_t		s_pid;
+	lwpid_t		s_tid;
+	processorid_t	cpuid = NULL;
+
+	if ((pid_t)pid < 0)
+		return (-EINVAL);
+
+	if (lx_lpid_to_spair(pid, &s_pid, &s_tid) < 0)
+		return (-ESRCH);
+
+	/*
+	 * We only support setting affinity masks for threads in
+	 * the calling process.
+	 */
+	if (s_pid != getpid())
+		return (-EPERM);
+
+	/*
+	 * First, get the minimum bitmask size from the kernel.
+	 */
+	sz = syscall(SYS_brand, B_GET_AFFINITY_MASK, 0, 0, 0);
+	if (sz == -1)
+		return (-errno);
+
+	lmask = SAFE_ALLOCA(sz);
+	if (lmask == NULL)
+		return (-ENOMEM);
+
+	if (uucopy((void *)maskp, lmask, sz) != 0)
+		return (-EFAULT);
+
+	/*
+	 * Make sure the mask contains at least one processor that is
+	 * physically on the system. Reduce the user's mask to the set of
+	 * physically present CPUs. Keep track of how many valid
+	 * bits are set in the user's mask.
+	 */
+
+	for (found = 0, i = 0; i < sz * 8; i++) {
+		if (p_online(i, P_STATUS) == -1) {
+			/*
+			 * This CPU doesn't exist, so clear this bit from
+			 * the user's mask.
+			 */
+			lmask[BITINDEX(i)] &= ~BITSHIFT(i);
+			continue;
+		}
+
+		if ((lmask[BITINDEX(i)] & BITSHIFT(i)) == BITSHIFT(i)) {
+			found++;
+			cpuid = i;
+		}
+	}
+
+	if (found == 0) {
+		lx_debug("\tlx_sched_setaffinity: mask has no present CPUs\n");
+		return (-EINVAL);
+	}
+
+	/*
+	 * If only one bit is set, bind the thread to that procesor;
+	 * otherwise, clear the binding.
+	 */
+	if (found == 1) {
+		lx_debug("\tlx_sched_setaffinity: binding thread %d to cpu%d\n",
+		    s_tid, cpuid);
+		if (processor_bind(P_LWPID, s_tid, cpuid, NULL) != 0)
+			/*
+			 * It could be that the requested processor is offline,
+			 * so we'll just abandon our good-natured attempt to
+			 * bind to it.
+			 */
+			lx_debug("couldn't bind LWP %d to cpu %d: %s\n", s_tid,
+			    cpuid, strerror(errno));
+	} else {
+		lx_debug("\tlx_sched_setaffinity: clearing thr %d binding\n",
+		    s_tid);
+		if (processor_bind(P_LWPID, s_tid, PBIND_NONE, NULL) != 0) {
+			lx_debug("couldn't clear CPU binding for LWP %d: %s\n",
+			    s_tid, strerror(errno));
+		}
+	}
+
+	/*
+	 * Finally, ask the kernel to make a note of our current (though fairly
+	 * meaningless) affinity mask.
+	 */
+	ret = syscall(SYS_brand, B_SET_AFFINITY_MASK, pid, sz, lmask);
+
+	return ((ret == 0) ? 0 : -errno);
+}
+
+long
+lx_sched_getparam(uintptr_t pid, uintptr_t param)
+{
+	int	policy, ret;
+	pid_t	s_pid;
+	lwpid_t	s_tid;
+
+	struct sched_param sp;
+
+	if (((pid_t)pid < 0) || (param == NULL))
+		return (-EINVAL);
+
+	if (lx_lpid_to_spair((pid_t)pid, &s_pid, &s_tid) < 0)
+		return (-ESRCH);
+
+	/*
+	 * If we're attempting to get information on our own process, we can
+	 * get data on a per-thread basis; if not, punt and use the specified
+	 * pid.
+	 */
+	if (s_pid == getpid()) {
+		if ((ret = pthread_getschedparam(s_tid, &policy, &sp)) != 0)
+			return (-ret);
+	} else {
+		if (sched_getparam(s_pid, &sp) == -1)
+			return (-errno);
+
+		if ((policy = sched_getscheduler(s_pid)) < 0)
+			return (-errno);
+	}
+
+	/*
+	 * Make sure that any non-SCHED_FIFO non-SCHED_RR scheduler is mapped
+	 * onto SCHED_OTHER.
+	 */
+	if (policy != SCHED_FIFO && policy != SCHED_RR)
+		policy = SCHED_OTHER;
+
+	return (stol_sparam(policy, &sp, (struct lx_sched_param *)param));
+}
+
+long
+lx_sched_setparam(uintptr_t pid, uintptr_t param)
+{
+	int	err, policy;
+	pid_t	s_pid;
+	lwpid_t	s_tid;
+	struct lx_sched_param lp;
+	struct sched_param sp;
+
+	if (((pid_t)pid < 0) || (param == NULL))
+		return (-EINVAL);
+
+	if (lx_lpid_to_spair((pid_t)pid, &s_pid, &s_tid) < 0)
+		return (-ESRCH);
+
+	if (s_pid == getpid()) {
+		struct sched_param dummy;
+
+		if ((err = pthread_getschedparam(s_tid, &policy, &dummy)) != 0)
+			return (-err);
+	} else
+		if ((policy = sched_getscheduler(s_pid)) < 0)
+			return (-errno);
+
+	lx_debug("sched_setparam(): current policy %d", policy);
+
+	if (uucopy((void *)param, &lp, sizeof (lp)) != 0)
+		return (-errno);
+
+	/*
+	 * In Linux, the only valid SCHED_OTHER scheduler priority is 0
+	 */
+	if ((policy == SCHED_OTHER) && (lp.lx_sched_prio != 0))
+		return (-EINVAL);
+
+	if ((err = ltos_sparam(policy, (struct lx_sched_param *)&lp,
+	    &sp)) != 0)
+		return (err);
+
+	/*
+	 * Check if we're allowed to change the scheduler for the process.
+	 *
+	 * If we're operating on a thread, we can't just call
+	 * pthread_setschedparam() because as all threads reside within a
+	 * single Solaris process, Solaris will allow the modification
+	 *
+	 * If we're operating on a process, we can't just call sched_setparam()
+	 * because Solaris will allow the call to succeed if the scheduler
+	 * parameters do not differ from those being installed, but Linux wants
+	 * the call to fail.
+	 */
+	if ((err = check_schedperms(s_pid)) != 0)
+		return (err);
+
+	if (s_pid == getpid())
+		return (((err = pthread_setschedparam(s_tid, policy, &sp)) != 0)
+		    ? -err : 0);
+
+	return ((sched_setparam(s_pid, &sp) == -1) ? -errno : 0);
+}
+
+long
+lx_sched_rr_get_interval(uintptr_t pid, uintptr_t ts)
+{
+	pid_t	s_pid;
+
+	if ((pid_t)pid < 0)
+		return (-EINVAL);
+
+	if (lx_lpid_to_spid((pid_t)pid, &s_pid) < 0)
+		return (-ESRCH);
+
+	if (sched_rr_get_interval(s_pid, (struct timespec *)ts) == 0)
+		return (0);
+	else
+		return (-errno);
+}
+
+long
+lx_sched_getscheduler(uintptr_t pid)
+{
+	int	policy, rv;
+	pid_t	s_pid;
+	lwpid_t	s_tid;
+
+	if ((pid_t)pid < 0)
+		return (-EINVAL);
+
+	if (lx_lpid_to_spair((pid_t)pid, &s_pid, &s_tid) < 0)
+		return (-ESRCH);
+
+	if (s_pid == getpid()) {
+		struct sched_param dummy;
+
+		if ((rv = pthread_getschedparam(s_tid, &policy, &dummy)) != 0)
+			return (-rv);
+	} else
+		if ((policy = sched_getscheduler(s_pid)) < 0)
+			return (-errno);
+
+	/*
+	 * Linux only supports certain policies; avoid confusing apps with
+	 * alien policies.
+	 */
+	switch (policy) {
+	case SCHED_FIFO:
+		return (LX_SCHED_FIFO);
+	case SCHED_OTHER:
+		return (LX_SCHED_OTHER);
+	case SCHED_RR:
+		return (LX_SCHED_RR);
+	default:
+		break;
+	}
+
+	return (LX_SCHED_OTHER);
+}
+
+long
+lx_sched_setscheduler(uintptr_t pid, uintptr_t policy, uintptr_t param)
+{
+	int	rt_pol;
+	int	rv;
+	pid_t	s_pid;
+	lwpid_t	s_tid;
+	struct lx_sched_param lp;
+
+	struct sched_param sp;
+
+	if (((pid_t)pid < 0) || (param == NULL))
+		return (-EINVAL);
+
+	if ((rt_pol = validate_policy((int)policy)) < 0)
+		return (rt_pol);
+
+	if ((rv = ltos_sparam(policy, (struct lx_sched_param *)param,
+	    &sp)) != 0)
+		return (rv);
+
+	if (uucopy((void *)param, &lp, sizeof (lp)) != 0)
+		return (-errno);
+
+	if (rt_pol == LX_SCHED_OTHER) {
+		/*
+		 * In Linux, the only valid SCHED_OTHER scheduler priority is 0
+		 */
+		if (lp.lx_sched_prio != 0)
+			return (-EINVAL);
+
+		/*
+		 * If we're already SCHED_OTHER, there's nothing else to do.
+		 */
+		if (lx_sched_getscheduler(pid) == LX_SCHED_OTHER)
+			return (0);
+	}
+
+	if (lx_lpid_to_spair((pid_t)pid, &s_pid, &s_tid) < 0)
+		return (-ESRCH);
+
+	/*
+	 * Check if we're allowed to change the scheduler for the process.
+	 *
+	 * If we're operating on a thread, we can't just call
+	 * pthread_setschedparam() because as all threads reside within a
+	 * single Solaris process, Solaris will allow the modification.
+	 *
+	 * If we're operating on a process, we can't just call
+	 * sched_setscheduler() because Solaris will allow the call to succeed
+	 * if the scheduler and scheduler parameters do not differ from those
+	 * being installed, but Linux wants the call to fail.
+	 */
+	if ((rv = check_schedperms(s_pid)) != 0)
+		return (rv);
+
+	if (s_pid == getpid()) {
+		struct sched_param param;
+		int pol;
+
+		if ((pol = sched_getscheduler(s_pid)) == -1)
+			return (-errno);
+
+		/*
+		 * sched_setscheduler() returns the previous scheduling policy
+		 * on success, so call pthread_getschedparam() to get the
+		 * current thread's scheduling policy and return that if the
+		 * call to pthread_setschedparam() succeeds.
+		 */
+		if ((rv = pthread_getschedparam(s_tid, &pol, &param)) != 0)
+			return (-rv);
+
+		return (((rv = pthread_setschedparam(s_tid, rt_pol, &sp)) != 0)
+		    ? -rv : pol);
+	}
+
+	return (((rv = sched_setscheduler(s_pid, rt_pol, &sp)) == -1)
+	    ? -errno : rv);
+}
+
+long
+lx_sched_get_priority_min(uintptr_t policy)
+{
+	/*
+	 * In Linux, the only valid SCHED_OTHER scheduler priority is 0.
+	 * Linux scheduling priorities are not alterable, so there is no
+	 * Solaris translation necessary.
+	 */
+	switch (policy) {
+	case LX_SCHED_FIFO:
+	case LX_SCHED_RR:
+		return (LX_SCHED_PRIORITY_MIN_RRFIFO);
+	case LX_SCHED_OTHER:
+		return (LX_SCHED_PRIORITY_MIN_OTHER);
+	default:
+		break;
+	}
+	return (-EINVAL);
+}
+
+long
+lx_sched_get_priority_max(uintptr_t policy)
+{
+	/*
+	 * In Linux, the only valid SCHED_OTHER scheduler priority is 0
+	 * Linux scheduling priorities are not alterable, so there is no
+	 * Solaris translation necessary.
+	 */
+	switch (policy) {
+	case LX_SCHED_FIFO:
+	case LX_SCHED_RR:
+		return (LX_SCHED_PRIORITY_MAX_RRFIFO);
+	case LX_SCHED_OTHER:
+		return (LX_SCHED_PRIORITY_MAX_OTHER);
+	default:
+		break;
+	}
+	return (-EINVAL);
+}
--- /dev/null	Wed Oct  5 00:31:48 2016
+++ new/usr/src/lib/brand/lx/lx_brand/common/sendfile.c	Wed Oct  5 00:31:48 2016
@@ -0,0 +1,143 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+/*
+ * lx_sendfile() and lx_sendfile64() are primarily branded versions of the
+ * library calls available in the Solaris libsendfile (see sendfile(3EXT)).
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/syscall.h>
+#include <sys/sendfile.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_syscall.h>
+
+#if defined(_ILP32)
+long
+lx_sendfile(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4)
+{
+	sysret_t rval;
+	off_t off = 0;
+	off_t *offp = (off_t *)p3;
+	int error;
+	struct sendfilevec sfv;
+	size_t xferred = 0;
+	size_t sz = (size_t)p4;
+
+	if (offp == NULL) {
+		/* If no offp, we must use the current offset */
+		if ((off = lseek((int)p2, 0, SEEK_CUR)) == -1)
+			return (-errno);
+	} else {
+		if (sz > 0 && uucopy(offp, &off, sizeof (off)) != 0)
+			return (-errno);
+		if (off < 0)
+			return (-EINVAL);
+	}
+
+	sfv.sfv_fd = p2;
+	sfv.sfv_flag = 0;
+	sfv.sfv_off = off;
+	sfv.sfv_len = sz;
+	error = __systemcall(&rval, SYS_sendfilev, SENDFILEV, p1, &sfv,
+	    1, &xferred);
+
+	/* Suppress errors if we were able to write any data at all. */
+	if (xferred > 0) {
+		error = 0;
+	}
+
+	if (error == 0) {
+		off += xferred;
+		if (offp == NULL) {
+			/* If no offp, we must adjust current offset */
+			if (lseek((int)p2, off, SEEK_SET) == -1)
+				return (-errno);
+		} else {
+			if (uucopy(&off, offp, sizeof (off)) != 0) {
+				return (-EFAULT);
+			}
+		}
+	}
+
+	return (error ? -error : xferred);
+}
+#endif
+
+long
+lx_sendfile64(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4)
+{
+	sysret_t rval;
+	off64_t off = 0;
+	off64_t *offp = (off64_t *)p3;
+	size_t sz = (size_t)p4;
+	int error;
+	struct sendfilevec64 sfv;
+	size_t xferred;
+
+	if (offp == NULL) {
+		/* If no offp, we must use the current offset */
+		if ((off = lseek((int)p2, 0, SEEK_CUR)) == -1)
+			return (-errno);
+	} else {
+		if (sz > 0 && uucopy(offp, &off, sizeof (off)) != 0)
+			return (-errno);
+		if (off < 0)
+			return (-EINVAL);
+	}
+
+	sfv.sfv_fd = p2;
+	sfv.sfv_flag = 0;
+	sfv.sfv_off = off;
+	sfv.sfv_len = sz;
+	xferred = 0;
+	error = __systemcall(&rval, SYS_sendfilev, SENDFILEV64, p1, &sfv,
+	    1, &xferred);
+
+	/* Suppress errors if we were able to write any data at all. */
+	if (xferred > 0) {
+		error = 0;
+	}
+
+	if (error == 0) {
+		off += xferred;
+		if (offp == NULL) {
+			/* If no offp, we must adjust current offset */
+			if (lseek((int)p2, off, SEEK_SET) == -1)
+				return (-errno);
+		} else {
+			if (uucopy(&off, offp, sizeof (off)) != 0) {
+				return (-EFAULT);
+			}
+		}
+	}
+
+	return (error ? -error : xferred);
+}
--- /dev/null	Wed Oct  5 00:31:48 2016
+++ new/usr/src/lib/brand/lx/lx_brand/common/signal.c	Wed Oct  5 00:31:48 2016
@@ -0,0 +1,2378 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright 2016 Joyent, Inc. All rights reserved.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/segments.h>
+#include <sys/lx_types.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_debug.h>
+#include <sys/lx_poll.h>
+#include <sys/lx_signal.h>
+#include <sys/lx_sigstack.h>
+#include <sys/lx_syscall.h>
+#include <sys/lx_thread.h>
+#include <sys/syscall.h>
+#include <lx_provider_impl.h>
+#include <sys/stack.h>
+#include <assert.h>
+#include <errno.h>
+#include <poll.h>
+#include <rctl.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <thread.h>
+#include <ucontext.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <libintl.h>
+#include <ieeefp.h>
+#include <sys/signalfd.h>
+
+#if defined(_ILP32)
+extern int pselect_large_fdset(int nfds, fd_set *in0, fd_set *out0, fd_set *ex0,
+	const timespec_t *tsp, const sigset_t *sp);
+#endif
+
+#define	MIN(a, b)	((a) < (b) ? (a) : (b))
+
+/*
+ * Delivering signals to a Linux process is complicated by differences in
+ * signal numbering, stack structure and contents, and the action taken when a
+ * signal handler exits.  In addition, many signal-related structures, such as
+ * sigset_ts, vary between Illumos and Linux.
+ *
+ * To support user-level signal handlers, the brand uses a double layer of
+ * indirection to process and deliver signals to branded threads.
+ *
+ * When a Linux process sends a signal using the kill(2) system call, we must
+ * translate the signal into the Illumos equivalent before handing control off
+ * to the standard signalling mechanism.  When a signal is delivered to a Linux
+ * process, we translate the signal number from Illumos to back to Linux.
+ * Translating signals both at generation and delivery time ensures both that
+ * Illumos signals are sent properly to Linux applications and that signals'
+ * default behavior works as expected.
+ *
+ * In a normal Illumos process, signal delivery is interposed on for any thread
+ * registering a signal handler by libc. Libc needs to do various bits of magic
+ * to provide thread-safe critical regions, so it registers its own handler,
+ * named sigacthandler(), using the sigaction(2) system call. When a signal is
+ * received, sigacthandler() is called, and after some processing, libc turns
+ * around and calls the user's signal handler via a routine named
+ * call_user_handler().
+ *
+ * Adding a Linux branded thread to the mix complicates things somewhat.
+ *
+ * First, when a thread receives a signal, it may either be running in an
+ * emulated Linux context or a native illumos context.  In either case, the
+ * in-kernel brand module is responsible for preserving the register state
+ * from the interrupted context, regardless of whether emulated or native
+ * software was running at the time.  The kernel is also responsible for
+ * ensuring that the illumos native sigacthandler() is called with register
+ * values appropriate for native code.  Of particular note is the %gs segment
+ * selector for 32-bit code, and the %fsbase segment base register for 64-bit
+ * code; these are used by libc to locate per-thread data structures.
+ *
+ * Second, the signal number translation referenced above must take place.
+ * Finally, when we hand control to the Linux signal handler we must do so
+ * on the brand stack, and with registers configured appropriately for the
+ * Linux application.
+ *
+ * This need to translate signal numbers (and manipulate the signal handling
+ * context) means that with standard Illumos libc, following a signal from
+ * generation to delivery looks something like:
+ *
+ * 	kernel ->
+ *	    sigacthandler() ->
+ *		call_user_handler() ->
+ *		    user signal handler
+ *
+ * but for the brand's Linux threads, this would look like:
+ *
+ *	kernel ->
+ *	    sigacthandler() ->
+ *		call_user_handler() ->
+ *		    lx_call_user_handler() ->
+ *			lx_sigdeliver() ->
+ *			    syscall(B_JUMP_TO_LINUX, ...) ->
+ *				Linux user signal handler
+ *
+ * The new addtions are:
+ *
+ * 	lx_call_user_handler
+ *	====================
+ *	This routine is responsible for translating Illumos signal numbers to
+ *	their Linux equivalents, building a Linux signal stack based on the
+ * 	information Illumos has provided, and passing the stack to the
+ *	registered Linux signal handler. It is, in effect, the Linux thread
+ *	equivalent to libc's call_user_handler().
+ *
+ * 	lx_sigdeliver
+ *	=============
+ *
+ * Note that none of this interposition is necessary unless a Linux thread
+ * registers a user signal handler, as the default action for all signals is the
+ * same between Illumos and Linux save for one signal, SIGPWR.  For this reason,
+ * the brand ALWAYS installs its own internal signal handler for SIGPWR that
+ * translates the action to the Linux default, to terminate the process.
+ * (Illumos' default action is to ignore SIGPWR.)
+ *
+ * A notable behavior of lx_sigdeliver is that it must replace the stack
+ * pointer in the context that will be handed to the Linux signal handler.
+ * There is at least one application (mono) which inspects the SP in the
+ * context it receives and which fails when the SP is not within the thread's
+ * stack range. There is not much else within the context that a signal
+ * handler could depend on, so we only ensure that the SP is from the Linux
+ * stack and not the alternate stack. lx_sigdeliver will restore the correct
+ * SP when setcontext returns into this function as part of returning from
+ * the signal handler.
+ *
+ * It is also important to note that when signals are not translated, the brand
+ * relies upon code interposing upon the wait(2) system call to translate
+ * signals to their proper values for any Linux threads retrieving the status
+ * of others.  So while the Illumos signal number for a particular signal is set
+ * in a process' data structures (and would be returned as the result of say,
+ * WTERMSIG()), the brand's interposiiton upon wait(2) is responsible for
+ * translating the value WTERMSIG() would return from a Illumos signal number
+ * to the appropriate Linux value.
+ *
+ * lx_call_user_handler() calls lx_sigdeliver() with a helper function
+ * (typically lx_build_signal_frame) which builds a stack frame for the 32-bit
+ * Linux signal handler, or populates a local (on the stack) structure for the
+ * 64-bit Linux signal handler. The stack at that time looks like this:
+ *
+ * 	=========================================================
+ * |	| lx_sigdeliver_frame_t -- includes LX_SIGRT_MAGIC and	|
+ * |	| a return context for the eventual sigreturn(2) call	|
+ * | 	=========================================================
+ * |	| Linux signal frame (32-bit) or local data		|
+ * V	| (64-bit) built by stack_builder()			|
+ * 	=========================================================
+ *
+ * The process of returning to an interrupted thread of execution from a user
+ * signal handler is entirely different between Illumos and Linux.  While
+ * Illumos generally expects to set the context to the interrupted one on a
+ * normal return from a signal handler, in the normal case Linux instead calls
+ * code that calls a specific Linux system call, rt_sigreturn(2) (or it also
+ * can call sigreturn(2) in 32-bit code).  Thus when a Linux signal handler
+ * completes execution, instead of returning through what would in libc be a
+ * call to setcontext(2), the rt_sigreturn(2) Linux system call is responsible
+ * for accomplishing much the same thing. It's for this reason that the stack
+ * frame we build has the lx_(rt_)sigreturn_tramp code on the top of the
+ * stack.  The code looks like this:
+ *
+ *	32-bit					64-bit
+ *	--------------------------------	-----------------------------
+ *	mov LX_SYS_rt_sigreturn, %eax		movq LX_SYS_rt_sigreturn, %rax
+ *	int $0x80				syscall
+ *
+ * We also use these same functions (lx_rt_sigreturn_tramp or
+ * lx_sigreturn_tramp) to actually return from the signal handler.
+ *
+ * (Note that this trampoline code actually lives in a proper executable segment
+ * and not on the stack, but gdb checks for the exact code sequence of the
+ * trampoline code on the stack to determine whether it is in a signal stack
+ * frame or not.  Really.)
+ *
+ * When the 32-bit Linux user signal handler is eventually called, the brand
+ * stack frame looks like this (in the case of a "modern" signal stack; see
+ * the lx_sigstack structure definition):
+ *
+ *	=========================================================
+ * |	| lx_sigdeliver_frame_t					|
+ * |	=========================================================
+ * |	| Trampoline code (marker for gdb, not really executed)	|
+ * |	=========================================================
+ * |	| Linux struct _fpstate					|
+ * |	=========================================================
+ * V	| Linux ucontext_t					| <--+
+ *	=========================================================    |
+ *	| Linux siginfo_t					| <--|-----+
+ *	=========================================================    |     |
+ *	| Pointer to Linux ucontext_t (or NULL)	(sigaction arg2)| ---+     |
+ *	=========================================================          |
+ *	| Pointer to Linux siginfo_t (or NULL)  (sigaction arg1)| ---------+
+ *	=========================================================
+ *	| Linux signal number                   (sigaction arg0)|
+ *	=========================================================
+ *	| Pointer to signal return code (trampoline code)	|
+ *	=========================================================
+ *
+ * The 64-bit stack-local data looks like this:
+ *
+ *	=========================================================
+ * |	| lx_sigdeliver_frame_t					|
+ * |	=========================================================
+ * |	| Trampoline code (marker for gdb, not really executed)	|
+ * |	=========================================================
+ * |	| Linux struct _fpstate					|
+ * |	=========================================================
+ * V	| Linux ucontext_t					| %rdx arg2
+ *	=========================================================
+ *	| Linux siginfo_t					| %rsi arg1
+ *	=========================================================
+ *	| Pointer to signal return code (trampoline code)	|
+ *	=========================================================
+ *
+ * As usual in 64-bit code, %rdi is arg0 which is the signal number.
+ *
+ * The *sigreturn(2) family of emulated system call handlers locates the
+ * "lx_sigdeliver_frame_t" struct on the Linux stack as part of processing
+ * the system call.  This object contains a guard value (LX_SIGRT_MAGIC) to
+ * detect stack smashing or an incorrect stack pointer.  It also contains a
+ * "return" context, which we use to get back to the "lx_sigdeliver()" frame
+ * on the native stack that originally dispatched to the Linux signal
+ * handler.  The lx_sigdeliver() function is then able to return to the
+ * native libc signal handler in the usual way.  This results in a further
+ * setcontext() back to whatever was running when we took the signal.
+ *
+ * There are some edge cases where the "return" context cannot be located
+ * by inspection of the Linux stack; e.g. if the guard value has been
+ * corrupted, or the emulated program has relocated parts of the signal
+ * delivery stack frame.  If this case is detected, a fallback mechanism is
+ * used to attempt to find the return context.  A chain of "lx_sigbackup_t"
+ * objects is maintained in signal interposer call frames, with the current
+ * head stored in the thread-specific "lx_tsd_t".  This mechanism is
+ * similar in principle to the "lwp_oldcontext" member of the "klwp_t" used
+ * by the native signal handling infrastructure.  This backup chain is used
+ * by the sigreturn(2) family of emulated system calls in the event that
+ * the Linux stack did not correctly reference a return context.
+ */
+
+typedef struct lx_sigdeliver_frame {
+	uintptr_t lxsdf_magic;
+	ucontext_t *lxsdf_retucp;
+	ucontext_t *lxsdf_sigucp;
+	lx_sigbackup_t *lxsdf_sigbackup;
+} lx_sigdeliver_frame_t;
+
+struct lx_oldsigstack {
+	void (*retaddr)();	/* address of real lx_sigreturn code */
+	int sig;		/* signal number */
+	lx_sigcontext_t sigc;	/* saved user context */
+	lx_fpstate_t fpstate;	/* saved FP state */
+	int sig_extra;		/* signal mask for signals [32 .. NSIG - 1] */
+	char trampoline[8];	/* code for trampoline to lx_sigreturn() */
+};
+
+/*
+ * The lx_sighandlers structure needs to be a global due to the semantics of
+ * clone().
+ *
+ * If CLONE_SIGHAND is set, the calling process and child share signal
+ * handlers, and if either calls sigaction(2) it should change the behavior
+ * in the other thread.  Each thread does, however, have its own signal mask
+ * and set of pending signals.
+ *
+ * If CLONE_SIGHAND is not set, the child process should inherit a copy of
+ * the signal handlers at the time of the clone() but later calls to
+ * sigaction(2) should only affect the individual thread calling it.
+ *
+ * This maps perfectly to a thr_create(3C) thread semantic in the first
+ * case and a fork(2)-type semantic in the second case.  By making
+ * lx_sighandlers global, we automatically get the correct behavior.
+ */
+static lx_sighandlers_t lx_sighandlers;
+
+static void lx_sigdeliver(int, siginfo_t *, ucontext_t *, size_t, void (*)(),
+    void (*)(), struct lx_sigaction *);
+
+/*
+ * stol_stack() and ltos_stack() convert between Illumos and Linux stack_t
+ * structures.
+ *
+ * These routines are needed because although the two structures have the same
+ * contents, their contents are declared in a different order, so the content
+ * of the structures cannot be copied with a simple bcopy().
+ */
+static void
+stol_stack(stack_t *fr, lx_stack_t *to)
+{
+	to->ss_sp = fr->ss_sp;
+	to->ss_flags = fr->ss_flags;
+	to->ss_size = fr->ss_size;
+}
+
+static void
+ltos_stack(lx_stack_t *fr, stack_t *to)
+{
+	to->ss_sp = fr->ss_sp;
+	to->ss_flags = fr->ss_flags;
+	to->ss_size = fr->ss_size;
+}
+
+static int
+ltos_sigset(lx_sigset_t *lx_sigsetp, sigset_t *s_sigsetp)
+{
+	lx_sigset_t l;
+	int lx_sig, sig;
+
+	if (uucopy(lx_sigsetp, &l, sizeof (lx_sigset_t)) != 0)
+		return (-errno);
+
+	(void) sigemptyset(s_sigsetp);
+
+	for (lx_sig = 1; lx_sig <= LX_NSIG; lx_sig++) {
+		if (lx_sigismember(&l, lx_sig) &&
+		    ((sig = ltos_signo[lx_sig]) > 0))
+			(void) sigaddset(s_sigsetp, sig);
+	}
+
+	return (0);
+}
+
+static int
+stol_sigset(sigset_t *s_sigsetp, lx_sigset_t *lx_sigsetp)
+{
+	lx_sigset_t l;
+	int sig, lx_sig;
+
+	bzero(&l, sizeof (lx_sigset_t));
+
+	for (sig = 1; sig < NSIG; sig++) {
+		if (sigismember(s_sigsetp, sig) &&
+		    ((lx_sig = stol_signo[sig]) > 0))
+			lx_sigaddset(&l, lx_sig);
+	}
+
+	return ((uucopy(&l, lx_sigsetp, sizeof (lx_sigset_t)) != 0)
+	    ? -errno : 0);
+}
+
+#if defined(_ILP32)
+static int
+ltos_osigset(lx_osigset_t *lx_osigsetp, sigset_t *s_sigsetp)
+{
+	lx_osigset_t lo;
+	int lx_sig, sig;
+
+	if (uucopy(lx_osigsetp, &lo, sizeof (lx_osigset_t)) != 0)
+		return (-errno);
+
+	(void) sigemptyset(s_sigsetp);
+
+	for (lx_sig = 1; lx_sig <= OSIGSET_NBITS; lx_sig++)
+		if ((lo & OSIGSET_BITSET(lx_sig)) &&
+		    ((sig = ltos_signo[lx_sig]) > 0))
+			(void) sigaddset(s_sigsetp, sig);
+
+	return (0);
+}
+
+static int
+stol_osigset(sigset_t *s_sigsetp, lx_osigset_t *lx_osigsetp)
+{
+	lx_osigset_t lo = 0;
+	int lx_sig, sig;
+
+	/*
+	 * Note that an lx_osigset_t can only represent the signals from
+	 * [1 .. OSIGSET_NBITS], so even though a signal may be present in the
+	 * Illumos sigset_t, it may not be representable as a bit in the
+	 * lx_osigset_t.
+	 */
+	for (sig = 1; sig < NSIG; sig++)
+		if (sigismember(s_sigsetp, sig) &&
+		    ((lx_sig = stol_signo[sig]) > 0) &&
+		    (lx_sig <= OSIGSET_NBITS))
+			lo |= OSIGSET_BITSET(lx_sig);
+
+	return ((uucopy(&lo, lx_osigsetp, sizeof (lx_osigset_t)) != 0)
+	    ? -errno : 0);
+}
+#endif
+
+static int
+ltos_sigcode(int si_code)
+{
+	switch (si_code) {
+		case LX_SI_USER:
+			return (SI_USER);
+		case LX_SI_TKILL:
+			return (SI_LWP);
+		case LX_SI_QUEUE:
+			return (SI_QUEUE);
+		case LX_SI_TIMER:
+			return (SI_TIMER);
+		case LX_SI_ASYNCIO:
+			return (SI_ASYNCIO);
+		case LX_SI_MESGQ:
+			return (SI_MESGQ);
+		default:
+			return (LX_SI_CODE_NOT_EXIST);
+	}
+}
+
+int
+stol_siginfo(siginfo_t *siginfop, lx_siginfo_t *lx_siginfop)
+{
+	int ret = 0;
+	lx_siginfo_t lx_siginfo;
+
+	bzero(&lx_siginfo, sizeof (*lx_siginfop));
+
+	if ((lx_siginfo.lsi_signo = stol_signo[siginfop->si_signo]) <= 0) {
+		/*
+		 * Depending on the caller we may still need to get a usable
+		 * converted siginfo struct.
+		 */
+		lx_siginfo.lsi_signo = LX_SIGKILL;
+		errno = EINVAL;
+		ret = -1;
+	}
+
+	lx_siginfo.lsi_code = lx_stol_sigcode(siginfop->si_code);
+	lx_siginfo.lsi_errno = siginfop->si_errno;
+
+	switch (lx_siginfo.lsi_signo) {
+		/*
+		 * Semantics ARE defined for SIGKILL, but since
+		 * we can't catch it, we can't translate it. :-(
+		 */
+		case LX_SIGPOLL:
+			lx_siginfo.lsi_band = siginfop->si_band;
+			lx_siginfo.lsi_fd = siginfop->si_fd;
+			break;
+
+		case LX_SIGCHLD:
+			lx_siginfo.lsi_pid = siginfop->si_pid;
+			if (siginfop->si_code <= 0 || siginfop->si_code ==
+			    CLD_EXITED) {
+				lx_siginfo.lsi_status = siginfop->si_status;
+			} else {
+				lx_siginfo.lsi_status = lx_stol_status(
+				    siginfop->si_status, -1);
+			}
+			lx_siginfo.lsi_utime = siginfop->si_utime;
+			lx_siginfo.lsi_stime = siginfop->si_stime;
+			break;
+
+		case LX_SIGILL:
+		case LX_SIGBUS:
+		case LX_SIGFPE:
+		case LX_SIGSEGV:
+			lx_siginfo.lsi_addr = siginfop->si_addr;
+			break;
+
+		default:
+			lx_siginfo.lsi_pid = siginfop->si_pid;
+			lx_siginfo.lsi_uid =
+			    LX_UID32_TO_UID16(siginfop->si_uid);
+			lx_siginfo.lsi_value = siginfop->si_value;
+			break;
+	}
+
+	if (uucopy(&lx_siginfo, lx_siginfop, sizeof (lx_siginfo_t)) != 0)
+		return (-errno);
+	return ((ret != 0) ? -errno : 0);
+}
+
+static void
+stol_fpstate(fpregset_t *fpr, lx_fpstate_t *lfpr)
+{
+	size_t copy_len;
+
+#if defined(_LP64)
+	/*
+	 * The 64-bit Illumos struct fpregset_t and lx_fpstate_t are identical
+	 * so just bcopy() those entries (see usr/src/uts/intel/sys/regset.h
+	 * for __amd64's struct fpu).
+	 */
+	copy_len = sizeof (fpr->fp_reg_set.fpchip_state);
+	bcopy(fpr, lfpr, copy_len);
+
+#else /* is _ILP32 */
+	struct _fpstate *fpsp = (struct _fpstate *)fpr;
+
+	/*
+	 * The Illumos struct _fpstate and lx_fpstate_t are identical from the
+	 * beginning of the structure to the lx_fpstate_t "magic" field, so
+	 * just bcopy() those entries.
+	 */
+	copy_len = (size_t)&(((lx_fpstate_t *)0)->magic);
+	bcopy(fpsp, lfpr, copy_len);
+
+	/*
+	 * These fields are all only significant for the first 16 bits.
+	 */
+	lfpr->cw &= 0xffff;		/* x87 control word */
+	lfpr->tag &= 0xffff;		/* x87 tag word */
+	lfpr->cssel &= 0xffff;		/* cs selector */
+	lfpr->datasel &= 0xffff;	/* ds selector */
+
+	/*
+	 * Linux wants the x87 status word field to contain the value of the
+	 * x87 saved exception status word.
+	 */
+	lfpr->sw = lfpr->status & 0xffff;	/* x87 status word */
+
+	lfpr->mxcsr = fpsp->mxcsr;
+
+	if (fpsp->mxcsr != 0) {
+		/*
+		 * Linux uses the "magic" field to denote whether the XMM
+		 * registers contain legal data or not.  Since we can't get to
+		 * %cr4 from userland to check the status of the OSFXSR bit,
+		 * check the mxcsr field to see if it's 0, which it should
+		 * never be on a system with the OXFXSR bit enabled.
+		 */
+		lfpr->magic = LX_X86_FXSR_MAGIC;
+		bcopy(fpsp->xmm, lfpr->_xmm, sizeof (lfpr->_xmm));
+	} else {
+		lfpr->magic = LX_X86_FXSR_NONE;
+	}
+#endif
+}
+
+static void
+ltos_fpstate(lx_fpstate_t *lfpr, fpregset_t *fpr)
+{
+	size_t copy_len;
+
+#if defined(_LP64)
+	/*
+	 * The 64-bit Illumos struct fpregset_t and lx_fpstate_t are identical
+	 * so just bcopy() those entries (see usr/src/uts/intel/sys/regset.h
+	 * for __amd64's struct fpu).
+	 */
+	copy_len = sizeof (fpr->fp_reg_set.fpchip_state);
+	bcopy(lfpr, fpr, copy_len);
+
+#else /* is _ILP32 */
+	struct _fpstate *fpsp = (struct _fpstate *)fpr;
+
+	/*
+	 * The lx_fpstate_t and Illumos struct _fpstate are identical from the
+	 * beginning of the structure to the struct _fpstate "mxcsr" field, so
+	 * just bcopy() those entries.
+	 *
+	 * Note that we do NOT have to propogate changes the user may have made
+	 * to the "status" word back to the "sw" word, unlike the way we have
+	 * to deal with processing the ESP and UESP register values on return
+	 * from a signal handler.
+	 */
+	copy_len = (size_t)&(((struct _fpstate *)0)->mxcsr);
+	bcopy(lfpr, fpsp, copy_len);
+
+	/*
+	 * These fields are all only significant for the first 16 bits.
+	 */
+	fpsp->cw &= 0xffff;		/* x87 control word */
+	fpsp->sw &= 0xffff;		/* x87 status word */
+	fpsp->tag &= 0xffff;		/* x87 tag word */
+	fpsp->cssel &= 0xffff;		/* cs selector */
+	fpsp->datasel &= 0xffff;	/* ds selector */
+	fpsp->status &= 0xffff;		/* saved status */
+
+	fpsp->mxcsr = lfpr->mxcsr;
+
+	if (lfpr->magic == LX_X86_FXSR_MAGIC)
+		bcopy(lfpr->_xmm, fpsp->xmm, sizeof (fpsp->xmm));
+#endif
+}
+
+/*
+ * We do not use the system sigaltstack() infrastructure as that would conflict
+ * with our handling of both system call emulation and native signals on the
+ * native stack.  Instead, we track the Linux stack structure in our
+ * thread-specific data.  This function is modeled on the behaviour of the
+ * native sigaltstack system call handler.
+ */
+long
+lx_sigaltstack(uintptr_t ssp, uintptr_t oss)
+{
+	lx_tsd_t *lxtsd = lx_get_tsd();
+	lx_stack_t ss;
+
+	if (ssp != NULL) {
+		if (lxtsd->lxtsd_sigaltstack.ss_flags & LX_SS_ONSTACK) {
+			/*
+			 * If we are currently using the installed alternate
+			 * stack for signal handling, the user may not modify
+			 * the stack for this thread.
+			 */
+			return (-EPERM);
+		}
+
+		if (uucopy((void *)ssp, &ss, sizeof (ss)) != 0) {
+			return (-EFAULT);
+		}
+
+		if (ss.ss_flags & ~LX_SS_DISABLE) {
+			/*
+			 * The user may not specify a value for flags other
+			 * than 0 or SS_DISABLE.
+			 */
+			return (-EINVAL);
+		}
+
+		if (!(ss.ss_flags & LX_SS_DISABLE) && ss.ss_size <
+		    LX_MINSIGSTKSZ) {
+			return (-ENOMEM);
+		}
+
+		if ((ss.ss_flags & LX_SS_DISABLE) != 0) {
+			ss.ss_sp = NULL;
+			ss.ss_size = 0;
+		}
+	}
+
+	if (oss != NULL) {
+		/*
+		 * User provided old and new stack_t pointers may point to
+		 * the same location.  Copy out before we modify.
+		 */
+		if (uucopy(&lxtsd->lxtsd_sigaltstack, (void *)oss,
+		    sizeof (lxtsd->lxtsd_sigaltstack)) != 0) {
+			return (-EFAULT);
+		}
+	}
+
+	if (ssp != NULL) {
+		lxtsd->lxtsd_sigaltstack = ss;
+	}
+
+	return (0);
+}
+
+#if defined(_ILP32)
+/*
+ * The following routines are needed because sigset_ts and siginfo_ts are
+ * different in format between Linux and Illumos.
+ *
+ * Note that there are two different lx_sigset structures, lx_sigset_ts and
+ * lx_osigset_ts:
+ *
+ *    + An lx_sigset_t is the equivalent of a Illumos sigset_t and supports
+ *	more than 32 signals.
+ *
+ *    + An lx_osigset_t is simply a uint32_t, so it by definition only supports
+ *	32 signals.
+ *
+ * When there are two versions of a routine, one prefixed with lx_rt_ and
+ * one prefixed with lx_ alone, in GENERAL the lx_rt_ routines deal with
+ * lx_sigset_ts while the lx_ routines deal with lx_osigset_ts.  Unfortunately,
+ * this is not always the case (e.g. lx_sigreturn() vs. lx_rt_sigreturn())
+ */
+long
+lx_sigpending(uintptr_t sigpend)
+{
+	sigset_t sigpendset;
+
+	if (sigpending(&sigpendset) != 0)
+		return (-errno);
+
+	return (stol_osigset(&sigpendset, (lx_osigset_t *)sigpend));
+}
+#endif
+
+long
+lx_rt_sigpending(uintptr_t sigpend, uintptr_t setsize)
+{
+	sigset_t sigpendset;
+
+	if ((size_t)setsize != sizeof (lx_sigset_t))
+		return (-EINVAL);
+
+	if (sigpending(&sigpendset) != 0)
+		return (-errno);
+
+	return (stol_sigset(&sigpendset, (lx_sigset_t *)sigpend));
+}
+
+/*
+ * Create a common routine to encapsulate all of the sigprocmask code,
+ * as the only difference between lx_sigprocmask() and lx_rt_sigprocmask()
+ * is the usage of lx_osigset_ts vs. lx_sigset_ts, as toggled in the code by
+ * the setting of the "sigset_type" flag.
+ */
+static int
+lx_sigprocmask_common(uintptr_t how, uintptr_t l_setp, uintptr_t l_osetp,
+    uintptr_t sigset_type)
+{
+	int err = 0;
+	sigset_t set, oset;
+	sigset_t *s_setp = NULL;
+	sigset_t *s_osetp;
+
+	if (l_setp) {
+		switch (how) {
+			case LX_SIG_BLOCK:
+				how = SIG_BLOCK;
+				break;
+
+			case LX_SIG_UNBLOCK:
+				how = SIG_UNBLOCK;
+				break;
+
+			case LX_SIG_SETMASK:
+				how = SIG_SETMASK;
+				break;
+
+			default:
+				return (-EINVAL);
+		}
+
+		s_setp = &set;
+
+		/* Only 32-bit code passes other than USE_SIGSET */
+		if (sigset_type == USE_SIGSET)
+			err = ltos_sigset((lx_sigset_t *)l_setp, s_setp);
+#if defined(_ILP32)
+		else
+			err = ltos_osigset((lx_osigset_t *)l_setp, s_setp);
+#endif
+
+		if (err != 0)
+			return (err);
+
+	}
+
+	s_osetp = (l_osetp ? &oset : NULL);
+
+	/*
+	 * In a multithreaded environment, a call to sigprocmask(2) should
+	 * only affect the current thread's signal mask so we don't need to
+	 * explicitly call thr_sigsetmask(3C) here.
+	 */
+	if (sigprocmask(how, s_setp, s_osetp) != 0)
+		return (-errno);
+
+	if (l_osetp) {
+		if (sigset_type == USE_SIGSET)
+			err = stol_sigset(s_osetp, (lx_sigset_t *)l_osetp);
+#if defined(_ILP32)
+		else
+			err = stol_osigset(s_osetp, (lx_osigset_t *)l_osetp);
+#endif
+
+		if (err != 0) {
+			/*
+			 * Encountered a fault while writing to the old signal
+			 * mask buffer, so unwind the signal mask change made
+			 * above.
+			 */
+			(void) sigprocmask(how, s_osetp, (sigset_t *)NULL);
+			return (err);
+		}
+	}
+
+	return (0);
+}
+
+#if defined(_ILP32)
+long
+lx_sigprocmask(uintptr_t how, uintptr_t setp, uintptr_t osetp)
+{
+	return (lx_sigprocmask_common(how, setp, osetp, USE_OSIGSET));
+}
+#endif
+
+long
+lx_rt_sigprocmask(uintptr_t how, uintptr_t setp, uintptr_t osetp,
+    uintptr_t setsize)
+{
+	if ((size_t)setsize != sizeof (lx_sigset_t))
+		return (-EINVAL);
+
+	return (lx_sigprocmask_common(how, setp, osetp, USE_SIGSET));
+}
+
+#if defined(_ILP32)
+long
+lx_sigsuspend(uintptr_t set)
+{
+	sigset_t s_set;
+
+	if (ltos_osigset((lx_osigset_t *)set, &s_set) != 0)
+		return (-errno);
+
+	return ((sigsuspend(&s_set) == -1) ? -errno : 0);
+}
+#endif
+
+long
+lx_rt_sigsuspend(uintptr_t set, uintptr_t setsize)
+{
+	sigset_t s_set;
+
+	if ((size_t)setsize != sizeof (lx_sigset_t))
+		return (-EINVAL);
+
+	if (ltos_sigset((lx_sigset_t *)set, &s_set) != 0)
+		return (-errno);
+
+	return ((sigsuspend(&s_set) == -1) ? -errno : 0);
+}
+
+long
+lx_rt_sigwaitinfo(uintptr_t set, uintptr_t sinfo, uintptr_t setsize)
+{
+	sigset_t s_set;
+	siginfo_t s_sinfo, *s_sinfop;
+	int rc;
+
+	lx_sigset_t *setp = (lx_sigset_t *)set;
+	lx_siginfo_t *sinfop = (lx_siginfo_t *)sinfo;
+
+	if ((size_t)setsize != sizeof (lx_sigset_t))
+		return (-EINVAL);
+
+	if (ltos_sigset(setp, &s_set) != 0)
+		return (-errno);
+
+	s_sinfop = (sinfop == NULL) ? NULL : &s_sinfo;
+
+	if ((rc = sigwaitinfo(&s_set, s_sinfop)) == -1)
+		return (-errno);
+
+	if (s_sinfop == NULL)
+		return (stol_signo[rc]);
+
+	return ((stol_siginfo(s_sinfop, sinfop) != 0)
+	    ? -errno : stol_signo[rc]);
+}
+
+long
+lx_rt_sigtimedwait(uintptr_t set, uintptr_t sinfo, uintptr_t toutp,
+    uintptr_t setsize)
+{
+	sigset_t s_set;
+	siginfo_t s_sinfo, *s_sinfop;
+	int rc;
+
+	lx_sigset_t *setp = (lx_sigset_t *)set;
+	lx_siginfo_t *sinfop = (lx_siginfo_t *)sinfo;
+
+	if ((size_t)setsize != sizeof (lx_sigset_t))
+		return (-EINVAL);
+
+	if (ltos_sigset(setp, &s_set) != 0)
+		return (-errno);
+
+	s_sinfop = (sinfop == NULL) ? NULL : &s_sinfo;
+
+	/*
+	 * "If timeout is the NULL pointer, the behavior is unspecified."
+	 * Match what LTP expects.
+	 */
+	if ((rc = sigtimedwait(&s_set, s_sinfop,
+	    (struct timespec *)toutp)) == -1)
+		return (toutp == NULL ? -EINTR : -errno);
+
+	if (s_sinfop == NULL)
+		return (stol_signo[rc]);
+
+	return ((stol_siginfo(s_sinfop, sinfop) != 0)
+	    ? -errno : stol_signo[rc]);
+}
+
+static void
+lx_sigreturn_find_native_context(const char *caller, ucontext_t **sigucp,
+    ucontext_t **retucp, uintptr_t sp)
+{
+	lx_tsd_t *lxtsd = lx_get_tsd();
+	lx_sigdeliver_frame_t *lxsdfp = (lx_sigdeliver_frame_t *)sp;
+	lx_sigdeliver_frame_t lxsdf;
+	boolean_t copy_ok;
+
+	lx_debug("%s: reading lx_sigdeliver_frame_t @ %p\n", caller, lxsdfp);
+	if (uucopy(lxsdfp, &lxsdf, sizeof (lxsdf)) != 0) {
+		lx_debug("%s: failed to read lx_sigdeliver_frame_t @ %p\n",
+		    lxsdfp);
+
+		copy_ok = B_FALSE;
+	} else {
+		lx_debug("%s: lxsdf: magic %p retucp %p sigucp %p\n", caller,
+		    lxsdf.lxsdf_magic, lxsdf.lxsdf_retucp, lxsdf.lxsdf_sigucp);
+
+		copy_ok = B_TRUE;
+	}
+
+	/*
+	 * lx_sigdeliver() pushes a lx_sigdeliver_frame_t onto the stack
+	 * before it creates the struct lx_oldsigstack.
+	 */
+	if (copy_ok && lxsdf.lxsdf_magic == LX_SIGRT_MAGIC) {
+		LX_SIGNAL_DELIVERY_FRAME_FOUND(lxsdfp);
+
+		/*
+		 * The guard value is intact; use the context pointers stored
+		 * in the signal delivery frame:
+		 */
+		*sigucp = lxsdf.lxsdf_sigucp;
+		*retucp = lxsdf.lxsdf_retucp;
+
+		/*
+		 * Ensure that the backup signal delivery chain is in sync with
+		 * the frame we are returning via:
+		 */
+		lxtsd->lxtsd_sigbackup = lxsdf.lxsdf_sigbackup;
+	} else {
+		/*
+		 * The guard value was not intact.  Either the program smashed
+		 * the stack unintentionally, or worse: intentionally moved
+		 * some parts of the signal delivery frame we constructed to
+		 * another location before calling rt_sigreturn(2).
+		 */
+		LX_SIGNAL_DELIVERY_FRAME_CORRUPT(lxsdfp);
+
+		if (lxtsd->lxtsd_sigbackup == NULL) {
+			/*
+			 * There was no backup context to use, so we must
+			 * kill the process.
+			 */
+			if (copy_ok) {
+				lx_err_fatal("%s: sp 0x%p, expected 0x%x, "
+				    "found 0x%lx!", caller, (void *)sp,
+				    LX_SIGRT_MAGIC,
+				    (unsigned long)lxsdf.lxsdf_magic);
+			} else {
+				lx_err_fatal("%s: sp 0x%p, could not read "
+				    "magic", caller, (void *)sp);
+			}
+		}
+
+		/*
+		 * Attempt to recover by using the backup signal delivery
+		 * chain:
+		 */
+		lx_debug("%s: SIGRT_MAGIC not found @ sp %p; using backup "
+		    "@ %p\n", caller, (void *)sp, lxtsd->lxtsd_sigbackup);
+		*sigucp = lxtsd->lxtsd_sigbackup->lxsb_sigucp;
+		*retucp = lxtsd->lxtsd_sigbackup->lxsb_retucp;
+	}
+}
+
+#if defined(_ILP32)
+/*
+ * Intercept the Linux sigreturn() syscall to turn it into the return through
+ * the libc call stack that Illumos expects.
+ *
+ * When control returns to libc's call_user_handler() routine, a setcontext(2)
+ * will be done that returns thread execution to the point originally
+ * interrupted by receipt of the signal.
+ *
+ * This is only used by 32-bit code.
+ */
+long
+lx_sigreturn(void)
+{
+	struct lx_oldsigstack *lx_ossp;
+	lx_sigset_t lx_sigset;
+	ucontext_t *ucp;
+	ucontext_t *sigucp;
+	ucontext_t *retucp;
+	uintptr_t sp;
+
+	ucp = lx_syscall_regs();
+
+	/*
+	 * NOTE:  The sp saved in the context is eight bytes off of where we
+	 *	  need it to be (either due to trampoline or the copying of
+	 *	  sp = uesp, not clear which).
+	 */
+	sp = LX_REG(ucp, REG_SP) - 8;
+
+	/*
+	 * At this point, the stack pointer should point to the struct
+	 * lx_oldsigstack that lx_build_old_signal_frame() constructed and
+	 * placed on the stack.  We need to reference it a bit later, so
+	 * save a pointer to it before incrementing our copy of the sp.
+	 */
+	lx_ossp = (struct lx_oldsigstack *)sp;
+	sp += SA(sizeof (struct lx_oldsigstack));
+
+	lx_sigreturn_find_native_context(__func__, &sigucp, &retucp, sp);
+
+	/*
+	 * We need to copy machine registers the Linux signal handler may have
+	 * modified back to the Illumos ucontext_t.
+	 *
+	 * General registers copy across as-is, except Linux expects that
+	 * changes made to uc_mcontext.gregs[ESP] will be reflected when the
+	 * interrupted thread resumes execution after the signal handler. To
+	 * emulate this behavior, we must modify uc_mcontext.gregs[UESP] to
+	 * match uc_mcontext.gregs[ESP] as Illumos will restore the UESP
+	 * value to ESP.
+	 */
+	lx_ossp->sigc.sc_esp_at_signal = lx_ossp->sigc.sc_esp;
+	bcopy(&lx_ossp->sigc, &sigucp->uc_mcontext, sizeof (gregset_t));
+
+	LX_SIGRETURN(NULL, sigucp, sp);
+
+	/* copy back FP regs if present */
+	if (lx_ossp->sigc.sc_fpstate != NULL)
+		ltos_fpstate(&lx_ossp->fpstate, &sigucp->uc_mcontext.fpregs);
+
+	/* convert Linux signal mask back to its Illumos equivalent */
+	bzero(&lx_sigset, sizeof (lx_sigset_t));
+	lx_sigset.__bits[0] = lx_ossp->sigc.sc_mask;
+	lx_sigset.__bits[1] = lx_ossp->sig_extra;
+	(void) ltos_sigset(&lx_sigset, &sigucp->uc_sigmask);
+
+	/*
+	 * For signal mask handling to be done properly, this call needs to
+	 * return to the libc routine that originally called the signal handler
+	 * rather than directly set the context back to the place the signal
+	 * interrupted execution as the original Linux code would do.
+	 */
+	lx_debug("lx_sigreturn: calling setcontext; retucp %p flags %lx "
+	    "link %p\n", retucp, retucp->uc_flags, retucp->uc_link);
+	(void) setcontext(retucp);
+	assert(0);
+
+	/*NOTREACHED*/
+	return (0);
+}
+#endif
+
+/*
+ * This signal return syscall is used by both 32-bit and 64-bit code.
+ */
+long
+lx_rt_sigreturn(void)
+{
+	struct lx_sigstack *lx_ssp;
+	lx_ucontext_t *lx_ucp;
+	ucontext_t *ucp;
+	ucontext_t *sigucp;
+	ucontext_t *retucp;
+	uintptr_t sp;
+
+	/*
+	 * Since we don't take the normal return path from this syscall, we
+	 * inform the kernel that we're returning, for the sake of ptrace.
+	 */
+	(void) syscall(SYS_brand, B_PTRACE_SIG_RETURN);
+
+	/* Get the registers at the emulated Linux rt_sigreturn syscall */
+	ucp = lx_syscall_regs();
+
+#if defined(_ILP32)
+	lx_debug("lx_rt_sigreturn: ESP %p UESP %p\n", LX_REG(ucp, ESP),
+	    LX_REG(ucp, UESP));
+	/*
+	 * For 32-bit
+	 *
+	 * NOTE:  Because of the silly compatibility measures done in the
+	 *	  signal trampoline code to make sure the stack holds the
+	 *	   _exact same_  instruction sequence Linux does, we have to
+	 *	  manually "pop" some extra instructions off the stack here
+	 *	  before passing the stack address to the syscall because the
+	 *	  trampoline code isn't allowed to do it due to the gdb
+	 *	  compatability issues.
+	 *
+	 *	  No, I'm not kidding.
+	 *
+	 *	  The sp saved in the context is eight bytes off of where we
+	 *	  need it to be (either due to trampoline or the copying of
+	 *	  sp = uesp, not clear which but looks like the uesp case), so
+	 *	  the need to pop the extra four byte instruction means we need
+	 *	  to subtract  a net four bytes from the sp before "popping" the
+	 *	  struct lx_sigstack off the stack.
+	 *
+	 *	  This will yield the value the stack pointer had before
+	 *	  lx_sigdeliver() created the stack frame for the Linux signal
+	 *	  handler.
+	 */
+	sp = (uintptr_t)LX_REG(ucp, REG_SP) - 4;
+#else
+	/*
+	 * We need to make an adjustment for 64-bit code as well. Since 64-bit
+	 * does not use the trampoline, it's probably for the same reason as
+	 * alluded to above.
+	 */
+	sp = (uintptr_t)LX_REG(ucp, REG_SP) - 8;
+#endif
+
+	/*
+	 * At this point, the stack pointer should point to the struct
+	 * lx_sigstack that lx_build_signal_frame() constructed and
+	 * placed on the stack.  We need to reference it a bit later, so
+	 * save a pointer to it before incrementing our copy of the sp.
+	 */
+	lx_ssp = (struct lx_sigstack *)sp;
+	sp += SA(sizeof (struct lx_sigstack));
+
+#if defined(_LP64)
+	/*
+	 * The 64-bit lx_sigdeliver() inserts 8 bytes of padding between
+	 * the lx_sigstack_t and the delivery frame to maintain ABI stack
+	 * alignment.
+	 */
+	sp += 8;
+#endif
+
+	lx_sigreturn_find_native_context(__func__, &sigucp, &retucp, sp);
+
+	/*
+	 * We need to copy machine registers the Linux signal handler may have
+	 * modified back to the Illumos version.
+	 */
+#if defined(_LP64)
+	lx_ucp = &lx_ssp->uc;
+
+	/*
+	 * General register layout is completely different.
+	 */
+	LX_REG(sigucp, REG_R15) = lx_ucp->uc_sigcontext.sc_r15;
+	LX_REG(sigucp, REG_R14) = lx_ucp->uc_sigcontext.sc_r14;
+	LX_REG(sigucp, REG_R13) = lx_ucp->uc_sigcontext.sc_r13;
+	LX_REG(sigucp, REG_R12) = lx_ucp->uc_sigcontext.sc_r12;
+	LX_REG(sigucp, REG_R11) = lx_ucp->uc_sigcontext.sc_r11;
+	LX_REG(sigucp, REG_R10) = lx_ucp->uc_sigcontext.sc_r10;
+	LX_REG(sigucp, REG_R9) = lx_ucp->uc_sigcontext.sc_r9;
+	LX_REG(sigucp, REG_R8) = lx_ucp->uc_sigcontext.sc_r8;
+	LX_REG(sigucp, REG_RDI) = lx_ucp->uc_sigcontext.sc_rdi;
+	LX_REG(sigucp, REG_RSI) = lx_ucp->uc_sigcontext.sc_rsi;
+	LX_REG(sigucp, REG_RBP) = lx_ucp->uc_sigcontext.sc_rbp;
+	LX_REG(sigucp, REG_RBX) = lx_ucp->uc_sigcontext.sc_rbx;
+	LX_REG(sigucp, REG_RDX) = lx_ucp->uc_sigcontext.sc_rdx;
+	LX_REG(sigucp, REG_RCX) = lx_ucp->uc_sigcontext.sc_rcx;
+	LX_REG(sigucp, REG_RAX) = lx_ucp->uc_sigcontext.sc_rax;
+	LX_REG(sigucp, REG_TRAPNO) = lx_ucp->uc_sigcontext.sc_trapno;
+	LX_REG(sigucp, REG_ERR) = lx_ucp->uc_sigcontext.sc_err;
+	LX_REG(sigucp, REG_RIP) = lx_ucp->uc_sigcontext.sc_rip;
+	LX_REG(sigucp, REG_CS) = lx_ucp->uc_sigcontext.sc_cs;
+	LX_REG(sigucp, REG_RFL) = lx_ucp->uc_sigcontext.sc_eflags;
+	LX_REG(sigucp, REG_RSP) = lx_ucp->uc_sigcontext.sc_rsp;
+	LX_REG(sigucp, REG_SS) = lx_ucp->uc_sigcontext.sc_pad0;
+	LX_REG(sigucp, REG_FS) = lx_ucp->uc_sigcontext.sc_fs;
+	LX_REG(sigucp, REG_GS) = lx_ucp->uc_sigcontext.sc_gs;
+
+#else /* is _ILP32 */
+	lx_ucp = &lx_ssp->uc;
+
+	/*
+	 * Illumos and Linux both follow the SysV i386 ABI layout for the
+	 * mcontext.
+	 *
+	 * General registers copy across as-is, except Linux expects that
+	 * changes made to uc_mcontext.gregs[ESP] will be reflected when the
+	 * interrupted thread resumes execution after the signal handler. To
+	 * emulate this behavior, we must modify uc_mcontext.gregs[UESP] to
+	 * match uc_mcontext.gregs[ESP] as Illumos will restore the UESP value
+	 * to ESP.
+	 */
+	lx_ucp->uc_sigcontext.sc_esp_at_signal = lx_ucp->uc_sigcontext.sc_esp;
+
+	bcopy(&lx_ucp->uc_sigcontext, &sigucp->uc_mcontext.gregs,
+	    sizeof (gregset_t));
+#endif
+
+	LX_SIGRETURN(lx_ucp, sigucp, sp);
+
+	if (lx_ucp->uc_sigcontext.sc_fpstate != NULL) {
+		ltos_fpstate(lx_ucp->uc_sigcontext.sc_fpstate,
+		    &sigucp->uc_mcontext.fpregs);
+	}
+
+	/*
+	 * Convert the Linux signal mask and stack back to their
+	 * Illumos equivalents.
+	 */
+	(void) ltos_sigset(&lx_ucp->uc_sigmask, &sigucp->uc_sigmask);
+	ltos_stack(&lx_ucp->uc_stack, &sigucp->uc_stack);
+
+	/*
+	 * For signal mask handling to be done properly, this call needs to
+	 * return to the libc routine that originally called the signal handler
+	 * rather than directly set the context back to the place the signal
+	 * interrupted execution as the original Linux code would do.
+	 */
+	lx_debug("lx_rt_sigreturn: calling setcontext; retucp %p\n", retucp);
+	(void) setcontext(retucp);
+	assert(0);
+
+	/*NOTREACHED*/
+	return (0);
+}
+
+
+#if defined(_ILP32)
+/*
+ * Build signal frame for processing for "old" (legacy) Linux signals
+ * This stack-builder function is only used by 32-bit code.
+ */
+/* ARGSUSED4 */
+static void
+lx_build_old_signal_frame(int lx_sig, siginfo_t *sip, void *p, void *sp,
+    uintptr_t *hargs)
+{
+	extern void lx_sigreturn_tramp();
+
+	lx_sigset_t lx_sigset;
+	ucontext_t *ucp = (ucontext_t *)p;
+	struct lx_sigaction *lxsap;
+	struct lx_oldsigstack *lx_ossp = sp;
+
+	lx_debug("building old signal frame for lx sig %d at 0x%p", lx_sig, sp);
+
+	lx_ossp->sig = lx_sig;
+	lxsap = &lx_sighandlers.lx_sa[lx_sig];
+	lx_debug("lxsap @ 0x%p", lxsap);
+
+	if (lxsap && (lxsap->lxsa_flags & LX_SA_RESTORER) &&
+	    lxsap->lxsa_restorer) {
+		lx_ossp->retaddr = lxsap->lxsa_restorer;
+		lx_debug("lxsa_restorer exists @ 0x%p", lx_ossp->retaddr);
+	} else {
+		lx_ossp->retaddr = lx_sigreturn_tramp;
+		lx_debug("lx_ossp->retaddr set to 0x%p", lx_sigreturn_tramp);
+	}
+
+	lx_debug("osf retaddr = 0x%p", lx_ossp->retaddr);
+
+	/* convert Illumos signal mask and stack to their Linux equivalents */
+	(void) stol_sigset(&ucp->uc_sigmask, &lx_sigset);
+	lx_ossp->sigc.sc_mask = lx_sigset.__bits[0];
+	lx_ossp->sig_extra = lx_sigset.__bits[1];
+
+	/*
+	 * General registers copy across as-is, except Linux expects that
+	 * uc_mcontext.gregs[ESP] == uc_mcontext.gregs[UESP] on receipt of a
+	 * signal.
+	 */
+	bcopy(&ucp->uc_mcontext, &lx_ossp->sigc, sizeof (gregset_t));
+	lx_ossp->sigc.sc_esp = lx_ossp->sigc.sc_esp_at_signal;
+
+	/*
+	 * cr2 contains the faulting address, and Linux only sets cr2 for a
+	 * a segmentation fault.
+	 */
+	lx_ossp->sigc.sc_cr2 = (((lx_sig == LX_SIGSEGV) && (sip)) ?
+	    (uintptr_t)sip->si_addr : 0);
+
+	/* convert FP regs if present */
+	if (ucp->uc_flags & UC_FPU) {
+		stol_fpstate(&ucp->uc_mcontext.fpregs, &lx_ossp->fpstate);
+		lx_ossp->sigc.sc_fpstate = &lx_ossp->fpstate;
+	} else {
+		lx_ossp->sigc.sc_fpstate = NULL;
+	}
+
+	/*
+	 * Believe it or not, gdb wants to SEE the trampoline code on the
+	 * bottom of the stack to determine whether the stack frame belongs to
+	 * a signal handler, even though this code is no longer actually
+	 * called.
+	 *
+	 * You can't make this stuff up.
+	 */
+	bcopy((void *)lx_sigreturn_tramp, lx_ossp->trampoline,
+	    sizeof (lx_ossp->trampoline));
+}
+#endif
+
+/*
+ * Build stack frame (32-bit) or stack local data (64-bit) for processing for
+ * modern Linux signals. This is the only stack-builder function for 64-bit
+ * code (32-bit code also calls this when using "modern" signals).
+ */
+/* ARGSUSED4 */
+static void
+lx_build_signal_frame(int lx_sig, siginfo_t *sip, void *p, void *sp,
+    uintptr_t *hargs)
+{
+	extern void lx_rt_sigreturn_tramp();
+
+	lx_ucontext_t *lx_ucp;
+	ucontext_t *ucp = (ucontext_t *)p;
+	struct lx_sigstack *lx_ssp = sp;
+	struct lx_sigaction *lxsap;
+
+	lx_debug("building signal frame for lx sig %d at 0x%p", lx_sig, sp);
+
+	lx_ucp = &lx_ssp->uc;
+#if defined(_ILP32)
+	/*
+	 * Arguments are passed to the 32-bit signal handler on the stack.
+	 */
+	lx_ssp->ucp = lx_ucp;
+	lx_ssp->sip = sip != NULL ? &lx_ssp->si : NULL;
+	lx_ssp->sig = lx_sig;
+#else
+	/*
+	 * Arguments to the 64-bit signal handler are passed in registers:
+	 *   hdlr(int sig, siginfo_t *sip, void *ucp);
+	 */
+	hargs[0] = lx_sig;
+	hargs[1] = sip != NULL ? (uintptr_t)&lx_ssp->si : NULL;
+	hargs[2] = (uintptr_t)lx_ucp;
+#endif
+
+	lxsap = &lx_sighandlers.lx_sa[lx_sig];
+	lx_debug("lxsap @ 0x%p", lxsap);
+
+	if (lxsap && (lxsap->lxsa_flags & LX_SA_RESTORER) &&
+	    lxsap->lxsa_restorer) {
+		/*
+		 * lxsa_restorer is explicitly set by sigaction in 32-bit code
+		 * but it can also be implicitly set for both 32 and 64 bit
+		 * code via lx_sigaction_common when we bcopy the user-supplied
+		 * lx_sigaction element into the proper slot in the sighandler
+		 * array.
+		 */
+		lx_ssp->retaddr = lxsap->lxsa_restorer;
+		lx_debug("lxsa_restorer exists @ 0x%p", lx_ssp->retaddr);
+	} else {
+		lx_ssp->retaddr = lx_rt_sigreturn_tramp;
+		lx_debug("lx_ssp->retaddr set to 0x%p", lx_rt_sigreturn_tramp);
+	}
+
+	/* Linux has these fields but always clears them to 0 */
+	lx_ucp->uc_flags = 0;
+	lx_ucp->uc_link = NULL;
+
+	/* convert Illumos signal mask and stack to their Linux equivalents */
+	(void) stol_sigset(&ucp->uc_sigmask, &lx_ucp->uc_sigmask);
+	stol_stack(&ucp->uc_stack, &lx_ucp->uc_stack);
+
+#if defined(_LP64)
+	/*
+	 * General register layout is completely different.
+	 */
+	lx_ucp->uc_sigcontext.sc_r8 = LX_REG(ucp, REG_R8);
+	lx_ucp->uc_sigcontext.sc_r9 = LX_REG(ucp, REG_R9);
+	lx_ucp->uc_sigcontext.sc_r10 = LX_REG(ucp, REG_R10);
+	lx_ucp->uc_sigcontext.sc_r11 = LX_REG(ucp, REG_R11);
+	lx_ucp->uc_sigcontext.sc_r12 = LX_REG(ucp, REG_R12);
+	lx_ucp->uc_sigcontext.sc_r13 = LX_REG(ucp, REG_R13);
+	lx_ucp->uc_sigcontext.sc_r14 = LX_REG(ucp, REG_R14);
+	lx_ucp->uc_sigcontext.sc_r15 = LX_REG(ucp, REG_R15);
+	lx_ucp->uc_sigcontext.sc_rdi = LX_REG(ucp, REG_RDI);
+	lx_ucp->uc_sigcontext.sc_rsi = LX_REG(ucp, REG_RSI);
+	lx_ucp->uc_sigcontext.sc_rbp = LX_REG(ucp, REG_RBP);
+	lx_ucp->uc_sigcontext.sc_rbx = LX_REG(ucp, REG_RBX);
+	lx_ucp->uc_sigcontext.sc_rdx = LX_REG(ucp, REG_RDX);
+	lx_ucp->uc_sigcontext.sc_rax = LX_REG(ucp, REG_RAX);
+	lx_ucp->uc_sigcontext.sc_rcx = LX_REG(ucp, REG_RCX);
+	lx_ucp->uc_sigcontext.sc_rsp = LX_REG(ucp, REG_RSP);
+	lx_ucp->uc_sigcontext.sc_rip = LX_REG(ucp, REG_RIP);
+	lx_ucp->uc_sigcontext.sc_eflags = LX_REG(ucp, REG_RFL);
+	lx_ucp->uc_sigcontext.sc_cs = LX_REG(ucp, REG_CS);
+	lx_ucp->uc_sigcontext.sc_gs = LX_REG(ucp, REG_GS);
+	lx_ucp->uc_sigcontext.sc_fs = LX_REG(ucp, REG_FS);
+	lx_ucp->uc_sigcontext.sc_pad0 = LX_REG(ucp, REG_SS);
+	lx_ucp->uc_sigcontext.sc_err = LX_REG(ucp, REG_ERR);
+	lx_ucp->uc_sigcontext.sc_trapno = LX_REG(ucp, REG_TRAPNO);
+
+#else /* is _ILP32 */
+	/*
+	 * General registers copy across as-is, except Linux expects that
+	 * uc_mcontext.gregs[ESP] == uc_mcontext.gregs[UESP] on receipt of a
+	 * signal.
+	 */
+	bcopy(&ucp->uc_mcontext, &lx_ucp->uc_sigcontext, sizeof (gregset_t));
+	lx_ucp->uc_sigcontext.sc_esp = lx_ucp->uc_sigcontext.sc_esp_at_signal;
+#endif
+
+	/*
+	 * cr2 contains the faulting address, which Linux only sets for a
+	 * a segmentation fault.
+	 */
+	lx_ucp->uc_sigcontext.sc_cr2 = ((lx_sig == LX_SIGSEGV) && (sip)) ?
+	    (uintptr_t)sip->si_addr : 0;
+
+	/*
+	 * This should only return an error if the signum is invalid but that
+	 * also gets converted into a LX_SIGKILL by this function.
+	 */
+	if (sip != NULL)
+		(void) stol_siginfo(sip, &lx_ssp->si);
+	else
+		bzero(&lx_ssp->si, sizeof (lx_siginfo_t));
+
+	/* convert FP regs if present */
+	if (ucp->uc_flags & UC_FPU) {
+		/*
+		 * Copy FP regs to the appropriate place in the the lx_sigstack
+		 * structure.
+		 */
+		stol_fpstate(&ucp->uc_mcontext.fpregs, &lx_ssp->fpstate);
+		lx_ucp->uc_sigcontext.sc_fpstate = &lx_ssp->fpstate;
+	} else {
+		lx_ucp->uc_sigcontext.sc_fpstate = NULL;
+	}
+
+#if defined(_ILP32)
+	/*
+	 * Believe it or not, gdb wants to SEE the sigreturn code on the
+	 * top of the stack to determine whether the stack frame belongs to
+	 * a signal handler, even though this code is not actually called.
+	 *
+	 * You can't make this stuff up.
+	 */
+	bcopy((void *)lx_rt_sigreturn_tramp, lx_ssp->trampoline,
+	    sizeof (lx_ssp->trampoline));
+#endif
+}
+
+/*
+ * This is the interposition handler for Linux signals.
+ */
+static void
+lx_call_user_handler(int sig, siginfo_t *sip, void *p)
+{
+	void (*user_handler)();
+	void (*stk_builder)();
+	struct lx_sigaction *lxsap;
+	ucontext_t *ucp = (ucontext_t *)p;
+	size_t stksize;
+	int lx_sig;
+
+	/*
+	 * If Illumos signal has no Linux equivalent, effectively ignore it.
+	 */
+	if ((lx_sig = stol_signo[sig]) == -1) {
+		lx_unsupported("caught Illumos signal %d, no Linux equivalent",
+		    sig);
+		return;
+	}
+
+	lx_debug("interpose caught Illumos signal %d, translating to Linux "
+	    "signal %d", sig, lx_sig);
+
+	lxsap = &lx_sighandlers.lx_sa[lx_sig];
+	lx_debug("lxsap @ 0x%p", lxsap);
+
+	if ((sig == SIGPWR) && (lxsap->lxsa_handler == SIG_DFL)) {
+		/*
+		 * Linux SIG_DFL for SIGPWR is to terminate. The lx wait
+		 * emulation will translate SIGPWR to LX_SIGPWR.
+		 */
+		(void) syscall(SYS_brand, B_EXIT_AS_SIG, SIGPWR);
+		/* This should never return */
+		assert(0);
+	}
+
+	if (lxsap->lxsa_handler == SIG_DFL || lxsap->lxsa_handler == SIG_IGN)
+		lx_err_fatal("lxsa_handler set to %s?  How?!?!?",
+		    (lxsap->lxsa_handler == SIG_DFL) ? "SIG_DFL" : "SIG_IGN");
+
+#if defined(_LP64)
+	stksize = sizeof (struct lx_sigstack);
+	stk_builder = lx_build_signal_frame;
+#else
+	if (lxsap->lxsa_flags & LX_SA_SIGINFO) {
+		stksize = sizeof (struct lx_sigstack);
+		stk_builder = lx_build_signal_frame;
+	} else  {
+		stksize = sizeof (struct lx_oldsigstack);
+		stk_builder = lx_build_old_signal_frame;
+	}
+#endif
+
+	user_handler = lxsap->lxsa_handler;
+
+	lx_debug("delivering %d (lx %d) to handler at 0x%p", sig, lx_sig,
+	    lxsap->lxsa_handler);
+
+	if (lxsap->lxsa_flags & LX_SA_RESETHAND)
+		lxsap->lxsa_handler = SIG_DFL;
+
+	lx_sigdeliver(lx_sig, sip, ucp, stksize, stk_builder, user_handler,
+	    lxsap);
+
+	/*
+	 * We need to handle restarting system calls if requested by the
+	 * program for this signal type:
+	 */
+	if (lxsap->lxsa_flags & LX_SA_RESTART) {
+		uintptr_t flags = (uintptr_t)ucp->uc_brand_data[0];
+		long ret = (long)LX_REG(ucp, REG_R0);
+		boolean_t interrupted = (ret == -lx_errno(EINTR, -1));
+
+		/*
+		 * If the system call returned EINTR, and the system
+		 * call handler set "br_syscall_restart" when returning,
+		 * we modify the context to try the system call again
+		 * when we return from this signal handler.
+		 */
+		if ((flags & LX_UC_RESTART_SYSCALL) && interrupted) {
+			int syscall_num = (int)(uintptr_t)ucp->uc_brand_data[2];
+
+			lx_debug("restarting interrupted system call %d",
+			    syscall_num);
+
+			/*
+			 * Both the "int 0x80" and the "syscall" instruction
+			 * are two bytes long.  Wind the program counter back
+			 * to the start of this instruction.
+			 *
+			 * The system call we interrupted is preserved in the
+			 * brand-specific data in the ucontext_t when the
+			 * LX_UC_RESTART_SYSCALL flag is set.  This is
+			 * analogous to the "orig_[er]ax" field in the Linux
+			 * "user_regs_struct".
+			 */
+			LX_REG(ucp, REG_PC) -= 2;
+			LX_REG(ucp, REG_R0) = syscall_num;
+		}
+	}
+}
+
+/*
+ * The "lx_sigdeliver()" function is responsible for constructing the emulated
+ * signal delivery frame on the brand stack for this LWP.  A context is saved
+ * on the stack which will be used by the "sigreturn(2)" family of emulated
+ * system calls to get us back here after the Linux signal handler returns.
+ * This function is modelled on the in-kernel "sendsig()" signal delivery
+ * mechanism.
+ */
+void
+lx_sigdeliver(int lx_sig, siginfo_t *sip, ucontext_t *ucp, size_t stacksz,
+    void (*stack_builder)(), void (*user_handler)(),
+    struct lx_sigaction *lxsap)
+{
+	lx_sigbackup_t sigbackup;
+	ucontext_t uc;
+	lx_tsd_t *lxtsd = lx_get_tsd();
+	int totsz = 0;
+	uintptr_t flags;
+	uintptr_t hargs[3];
+	uintptr_t orig_sp = 0;
+
+	/*
+	 * These variables must be "volatile", as they are modified after the
+	 * getcontext() stores the register state:
+	 */
+	volatile boolean_t signal_delivered = B_FALSE;
+	volatile boolean_t sp_modified = B_FALSE;
+	volatile uintptr_t lxfp = 0;
+	volatile uintptr_t old_tsd_sp = 0;
+	volatile int newstack = 0;
+
+	/*
+	 * This function involves modifying the Linux process stack for this
+	 * thread.  To do so without corruption requires us to exclude other
+	 * signal handlers (or emulated system calls called from within those
+	 * handlers) from running while we reserve space on that stack.  We
+	 * defer the execution of further instances of lx_call_user_handler()
+	 * until we have completed this operation.
+	 */
+	_sigoff();
+
+	/*
+	 * Clear register arguments vector.
+	 */
+	bzero(hargs, sizeof (hargs));
+
+	/* Save our SP so we can restore it after coming back in. */
+	orig_sp = LX_REG(ucp, REG_SP);
+
+	/*
+	 * We save a context here so that we can be returned later to complete
+	 * handling the signal.
+	 */
+	lx_debug("lx_sigdeliver: STORING RETURN CONTEXT @ %p\n", &uc);
+	assert(getcontext(&uc) == 0);
+	lx_debug("lx_sigdeliver: RETURN CONTEXT %p LINK %p FLAGS %lx\n",
+	    &uc, uc.uc_link, uc.uc_flags);
+	if (signal_delivered) {
+		/*
+		 * If the "signal_delivered" flag is set, we are returned here
+		 * via setcontext() as called by the emulated Linux signal
+		 * return system call.
+		 */
+		lx_debug("lx_sigdeliver: WE ARE BACK, VIA UC @ %p!\n", &uc);
+
+		if (sp_modified) {
+			/*
+			 * Restore the original stack pointer, which we saved
+			 * on our alt. stack, back into the context.
+			 */
+			LX_REG(ucp, REG_SP) = orig_sp;
+		}
+
+		goto after_signal_handler;
+	}
+	signal_delivered = B_TRUE;
+
+	/*
+	 * Preserve the current tsd value of the Linux process stack pointer,
+	 * even if it is zero.  We will restore it when we are returned here
+	 * via setcontext() after the Linux process has completed execution of
+	 * its signal handler.
+	 */
+	old_tsd_sp = lxtsd->lxtsd_lx_sp;
+
+	/*
+	 * Figure out whether we will be handling this signal on an alternate
+	 * stack specified by the user.
+	 */
+	newstack = (lxsap->lxsa_flags & LX_SA_ONSTACK) &&
+	    !(lxtsd->lxtsd_sigaltstack.ss_flags & (LX_SS_ONSTACK |
+	    LX_SS_DISABLE));
+
+	/*
+	 * Find the first unused region of the Linux process stack, where
+	 * we will assemble our signal delivery frame.
+	 */
+	flags = (uintptr_t)ucp->uc_brand_data[0];
+	if (newstack) {
+		/*
+		 * We are moving to the user-provided alternate signal
+		 * stack.
+		 */
+		lxfp = SA((uintptr_t)lxtsd->lxtsd_sigaltstack.ss_sp) +
+		    SA(lxtsd->lxtsd_sigaltstack.ss_size) - STACK_ALIGN;
+		lx_debug("lx_sigdeliver: moving to ALTSTACK sp %p\n", lxfp);
+		LX_SIGNAL_ALTSTACK_ENABLE(lxfp);
+	} else if (flags & LX_UC_STACK_BRAND) {
+		/*
+		 * We interrupted the Linux process to take this signal.  The
+		 * stack pointer is the one saved in this context.
+		 */
+		lxfp = LX_REG(ucp, REG_SP);
+	} else {
+		/*
+		 * We interrupted a native (emulation) routine, so we must get
+		 * the current stack pointer from either the tsd (if one is
+		 * stored there) or via the context chain.
+		 *
+		 */
+		lxfp = lx_find_brand_sp();
+		if (lxtsd->lxtsd_lx_sp != 0) {
+			/*
+			 * We must also make room for the possibility of nested
+			 * signal delivery -- we may be pre-empting the
+			 * in-progress handling of another signal.
+			 *
+			 * Note that if we were already on the alternate stack,
+			 * any emulated Linux system calls would be betwixt
+			 * that original signal frame and this new one on the
+			 * one contiguous stack, so this logic holds either
+			 * way:
+			 */
+			lxfp = MIN(lxtsd->lxtsd_lx_sp, lxfp);
+		}
+
+		/* Replace the context SP with the one from the Linux context */
+		LX_REG(ucp, REG_SP) = lxfp;
+		sp_modified = B_TRUE;
+	}
+
+	/*
+	 * Account for a reserved stack region (for amd64, this is 128 bytes),
+	 * and align the stack:
+	 */
+	lxfp -= STACK_RESERVE;
+	lxfp &= ~(STACK_ALIGN - 1);
+
+	/*
+	 * Allocate space on the Linux process stack for our delivery frame,
+	 * including:
+	 *
+	 *   ----------------------------------------------------- old %sp
+	 *   - lx_sigdeliver_frame_t
+	 *   - (ucontext_t pointers and stack magic)
+	 *   -----------------------------------------------------
+	 *   - (amd64-only 8-byte alignment gap)
+	 *   -----------------------------------------------------
+	 *   - frame of size "stacksz" from the stack builder
+	 *   ----------------------------------------------------- new %sp
+	 */
+#if defined(_LP64)
+	/*
+	 * The AMD64 ABI requires us to align the stack such that when the
+	 * called function pushes the base pointer, the stack is 16 byte
+	 * aligned.  The stack must, therefore, be 8- but _not_ 16-byte
+	 * aligned.
+	 */
+#if (STACK_ALIGN != 16) || (STACK_ENTRY_ALIGN != 8)
+#error "lx_sigdeliver() did not find expected stack alignment"
+#endif
+	totsz = SA(sizeof (lx_sigdeliver_frame_t)) + SA(stacksz) + 8;
+	assert((totsz & (STACK_ENTRY_ALIGN - 1)) == 0);
+	assert((totsz & (STACK_ALIGN - 1)) == 8);
+#else
+	totsz = SA(sizeof (lx_sigdeliver_frame_t)) + SA(stacksz);
+	assert((totsz & (STACK_ALIGN - 1)) == 0);
+#endif
+
+	/*
+	 * Copy our return frame into place:
+	 */
+	lxfp -= SA(sizeof (lx_sigdeliver_frame_t));
+	lx_debug("lx_sigdeliver: lx_sigdeliver_frame_t @ %p\n", lxfp);
+	{
+		lx_sigdeliver_frame_t frm;
+
+		frm.lxsdf_magic = LX_SIGRT_MAGIC;
+		frm.lxsdf_retucp = &uc;
+		frm.lxsdf_sigucp = ucp;
+		frm.lxsdf_sigbackup = &sigbackup;
+
+		lx_debug("lx_sigdeliver: retucp %p sigucp %p\n",
+		    frm.lxsdf_retucp, frm.lxsdf_sigucp);
+
+		if (uucopy(&frm, (void *)lxfp, sizeof (frm)) != 0) {
+			/*
+			 * We could not modify the stack of the emulated Linux
+			 * program.  Act like the kernel and terminate the
+			 * program with a segmentation violation.
+			 */
+			(void) syscall(SYS_brand, B_EXIT_AS_SIG, SIGSEGV);
+		}
+
+		LX_SIGNAL_DELIVERY_FRAME_CREATE((void *)lxfp);
+
+		/*
+		 * Populate a backup copy of signal linkage to use in case
+		 * the Linux program completely destroys (or relocates) the
+		 * delivery frame.
+		 *
+		 * This is necessary for programs that have flown so far off
+		 * the architectural rails that they believe it is
+		 * acceptable to make assumptions about the precise size and
+		 * layout of the signal handling frame assembled by the
+		 * kernel.
+		 */
+		sigbackup.lxsb_retucp = frm.lxsdf_retucp;
+		sigbackup.lxsb_sigucp = frm.lxsdf_sigucp;
+		sigbackup.lxsb_sigdeliver_frame = lxfp;
+		sigbackup.lxsb_previous = lxtsd->lxtsd_sigbackup;
+		lxtsd->lxtsd_sigbackup = &sigbackup;
+
+		lx_debug("lx_sigdeliver: installed sigbackup %p; prev %p\n",
+		    &sigbackup, sigbackup.lxsb_previous);
+	}
+
+	/*
+	 * Build the Linux signal handling frame:
+	 */
+#if defined(_LP64)
+	lxfp -= SA(stacksz) + 8;
+#else
+	lxfp -= SA(stacksz);
+#endif
+	lx_debug("lx_sigdeliver: Linux sig frame @ %p\n", lxfp);
+	stack_builder(lx_sig, sip, ucp, lxfp, hargs);
+
+	/*
+	 * Record our reservation so that any nested signal handlers
+	 * can see it.
+	 */
+	lx_debug("lx_sigdeliver: Linux tsd sp %p -> %p\n", lxtsd->lxtsd_lx_sp,
+	    lxfp);
+	lxtsd->lxtsd_lx_sp = lxfp;
+
+	if (newstack) {
+		lxtsd->lxtsd_sigaltstack.ss_flags |= LX_SS_ONSTACK;
+	}
+
+	LX_SIGDELIVER(lx_sig, lxsap, (void *)lxfp);
+
+	/*
+	 * Re-enable signal delivery.  If a signal was queued while we were
+	 * in the critical section, it will be delivered immediately.
+	 */
+	_sigon();
+
+	/*
+	 * Pass control to the Linux signal handler:
+	 */
+	lx_debug("lx_sigdeliver: JUMPING TO LINUX (sig %d sp %p eip %p)\n",
+	    lx_sig, lxfp, user_handler);
+	{
+		ucontext_t jump_uc;
+
+		bcopy(lx_find_brand_uc(), &jump_uc, sizeof (jump_uc));
+
+		/*
+		 * We want to load the general registers from this context, and
+		 * switch to the BRAND stack.  We do _not_ want to restore the
+		 * uc_link value from this synthetic context, as that would
+		 * break the signal handling context chain.
+		 */
+		jump_uc.uc_flags = UC_CPU;
+		jump_uc.uc_brand_data[0] = (void *)(LX_UC_STACK_BRAND |
+		    LX_UC_IGNORE_LINK);
+
+		LX_REG(&jump_uc, REG_FP) = 0;
+		LX_REG(&jump_uc, REG_SP) = lxfp;
+		LX_REG(&jump_uc, REG_PC) = (uintptr_t)user_handler;
+
+#if defined(_LP64)
+		/*
+		 * Pass signal handler arguments by registers on AMD64.
+		 */
+		LX_REG(&jump_uc, REG_RDI) = hargs[0];
+		LX_REG(&jump_uc, REG_RSI) = hargs[1];
+		LX_REG(&jump_uc, REG_RDX) = hargs[2];
+#endif
+
+		lx_jump_to_linux(&jump_uc);
+	}
+
+	assert(0);
+	abort();
+
+after_signal_handler:
+	/*
+	 * Ensure all nested signal handlers have completed correctly
+	 * and then remove our stack reservation.
+	 */
+	_sigoff();
+	LX_SIGNAL_POST_HANDLER(lxfp, old_tsd_sp);
+	assert(lxtsd->lxtsd_lx_sp == lxfp);
+	lx_debug("lx_sigdeliver: after; Linux tsd sp %p -> %p\n", lxfp,
+	    old_tsd_sp);
+	lxtsd->lxtsd_lx_sp = old_tsd_sp;
+	if (newstack) {
+		LX_SIGNAL_ALTSTACK_DISABLE();
+		lx_debug("lx_sigdeliver: disabling ALTSTACK sp %p\n", lxfp);
+		lxtsd->lxtsd_sigaltstack.ss_flags &= ~LX_SS_ONSTACK;
+	}
+	/*
+	 * Restore backup signal tracking chain pointer to previous value:
+	 */
+	if (lxtsd->lxtsd_sigbackup != NULL) {
+		lx_sigbackup_t *bprev = lxtsd->lxtsd_sigbackup->lxsb_previous;
+
+		lx_debug("lx_sigdeliver: restoring sigbackup %p to %p\n",
+		    lxtsd->lxtsd_sigbackup, bprev);
+
+		lxtsd->lxtsd_sigbackup = bprev;
+	}
+	_sigon();
+
+	/*
+	 * Here we return to libc so that it may clean up and restore the
+	 * context originally interrupted by this signal.
+	 */
+}
+
+/*
+ * Common routine to modify sigaction characteristics of a thread.
+ *
+ * We shouldn't need any special locking code here as we actually use our copy
+ * of libc's sigaction() to do all the real work, so its thread locking should
+ * take care of any issues for us.
+ */
+static int
+lx_sigaction_common(int lx_sig, struct lx_sigaction *lxsp,
+    struct lx_sigaction *olxsp)
+{
+	struct lx_sigaction *lxsap;
+	struct sigaction sa;
+
+	if (lx_sig <= 0 || lx_sig > LX_NSIG)
+		return (-EINVAL);
+
+	lxsap = &lx_sighandlers.lx_sa[lx_sig];
+	lx_debug("&lx_sighandlers.lx_sa[%d] = 0x%p", lx_sig, lxsap);
+
+	if ((olxsp != NULL) &&
+	    ((uucopy(lxsap, olxsp, sizeof (struct lx_sigaction))) != 0))
+		return (-errno);
+
+	if (lxsp != NULL) {
+		int err, sig;
+		struct lx_sigaction lxsa;
+		sigset_t new_set, oset;
+
+		if (uucopy(lxsp, &lxsa, sizeof (struct lx_sigaction)) != 0)
+			return (-errno);
+
+		if ((sig = ltos_signo[lx_sig]) != -1) {
+			if (lx_no_abort_handler) {
+				/*
+				 * If LX_NO_ABORT_HANDLER has been set, we will
+				 * not allow the emulated program to do
+				 * anything hamfisted with SIGSEGV or SIGABRT
+				 * signals.
+				 */
+				if (sig == SIGSEGV || sig == SIGABRT) {
+					return (0);
+				}
+			}
+
+			/*
+			 * Block this signal while messing with its dispostion
+			 */
+			(void) sigemptyset(&new_set);
+			(void) sigaddset(&new_set, sig);
+
+			if (sigprocmask(SIG_BLOCK, &new_set, &oset) < 0) {
+				err = errno;
+				lx_debug("unable to block signal %d: %s", sig,
+				    strerror(err));
+				return (-err);
+			}
+
+			/*
+			 * We don't really need the old signal disposition at
+			 * this point, but this weeds out signals that would
+			 * cause sigaction() to return an error before we change
+			 * anything other than the current signal mask.
+			 */
+			if (sigaction(sig, NULL, &sa) < 0) {
+				err = errno;
+				lx_debug("sigaction() to get old "
+				    "disposition for signal %d failed: "
+				    "%s", sig, strerror(err));
+				(void) sigprocmask(SIG_SETMASK, &oset, NULL);
+				return (-err);
+			}
+
+			if ((lxsa.lxsa_handler != SIG_DFL) &&
+			    (lxsa.lxsa_handler != SIG_IGN)) {
+				sa.sa_handler = lx_call_user_handler;
+
+				/*
+				 * The interposition signal handler needs the
+				 * information provided via the SA_SIGINFO flag.
+				 */
+				sa.sa_flags = SA_SIGINFO;
+
+				/*
+				 * When translating from Linux to illumos
+				 * sigaction(2) flags, we explicitly do not
+				 * pass SA_ONSTACK to the kernel.  The
+				 * alternate stack for Linux signal handling is
+				 * handled entirely by the emulation code.
+				 */
+				if (lxsa.lxsa_flags & LX_SA_NOCLDSTOP)
+					sa.sa_flags |= SA_NOCLDSTOP;
+				if (lxsa.lxsa_flags & LX_SA_NOCLDWAIT)
+					sa.sa_flags |= SA_NOCLDWAIT;
+				if (lxsa.lxsa_flags & LX_SA_RESTART)
+					sa.sa_flags |= SA_RESTART;
+				if (lxsa.lxsa_flags & LX_SA_NODEFER)
+					sa.sa_flags |= SA_NODEFER;
+
+				/*
+				 * RESETHAND cannot be used be passed through
+				 * for SIGPWR due to different default actions
+				 * between Linux and Illumos.
+				 */
+				if ((sig != SIGPWR) &&
+				    (lxsa.lxsa_flags & LX_SA_RESETHAND))
+					sa.sa_flags |= SA_RESETHAND;
+
+				if (ltos_sigset(&lxsa.lxsa_mask,
+				    &sa.sa_mask) != 0) {
+					err = errno;
+					(void) sigprocmask(SIG_SETMASK, &oset,
+					    NULL);
+					return (-err);
+				}
+
+				lx_debug("interposing handler @ 0x%p for "
+				    "signal %d (lx %d), flags 0x%x",
+				    lxsa.lxsa_handler, sig, lx_sig,
+				    lxsa.lxsa_flags);
+
+				if (sigaction(sig, &sa, NULL) < 0) {
+					err = errno;
+					lx_debug("sigaction() to set new "
+					    "disposition for signal %d failed: "
+					    "%s", sig, strerror(err));
+					(void) sigprocmask(SIG_SETMASK, &oset,
+					    NULL);
+					return (-err);
+				}
+			} else if ((sig != SIGPWR) ||
+			    ((sig == SIGPWR) &&
+			    (lxsa.lxsa_handler == SIG_IGN))) {
+				/*
+				 * There's no need to interpose for SIG_DFL or
+				 * SIG_IGN so just call our copy of libc's
+				 * sigaction(), but don't allow SIG_DFL for
+				 * SIGPWR due to differing default actions
+				 * between Linux and Illumos.
+				 *
+				 * Get the previous disposition first so things
+				 * like sa_mask and sa_flags are preserved over
+				 * a transition to SIG_DFL or SIG_IGN, which is
+				 * what Linux expects.
+				 */
+
+				sa.sa_handler = lxsa.lxsa_handler;
+
+				if (sigaction(sig, &sa, NULL) < 0) {
+					err = errno;
+					lx_debug("sigaction(%d, %s) failed: %s",
+					    sig, ((sa.sa_handler == SIG_DFL) ?
+					    "SIG_DFL" : "SIG_IGN"),
+					    strerror(err));
+					(void) sigprocmask(SIG_SETMASK, &oset,
+					    NULL);
+					return (-err);
+				}
+			}
+		} else {
+			lx_debug("Linux signal with no kill support "
+			    "specified: %d", lx_sig);
+		}
+
+		/*
+		 * Save the new disposition for the signal in the global
+		 * lx_sighandlers structure.
+		 */
+		bcopy(&lxsa, lxsap, sizeof (struct lx_sigaction));
+
+		/*
+		 * Reset the signal mask to what we came in with if
+		 * we were modifying a kill-supported signal.
+		 */
+		if (sig != -1)
+			(void) sigprocmask(SIG_SETMASK, &oset, NULL);
+	}
+
+	return (0);
+}
+
+#if defined(_ILP32)
+/*
+ * sigaction is only used in 32-bit code.
+ */
+long
+lx_sigaction(uintptr_t lx_sig, uintptr_t actp, uintptr_t oactp)
+{
+	int val;
+	struct lx_sigaction sa, osa;
+	struct lx_sigaction *sap, *osap;
+	struct lx_osigaction *osp;
+
+	sap = (actp ? &sa : NULL);
+	osap = (oactp ? &osa : NULL);
+
+	/*
+	 * If we have a source pointer, convert source lxsa_mask from
+	 * lx_osigset_t to lx_sigset_t format.
+	 */
+	if (sap) {
+		osp = (struct lx_osigaction *)actp;
+		sap->lxsa_handler = osp->lxsa_handler;
+
+		bzero(&sap->lxsa_mask, sizeof (lx_sigset_t));
+
+		for (val = 1; val <= OSIGSET_NBITS; val++)
+			if (osp->lxsa_mask & OSIGSET_BITSET(val))
+				(void) lx_sigaddset(&sap->lxsa_mask, val);
+
+		sap->lxsa_flags = osp->lxsa_flags;
+		sap->lxsa_restorer = osp->lxsa_restorer;
+	}
+
+	if ((val = lx_sigaction_common(lx_sig, sap, osap)))
+		return (val);
+
+	/*
+	 * If we have a save pointer, convert the old lxsa_mask from
+	 * lx_sigset_t to lx_osigset_t format.
+	 */
+	if (osap) {
+		osp = (struct lx_osigaction *)oactp;
+
+		osp->lxsa_handler = osap->lxsa_handler;
+
+		bzero(&osp->lxsa_mask, sizeof (osp->lxsa_mask));
+		for (val = 1; val <= OSIGSET_NBITS; val++)
+			if (lx_sigismember(&osap->lxsa_mask, val))
+				osp->lxsa_mask |= OSIGSET_BITSET(val);
+
+		osp->lxsa_flags = osap->lxsa_flags;
+		osp->lxsa_restorer = osap->lxsa_restorer;
+	}
+
+	return (0);
+}
+#endif
+
+long
+lx_rt_sigaction(uintptr_t lx_sig, uintptr_t actp, uintptr_t oactp,
+    uintptr_t setsize)
+{
+	/*
+	 * The "new" rt_sigaction call checks the setsize
+	 * parameter.
+	 */
+	if ((size_t)setsize != sizeof (lx_sigset_t))
+		return (-EINVAL);
+
+	return (lx_sigaction_common(lx_sig, (struct lx_sigaction *)actp,
+	    (struct lx_sigaction *)oactp));
+}
+
+#if defined(_ILP32)
+/*
+ * Convert signal syscall to a call to the lx_sigaction() syscall
+ * Only used in 32-bit code.
+ */
+long
+lx_signal(uintptr_t lx_sig, uintptr_t handler)
+{
+	struct sigaction act;
+	struct sigaction oact;
+	int rc;
+
+	/*
+	 * Use sigaction to mimic SYSV signal() behavior; glibc will
+	 * actually call sigaction(2) itself, so we're really reaching
+	 * back for signal(2) semantics here.
+	 */
+	bzero(&act, sizeof (act));
+	act.sa_handler = (void (*)())handler;
+	act.sa_flags = SA_RESETHAND | SA_NODEFER;
+
+	rc = lx_sigaction(lx_sig, (uintptr_t)&act, (uintptr_t)&oact);
+	return ((rc == 0) ? ((ssize_t)oact.sa_handler) : rc);
+}
+#endif
+
+void
+lx_sighandlers_save(lx_sighandlers_t *saved)
+{
+	bcopy(&lx_sighandlers, saved, sizeof (lx_sighandlers_t));
+}
+
+void
+lx_sighandlers_restore(lx_sighandlers_t *saved)
+{
+	bcopy(saved, &lx_sighandlers, sizeof (lx_sighandlers_t));
+}
+
+int
+lx_siginit(void)
+{
+	extern void set_setcontext_enforcement(int);
+	extern void set_escaped_context_cleanup(int);
+
+	struct sigaction sa;
+	sigset_t new_set, oset;
+	int lx_sig, sig;
+
+	/*
+	 * Block all signals possible while setting up the signal imposition
+	 * mechanism.
+	 */
+	(void) sigfillset(&new_set);
+
+	if (sigprocmask(SIG_BLOCK, &new_set, &oset) < 0)
+		lx_err_fatal("unable to block signals while setting up "
+		    "imposition mechanism: %s", strerror(errno));
+
+	/*
+	 * Ignore any signals that have no Linux analog so that those
+	 * signals cannot be sent to Linux processes from the global zone
+	 */
+	for (sig = 1; sig < NSIG; sig++)
+		if (stol_signo[sig] < 0)
+			(void) sigignore(sig);
+
+	/*
+	 * Mark any signals that are ignored as ignored in our interposition
+	 * handler array
+	 */
+	for (lx_sig = 1; lx_sig <= LX_NSIG; lx_sig++) {
+		if (((sig = ltos_signo[lx_sig]) != -1) &&
+		    (sigaction(sig, NULL, &sa) < 0))
+			lx_err_fatal("unable to determine previous disposition "
+			    "for signal %d: %s", sig, strerror(errno));
+
+		if (sa.sa_handler == SIG_IGN) {
+			lx_debug("marking signal %d (lx %d) as SIG_IGN",
+			    sig, lx_sig);
+			lx_sighandlers.lx_sa[lx_sig].lxsa_handler = SIG_IGN;
+		}
+	}
+
+	/*
+	 * Have our interposition handler handle SIGPWR to start with,
+	 * as it has a default action of terminating the process in Linux
+	 * but its default is to be ignored in Illumos.
+	 */
+	(void) sigemptyset(&sa.sa_mask);
+	sa.sa_sigaction = lx_call_user_handler;
+	sa.sa_flags = SA_SIGINFO;
+
+	if (sigaction(SIGPWR, &sa, NULL) < 0)
+		lx_err_fatal("sigaction(SIGPWR) failed: %s", strerror(errno));
+
+	/*
+	 * Illumos' libc forces certain register values in the ucontext_t
+	 * used to restore a post-signal user context to be those Illumos
+	 * expects; however that is not what we want to happen if the signal
+	 * was taken while branded code was executing, so we must disable
+	 * that behavior.
+	 */
+	set_setcontext_enforcement(0);
+
+	/*
+	 * The illumos libc attempts to clean up dangling uc_link pointers in
+	 * signal handling contexts when libc believes us to have escaped a
+	 * signal handler incorrectly in the past.  We want to disable this
+	 * behaviour, so that the system call emulation context saved by the
+	 * kernel brand module for lx_emulate() may be part of the context
+	 * chain without itself being used for signal handling.
+	 */
+	set_escaped_context_cleanup(0);
+
+	/*
+	 * Reset the signal mask to what we came in with.
+	 */
+	(void) sigprocmask(SIG_SETMASK, &oset, NULL);
+
+	lx_debug("interposition handler setup for SIGPWR");
+	return (0);
+}
+
+/*
+ * The first argument is the pid (Linux tgid) to send the signal to, second
+ * argument is the signal to send (an lx signal), and third is the siginfo_t
+ * with extra information. We translate the code and signal only from the
+ * siginfo_t, and leave everything else the same as it gets passed through the
+ * signalling system. This is enough to get sigqueue working. See Linux man
+ * page rt_sigqueueinfo(2).
+ */
+long
+lx_rt_sigqueueinfo(uintptr_t p1, uintptr_t p2, uintptr_t p3)
+{
+	pid_t tgid = (pid_t)p1;
+	int lx_sig = (int)p2;
+	int sig;
+	lx_siginfo_t lx_siginfo;
+	siginfo_t siginfo;
+	int s_code;
+	pid_t s_pid;
+
+	if (uucopy((void *)p3, &lx_siginfo, sizeof (lx_siginfo_t)) != 0)
+		return (-EFAULT);
+	s_code = ltos_sigcode(lx_siginfo.lsi_code);
+	if (s_code == LX_SI_CODE_NOT_EXIST)
+		return (-EINVAL);
+	if (lx_sig < 0 || lx_sig > LX_NSIG || (sig = ltos_signo[lx_sig]) < 0) {
+		return (-EINVAL);
+	}
+	/*
+	 * This case (when trying to kill pid 0) just has a different errno
+	 * returned in illumos than in Linux.
+	 */
+	if (tgid == 0)
+		return (-ESRCH);
+	if (lx_lpid_to_spid(tgid, &s_pid) != 0)
+		return (-ESRCH);
+	if (SI_CANQUEUE(s_code)) {
+		return ((syscall(SYS_sigqueue, s_pid, sig,
+		    lx_siginfo.lsi_value, s_code, 0) == -1) ?
+		    (-errno): 0);
+	} else {
+		/*
+		 * This case is unlikely, as the main entry point is through
+		 * sigqueue, which always has a queuable si_code.
+		 */
+		siginfo.si_signo = sig;
+		siginfo.si_code = s_code;
+		siginfo.si_pid = lx_siginfo.lsi_pid;
+		siginfo.si_value = lx_siginfo.lsi_value;
+		siginfo.si_uid = lx_siginfo.lsi_uid;
+		return ((syscall(SYS_brand, B_HELPER_SIGQUEUE,
+		    tgid, sig, &siginfo)) ? (-errno) : 0);
+	}
+}
+
+/*
+ * Adds an additional argument for which thread within a thread group to send
+ * the signal to (added as the second argument).
+ */
+long
+lx_rt_tgsigqueueinfo(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4)
+{
+	pid_t tgid = (pid_t)p1;
+	pid_t tid = (pid_t)p2;
+	int lx_sig = (int)p3;
+	int sig;
+	lx_siginfo_t lx_siginfo;
+	siginfo_t siginfo;
+	int si_code;
+
+	if (uucopy((void *)p4, &lx_siginfo, sizeof (lx_siginfo_t)) != 0)
+		return (-EFAULT);
+	if (lx_sig < 0 || lx_sig > LX_NSIG || (sig = ltos_signo[lx_sig]) < 0) {
+		return (-EINVAL);
+	}
+	si_code = ltos_sigcode(lx_siginfo.lsi_code);
+	if (si_code == LX_SI_CODE_NOT_EXIST)
+		return (-EINVAL);
+	/*
+	 * Check for invalid tgid and tids. That appears to be only negatives
+	 * and 0 values. Everything else that doesn't exist is instead ESRCH.
+	 */
+	if (tgid <= 0 || tid <= 0)
+		return (-EINVAL);
+	siginfo.si_signo = sig;
+	siginfo.si_code = si_code;
+	siginfo.si_pid = lx_siginfo.lsi_pid;
+	siginfo.si_value = lx_siginfo.lsi_value;
+	siginfo.si_uid = lx_siginfo.lsi_uid;
+
+	return ((syscall(SYS_brand, B_HELPER_TGSIGQUEUE, tgid, tid, sig,
+	    &siginfo)) ? (-errno) : 0);
+}
+
+long
+lx_signalfd(int fd, uintptr_t mask, size_t msize)
+{
+	return (lx_signalfd4(fd, mask, msize, 0));
+}
+
+long
+lx_signalfd4(int fd, uintptr_t mask, size_t msize, int flags)
+{
+	sigset_t s_set;
+	int r;
+
+	if (msize != sizeof (int64_t))
+		return (-EINVAL);
+
+	if (ltos_sigset((lx_sigset_t *)mask, &s_set) != 0)
+		return (-errno);
+
+	r = signalfd(fd, &s_set, flags);
+
+	/*
+	 * signalfd(3C) may fail with ENOENT if /dev/signalfd is not available.
+	 * It is less jarring to Linux programs to tell them that internal
+	 * allocation failed than to report an error number they are not
+	 * expecting.
+	 */
+	if (r == -1 && errno == ENOENT)
+		return (-ENODEV);
+
+	return (r == -1 ? -errno : r);
+}
--- /dev/null	Wed Oct  5 00:31:48 2016
+++ new/usr/src/lib/brand/lx/lx_brand/common/stack.c	Wed Oct  5 00:31:48 2016
@@ -0,0 +1,338 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+/*
+ * Manage the native/emulation stack for LX-branded LWPs.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <errno.h>
+
+#include <thread.h>
+#include <sys/mman.h>
+#include <sys/brand.h>
+#include <sys/syscall.h>
+#include <sys/debug.h>
+
+#include <sys/lx_brand.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_debug.h>
+#include <sys/lx_thread.h>
+
+
+typedef struct lx_stack_list_ent {
+	thread_t sle_tid;
+	void *sle_stack;
+	size_t sle_stack_size;
+	lx_tsd_t *sle_tsd;
+} lx_stack_list_ent_t;
+
+static mutex_t lx_stack_list_lock = ERRORCHECKMUTEX;
+lx_stack_list_ent_t *lx_stack_list = NULL;
+unsigned int lx_stack_list_elems = 0;
+
+/*
+ * Usermode emulation alternate stack size, expressed as a page count:
+ */
+int lx_native_stack_page_count = LX_NATIVE_STACK_PAGE_COUNT;
+
+/*
+ * We use these private functions from libc to suspend signal delivery in
+ * critical sections:
+ */
+extern void _sigon(void);
+extern void _sigoff(void);
+
+void
+lx_stack_prefork(void)
+{
+	lx_tsd_t *lx_tsd = lx_get_tsd();
+
+	/*
+	 * The "lx_stack_list_lock" mutex is used to protect access to the list
+	 * of per-thread native stacks.  Management of native stacks is
+	 * generally performed while servicing an emulated fork(2), vfork(2) or
+	 * clone(2) system call.
+	 *
+	 * Multiple threads may be attempting to create new threads or
+	 * processes concurrently, but in the case of fork(2) only the
+	 * currently executing thread is duplicated in the child process.  We
+	 * require that the stack list lock be taken before the native fork1()
+	 * or forkx(), and released in both the parent and the child once the
+	 * operation is complete. For vfork() the lock must only be released in
+	 * the parent (once it resumes execution) since the child is borrowing
+	 * the parent's thread. The _sigoff/_sigon dance will also only take
+	 * place in the parent.
+	 *
+	 * Holding this mutex prevents the forked child from containing a
+	 * copy-on-write copy of a locked mutex without the thread that would
+	 * later unlock it.  We also suspend signal delivery while entering
+	 * this critical section to ensure async signal safety.
+	 *
+	 * Unfortunately some Linux applications (e.g. busybox) will call vfork
+	 * and then call fork (without the expected intervening exec). We
+	 * avoid the mutex deadlock by skipping the call since we know this
+	 * thread has borrowed the parent's address space and the parent cannot
+	 * execute until we exit/exec.
+	 */
+	_sigoff();
+	if (lx_tsd->lxtsd_is_vforked == 0)
+		VERIFY0(mutex_lock(&lx_stack_list_lock));
+}
+
+void
+lx_stack_postfork(void)
+{
+	lx_tsd_t *lx_tsd = lx_get_tsd();
+
+	if (lx_tsd->lxtsd_is_vforked == 0)
+		VERIFY0(mutex_unlock(&lx_stack_list_lock));
+	_sigon();
+}
+
+/*
+ * Free the alternate stack for this thread.
+ */
+void
+lx_free_stack(void)
+{
+	thread_t me = thr_self();
+	int i;
+
+	_sigoff();
+	VERIFY0(mutex_lock(&lx_stack_list_lock));
+
+	/*
+	 * Find this thread's stack in the list of stacks.
+	 */
+	for (i = 0; i < lx_stack_list_elems; i++) {
+		if (lx_stack_list[i].sle_tid != me) {
+			continue;
+		}
+
+		(void) munmap(lx_stack_list[i].sle_stack,
+		    lx_stack_list[i].sle_stack_size);
+
+		/*
+		 * Free the thread-specific data structure for this thread.
+		 */
+		if (lx_stack_list[i].sle_tsd != NULL) {
+			free(lx_stack_list[i].sle_tsd->lxtsd_clone_state);
+			free(lx_stack_list[i].sle_tsd);
+		}
+
+		/*
+		 * Free up this stack list entry:
+		 */
+		bzero(&lx_stack_list[i], sizeof (lx_stack_list[i]));
+
+		VERIFY0(mutex_unlock(&lx_stack_list_lock));
+		_sigon();
+		return;
+	}
+
+	/*
+	 * Did not find the stack in the list.
+	 */
+	assert(0);
+}
+
+/*
+ * After fork1(), we must unmap the stack of every thread other than the
+ * one copied into the child process.
+ */
+void
+lx_free_other_stacks(void)
+{
+	int i, this_stack = -1;
+	thread_t me = thr_self();
+	lx_tsd_t *lx_tsd = lx_get_tsd();
+
+	_sigoff();
+
+	/*
+	 * We don't need to check or take the lx_stack_list_lock here because
+	 * we are the only thread in this process, but if we got here via an
+	 * evil vfork->fork path then we must drop the lock for the new child
+	 * and reset our "is_vforked" counter.
+	 */
+	if (lx_tsd->lxtsd_is_vforked != 0) {
+		VERIFY0(mutex_unlock(&lx_stack_list_lock));
+		lx_tsd->lxtsd_is_vforked = 0;
+	}
+
+	for (i = 0; i < lx_stack_list_elems; i++) {
+		if (lx_stack_list[i].sle_tid == me) {
+			/*
+			 * Do not unmap the stack for this LWP.
+			 */
+			this_stack = i;
+			continue;
+		} else if (lx_stack_list[i].sle_tid == 0) {
+			/*
+			 * Skip any holes in the list.
+			 */
+			continue;
+		}
+
+		/*
+		 * Free the thread-specific data structure for this thread.
+		 */
+		if (lx_stack_list[i].sle_tsd != NULL) {
+			free(lx_stack_list[i].sle_tsd->lxtsd_clone_state);
+			free(lx_stack_list[i].sle_tsd);
+		}
+
+		/*
+		 * Unmap the stack of every other LWP.
+		 */
+		(void) munmap(lx_stack_list[i].sle_stack,
+		    lx_stack_list[i].sle_stack_size);
+	}
+	/*
+	 * Did not find the stack for this LWP in the list.
+	 */
+	assert(this_stack != -1);
+
+	/*
+	 * Ensure the stack data for this LWP is in the first slot and shrink
+	 * the list.
+	 */
+	if (this_stack != 0) {
+		lx_stack_list[0] = lx_stack_list[this_stack];
+	}
+	lx_stack_list_elems = 1;
+	lx_stack_list = realloc(lx_stack_list, lx_stack_list_elems *
+	    sizeof (lx_stack_list[0]));
+	if (lx_stack_list == NULL) {
+		lx_err_fatal("failed to shrink stack list: %s",
+		    strerror(errno));
+	}
+
+	_sigon();
+}
+
+/*
+ * Allocate an alternate stack for the execution of native emulation routines.
+ * This routine is based, in part, on find_stack() from libc.
+ */
+int
+lx_alloc_stack(void **nstack, size_t *nstack_size)
+{
+	static int pagesize = 0;
+	static int stackprot = 0;
+	int stacksize = 0;
+	void *stack;
+
+	/*
+	 * Fetch configuration once:
+	 */
+	if (pagesize == 0) {
+		pagesize = _sysconf(_SC_PAGESIZE);
+		assert(pagesize > 0);
+	}
+	if (stackprot == 0) {
+		long lprot = _sysconf(_SC_STACK_PROT);
+
+		stackprot = lprot > 0 ? lprot : (PROT_READ | PROT_WRITE);
+	}
+
+	stacksize = lx_native_stack_page_count * pagesize;
+
+	if ((stack = mmap(NULL, stacksize, stackprot, MAP_PRIVATE |
+	    MAP_NORESERVE | MAP_ANON, -1, (off_t)0)) == MAP_FAILED) {
+		int en = errno;
+		lx_debug("lx_alloc_stack: failed to allocate stack: %s",
+		    strerror(errno));
+		errno = en;
+		return (-1);
+	}
+
+#if DEBUG
+	/*
+	 * Write a recognisable pattern into the allocated stack pages.
+	 */
+	for (pos = 0; pos < ((stacksize - 1) / 4); pos++) {
+		((uint32_t *)stack)[pos] = 0x0facade0;
+	}
+#endif
+
+	*nstack = stack;
+	*nstack_size = stacksize;
+
+	return (0);
+}
+
+/*
+ * Configure the in-kernel brand-specific LWP data with the native stack
+ * pointer for this thread.  If a stack is not passed, allocate one first.
+ */
+void
+lx_install_stack(void *stack, size_t stacksize, lx_tsd_t *tsd)
+{
+	thread_t me = thr_self();
+	int i;
+	uintptr_t stack_top;
+
+	if (stack == NULL) {
+		/*
+		 * If we were not passed a stack, then allocate one:
+		 */
+		if (lx_alloc_stack(&stack, &stacksize) == -1) {
+			lx_err_fatal("failed to allocate stack for thread "
+			    "%d: %s", me, strerror(errno));
+		}
+	}
+
+	/*
+	 * Install the stack in the global list of thread stacks.
+	 */
+	_sigoff();
+	VERIFY0(mutex_lock(&lx_stack_list_lock));
+
+	for (i = 0; i < lx_stack_list_elems; i++) {
+		assert(lx_stack_list[i].sle_tid != me);
+		if (lx_stack_list[i].sle_tid == 0)
+			break;
+	}
+	if (i >= lx_stack_list_elems) {
+		lx_stack_list_elems++;
+		lx_stack_list = realloc(lx_stack_list, lx_stack_list_elems *
+		    sizeof (lx_stack_list[0]));
+		if (lx_stack_list == NULL) {
+			lx_err_fatal("failed to extend stack list: %s",
+			    strerror(errno));
+		}
+	}
+	lx_stack_list[i].sle_tid = me;
+	lx_stack_list[i].sle_stack = stack;
+	lx_stack_list[i].sle_stack_size = stacksize;
+	lx_stack_list[i].sle_tsd = tsd;
+
+	VERIFY0(mutex_unlock(&lx_stack_list_lock));
+	_sigon();
+
+	/*
+	 * Inform the kernel of the location of the brand emulation
+	 * stack for this LWP:
+	 */
+	stack_top = (uintptr_t)stack + stacksize;
+	lx_debug("stack %p stack_top %p\n", stack, stack_top);
+	if (syscall(SYS_brand, B_SET_NATIVE_STACK, stack_top) != 0) {
+		lx_err_fatal("unable to set native stack: %s", strerror(errno));
+	}
+}
--- /dev/null	Wed Oct  5 00:31:48 2016
+++ new/usr/src/lib/brand/lx/lx_brand/common/statfs.c	Wed Oct  5 00:31:48 2016
@@ -0,0 +1,346 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.  All rights reserved.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <libintl.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <sys/statvfs.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <sys/lx_debug.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_statfs.h>
+#include <sys/lx_syscall.h>
+
+/*
+ * these defines must exist before we include regexp.h, see regexp(5)
+ */
+#define	RE_SIZE		1024
+#define	INIT		char *sp = instring;
+#define	GETC()		(*sp++)
+#define	PEEKC()		(*sp)
+#define	UNGETC(c)	(--sp)
+#define	RETURN(c)	return (NULL);
+#define	ERROR(c)	return ((char *)c);
+
+/*
+ * for regular expressions we're using regexp(5).
+ *
+ * we'd really prefer to use some other nicer regular expressions
+ * interfaces (like regcmp(3c), regcomp(3c), or re_comp(3c)) but we
+ * can't because all these other interfaces rely on the ability
+ * to allocate memory via libc malloc()/calloc() calls, which
+ * we can't really do here.
+ *
+ * we could optionally use regexpr(3gen) but we don't since the
+ * interfaces there are incredibly similar to the regexp(5)
+ * interfaces we're already using and we'd have the added
+ * requirement of linking against libgen.
+ *
+ * another option that was considered is fnmatch(3c) but the
+ * limited pattern expansion capability of this interface would
+ * force us to include more patterns to check against.
+ */
+#include <regexp.h>
+
+static struct lx_ftype_path {
+	char		*lfp_path;
+	char		lfp_re[RE_SIZE];
+	int		lfp_magic;
+	char		*lfp_magic_str;
+} ftype_path_list[] = {
+	{ "^/dev/pts$",		"",
+		LX_DEVPTS_SUPER_MAGIC,	"LX_DEVPTS_SUPER_MAGIC"	},
+	{ "^/dev/pts/$",	"",
+		LX_DEVPTS_SUPER_MAGIC,	"LX_DEVPTS_SUPER_MAGIC"	},
+	{ "^/dev/pts/[0-9][0-9]*$",	"",
+		LX_DEVPTS_SUPER_MAGIC,	"LX_DEVPTS_SUPER_MAGIC"	},
+	{ NULL,			"",
+		0,			NULL			}
+};
+
+/*
+ * For lack of linux equivalents, we present lofs and zfs as being ext2. Yes,
+ * this is a total lie, but Linux can't handle the truth of ZFS -- and ext2's
+ * small surface area seems to make it the most tenable lie to tell.
+ */
+static struct lx_ftype_name {
+	const char	*lfn_name;
+	int		lfn_magic;
+	char		*lfn_magic_str;
+} ftype_name_list[] = {
+	{ "hsfs",	LX_ISOFS_SUPER_MAGIC,	"LX_ISOFS_SUPER_MAGIC"	},
+	{ "nfs",	LX_NFS_SUPER_MAGIC,	"LX_NFS_SUPER_MAGIC"	},
+	{ "pcfs",	LX_MSDOS_SUPER_MAGIC,	"LX_MSDOS_SUPER_MAGIC"	},
+	{ "lx_proc",	LX_PROC_SUPER_MAGIC,	"LX_PROC_SUPER_MAGIC"	},
+	{ "tmpfs",	LX_TMPFS_SUPER_MAGIC,	"LX_TMPFS_SUPER_MAGIC"	},
+	{ "ufs",	LX_UFS_MAGIC,		"LX_UFS_MAGIC"		},
+	{ "lofs",	LX_EXT2_SUPER_MAGIC,	"LX_EXT2_SUPER_MAGIC"	},
+	{ "zfs",	LX_EXT2_SUPER_MAGIC,	"LX_EXT2_SUPER_MAGIC"	},
+	{ "lxautofs",	LX_AUTOFS_SUPER_MAGIC,	"LX_AUTOFS_SUPER_MAGIC"	},
+	{ NULL,		0,			NULL	}
+};
+
+int
+lx_statfs_init()
+{
+	int	i;
+	char	*rv;
+
+	for (i = 0; ftype_path_list[i].lfp_path != NULL; i++) {
+		rv = compile(
+		    ftype_path_list[i].lfp_path,
+		    ftype_path_list[i].lfp_re,
+		    ftype_path_list[i].lfp_re + RE_SIZE, '\0');
+		if (rv == NULL)
+			continue;
+
+		lx_debug("lx_statfs_init compile(\"%s\") failed",
+		    ftype_path_list[i].lfp_path);
+		return (1);
+	}
+	return (0);
+}
+
+static int
+stol_type(const char *path, const char *name)
+{
+	int	i;
+	lx_debug("\tstol_type(\"%s\", \"%s\")\n", path == NULL ? "NULL" : path,
+	    name == NULL ? "NULL" : name);
+
+	if (path != NULL) {
+		char userpath[MAXPATHLEN];
+
+		if (uucopystr(path, userpath, MAXPATHLEN) == -1)
+			return (-errno);
+
+		for (i = 0; ftype_path_list[i].lfp_path != NULL; i++) {
+			if (step(userpath, ftype_path_list[i].lfp_re) == 0)
+				continue;
+
+			/* got a match on the fs path */
+			lx_debug("\ttranslated f_type to 0x%x - %s",
+			    ftype_path_list[i].lfp_magic,
+			    ftype_path_list[i].lfp_magic_str);
+			return (ftype_path_list[i].lfp_magic);
+		}
+	}
+
+	assert(name != NULL);
+	for (i = 0; ftype_name_list[i].lfn_name != NULL; i++) {
+		if (strcmp(name, ftype_name_list[i].lfn_name) == 0) {
+
+			/* got a match on the fs name */
+			lx_debug("\ttranslated f_type to 0x%x - %s",
+			    ftype_name_list[i].lfn_magic,
+			    ftype_name_list[i].lfn_magic_str);
+			return (ftype_name_list[i].lfn_magic);
+		}
+	}
+
+	/* we don't know what the fs type is so just set it to 0 */
+	return (0);
+}
+
+/*
+ * The Linux statfs() is similar to the Solaris statvfs() call, the main
+ * difference being the use of a numeric 'f_type' identifier instead of the
+ * 'f_basetype' string.
+ */
+static int
+stol_statfs(const char *path, struct lx_statfs *l, struct statvfs *s)
+{
+	int type;
+
+	if ((type = stol_type(path, s->f_basetype)) < 0)
+		return (type);
+
+	l->f_type = type;
+	l->f_bsize = s->f_frsize;	/* other fields depend on frsize */
+	l->f_blocks = s->f_blocks;
+	l->f_bfree = s->f_bfree;
+	l->f_bavail = s->f_bavail;
+	l->f_files = s->f_files;
+	l->f_ffree = s->f_ffree;
+	l->f_fsid = s->f_fsid;
+	l->f_namelen = s->f_namemax;
+	l->f_frsize = s->f_frsize;
+	bzero(&(l->f_spare), sizeof (l->f_spare));
+
+	return (0);
+}
+
+static int
+stol_statfs64(const char *path, struct lx_statfs64 *l, struct statvfs64 *s)
+{
+	int type;
+
+	if ((type = stol_type(path, s->f_basetype)) < 0)
+		return (type);
+
+	l->f_type = type;
+	l->f_bsize = s->f_frsize;	/* other fields depend on frsize */
+	l->f_blocks = s->f_blocks;
+	l->f_bfree = s->f_bfree;
+	l->f_bavail = s->f_bavail;
+	l->f_files = s->f_files;
+	l->f_ffree = s->f_ffree;
+	l->f_fsid = s->f_fsid;
+	l->f_namelen = s->f_namemax;
+	l->f_frsize = s->f_frsize;
+	bzero(&(l->f_spare), sizeof (l->f_spare));
+
+	return (0);
+}
+
+long
+lx_statfs(uintptr_t p1, uintptr_t p2)
+{
+	const char *path = (const char *)p1;
+	struct lx_statfs lxfs, *fs = (struct lx_statfs *)p2;
+	struct statvfs vfs;
+	int err;
+
+	lx_debug("\tstatvfs(%s, 0x%p)", path, fs);
+	if (statvfs(path, &vfs) != 0)
+		return (-errno);
+
+	if ((err = stol_statfs(path, &lxfs, &vfs)) != 0)
+		return (err);
+
+	if (uucopy(&lxfs, fs, sizeof (struct lx_statfs)) != 0)
+		return (-errno);
+
+	return (0);
+}
+
+long
+lx_fstatfs(uintptr_t p1, uintptr_t p2)
+{
+	struct lx_statfs lxfs, *fs = (struct lx_statfs *)p2;
+	struct stat64 sb;
+	struct statvfs vfs;
+	char *path, path_buf[MAXPATHLEN];
+	int fd = (int)p1;
+	int err;
+
+	lx_debug("\tfstatvfs(%d, 0x%p)", fd, fs);
+
+	/*
+	 * fstatfs emulation for a pipe.
+	 */
+	if (fstat64(fd, &sb) == 0 && S_ISFIFO(sb.st_mode)) {
+		lxfs.f_type = LX_PIPEFS_MAGIC;
+		lxfs.f_bsize = 4096;
+		lxfs.f_blocks = 0;
+		lxfs.f_bfree = 0;
+		lxfs.f_bavail = 0;
+		lxfs.f_files = 0;
+		lxfs.f_ffree = 0;
+		lxfs.f_fsid = 0;
+		lxfs.f_namelen = 255;
+		lxfs.f_frsize = 4096;
+	} else {
+		if (fstatvfs(fd, &vfs) != 0)
+			return (-errno);
+
+		path = lx_fd_to_path(fd, path_buf, sizeof (path_buf));
+
+		if ((err = stol_statfs(path, &lxfs, &vfs)) != 0)
+			return (err);
+	}
+
+	if (uucopy(&lxfs, fs, sizeof (struct lx_statfs)) != 0)
+		return (-errno);
+
+	return (0);
+}
+
+/* ARGSUSED */
+long
+lx_statfs64(uintptr_t p1, uintptr_t p2, uintptr_t p3)
+{
+	const char *path = (const char *)p1;
+	struct lx_statfs64 lxfs, *fs = (struct lx_statfs64 *)p3;
+	struct statvfs64 vfs;
+	int err;
+
+	lx_debug("\tstatvfs64(%s, %d, 0x%p)", path, p2, fs);
+	if (statvfs64(path, &vfs) != 0)
+		return (-errno);
+
+	if ((err = stol_statfs64(path, &lxfs, &vfs)) != 0)
+		return (err);
+
+	if (uucopy(&lxfs, fs, sizeof (struct lx_statfs64)) != 0)
+		return (-errno);
+
+	return (0);
+}
+
+/* ARGSUSED */
+long
+lx_fstatfs64(uintptr_t p1, uintptr_t p2, uintptr_t p3)
+{
+	struct lx_statfs64 lxfs, *fs = (struct lx_statfs64 *)p3;
+	struct stat64 sb;
+	struct statvfs64 vfs;
+	char *path, path_buf[MAXPATHLEN];
+	int fd = (int)p1;
+	int err;
+
+	lx_debug("\tfstatvfs64(%d, %d, 0x%p)", fd, p2, fs);
+	if (fstat64(fd, &sb) == 0 && S_ISFIFO(sb.st_mode)) {
+		lxfs.f_type = LX_PIPEFS_MAGIC;
+		lxfs.f_bsize = 4096;
+		lxfs.f_blocks = 0;
+		lxfs.f_bfree = 0;
+		lxfs.f_bavail = 0;
+		lxfs.f_files = 0;
+		lxfs.f_ffree = 0;
+		lxfs.f_fsid = 0;
+		lxfs.f_namelen = 255;
+		lxfs.f_frsize = 4096;
+	} else {
+		if (fstatvfs64(fd, &vfs) != 0)
+			return (-errno);
+
+		path = lx_fd_to_path(fd, path_buf, sizeof (path_buf));
+
+		if ((err = stol_statfs64(path, &lxfs, &vfs)) != 0)
+			return (err);
+	}
+
+	if (uucopy(&lxfs, fs, sizeof (struct lx_statfs64)) != 0)
+		return (-errno);
+
+	return (0);
+}
--- /dev/null	Wed Oct  5 00:31:48 2016
+++ new/usr/src/lib/brand/lx/lx_brand/common/sysctl.c	Wed Oct  5 00:31:48 2016
@@ -0,0 +1,137 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2014 Joyent, Inc.  All rights reserved.
+ */
+
+#include <alloca.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/lx_syscall.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_debug.h>
+
+/*
+ * sysctl() implementation.  The full set of possible values is incredibly
+ * large; we only implement the bare minimum here, namely basic kernel
+ * information.
+ *
+ * For the moment, we also print out debugging messages if the application
+ * attempts to write or access any other values, so we can tell if we are not
+ * supporting something we should be.
+ */
+
+struct lx_sysctl_args {
+	int *name;
+	int nlen;
+	void *oldval;
+	size_t *oldlenp;
+	void *newval;
+	size_t newlen;
+};
+
+#define	LX_CTL_KERN		1
+
+#define	LX_KERN_OSTYPE		1
+#define	LX_KERN_OSRELEASE	2
+#define	LX_KERN_OSREV		3
+#define	LX_KERN_VERSION		4
+
+long
+lx_sysctl(uintptr_t raw)
+{
+	struct lx_sysctl_args args;
+	int name[2];
+	size_t oldlen;
+	char *namebuf;
+
+	if (uucopy((void *)raw, &args, sizeof (args)) < 0)
+		return (-EFAULT);
+
+	/*
+	 * We only allow [ CTL_KERN, KERN_* ] pairs, so reject anything that
+	 * doesn't have exactly two values starting with LX_CTL_KERN.
+	 */
+	if (args.nlen != 2)
+		return (-ENOTDIR);
+
+	if (uucopy(args.name, name, sizeof (name)) < 0)
+		return (-EFAULT);
+
+	if (name[0] != LX_CTL_KERN) {
+		lx_debug("sysctl: read of [%d, %d] unsupported",
+		    name[0], name[1]);
+		return (-ENOTDIR);
+	}
+
+	/* We don't support writing new sysctl values. */
+	if ((args.newval != NULL) || (args.newlen != 0)) {
+		lx_debug("sysctl: write of [%d, %d] unsupported",
+		    name[0], name[1]);
+		return (-EPERM);
+	}
+
+	/*
+	 * It may seem silly, but passing in a NULL oldval pointer and not
+	 * writing any new values is a perfectly legal thing to do and should
+	 * succeed.
+	 */
+	if (args.oldval == NULL)
+		return (0);
+
+	/*
+	 * Likewise, Linux specifies that setting a non-NULL oldval but a
+	 * zero *oldlenp should result in an errno of EFAULT.
+	 */
+	if ((uucopy(args.oldlenp, &oldlen, sizeof (oldlen)) < 0) ||
+	    (oldlen == 0))
+		return (-EFAULT);
+
+	namebuf = SAFE_ALLOCA(oldlen);
+	if (namebuf == NULL)
+		return (-ENOMEM);
+
+	switch (name[1]) {
+	case LX_KERN_OSTYPE:
+		(void) strlcpy(namebuf, LX_UNAME_SYSNAME, oldlen);
+		break;
+	case LX_KERN_OSRELEASE:
+		(void) strlcpy(namebuf, lx_release, oldlen);
+		break;
+	case LX_KERN_VERSION:
+		(void) strlcpy(namebuf, LX_UNAME_VERSION, oldlen);
+		break;
+	default:
+		lx_debug("sysctl: read of [CTL_KERN, %d] unsupported", name[1]);
+		return (-ENOTDIR);
+	}
+
+	oldlen = strlen(namebuf);
+
+	if ((uucopy(namebuf, args.oldval, oldlen) < 0) ||
+	    (uucopy(&oldlen, args.oldlenp, sizeof (oldlen)) < 0))
+		return (-EFAULT);
+
+	return (0);
+}
--- /dev/null	Wed Oct  5 00:31:48 2016
+++ new/usr/src/lib/brand/lx/lx_brand/common/sysv_ipc.c	Wed Oct  5 00:31:48 2016
@@ -0,0 +1,975 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.  All rights reserved.
+ */
+
+#include <errno.h>
+#include <unistd.h>
+#include <strings.h>
+#include <rctl.h>
+#include <alloca.h>
+#include <values.h>
+#include <sys/syscall.h>
+#include <sys/msg.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <sys/shm.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/lx_debug.h>
+#include <sys/lx_types.h>
+#include <sys/lx_sysv_ipc.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_syscall.h>
+
+#define	SLOT_SEM	0
+#define	SLOT_SHM	1
+#define	SLOT_MSG	2
+
+/* Use private SHM_RMID interface for IPC_RMID */
+#define	SHM_RMID	5
+
+static int
+get_rctlval(rctlblk_t *rblk, char *name, ulong_t limit, uint64_t *val)
+{
+	rctl_qty_t r;
+
+	if (getrctl(name, NULL, rblk, RCTL_FIRST) == -1)
+		return (-errno);
+
+	r = rctlblk_get_value(rblk);
+	if (r > limit)
+		return (-EOVERFLOW);
+
+	*val = r;
+	return (0);
+}
+
+/*
+ * Given a slot number and a maximum number of ids to extract from the
+ * kernel, return the msgid in the provided slot.
+ */
+static int
+slot_to_id(int type, int slot)
+{
+	uint_t nids, max;
+	int *idbuf = NULL;
+	int r = 0;
+
+	nids = 0;
+	for (;;) {
+		switch (type) {
+		case SLOT_SEM:
+			r = semids(idbuf, nids, &max);
+			break;
+		case SLOT_SHM:
+			r = shmids(idbuf, nids, &max);
+			break;
+		case SLOT_MSG:
+			r = msgids(idbuf, nids, &max);
+			break;
+		}
+
+		if (r < 0)
+			return (-errno);
+
+		if (max == 0)
+			return (-EINVAL);
+
+		if (max <= nids)
+			return (idbuf[slot]);
+
+		nids = max;
+		if ((idbuf = (int *)SAFE_ALLOCA(sizeof (int) * nids)) == NULL)
+			return (-ENOMEM);
+	}
+}
+
+/*
+ * Semaphore operations.
+ */
+long
+lx_semget(key_t key, int nsems, int semflg)
+{
+	int sol_flag;
+	int r;
+
+	lx_debug("\nsemget(%d, %d, %d)\n", key, nsems, semflg);
+	sol_flag = semflg & S_IAMB;
+	if (semflg & LX_IPC_CREAT)
+		sol_flag |= IPC_CREAT;
+	if (semflg & LX_IPC_EXCL)
+		sol_flag |= IPC_EXCL;
+
+	r = semget(key, nsems, sol_flag);
+	return ((r < 0) ? -errno : r);
+}
+
+long
+lx_semop(int semid, void *p1, size_t nsops)
+{
+	int r;
+	struct sembuf *sops = (struct sembuf *)p1;
+
+	lx_debug("\nsemop(%d, 0x%p, %u)\n", semid, sops, nsops);
+	if (nsops == 0)
+		return (-EINVAL);
+
+	r = semop(semid, sops, nsops);
+	return ((r < 0) ? -errno : r);
+}
+
+long
+lx_semtimedop(int semid, void *p1, size_t nsops, struct timespec *timeout)
+{
+	int r;
+	struct sembuf *sops = (struct sembuf *)p1;
+
+	lx_debug("\nsemtimedop(%d, 0x%p, %u, 0x%p)\n", semid, sops, nsops,
+	    timeout);
+	if (nsops == 0)
+		return (-EINVAL);
+
+	r = semtimedop(semid, sops, nsops, timeout);
+	return ((r < 0) ? -errno : r);
+}
+
+static int
+lx_semctl_ipcset(int semid, void *buf)
+{
+	struct lx_semid_ds semds;
+	struct semid_ds sol_semds;
+	int r;
+
+	if (uucopy(buf, &semds, sizeof (semds)))
+		return (-errno);
+
+	bzero(&sol_semds, sizeof (sol_semds));
+	sol_semds.sem_perm.uid = semds.sem_perm.uid;
+	sol_semds.sem_perm.gid = semds.sem_perm.gid;
+	sol_semds.sem_perm.mode = semds.sem_perm.mode;
+
+	r = semctl(semid, 0, IPC_SET, &sol_semds);
+	return ((r < 0) ? -errno : r);
+}
+
+static int
+lx_semctl_ipcstat(int semid, void *buf)
+{
+	struct lx_semid_ds semds;
+	struct semid_ds sol_semds;
+
+	if (semctl(semid, 0, IPC_STAT, &sol_semds) != 0)
+		return (-errno);
+
+	bzero(&semds, sizeof (semds));
+	semds.sem_perm.key = sol_semds.sem_perm.key;
+	semds.sem_perm.seq = sol_semds.sem_perm.seq;
+	semds.sem_perm.uid = sol_semds.sem_perm.uid;
+	semds.sem_perm.gid = sol_semds.sem_perm.gid;
+	semds.sem_perm.cuid = sol_semds.sem_perm.cuid;
+	semds.sem_perm.cgid = sol_semds.sem_perm.cgid;
+
+	/* Linux only uses the bottom 9 bits */
+	semds.sem_perm.mode = sol_semds.sem_perm.mode & S_IAMB;
+	semds.sem_otime = sol_semds.sem_otime;
+	semds.sem_ctime = sol_semds.sem_ctime;
+	semds.sem_nsems = sol_semds.sem_nsems;
+
+	if (uucopy(&semds, buf, sizeof (semds)))
+		return (-errno);
+
+	return (0);
+}
+
+static int
+lx_semctl_ipcinfo(void *buf)
+{
+	struct lx_seminfo i;
+	rctlblk_t *rblk;
+	int rblksz;
+	uint_t nids;
+	int idbuf;
+	int err;
+	uint64_t val;
+
+	rblksz = rctlblk_size();
+	if ((rblk = (rctlblk_t *)SAFE_ALLOCA(rblksz)) == NULL)
+		return (-ENOMEM);
+
+	bzero(&i, sizeof (i));
+	err = get_rctlval(rblk, "project.max-sem-ids", (ulong_t)MAXINT, &val);
+	if (err < 0)
+		return (err);
+	i.semmni = (int)val;
+	err = get_rctlval(rblk, "process.max-sem-nsems", (ulong_t)MAXINT, &val);
+	if (err < 0)
+		return (err);
+	i.semmsl = (int)val;
+	err = get_rctlval(rblk, "process.max-sem-ops", (ulong_t)MAXINT, &val);
+	if (err < 0)
+		return (err);
+	i.semopm = (int)val;
+
+	/*
+	 * We don't have corresponding rctls for these fields.  The values
+	 * are taken from the formulas used to derive the defaults listed
+	 * in the Linux header file.  We're lying, but trying to be
+	 * coherent about it.
+	 */
+	i.semmap = i.semmni;
+	i.semmns = i.semmni * i.semmsl;
+	i.semmnu = INT_MAX;
+	i.semume = INT_MAX;
+	i.semvmx = LX_SEMVMX;
+	if (semids(&idbuf, 0, &nids) < 0)
+		return (-errno);
+	i.semusz = nids;
+	i.semaem = INT_MAX;
+
+	if (uucopy(&i, buf, sizeof (i)) != 0)
+		return (-errno);
+
+	return (nids);
+}
+
+static int
+lx_semctl_semstat(int slot, void *buf)
+{
+	int r, semid;
+
+	semid = slot_to_id(SLOT_SEM, slot);
+	if (semid < 0)
+		return (semid);
+
+	r = lx_semctl_ipcstat(semid, buf);
+	return (r < 0 ? r : semid);
+}
+
+/*
+ * For the SETALL operation, we have to examine each of the semaphore
+ * values to be sure it is legal.
+ */
+static int
+lx_semctl_setall(int semid, ushort_t *arg)
+{
+	struct semid_ds semds;
+	ushort_t *vals;
+	int i, sz, r;
+
+	/*
+	 * Find out how many semaphores are involved, reserve enough
+	 * memory for an internal copy of the array, and then copy it in
+	 * from the process.
+	 */
+	if (semctl(semid, 0, IPC_STAT, &semds) != 0)
+		return (-errno);
+	sz = semds.sem_nsems * sizeof (ushort_t);
+	if ((vals = SAFE_ALLOCA(sz)) == NULL)
+		return (-ENOMEM);
+	if (uucopy(arg, vals, sz))
+		return (-errno);
+
+	/* Validate each of the values. */
+	for (i = 0; i < semds.sem_nsems; i++)
+		if (vals[i] > LX_SEMVMX)
+			return (-ERANGE);
+
+	r = semctl(semid, 0, SETALL, arg);
+
+	return ((r < 0) ? -errno : r);
+}
+
+long
+lx_semctl(int semid, int semnum, int cmd, void *ptr)
+{
+#if defined(_ILP32)
+	union lx_semun arg;
+#endif
+	int rval;
+	int opt = cmd & ~LX_IPC_64;
+	int use_errno = 0;
+	uint_t val;
+
+	lx_debug("\nsemctl(%d, %d, %d, 0x%p)\n", semid, semnum, cmd, ptr);
+
+#if defined(_ILP32)
+	/*
+	 * The final arg to semctl() is a pointer to a union.  For some
+	 * commands we can hand that pointer directly to the kernel.  For
+	 * these commands, we need to extract an argument from the union
+	 * before calling into the kernel.
+	 */
+	if (opt == LX_SETVAL || opt == LX_SETALL || opt == LX_GETALL ||
+	    opt == LX_IPC_SET || opt == LX_IPC_STAT || opt == LX_SEM_STAT ||
+	    opt == LX_IPC_INFO || opt == LX_SEM_INFO)
+		if (uucopy(ptr, &arg, sizeof (arg)))
+			return (-errno);
+#endif
+
+	switch (opt) {
+	case LX_GETVAL:
+		use_errno = 1;
+		rval = semctl(semid, semnum, GETVAL, NULL);
+		break;
+	case LX_SETVAL:
+#if defined(_ILP32)
+		val = arg.val;
+#else
+		val = (uint_t)(uintptr_t)ptr;
+#endif
+		if (val > LX_SEMVMX) {
+			return (-ERANGE);
+		}
+		use_errno = 1;
+		rval = semctl(semid, semnum, SETVAL, val);
+		break;
+	case LX_GETPID:
+		use_errno = 1;
+		rval = semctl(semid, semnum, GETPID, NULL);
+		break;
+	case LX_GETNCNT:
+		use_errno = 1;
+		rval = semctl(semid, semnum, GETNCNT, NULL);
+		break;
+	case LX_GETZCNT:
+		use_errno = 1;
+		rval = semctl(semid, semnum, GETZCNT, NULL);
+		break;
+	case LX_GETALL:
+		use_errno = 1;
+#if defined(_ILP32)
+		rval = semctl(semid, semnum, GETALL, arg.sems);
+#else
+		rval = semctl(semid, semnum, GETALL, ptr);
+#endif
+		break;
+	case LX_SETALL:
+#if defined(_ILP32)
+		rval = lx_semctl_setall(semid, arg.sems);
+#else
+		rval = lx_semctl_setall(semid, ptr);
+#endif
+		break;
+	case LX_IPC_RMID:
+		use_errno = 1;
+		rval = semctl(semid, semnum, IPC_RMID, NULL);
+		break;
+	case LX_SEM_STAT:
+#if defined(_ILP32)
+		rval = lx_semctl_semstat(semid, arg.semds);
+#else
+		rval = lx_semctl_semstat(semid, ptr);
+#endif
+		break;
+	case LX_IPC_STAT:
+#if defined(_ILP32)
+		rval = lx_semctl_ipcstat(semid, arg.semds);
+#else
+		rval = lx_semctl_ipcstat(semid, ptr);
+#endif
+		break;
+
+	case LX_IPC_SET:
+#if defined(_ILP32)
+		rval = lx_semctl_ipcset(semid, arg.semds);
+#else
+		rval = lx_semctl_ipcset(semid, ptr);
+#endif
+		break;
+
+	case LX_IPC_INFO:
+	case LX_SEM_INFO:
+#if defined(_ILP32)
+		rval = lx_semctl_ipcinfo(arg.semds);
+#else
+		rval = lx_semctl_ipcinfo(ptr);
+#endif
+		break;
+
+	default:
+		return (-EINVAL);
+	}
+
+	if (use_errno == 1 && rval < 0)
+		return (-errno);
+	return (rval);
+}
+
+/*
+ * msg operations.
+ */
+long
+lx_msgget(key_t key, int flag)
+{
+	int sol_flag;
+	int r;
+
+	lx_debug("\tlx_msgget(%d, %d)\n", key, flag);
+
+	sol_flag = flag & S_IAMB;
+	if (flag & LX_IPC_CREAT)
+		sol_flag |= IPC_CREAT;
+	if (flag & LX_IPC_EXCL)
+		sol_flag |= IPC_EXCL;
+
+	r = msgget(key, sol_flag);
+	return (r < 0 ? -errno : r);
+}
+
+long
+lx_msgsnd(int id, void *p1, size_t sz, int flag)
+{
+	int sol_flag = 0;
+	int r;
+	struct msgbuf *buf = (struct msgbuf *)p1;
+
+	lx_debug("\tlx_msgsnd(%d, 0x%p, %d, %d)\n", id, buf, sz, flag);
+
+	if (flag & LX_IPC_NOWAIT)
+		sol_flag |= IPC_NOWAIT;
+
+	if (((ssize_t)sz < 0) || (sz > LX_MSGMAX))
+		return (-EINVAL);
+
+	r = msgsnd(id, buf, sz, sol_flag);
+	return (r < 0 ? -errno : r);
+}
+
+long
+lx_msgrcv(int id, void *msgp, size_t sz, long msgtype, int flag)
+{
+	int sol_flag = 0;
+	ssize_t r;
+
+	lx_debug("\tlx_msgrcv(%d, 0x%p, %d, %d, %ld, %d)\n",
+	    id, msgp, sz, msgtype, flag);
+
+	/*
+	 * Check for a negative sz parameter.
+	 *
+	 * Unlike msgsnd(2), the Linux man page does not specify that
+	 * msgrcv(2) should return EINVAL if (sz > MSGMAX), only if (sz < 0).
+	 */
+	if ((ssize_t)sz < 0)
+		return (-EINVAL);
+
+	if (flag & LX_MSG_NOERROR)
+		sol_flag |= MSG_NOERROR;
+	if (flag & LX_IPC_NOWAIT)
+		sol_flag |= IPC_NOWAIT;
+
+	r = msgrcv(id, msgp, sz, msgtype, sol_flag);
+	return (r < 0 ? -errno : r);
+}
+
+static int
+lx_msgctl_ipcstat(int msgid, void *buf)
+{
+	struct lx_msqid_ds msgids;
+	struct msqid_ds sol_msgids;
+	int r;
+
+	r = msgctl(msgid, IPC_STAT, &sol_msgids);
+	if (r < 0)
+		return (-errno);
+
+	bzero(&msgids, sizeof (msgids));
+	msgids.msg_perm.key = sol_msgids.msg_perm.key;
+	msgids.msg_perm.seq = sol_msgids.msg_perm.seq;
+	msgids.msg_perm.uid = sol_msgids.msg_perm.uid;
+	msgids.msg_perm.gid = sol_msgids.msg_perm.gid;
+	msgids.msg_perm.cuid = sol_msgids.msg_perm.cuid;
+	msgids.msg_perm.cgid = sol_msgids.msg_perm.cgid;
+
+	/* Linux only uses the bottom 9 bits */
+	msgids.msg_perm.mode = sol_msgids.msg_perm.mode & S_IAMB;
+
+	msgids.msg_stime = sol_msgids.msg_stime;
+	msgids.msg_rtime = sol_msgids.msg_rtime;
+	msgids.msg_ctime = sol_msgids.msg_ctime;
+	msgids.msg_qbytes = sol_msgids.msg_qbytes;
+	msgids.msg_cbytes = sol_msgids.msg_cbytes;
+	msgids.msg_qnum = sol_msgids.msg_qnum;
+	msgids.msg_lspid = sol_msgids.msg_lspid;
+	msgids.msg_lrpid = sol_msgids.msg_lrpid;
+
+	if (uucopy(&msgids, buf, sizeof (msgids)))
+		return (-errno);
+
+	return (0);
+}
+
+static int
+lx_msgctl_ipcinfo(int cmd, void *buf)
+{
+	struct lx_msginfo m;
+	rctlblk_t *rblk;
+	int idbuf, rblksz, msgseg, maxmsgs;
+	uint_t nids;
+	int rval;
+	int err;
+	uint64_t val;
+
+	rblksz = rctlblk_size();
+	if ((rblk = (rctlblk_t *)SAFE_ALLOCA(rblksz)) == NULL)
+		return (-ENOMEM);
+
+	bzero(&m, sizeof (m));
+	err = get_rctlval(rblk, "project.max-msg-ids", (ulong_t)MAXINT, &val);
+	if (err < 0)
+		return (err);
+	m.msgmni = (int)val;
+	err = get_rctlval(rblk, "process.max-msg-qbytes", (ulong_t)MAXINT,
+	    &val);
+	if (err < 0)
+		return (err);
+	m.msgmnb = (int)val;
+
+	if (cmd == LX_IPC_INFO) {
+		err = get_rctlval(rblk, "process.max-msg-messages",
+		    (ulong_t)MAXINT, &val);
+		if (err < 0)
+			return (err);
+		maxmsgs = (int)val;
+		m.msgtql = maxmsgs * m.msgmni;
+		m.msgmap = m.msgmnb;
+		m.msgpool = m.msgmax * m.msgmnb;
+		rval = 0;
+	} else {
+		if (msgids(&idbuf, 0, &nids) < 0)
+			return (-errno);
+		m.msgpool = nids;
+
+		/*
+		 * For these fields, we can't even come up with a good fake
+		 * approximation.  These are listed as 'obsolete' or
+		 * 'unused' in the header files, so hopefully nobody is
+		 * relying on them anyway.
+		 */
+		m.msgtql = INT_MAX;
+		m.msgmap = INT_MAX;
+		rval = nids;
+	}
+
+	/*
+	 * We don't have corresponding rctls for these fields.  The values
+	 * are taken from the formulas used to derive the defaults listed
+	 * in the Linux header file.  We're lying, but trying to be
+	 * coherent about it.
+	 */
+	m.msgmax = m.msgmnb;
+	m.msgssz = 16;
+	msgseg = (m.msgpool * 1024) / m.msgssz;
+	m.msgseg = (msgseg > 0xffff) ? 0xffff : msgseg;
+
+	if (uucopy(&m, buf, sizeof (m)))
+		return (-errno);
+	return (rval);
+}
+
+static int
+lx_msgctl_ipcset(int msgid, void *buf)
+{
+	struct lx_msqid_ds msgids;
+	struct msqid_ds sol_msgids;
+	int r;
+
+	if (uucopy(buf, &msgids, sizeof (msgids)))
+		return (-errno);
+
+	bzero(&sol_msgids, sizeof (sol_msgids));
+	sol_msgids.msg_perm.uid = LX_UID16_TO_UID32(msgids.msg_perm.uid);
+	sol_msgids.msg_perm.gid = LX_UID16_TO_UID32(msgids.msg_perm.gid);
+
+	/* Linux only uses the bottom 9 bits */
+	sol_msgids.msg_perm.mode = msgids.msg_perm.mode & S_IAMB;
+	sol_msgids.msg_qbytes = msgids.msg_qbytes;
+
+	r = msgctl(msgid, IPC_SET, &sol_msgids);
+	return (r < 0 ? -errno : r);
+}
+
+static int
+lx_msgctl_msgstat(int slot, void *buf)
+{
+	int r, msgid;
+
+	lx_debug("msgstat(%d, 0x%p)\n", slot, buf);
+
+	msgid = slot_to_id(SLOT_MSG, slot);
+
+	if (msgid < 0)
+		return (msgid);
+
+	r = lx_msgctl_ipcstat(msgid, buf);
+	return (r < 0 ? r : msgid);
+}
+
+/*
+ * Split off the various msgctl's here
+ */
+long
+lx_msgctl(int msgid, int cmd, void *buf)
+{
+	int r;
+
+	lx_debug("\tlx_msgctl(%d, %d, 0x%p)\n", msgid, cmd, buf);
+	switch (cmd & ~LX_IPC_64) {
+	case LX_IPC_RMID:
+		r = msgctl(msgid, IPC_RMID, NULL);
+		if (r < 0)
+			r = -errno;
+		break;
+	case LX_IPC_SET:
+		r = lx_msgctl_ipcset(msgid, buf);
+		break;
+	case LX_IPC_STAT:
+		r = lx_msgctl_ipcstat(msgid, buf);
+		break;
+	case LX_MSG_STAT:
+		r = lx_msgctl_msgstat(msgid, buf);
+		break;
+
+	case LX_IPC_INFO:
+	case LX_MSG_INFO:
+		r = lx_msgctl_ipcinfo(cmd, buf);
+		break;
+
+	default:
+		r = -EINVAL;
+		break;
+	}
+
+	return (r);
+}
+
+/*
+ * shm-related operations.
+ */
+long
+lx_shmget(key_t key, size_t size, int flag)
+{
+	int sol_flag;
+	int r;
+
+	lx_debug("\tlx_shmget(%d, %d, %d)\n", key, size, flag);
+
+	sol_flag = flag & S_IAMB;
+	if (flag & LX_IPC_CREAT)
+		sol_flag |= IPC_CREAT;
+	if (flag & LX_IPC_EXCL)
+		sol_flag |= IPC_EXCL;
+
+	r = shmget(key, size, sol_flag);
+	return (r < 0 ? -errno : r);
+}
+
+long
+lx_shmat(int shmid, void *addr, int flags)
+{
+	int sol_flags;
+	void *ptr;
+
+	lx_debug("\tlx_shmat(%d, 0x%p, %d)\n", shmid, addr, flags);
+
+	sol_flags = 0;
+	if (flags & LX_SHM_RDONLY)
+		sol_flags |= SHM_RDONLY;
+	if (flags & LX_SHM_RND)
+		sol_flags |= SHM_RND;
+	if ((flags & LX_SHM_REMAP) && (addr == NULL))
+		return (-EINVAL);
+
+	ptr = shmat(shmid, addr, sol_flags);
+	if (ptr == (void *)-1)
+		return (-errno);
+
+	return ((ssize_t)ptr);
+}
+
+static int
+lx_shmctl_ipcinfo(void *buf)
+{
+	struct lx_shminfo s;
+	rctlblk_t *rblk;
+	int rblksz;
+	int err;
+	uint64_t val;
+
+	rblksz = rctlblk_size();
+	if ((rblk = (rctlblk_t *)SAFE_ALLOCA(rblksz)) == NULL)
+		return (-ENOMEM);
+
+	bzero(&s, sizeof (s));
+	err = get_rctlval(rblk, "project.max-shm-ids", ULONG_MAX, &val);
+	if (err < 0)
+		return (err);
+	s.shmmni = val;
+	err = get_rctlval(rblk, "project.max-shm-memory", ULONG_MAX, &val);
+	if (err < 0)
+		return (err);
+	s.shmmax = val;
+
+	/*
+	 * We don't have corresponding rctls for these fields.  The values
+	 * are taken from the formulas used to derive the defaults listed
+	 * in the Linux header file.  We're lying, but trying to be
+	 * coherent about it.
+	 */
+	s.shmmin = 1;
+	s.shmseg = ULONG_MAX;
+	s.shmall = s.shmmax / getpagesize();
+
+	if (uucopy(&s, buf, sizeof (s)))
+		return (-errno);
+
+	return (0);
+}
+
+static int
+lx_shmctl_ipcstat(int shmid, void *buf)
+{
+	struct lx_shmid_ds shmds;
+	struct shmid_ds sol_shmds;
+
+	if (shmctl(shmid, IPC_STAT, &sol_shmds) != 0)
+		return (-errno);
+
+	bzero(&shmds, sizeof (shmds));
+	shmds.shm_perm.key = sol_shmds.shm_perm.key;
+	shmds.shm_perm.seq = sol_shmds.shm_perm.seq;
+	shmds.shm_perm.uid = sol_shmds.shm_perm.uid;
+	shmds.shm_perm.gid = sol_shmds.shm_perm.gid;
+	shmds.shm_perm.cuid = sol_shmds.shm_perm.cuid;
+	shmds.shm_perm.cgid = sol_shmds.shm_perm.cgid;
+	shmds.shm_perm.mode = sol_shmds.shm_perm.mode & S_IAMB;
+	if (sol_shmds.shm_lkcnt > 0)
+		shmds.shm_perm.mode |= LX_SHM_LOCKED;
+	shmds.shm_segsz = sol_shmds.shm_segsz;
+	shmds.shm_atime	 = sol_shmds.shm_atime;
+	shmds.shm_dtime = sol_shmds.shm_dtime;
+	shmds.shm_ctime = sol_shmds.shm_ctime;
+	shmds.shm_cpid = sol_shmds.shm_cpid;
+	shmds.shm_lpid = sol_shmds.shm_lpid;
+	shmds.shm_nattch = (ushort_t)sol_shmds.shm_nattch;
+
+	if (uucopy(&shmds, buf, sizeof (shmds)))
+		return (-errno);
+
+	return (0);
+}
+
+static int
+lx_shmctl_ipcset(int shmid, void *buf)
+{
+	struct lx_shmid_ds shmds;
+	struct shmid_ds sol_shmds;
+	int r;
+
+	if (uucopy(buf, &shmds, sizeof (shmds)))
+		return (-errno);
+
+	bzero(&sol_shmds, sizeof (sol_shmds));
+	sol_shmds.shm_perm.uid = shmds.shm_perm.uid;
+	sol_shmds.shm_perm.gid = shmds.shm_perm.gid;
+	sol_shmds.shm_perm.mode = shmds.shm_perm.mode & S_IAMB;
+
+	r = shmctl(shmid, IPC_SET, &sol_shmds);
+	return (r < 0 ? -errno : r);
+}
+
+/*
+ * Build and return a shm_info structure. We only return the bare
+ * essentials required by ipcs. The rest of the info is not readily
+ * available.
+ */
+static int
+lx_shmctl_shminfo(void *buf)
+{
+	struct lx_shm_info shminfo;
+	uint_t nids;
+	int idbuf;
+
+	bzero(&shminfo, sizeof (shminfo));
+
+	if (shmids(&idbuf, 0, &nids) < 0)
+		return (-errno);
+
+	shminfo.used_ids = nids;
+	if (uucopy(&shminfo, buf, sizeof (shminfo)) != 0)
+		return (-errno);
+
+	return (nids);
+}
+
+static int
+lx_shmctl_shmstat(int slot, void *buf)
+{
+	int r, shmid;
+
+	lx_debug("shmctl_shmstat(%d, 0x%p)\n", slot, buf);
+	shmid = slot_to_id(SLOT_SHM, slot);
+	if (shmid < 0)
+		return (shmid);
+
+	r = lx_shmctl_ipcstat(shmid, buf);
+	return (r < 0 ? r : shmid);
+}
+
+long
+lx_shmctl(int shmid, int cmd, void *buf)
+{
+	int r;
+	int use_errno = 0;
+
+	lx_debug("\tlx_shmctl(%d, %d, 0x%p)\n", shmid, cmd, buf);
+	switch (cmd & ~LX_IPC_64) {
+	case LX_IPC_RMID:
+		use_errno = 1;
+		r = shmctl(shmid, SHM_RMID, NULL);	/* lx-private cmd */
+		break;
+
+	case LX_IPC_SET:
+		r = lx_shmctl_ipcset(shmid, buf);
+		break;
+
+	case LX_IPC_STAT:
+		r = lx_shmctl_ipcstat(shmid, buf);
+		break;
+
+	case LX_IPC_INFO:
+		r = lx_shmctl_ipcinfo(buf);
+		break;
+
+	case LX_SHM_LOCK:
+		use_errno = 1;
+		r = shmctl(shmid, SHM_LOCK, NULL);
+		break;
+
+	case LX_SHM_UNLOCK:
+		use_errno = 1;
+		r = shmctl(shmid, SHM_UNLOCK, NULL);
+		break;
+
+	case LX_SHM_INFO:
+		r = lx_shmctl_shminfo(buf);
+		break;
+
+	case LX_SHM_STAT:
+		r = lx_shmctl_shmstat(shmid, buf);
+		break;
+	default:
+		r = -EINVAL;
+		break;
+	}
+
+	if (use_errno == 1 && r < 0)
+		return (-errno);
+
+	return (r);
+}
+
+/*
+ * Under 32-bit Linux, glibc funnels all of the sysv IPC operations into this
+ * single ipc(2) system call.  We need to blow that up and filter the
+ * remnants into the proper Solaris system calls.
+ */
+long
+lx_ipc(uintptr_t cmd, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3,
+    uintptr_t arg4)
+{
+	int r;
+	void *bufptr = (void *)arg4;
+
+	lx_debug("lx_ipc(%d, %d, %d, %d, 0x%p, %d)\n",
+	    cmd, arg1, arg2, arg3, bufptr, arg4);
+
+	switch (cmd) {
+	case LX_MSGGET:
+		r = lx_msgget((key_t)arg1, (int)arg2);
+		break;
+	case LX_MSGSND:
+		r = lx_msgsnd((int)arg1, bufptr, (size_t)arg2, (int)arg3);
+		break;
+	case LX_MSGRCV:
+		{
+			struct {
+				void *msgp;
+				long msgtype;
+			} args;
+
+			/*
+			 * Rather than passing 5 args into ipc(2) directly,
+			 * glibc passes 4 args and uses the buf argument to
+			 * point to a structure containing two args: a pointer
+			 * to the message and the message type.
+			 */
+			if (uucopy(bufptr, &args, sizeof (args)))
+				return (-errno);
+			r = lx_msgrcv((int)arg1, args.msgp, (size_t)arg2,
+			    args.msgtype, (int)arg3);
+		}
+		break;
+	case LX_MSGCTL:
+		r = lx_msgctl((int)arg1, (int)arg2, bufptr);
+		break;
+	case LX_SEMCTL:
+		r = lx_semctl((int)arg1, (size_t)arg2, (int)arg3, bufptr);
+		break;
+	case LX_SEMOP:
+		/*
+		 * 'struct sembuf' is the same on Linux and Solaris, so we
+		 * pass bufptr straight through.
+		 */
+		r = lx_semop((int)arg1, bufptr, (size_t)arg2);
+		break;
+	case LX_SEMGET:
+		r = lx_semget((int)arg1, (size_t)arg2, (int)arg3);
+		break;
+	case LX_SHMAT:
+		r = lx_shmat((int)arg1, bufptr, (size_t)arg2);
+		if (r >= 0 || r <= -4096) {
+			if (uucopy(&r, (void *)arg3, sizeof (r)) != 0)
+				r = -errno;
+		}
+		break;
+	case LX_SHMDT:
+		r = shmdt(bufptr);
+		if (r < 0)
+			r = -errno;
+		break;
+	case LX_SHMGET:
+		r = lx_shmget((int)arg1, (size_t)arg2, (int)arg3);
+		break;
+	case LX_SHMCTL:
+		r = lx_shmctl((int)arg1, (int)arg2, bufptr);
+		break;
+
+	default:
+		r = -EINVAL;
+	}
+
+	return (r);
+}
--- /dev/null	Wed Oct  5 00:31:48 2016
+++ new/usr/src/lib/brand/lx/lx_brand/common/time.c	Wed Oct  5 00:31:48 2016
@@ -0,0 +1,132 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2014 Joyent, Inc.  All rights reserved.
+ */
+
+#include <errno.h>
+#include <time.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/times.h>
+#include <sys/lx_syscall.h>
+#include <sys/lx_misc.h>
+
+/*
+ * times() - The Linux implementation avoids writing to NULL, while Illumos
+ *	     returns EFAULT.
+ */
+long
+lx_times(uintptr_t p1)
+{
+	clock_t ret;
+	struct tms buf, *tp = (struct tms *)p1;
+
+	ret = times(&buf);
+
+	if ((ret == -1) ||
+	    ((tp != NULL) && uucopy((void *)&buf, tp, sizeof (buf)) != 0))
+		return (-errno);
+
+	return ((ret == -1) ? -errno : ret);
+}
+
+/*
+ * setitimer() - the Linux implementation can handle tv_usec values greater
+ *		 than 1,000,000 where Illumos would return EINVAL.
+ *
+ *		 There's still an issue here where Linux can handle a
+ *		 tv_sec value greater than 100,000,000 but Illumos cannot,
+ *		 but that would also mean setting an interval timer to fire
+ *		 over _three years_ in the future so it's unlikely anything
+ *		 other than Linux test suites will trip over it.
+ */
+long
+lx_setitimer(uintptr_t p1, uintptr_t p2, uintptr_t p3)
+{
+	struct itimerval itv;
+	struct itimerval *itp = (struct itimerval *)p2;
+
+	if (itp != NULL) {
+		if (uucopy(itp, &itv, sizeof (itv)) != 0)
+			return (-errno);
+
+		/*
+		 * Adjust any tv_usec fields >= 1,000,000 by adding any whole
+		 * seconds so indicated to tv_sec and leaving tv_usec as the
+		 * remainder.
+		 */
+		if (itv.it_interval.tv_usec >= MICROSEC) {
+			itv.it_interval.tv_sec +=
+			    itv.it_interval.tv_usec / MICROSEC;
+
+			itv.it_interval.tv_usec %= MICROSEC;
+		}
+		if (itv.it_value.tv_usec >= MICROSEC) {
+			itv.it_value.tv_sec +=
+			    itv.it_value.tv_usec / MICROSEC;
+
+			itv.it_value.tv_usec %= MICROSEC;
+		}
+
+		itp = &itv;
+	}
+
+	return ((setitimer((int)p1, itp, (struct itimerval *)p3) != 0) ?
+	    -errno : 0);
+}
+
+/*
+ * NOTE: The Linux man pages state this structure is obsolete and is
+ *	 unsupported, so it is declared here for sizing purposes only.
+ */
+struct lx_timezone {
+	int tz_minuteswest;	/* minutes W of Greenwich */
+	int tz_dsttime;		/* type of dst correction */
+};
+
+long
+lx_settimeofday(uintptr_t p1, uintptr_t p2)
+{
+	struct timeval tv;
+	struct lx_timezone tz;
+
+	if ((p1 != NULL) &&
+	    (uucopy((struct timeval *)p1, &tv, sizeof (tv)) < 0))
+		return (-errno);
+
+	/*
+	 * The Linux man page states use of the second parameter is obsolete,
+	 * but settimeofday(2) should still return EFAULT if it is set
+	 * to a bad non-NULL pointer (sigh...)
+	 */
+	if ((p2 != NULL) &&
+	    (uucopy((struct lx_timezone *)p2, &tz, sizeof (tz)) < 0))
+		return (-errno);
+
+	if ((p1 != NULL) && (settimeofday(&tv, NULL) < 0))
+		return (-errno);
+
+	return (0);
+}
--- /dev/null	Wed Oct  5 00:31:48 2016
+++ new/usr/src/lib/brand/lx/lx_brand/common/truncate.c	Wed Oct  5 00:31:48 2016
@@ -0,0 +1,173 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2014 Joyent, Inc.  All rights reserved.
+ */
+
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/resource.h>
+#include <sys/lx_types.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_syscall.h>
+
+/*
+ * ZFS does not enforce the process.max-file-size rctl on a file which is
+ * grown in size via truncate/ftruncate, since that is simply metadata which
+ * does  not consume any additional space. However, LTP truncate03 depends on
+ * this behavior so we enforce it here.
+ */
+static boolean_t
+p_fsize_excd(const char *path, off_t length)
+{
+	struct stat64 sb;
+	struct rlimit rl;
+
+	if (stat64(path, &sb) == 0 && sb.st_size < length) {
+		/* We are growing the file, check the rlimit */
+		if (getrlimit(RLIMIT_FSIZE, &rl) == 0 && length > rl.rlim_cur)
+			return (B_TRUE);
+	}
+
+	return (B_FALSE);
+}
+
+static boolean_t
+f_fsize_excd(int fd, off_t length)
+{
+	struct stat64 sb;
+	struct rlimit rl;
+
+	if (fstat64(fd, &sb) == 0 && sb.st_size < length) {
+		/* We are growing the file, check the rlimit */
+		if (getrlimit(RLIMIT_FSIZE, &rl) == 0 && length > rl.rlim_cur)
+			return (B_TRUE);
+	}
+
+	return (B_FALSE);
+}
+
+/*
+ * On Illumos, truncate() and ftruncate() are implemented in libc, so these are
+ * layered on those interfaces.
+ */
+
+long
+lx_truncate(uintptr_t path, uintptr_t length)
+{
+#if defined(_ILP32)
+	if ((off_t)length >= 0xffffffffUL)
+		return (-EFBIG);
+#endif
+
+	if (length > 0 && p_fsize_excd((const char *)path, (off_t)length))
+		return (-EFBIG);
+
+	return (truncate((const char *)path, (off_t)length) == 0 ? 0 : -errno);
+}
+
+long
+lx_ftruncate(uintptr_t fd, uintptr_t length)
+{
+	int r;
+
+#if defined(_ILP32)
+	if ((off_t)length >= 0xffffffffUL)
+		return (-EFBIG);
+#endif
+
+	if (length > 0 && f_fsize_excd((int)fd, (off_t)length))
+		return (-EFBIG);
+
+	r = ftruncate((int)fd, (off_t)length);
+	/*
+	 * On Linux, truncating a file opened read-only returns EINVAL whereas
+	 * Illumos returns EBADF.
+	 */
+	if (r != 0) {
+		if (errno == EBADF) {
+			int mode;
+
+			if ((mode = fcntl(fd, F_GETFL, 0)) != -1 &&
+			    (mode & O_ACCMODE) == O_RDONLY)
+				r = -EINVAL;
+			else
+				r = -EBADF; /* keep existing errno */
+		} else {
+			r = -errno;
+		}
+	}
+	return (r);
+}
+
+long
+lx_truncate64(uintptr_t path, uintptr_t length_lo, uintptr_t length_hi)
+{
+	uint64_t len = LX_32TO64(length_lo, length_hi);
+
+	if (len >= 0x7fffffffffffffffULL)
+		return (-EFBIG);
+
+	if (len > 0 && p_fsize_excd((const char *)path, (off_t)len))
+		return (-EFBIG);
+
+	return (truncate64((const char *)path, len) == 0 ? 0 : -errno);
+}
+
+long
+lx_ftruncate64(uintptr_t fd, uintptr_t length_lo, uintptr_t length_hi)
+{
+	int r;
+	uint64_t len = LX_32TO64(length_lo, length_hi);
+
+	if (len >= 0x7fffffffffffffffULL)
+		return (-EFBIG);
+
+	if (len > 0 && f_fsize_excd((int)fd, (off_t)len))
+		return (-EFBIG);
+
+	r = ftruncate64((int)fd, len);
+	/*
+	 * On Linux, truncating a file opened read-only returns EINVAL whereas
+	 * Illumos returns EBADF.
+	 */
+	if (r != 0) {
+		if (errno == EBADF) {
+			int mode;
+
+			if ((mode = fcntl(fd, F_GETFL, 0)) != -1 &&
+			    (mode & O_ACCMODE) == O_RDONLY)
+				r = -EINVAL;
+			else
+				r = -EBADF; /* keep existing errno */
+		} else {
+			r = -errno;
+		}
+	}
+
+	return (r);
+}
--- /dev/null	Wed Oct  5 00:31:48 2016
+++ new/usr/src/lib/brand/lx/lx_brand/i386/Makefile	Wed Oct  5 00:31:48 2016
@@ -0,0 +1,48 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+# Copyright 2015 Joyent, Inc.
+#
+# lib/brand/lx/i386/Makefile
+
+ISASRCDIR=.
+
+ASFLAGS	+= -P -D_ASM
+
+include ../Makefile.com
+
+DYNFLAGS +=	-Wl,-I/native/lib/ld.so.1
+
+POFILE=		lx_brand.po
+MSGFILES=	$(CSRCS)
+
+ASSYMDEP_OBJS = lx_handler.o
+
+install: all $(ROOTLIBS)
+
+$(POFILE): $(MSGFILES)
+	$(BUILDPO.msgfiles)
+
+_msg: $(MSGDOMAINPOFILE)
+
+include $(SRC)/Makefile.msg.targ
--- /dev/null	Wed Oct  5 00:31:48 2016
+++ new/usr/src/lib/brand/lx/lx_brand/i386/lx_crt.s	Wed Oct  5 00:31:48 2016
@@ -0,0 +1,65 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <sys/asm_linkage.h>
+
+#if defined(lint)
+
+void
+_start(void)
+{
+}
+
+#else	/* lint */
+
+	/*
+	 * C language startup routine for the lx brand shared library.
+	 */
+	ENTRY_NP(_start)
+	pushl	$0			/ Build a stack frame. retpc = NULL
+	pushl	$0			/ fp = NULL
+	movl	%esp, %ebp		/ first stack frame
+
+	/*
+	 * Calculate the location of the envp array by adding the size of
+	 * the argv array to the start of the argv array.
+	 */
+	movl	8(%ebp), %eax		/ argc in %eax
+	leal	16(%ebp,%eax,4), %edx	/ envp in %edx
+	andl	$-16, %esp
+	pushl	%edx			/ push envp
+	leal	12(%ebp),%edx		/ compute &argv[0]
+	pushl	%edx			/ push argv
+	pushl	%eax			/ push argc
+	call	lx_init
+	/*
+	 * lx_init will never return.
+	 */
+	SET_SIZE(_start)
+
+#endif	/* lint */
--- /dev/null	Wed Oct  5 00:31:49 2016
+++ new/usr/src/lib/brand/lx/lx_brand/i386/lx_handler.s	Wed Oct  5 00:31:49 2016
@@ -0,0 +1,91 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
+ */
+
+#include <sys/asm_linkage.h>
+#include <sys/regset.h>
+#include <sys/segments.h>
+#include <sys/syscall.h>
+#include <sys/lx_brand.h>
+
+#if defined(_ASM)
+#include <sys/lx_signal.h>
+#include <sys/lx_syscall.h>
+#endif	/* _ASM */
+
+/* 32-bit syscall numbers */
+#define	LX_SYS_sigreturn	119
+#define	LX_SYS_rt_sigreturn	173
+
+#if defined(lint)
+
+#include <sys/types.h>
+#include <sys/regset.h>
+#include <sys/signal.h>
+
+void
+lx_sigreturn_tramp(void)
+{}
+
+void
+lx_rt_sigreturn_tramp(void)
+{}
+
+#else	/* lint */
+
+	ENTRY_NP(lx_swap_gs)
+	push	%eax		/* save the current eax value */
+	movl	0xc(%esp),%eax	/* 2nd param is a pointer */
+	movw	%gs,(%eax)	/* use the pointer to save current gs */
+	movl	0x8(%esp),%eax	/* first parameter is the new gs value */
+	movw	%ax, %gs	/* switch to the new gs value */
+	pop	%eax		/* restore eax */
+	ret
+	SET_SIZE(lx_swap_gs)
+
+	/*
+	 * Trampoline code is called by the return at the end of a Linux
+	 * signal handler to return control to the interrupted application
+	 * via the lx_sigreturn() or lx_rt_sigreturn() syscalls.
+	 *
+	 * (lx_sigreturn() is called for legacy signal handling, and
+	 * lx_rt_sigreturn() is called for "new"-style signals.)
+	 *
+	 * These two routines must consist of the EXACT code sequences below
+	 * as gdb looks at the sequence of instructions a routine will return
+	 * to determine whether it is in a signal handler or not.
+	 * See the Linux code setup_signal_stack_sc() in arch/x86/um/signal.c.
+	 */
+	ENTRY_NP(lx_sigreturn_tramp)
+	popl	%eax
+	movl	$LX_SYS_sigreturn, %eax
+	int	$0x80
+	SET_SIZE(lx_sigreturn_tramp)
+
+	ENTRY_NP(lx_rt_sigreturn_tramp)
+	movl	$LX_SYS_rt_sigreturn, %eax
+	int	$0x80
+	SET_SIZE(lx_rt_sigreturn_tramp)
+#endif	/* lint */
--- /dev/null	Wed Oct  5 00:31:49 2016
+++ new/usr/src/lib/brand/lx/lx_brand/sys/lx_aio.h	Wed Oct  5 00:31:49 2016
@@ -0,0 +1,80 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#ifndef _SYS_LX_AIO_H
+#define	_SYS_LX_AIO_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define	LX_IOCB_FLAG_RESFD		0x0001
+
+#define	LX_IOCB_CMD_PREAD		0
+#define	LX_IOCB_CMD_PWRITE		1
+#define	LX_IOCB_CMD_FSYNC		2
+#define	LX_IOCB_CMD_FDSYNC		3
+#define	LX_IOCB_CMD_PREADX		4
+#define	LX_IOCB_CMD_POLL		5
+#define	LX_IOCB_CMD_NOOP		6
+#define	LX_IOCB_CMD_PREADV		7
+#define	LX_IOCB_CMD_PWRITEV		8
+
+#define	LX_KIOCB_KEY			0
+
+typedef struct lx_io_event lx_io_event_t;
+typedef struct lx_iocb lx_iocb_t;
+typedef ulong_t lx_aio_context_t;
+
+/*
+ * Linux binary definition of an I/O event.
+ */
+struct lx_io_event {
+	uint64_t	lxioe_data;	/* data payload */
+	uint64_t	lxioe_object;	/* object of origin */
+	int64_t		lxioe_res;	/* result code */
+	int64_t		lxioe_res2;	/* "secondary" result (WTF?) */
+};
+
+/*
+ * Linux binary definition of an I/O control block.
+ */
+struct lx_iocb {
+	uint64_t	lxiocb_data;		/* data payload */
+	uint32_t	lxiocb_key;		/* must be LX_KIOCB_KEY (!) */
+	uint32_t	lxiocb_reserved1;
+	uint16_t	lxiocb_op;		/* operation */
+	int16_t		lxiocb_reqprio;		/* request priority */
+	uint32_t	lxiocb_fd;		/* file descriptor */
+	uint64_t	lxiocb_buf;		/* data buffer */
+	uint64_t	lxiocb_nbytes;		/* number of bytes */
+	int64_t		lxiocb_offset;		/* offset in file */
+	uint64_t	lxiocb_reserved2;
+	uint32_t	lxiocb_flags;		/* LX_IOCB_FLAG_* flags */
+	uint32_t	lxiocb_resfd;		/* eventfd fd, if any */
+};
+
+extern long lx_io_setup(unsigned int, lx_aio_context_t *);
+extern long lx_io_submit(lx_aio_context_t, long nr, uintptr_t **);
+extern long lx_io_getevents(lx_aio_context_t, long, long,
+    lx_io_event_t *, struct timespec *);
+extern long lx_io_cancel(lx_aio_context_t, lx_iocb_t *, lx_io_event_t *);
+extern long lx_io_destroy(lx_aio_context_t);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_LX_AIO_H */
--- /dev/null	Wed Oct  5 00:31:49 2016
+++ new/usr/src/lib/brand/lx/lx_brand/sys/lx_debug.h	Wed Oct  5 00:31:49 2016
@@ -0,0 +1,54 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#ifndef	_LX_DEBUG_H
+#define	_LX_DEBUG_H
+
+#include <lx_provider_impl.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/* initialize the debugging subsystem */
+extern void lx_debug_init(boolean_t, boolean_t, const char *);
+
+/* printf() style debug message functionality */
+extern void lx_debug(const char *, ...);
+
+extern int lx_debug_enabled;
+
+#define	LX_DEBUG_ISENABLED	\
+	(lx_debug_enabled || LX_DEBUG_ENABLED())
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _LX_DEBUG_H */
--- /dev/null	Wed Oct  5 00:31:49 2016
+++ new/usr/src/lib/brand/lx/lx_brand/sys/lx_misc.h	Wed Oct  5 00:31:49 2016
@@ -0,0 +1,175 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright 2016 Joyent, Inc.  All rights reserved.
+ */
+
+#ifndef _SYS_LX_H
+#define	_SYS_LX_H
+
+#include <stdio.h>
+#include <alloca.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/lwp.h>
+
+#include <sys/lx_brand.h>
+#include <sys/lx_thread.h>
+
+#include <lx_errno.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern char lx_release[LX_KERN_RELEASE_MAX];
+extern char lx_cmd_name[MAXNAMLEN];
+extern pid_t zoneinit_pid;
+extern int lx_is_vforked;
+extern boolean_t lx_no_abort_handler;
+
+/*
+ * Values Linux expects for init
+ */
+#define	LX_INIT_PID	1
+
+/*
+ * the maximum length of messages to be output with lx_msg(), lx_err(),
+ * lx_debug(), or lx_unsupported().
+ */
+#define	LX_MSG_MAXLEN			(128 + MAXPATHLEN)
+
+/*
+ * Linux scheduler priority ranges.
+ */
+#define	LX_SCHED_PRIORITY_MIN_OTHER	0
+#define	LX_SCHED_PRIORITY_MAX_OTHER	0
+#define	LX_SCHED_PRIORITY_MIN_RRFIFO	1
+#define	LX_SCHED_PRIORITY_MAX_RRFIFO	99
+
+/*
+ * Based on code from brand_misc.h, but use of that is incompatible with the
+ * lx brand.
+ *
+ * These macros invoke a brandsys subcommand, B_TRUSS_POINT, which makes it
+ * easy to debug with DTrace.
+ */
+#define	B_TRUSS_POINT	6
+
+#define	B_TRACE_POINT_5(a0, a1, a2, a3, a4) \
+	(void) syscall(SYS_brand, B_TRUSS_POINT, (a0), (a1), (a2), (a3), (a4))
+
+#define	B_TRACE_POINT_4(a0, a1, a2, a3) \
+	B_TRACE_POINT_5((a0), (a1), (a2), (a3), 0)
+
+#define	B_TRACE_POINT_3(a0, a1, a2) \
+	B_TRACE_POINT_5((a0), (a1), (a2), 0, 0)
+
+#define	B_TRACE_POINT_2(a0, a1) \
+	B_TRACE_POINT_5((a0), (a1), 0, 0, 0)
+
+#define	B_TRACE_POINT_1(a0) \
+	B_TRACE_POINT_5((a0), 0, 0, 0, 0)
+
+#define	B_TRACE_POINT_0() \
+	B_TRACE_POINT_5(0, 0, 0, 0, 0)
+
+/*
+ * Macros to access register state within a ucontext_t:
+ */
+#define	LX_REG(ucp, r)	((ucp)->uc_mcontext.gregs[(r)])
+
+/*
+ * normally we never want to write to stderr or stdout because it's unsafe
+ * to make assumptions about the underlying file descriptors.  to protect
+ * against writes to these file descriptors we go ahead and close them
+ * our brand process initalization code.  but there are still occasions
+ * where we are willing to make assumptions about our file descriptors
+ * and write to them.  at thes times we should use one lx_msg() or
+ * lx_msg_error()
+ */
+extern void lx_msg(char *, ...);
+extern void lx_err(char *, ...);
+extern void lx_err_fatal(char *, ...);
+extern void lx_unsupported(char *, ...);
+
+struct ucontext;
+
+extern ucontext_t *lx_syscall_regs(void);
+extern uintptr_t lx_find_brand_sp(void);
+extern const ucontext_t *lx_find_brand_uc(void);
+
+extern void lx_jump_to_linux(ucontext_t *) __NORETURN;
+
+extern char *lx_fd_to_path(int fd, char *buf, int buf_size);
+extern int lx_lpid_to_spair(pid_t, pid_t *, lwpid_t *);
+extern int lx_lpid_to_spid(pid_t, pid_t *);
+
+extern void lx_ptrace_init();
+extern int lx_ptrace_wait(siginfo_t *);
+extern void lx_ptrace_fork(void);
+extern void lx_ptrace_stop_if_option(int, boolean_t, ulong_t msg, ucontext_t *);
+extern void lx_ptrace_clone_begin(int, boolean_t);
+
+extern int lx_check_alloca(size_t);
+#define	SAFE_ALLOCA(sz)	(lx_check_alloca(sz) ? alloca(sz) : NULL)
+
+extern void lx_init_tsd(lx_tsd_t *);
+extern int lx_alloc_stack(void **, size_t *);
+extern void lx_install_stack(void *, size_t, lx_tsd_t *);
+extern void lx_free_stack(void);
+extern void lx_free_other_stacks(void);
+extern void lx_stack_prefork(void);
+extern void lx_stack_postfork(void);
+
+/*
+ * NO_UUCOPY disables calls to the uucopy* system calls to help with
+ * debugging brand library accesses to linux application memory.
+ */
+#ifdef NO_UUCOPY
+
+int uucopy_unsafe(const void *src, void *dst, size_t n);
+int uucopystr_unsafe(const void *src, void *dst, size_t n);
+
+#define	uucopy(src, dst, n)	uucopy_unsafe((src), (dst), (n))
+#define	uucopystr(src, dst, n)	uucopystr_unsafe((src), (dst), (n))
+
+#endif /* NO_UUCOPY */
+
+/*
+ * We use these Private libc interfaces to defer signals during critical
+ * sections.
+ */
+extern void _sigon(void);
+extern void _sigoff(void);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_LX_H */
--- /dev/null	Wed Oct  5 00:31:49 2016
+++ new/usr/src/lib/brand/lx/lx_brand/sys/lx_mount.h	Wed Oct  5 00:31:49 2016
@@ -0,0 +1,163 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#ifndef	_LX_MOUNT_H
+#define	_LX_MOUNT_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#include <rpc/rpc.h>
+#include <nfs/nfs.h>
+
+extern int lx_nfs_mount(char *, char *, char *, int, char *);
+
+/*
+ * mount() is significantly different between Linux and Solaris.  The main
+ * difference is between the set of flags.  Some flags on Linux can be
+ * translated to a Solaris equivalent, some are converted to a
+ * filesystem-specific option, while others have no equivalent whatsoever.
+ */
+#define	LX_MS_MGC_VAL		0xC0ED0000
+#define	LX_MS_RDONLY		0x00000001
+#define	LX_MS_NOSUID		0x00000002
+#define	LX_MS_NODEV		0x00000004
+#define	LX_MS_NOEXEC		0x00000008
+#define	LX_MS_SYNCHRONOUS	0x00000010
+#define	LX_MS_REMOUNT		0x00000020
+#define	LX_MS_MANDLOCK		0x00000040
+#define	LX_MS_NOATIME		0x00000400
+#define	LX_MS_NODIRATIME	0x00000800
+#define	LX_MS_BIND		0x00001000
+#define	LX_MS_MOVE		0x00002000
+#define	LX_MS_REC		0x00004000
+#define	LX_MS_SILENT		0x00008000
+#define	LX_MS_POSIXACL		0x00010000
+#define	LX_MS_UNBINDABLE	0x00020000
+#define	LX_MS_PRIVATE		0x00040000
+#define	LX_MS_SLAVE		0x00080000
+#define	LX_MS_SHARED		0x00100000
+#define	LX_MS_RELATIME		0x00200000
+#define	LX_MS_KERNMOUNT		0x00400000
+#define	LX_MS_I_VERSION		0x00800000
+#define	LX_MS_STRICTATIME	0x01000000
+#define	LX_MS_LAZYTIME		0x02000000
+
+/* internal flags - ignored if passed in */
+#define	LX_MS_NOSEC		0x10000000
+#define	LX_MS_BORN		0x20000000
+#define	LX_MS_ACTIVE		0x40000000
+#define	LX_MS_NOUSER		0x80000000
+
+#define	LX_MS_SUPPORTED		(LX_MS_MGC_VAL | \
+				LX_MS_RDONLY | LX_MS_NOSUID | \
+				LX_MS_NODEV | LX_MS_NOEXEC | \
+				LX_MS_REMOUNT | LX_MS_NOATIME | \
+				LX_MS_BIND | LX_MS_SILENT | \
+				LX_MS_STRICTATIME | LX_MS_NOSEC | \
+				LX_MS_BORN | LX_MS_ACTIVE | LX_MS_NOUSER)
+
+/*
+ * support for nfs mounts
+ */
+#define	LX_NMD_MAXHOSTNAMELEN		256
+
+#define	LX_NFS_MOUNT_SOFT		0x00000001
+#define	LX_NFS_MOUNT_INTR		0x00000002
+#define	LX_NFS_MOUNT_SECURE		0x00000004
+#define	LX_NFS_MOUNT_POSIX		0x00000008
+#define	LX_NFS_MOUNT_NOCTO		0x00000010
+#define	LX_NFS_MOUNT_NOAC		0x00000020
+#define	LX_NFS_MOUNT_TCP		0x00000040
+#define	LX_NFS_MOUNT_VER3		0x00000080
+#define	LX_NFS_MOUNT_KERBEROS		0x00000100
+#define	LX_NFS_MOUNT_NONLM		0x00000200
+#define	LX_NFS_MOUNT_BROKEN_SUID	0x00000400
+#define	LX_NFS_MOUNT_SUPPORTED		(LX_NFS_MOUNT_SOFT | \
+					LX_NFS_MOUNT_INTR | \
+					LX_NFS_MOUNT_POSIX | \
+					LX_NFS_MOUNT_NOCTO | \
+					LX_NFS_MOUNT_NOAC | \
+					LX_NFS_MOUNT_TCP | \
+					LX_NFS_MOUNT_VER3 | \
+					LX_NFS_MOUNT_NONLM)
+
+#define	LX_NMD_DEFAULT_RSIZE		0
+#define	LX_NMD_DEFAULT_WSIZE		0
+
+/*
+ * the nfs v3 file handle structure definitions are _almost_ the same
+ * on linux and solaris.  the key difference are:
+ *
+ * 1) on linux fh3_length is an unsigned short where as on solaris it's
+ *    an int.
+ *
+ * 2) on linux the file handle data doesn't 32 bit members, so the structure
+ *    is not 32 bit aligned.  (where as on solaris it is.)
+ *
+ * so rather than defining a structure that would allow us to intrepret
+ * all the contents of the nfs v3 file handle here, we decide to treate
+ * the file handle as an array of chars.  this works just fine since it
+ * avoids the alignment issues and the actual file handle handle contects
+ * are defined by the nfs specification so they are common across solaris
+ * and linux.  we do the same thing for nfs v2 file handles.
+ */
+struct lx_nfs_fh2 {
+	unsigned char	lx_fh_data[NFS_FHSIZE];
+} lx_nfs_fh2;
+
+struct lx_nfs_fh3 {
+	unsigned short	lx_fh3_length;
+	unsigned char	lx_fh3_data[NFS3_FHSIZE];
+} lx_nfs_fh3;
+
+typedef struct lx_nfs_mount_data {
+	int			nmd_version;
+	int			nmd_fd;
+	struct lx_nfs_fh2	nmd_old_root;
+	int			nmd_flags;
+	int			nmd_rsize;
+	int			nmd_wsize;
+	int			nmd_timeo;
+	int			nmd_retrans;
+	int			nmd_acregmin;
+	int			nmd_acregmax;
+	int			nmd_acdirmin;
+	int			nmd_acdirmax;
+	struct sockaddr_in	nmd_addr;
+	char			nmd_hostname[LX_NMD_MAXHOSTNAMELEN];
+	int			nmd_namlen;
+	uint_t			nmd_bsize;
+	struct lx_nfs_fh3	nmd_root;
+} lx_nfs_mount_data_t;
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _LX_MOUNT_H */
--- /dev/null	Wed Oct  5 00:31:49 2016
+++ new/usr/src/lib/brand/lx/lx_brand/sys/lx_poll.h	Wed Oct  5 00:31:49 2016
@@ -0,0 +1,65 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.	All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_LX_POLL_H
+#define	_SYS_LX_POLL_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * These events are identical between Linux and Solaris
+ */
+#define	LX_POLLIN	0x001
+#define	LX_POLLPRI	0x002
+#define	LX_POLLOUT	0x004
+#define	LX_POLLERR	0x008
+#define	LX_POLLHUP	0x010
+#define	LX_POLLNVAL	0x020
+#define	LX_POLLRDNORM	0x040
+#define	LX_POLLRDBAND	0x080
+
+#define	LX_POLL_COMMON_EVENTS (LX_POLLIN | LX_POLLPRI | LX_POLLOUT |	\
+	LX_POLLERR | LX_POLLHUP | LX_POLLNVAL | LX_POLLRDNORM | LX_POLLRDBAND)
+
+/*
+ * These events differ between Linux and Solaris
+ */
+#define	LX_POLLWRNORM	0x0100
+#define	LX_POLLWRBAND	0x0200
+#define	LX_POLLRDHUP	0x2000
+
+
+#define	LX_POLL_SUPPORTED_EVENTS	\
+	(LX_POLL_COMMON_EVENTS | LX_POLLWRNORM | LX_POLLWRBAND | LX_POLLRDHUP)
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_LX_POLL_H */
--- /dev/null	Wed Oct  5 00:31:49 2016
+++ new/usr/src/lib/brand/lx/lx_brand/sys/lx_signal.h	Wed Oct  5 00:31:49 2016
@@ -0,0 +1,289 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
+ */
+
+#ifndef _SYS_LX_SIGNAL_H
+#define	_SYS_LX_SIGNAL_H
+
+#if !defined(_ASM)
+#include <sys/lx_types.h>
+#include <sys/ucontext.h>
+#include <sys/lx_siginfo.h>
+#include <lx_signum.h>
+
+#endif	/* !defined(_ASM) */
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/*
+ * Linux sigaction flags
+ */
+#define	LX_SA_NOCLDSTOP		0x00000001
+#define	LX_SA_NOCLDWAIT		0x00000002
+#define	LX_SA_SIGINFO		0x00000004
+#define	LX_SA_RESTORER		0x04000000
+#define	LX_SA_ONSTACK		0x08000000
+#define	LX_SA_RESTART		0x10000000
+#define	LX_SA_NODEFER		0x40000000
+#define	LX_SA_RESETHAND		0x80000000
+#define	LX_SA_NOMASK		LX_SA_NODEFER
+#define	LX_SA_ONESHOT		LX_SA_RESETHAND
+
+#define	LX_SIG_BLOCK		0
+#define	LX_SIG_UNBLOCK		1
+#define	LX_SIG_SETMASK		2
+
+#define	LX_MINSIGSTKSZ		2048
+#define	LX_SS_ONSTACK		1
+#define	LX_SS_DISABLE		2
+
+#define	LX_SIGRT_MAGIC		0xdeadf00d
+
+#if !defined(_ASM)
+
+typedef struct lx_sigaction {
+	void (*lxsa_handler)();
+	int lxsa_flags;
+	void (*lxsa_restorer)(void);
+	lx_sigset_t lxsa_mask;
+} lx_sigaction_t;
+
+#if defined(_ILP32)
+typedef	uint32_t lx_osigset_t;
+
+#define	OSIGSET_NBITS		(sizeof (lx_osigset_t) * NBBY)
+#define	OSIGSET_BITSET(sig)	(1U << (((sig) - 1) % OSIGSET_NBITS))
+
+typedef struct lx_osigaction {
+	void (*lxsa_handler)();
+	lx_osigset_t lxsa_mask;
+	int lxsa_flags;
+	void (*lxsa_restorer)(void);
+} lx_osigaction_t;
+#endif
+
+/*
+ * Flag settings to determine whether common routines should operate on
+ * lx_sigset_ts or lx_osigset_ts.
+ */
+#define	USE_OSIGSET	0
+#define	USE_SIGSET	1
+
+typedef struct lx_sighandlers {
+	struct lx_sigaction lx_sa[LX_NSIG + 1];
+} lx_sighandlers_t;
+
+typedef struct lx_sigaltstack {
+	void *ss_sp;
+	int ss_flags;
+	size_t ss_size;
+} lx_stack_t;
+
+/*
+ * _fpreg, _fpxreg, _xmmreg and _fpstate are defined in Linux src in:
+ *     arch/x86/include/uapi/asm/sigcontext.h
+ */
+#define	LX_X86_FXSR_MAGIC	0x0000
+#define	LX_X86_FXSR_NONE	0xffff
+
+#if defined(_LP64)
+
+typedef struct lx_fpstate {
+	ushort_t cwd;
+	ushort_t swd;
+	ushort_t twd; /* Note this is not the same as the 32bit/x87/FSAVE twd */
+	ushort_t fop;
+	uint64_t rip;
+	uint64_t rdp;
+	uint32_t mxcsr;
+	uint32_t mxcsr_mask;
+	uint32_t st_space[32];   /* 8 * 16 bytes for each FP-reg */
+	uint32_t xmm_space[64];  /* 16 * 16 bytes for each XMM-reg  */
+	uint32_t reserved2[12];
+	uint32_t reserved3[12];
+} lx_fpstate_t;
+
+/*
+ * The Linux layout is defined in the Linux src tree in:
+ *     arch/x86/include/asm/sigcontext.h
+ * and the user-level def (which is what we want) at:
+ *     arch/x86/include/uapi/asm/sigcontext.h
+ *
+ * The Illumos offsets of the registers in the context are defined in:
+ *     usr/src/uts/intel/sys/regset.h
+ * this is an mcontext_t from uc_mcontext.
+ *
+ * For the 64-bit case the register layout is completely different in the
+ * context.
+ */
+typedef struct lx_sigcontext {
+	ulong_t sc_r8;
+	ulong_t sc_r9;
+	ulong_t sc_r10;
+	ulong_t sc_r11;
+	ulong_t sc_r12;
+	ulong_t sc_r13;
+	ulong_t sc_r14;
+	ulong_t sc_r15;
+	ulong_t sc_rdi;
+	ulong_t sc_rsi;
+	ulong_t sc_rbp;
+	ulong_t sc_rbx;
+	ulong_t sc_rdx;
+	ulong_t sc_rax;
+	ulong_t sc_rcx;
+	ulong_t sc_rsp;
+	ulong_t sc_rip;
+	ulong_t sc_eflags;
+	ushort_t sc_cs;
+	ushort_t sc_gs;
+	ushort_t sc_fs;
+	ushort_t sc_pad0;
+	ulong_t sc_err;
+	ulong_t sc_trapno;
+
+	ulong_t sc_mask;
+	ulong_t sc_cr2;
+	lx_fpstate_t *sc_fpstate;
+
+	ulong_t reserved[8];
+} lx_sigcontext_t;
+
+#else /* is _ILP32 */
+
+struct lx_fpreg {
+	ushort_t significand[4];
+	ushort_t exponent;
+};
+
+struct lx_fpxreg {
+	ushort_t significand[4];
+	ushort_t exponent;
+	ushort_t padding[3];
+};
+
+struct lx_xmmreg {
+	uint32_t element[4];
+};
+
+typedef struct lx_fpstate {
+	/* Regular FPU environment */
+	ulong_t cw;
+	ulong_t sw;
+	ulong_t tag;
+	ulong_t ipoff;
+	ulong_t cssel;
+	ulong_t dataoff;
+	ulong_t datasel;
+	struct lx_fpreg _st[8];
+	ushort_t status;
+	ushort_t magic;  		/* 0xffff = regular FPU data */
+
+	/* FXSR FPU environment */
+	ulong_t _fxsr_env[6]; 		/* env is ignored */
+	ulong_t mxcsr;
+	ulong_t reserved;
+	struct lx_fpxreg _fxsr_st[8];  /* reg data is ignored */
+	struct lx_xmmreg _xmm[8];
+	ulong_t padding[56];
+} lx_fpstate_t;
+
+/*
+ * The Linux layout is defined in the Linux src tree in:
+ *     arch/x86/include/asm/sigcontext.h
+ * and the user-level def (which is what we want) at:
+ *     arch/x86/include/uapi/asm/sigcontext.h
+ *
+ * The Illumos offsets of the registers in the context are defined by the
+ * i386 ABI (see usr/src/uts/intel/sys/regset.h).
+ *
+ * Both Illumos and Linux match up here.
+ */
+typedef struct lx_sigcontext {
+	ulong_t sc_gs;
+	ulong_t sc_fs;
+	ulong_t sc_es;
+	ulong_t sc_ds;
+	ulong_t sc_edi;
+	ulong_t sc_esi;
+	ulong_t sc_ebp;
+	ulong_t sc_esp;
+	ulong_t sc_ebx;
+	ulong_t sc_edx;
+	ulong_t sc_ecx;
+	ulong_t sc_eax;
+	ulong_t sc_trapno;
+	ulong_t sc_err;
+	ulong_t sc_eip;
+	ulong_t sc_cs;
+	ulong_t sc_eflags;
+	ulong_t sc_esp_at_signal;
+	ulong_t sc_ss;
+
+	lx_fpstate_t *sc_fpstate;
+	ulong_t sc_mask;
+	ulong_t sc_cr2;
+} lx_sigcontext_t;
+#endif
+
+typedef struct lx_ucontext {
+	ulong_t uc_flags;		/* Linux always sets this to 0 */
+	struct lx_ucontext *uc_link;	/* Linux always sets this to NULL */
+	lx_stack_t uc_stack;
+	lx_sigcontext_t uc_sigcontext;
+	lx_sigset_t uc_sigmask;
+} lx_ucontext_t;
+
+typedef struct lx_sigbackup lx_sigbackup_t;
+struct lx_sigbackup {
+	ucontext_t *lxsb_retucp;
+	ucontext_t *lxsb_sigucp;
+	uintptr_t lxsb_sigdeliver_frame;
+	lx_sigbackup_t *lxsb_previous;
+};
+
+extern const int ltos_signo[];
+extern const int stol_signo[];
+
+extern void setsigacthandler(void (*)(int, siginfo_t *, void *),
+    void (**)(int, siginfo_t *, void *),
+    int (*)(const ucontext_t *));
+
+extern int lx_siginit(void);
+extern void lx_sighandlers_save(lx_sighandlers_t *);
+extern void lx_sighandlers_restore(lx_sighandlers_t *);
+
+extern int stol_siginfo(siginfo_t *siginfop, lx_siginfo_t *lx_siginfop);
+extern int stol_status(int);
+
+#endif	/* !defined(_ASM) */
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_LX_SIGNAL_H */
--- /dev/null	Wed Oct  5 00:31:49 2016
+++ new/usr/src/lib/brand/lx/lx_brand/sys/lx_sigstack.h	Wed Oct  5 00:31:49 2016
@@ -0,0 +1,78 @@
+/*
+ * 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 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
+ */
+
+#ifndef _SYS_LX_SIGSTACK_H
+#define	_SYS_LX_SIGSTACK_H
+
+#if !defined(_ASM)
+#include <sys/lx_types.h>
+#include <sys/ucontext.h>
+#include <sys/lx_signal.h>
+#include <lx_signum.h>
+
+#endif	/* !defined(_ASM) */
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/*
+ * Two flavors of Linux signal stacks:
+ *
+ * lx_sigstack - used for "modern" signal handlers, in practice those
+ *               that have the sigaction(2) flag SA_SIGINFO set
+ *
+ * lx_oldsigstack - used for legacy signal handlers, those that do not have
+ *		    the sigaction(2) flag SA_SIGINFO set or that were setup via
+ *		    the signal(2) call.
+ *
+ * NOTE: Since these structures will be placed on the stack and stack math will
+ *	 be done with their sizes, for the 32-bit code they must be word
+ *	 aligned in size (4 bytes) so the stack remains word aligned per the
+ *	 i386 ABI, or, for 64-bit code they must be 16 byte aligned as per the
+ *	 AMD64 ABI.
+ *
+ *	 The precise layout of these stack frames is also potentially
+ *	 depended on by particularly esoteric (or broken) software, and
+ *	 should be preserved.  The Linux structures (rt_sigframe, et al)
+ *	 are defined in "arch/x86/include/asm/sigframe.h".
+ */
+#if defined(_LP64)
+typedef struct lx_sigstack {
+	void (*retaddr)();	/* address of real lx_rt_sigreturn code */
+	lx_ucontext_t uc;	/* saved user context */
+	lx_siginfo_t si;	/* saved signal information */
+	lx_fpstate_t fpstate;	/* saved FP state */
+	char pad[2];		/* stack alignment */
+} lx_sigstack_t;
+#else
+struct lx_sigstack {
+	void (*retaddr)();	/* address of real lx_rt_sigreturn code */
+	int sig;		/* signal number */
+	lx_siginfo_t *sip;	/* points to "si" if valid, NULL if not */
+	lx_ucontext_t *ucp;	/* points to "uc" */
+	lx_siginfo_t si;	/* saved signal information */
+	lx_ucontext_t uc;	/* saved user context */
+	lx_fpstate_t fpstate;	/* saved FP state */
+	char trampoline[8];	/* code for trampoline to lx_rt_sigreturn() */
+};
+#endif
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_LX_SIGSTACK_H */
--- /dev/null	Wed Oct  5 00:31:49 2016
+++ new/usr/src/lib/brand/lx/lx_brand/sys/lx_socket.h	Wed Oct  5 00:31:49 2016
@@ -0,0 +1,147 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.	All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc. All rights reserved.
+ */
+
+#ifndef _SYS_LX_SOCKET_H
+#define	_SYS_LX_SOCKET_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/lx_types.h>
+
+/*
+ * Linux address family definitions
+ * Some of these are not supported
+ */
+#define	LX_AF_UNSPEC		0  /* Unspecified */
+#define	LX_AF_UNIX		1  /* local file/pipe name */
+#define	LX_AF_INET		2  /* IP protocol family */
+#define	LX_AF_AX25		3  /* Amateur Radio AX.25 */
+#define	LX_AF_IPX		4  /* Novell Internet Protocol */
+#define	LX_AF_APPLETALK		5  /* Appletalk */
+#define	LX_AF_NETROM		6  /* Amateur radio */
+#define	LX_AF_BRIDGE		7  /* Multiprotocol bridge */
+#define	LX_AF_ATMPVC		8  /* ATM PVCs */
+#define	LX_AF_X25		9  /* X.25 */
+#define	LX_AF_INET6		10 /* IPV 6 */
+#define	LX_AF_ROSE		11 /* Amateur Radio X.25 */
+#define	LX_AF_DECnet		12 /* DECnet */
+#define	LX_AF_NETBEUI		13 /* 802.2LLC */
+#define	LX_AF_SECURITY		14 /* Security callback */
+#define	LX_AF_KEY		15 /* key management */
+#define	LX_AF_ROUTE		16 /* Alias to emulate 4.4BSD */
+#define	LX_AF_PACKET		17 /* Packet family */
+#define	LX_AF_ASH		18 /* Ash ? */
+#define	LX_AF_ECONET		19 /* Acorn Econet */
+#define	LX_AF_ATMSVC		20 /* ATM SVCs */
+#define	LX_AF_SNA		22 /* Linux SNA */
+#define	LX_AF_IRDA		23 /* IRDA sockets */
+#define	LX_AF_PPPOX		24 /* PPPoX sockets */
+#define	LX_AF_WANPIPE		25 /* Wanpipe API sockets */
+#define	LX_AF_BLUETOOTH		31 /* Bluetooth sockets */
+#define	LX_AF_MAX		33 /* MAX socket type  */
+
+#define	AF_NOTSUPPORTED		-1
+#define	AF_INVAL		-2
+
+/*
+ * Linux ARP protocol hardware identifiers
+ */
+#define	LX_ARPHRD_ETHER		1	/* Ethernet */
+#define	LX_ARPHRD_LOOPBACK	772	/* Loopback */
+#define	LX_ARPHRD_VOID		0xffff	/* Unknown */
+
+/*
+ * Linux socket type definitions
+ */
+#define	LX_SOCK_STREAM		1  /* Connection-based byte streams */
+#define	LX_SOCK_DGRAM		2  /* Connectionless, datagram */
+#define	LX_SOCK_RAW		3  /* Raw protocol interface */
+#define	LX_SOCK_RDM		4  /* Reliably-delivered message */
+#define	LX_SOCK_SEQPACKET	5  /* Sequenced packet stream */
+#define	LX_SOCK_PACKET		10 /* Linux specific */
+#define	LX_SOCK_MAX		11
+
+/*
+ * The Linux socket type can be or-ed with other flags (e.g. SOCK_CLOEXEC).
+ */
+#define	LX_SOCK_TYPE_MASK	0xf
+
+/*
+ * Linux flags for socket, socketpair and accept4. These are or-ed into the
+ * socket type value. In the Linux net.h header these come from fcntl.h (note
+ * that they are in octal in the Linux header).
+ */
+#define	LX_SOCK_CLOEXEC		0x80000
+#define	LX_SOCK_NONBLOCK	0x800
+
+#define	SOCK_NOTSUPPORTED	-1
+#define	SOCK_INVAL		-2
+
+/*
+ * PF_PACKET protocol definitions.
+ */
+#define	LX_ETH_P_802_3	0x0001
+#define	LX_ETH_P_ALL	0x0003
+#define	LX_ETH_P_802_2	0x0004
+#define	LX_ETH_P_IP	0x0800
+#define	LX_ETH_P_ARP	0x0806
+#define	LX_ETH_P_IPV6	0x86DD
+
+/*
+ * Linux socketcall indices.
+ * These constitute all 17 socket related system calls
+ *
+ * These system calls are called via a single system call socketcall().
+ * The first arg being the endex of the system call type
+ */
+#define	LX_SOCKET		1
+#define	LX_BIND			2
+#define	LX_CONNECT		3
+#define	LX_LISTEN		4
+#define	LX_ACCEPT		5
+#define	LX_GETSOCKNAME		6
+#define	LX_GETPEERNAME		7
+#define	LX_SOCKETPAIR		8
+#define	LX_SEND			9
+#define	LX_RECV			10
+#define	LX_SENDTO		11
+#define	LX_RECVFROM		12
+#define	LX_SHUTDOWN		13
+#define	LX_SETSOCKOPT		14
+#define	LX_GETSOCKOPT		15
+#define	LX_SENDMSG		16
+#define	LX_RECVMSG		17
+#define	LX_ACCEPT4		18
+#define	LX_RECVMMSG		19
+#define	LX_SENDMMSG		20
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_LX_SOCKET_H */
--- /dev/null	Wed Oct  5 00:31:49 2016
+++ new/usr/src/lib/brand/lx/lx_brand/sys/lx_statfs.h	Wed Oct  5 00:31:49 2016
@@ -0,0 +1,83 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.  All rights reserved.
+ */
+
+#ifndef	_LX_STATFS_H
+#define	_LX_STATFS_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+extern int lx_statfs_init(void);
+
+struct lx_statfs {
+	size_t		f_type;
+	size_t		f_bsize;
+	ulong_t		f_blocks;
+	ulong_t		f_bfree;
+	ulong_t		f_bavail;
+	ulong_t		f_files;
+	ulong_t		f_ffree;
+	u_longlong_t	f_fsid;
+	size_t		f_namelen;
+	size_t		f_frsize;
+	size_t		f_spare[5];
+};
+
+struct lx_statfs64 {
+	int		f_type;
+	int		f_bsize;
+	u_longlong_t	f_blocks;
+	u_longlong_t	f_bfree;
+	u_longlong_t	f_bavail;
+	u_longlong_t	f_files;
+	u_longlong_t	f_ffree;
+	u_longlong_t	f_fsid;
+	int		f_namelen;
+	int		f_frsize;
+	int		f_spare[5];
+};
+
+/*
+ * These magic values are taken mostly from statfs(2) or magic.h
+ */
+#define	LX_AUTOFS_SUPER_MAGIC		0x0187
+#define	LX_DEVFS_SUPER_MAGIC		0x1373
+#define	LX_DEVPTS_SUPER_MAGIC		0x1cd1
+#define	LX_EXT2_SUPER_MAGIC		0xEF53
+#define	LX_ISOFS_SUPER_MAGIC		0x9660
+#define	LX_MSDOS_SUPER_MAGIC		0x4d44
+#define	LX_NFS_SUPER_MAGIC		0x6969
+#define	LX_PROC_SUPER_MAGIC		0x9fa0
+#define	LX_TMPFS_SUPER_MAGIC		0x01021994
+#define	LX_UFS_MAGIC			0x00011954
+#define	LX_PIPEFS_MAGIC			0x50495045
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _LX_STATFS_H */
--- /dev/null	Wed Oct  5 00:31:49 2016
+++ new/usr/src/lib/brand/lx/lx_brand/sys/lx_syscall.h	Wed Oct  5 00:31:49 2016
@@ -0,0 +1,226 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#ifndef _SYS_LX_SYSCALL_H
+#define	_SYS_LX_SYSCALL_H
+
+#include <sys/lx_brand.h>
+
+#if !defined(_ASM)
+
+#include <sys/types.h>
+#include <sys/procset.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int lx_install;
+
+extern long lx_mknodat(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+extern long lx_futimesat(uintptr_t, uintptr_t, uintptr_t);
+extern long lx_utimensat(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+extern long lx_fstatat64(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+
+extern long lx_stat(uintptr_t, uintptr_t);
+extern long lx_fstat(uintptr_t, uintptr_t);
+extern long lx_lstat(uintptr_t, uintptr_t);
+extern long lx_stat64(uintptr_t, uintptr_t);
+extern long lx_fstat64(uintptr_t, uintptr_t);
+extern long lx_lstat64(uintptr_t, uintptr_t);
+extern long lx_fcntl(uintptr_t, uintptr_t, uintptr_t);
+extern long lx_fcntl64(uintptr_t, uintptr_t, uintptr_t);
+extern long lx_flock(uintptr_t, uintptr_t);
+extern long lx_readdir(uintptr_t, uintptr_t, uintptr_t);
+extern long lx_execve(uintptr_t, uintptr_t, uintptr_t);
+extern long lx_ioctl(uintptr_t, uintptr_t, uintptr_t);
+
+extern long lx_settimeofday(uintptr_t, uintptr_t);
+extern long lx_mknod(uintptr_t, uintptr_t, uintptr_t);
+
+extern long lx_capget(uintptr_t, uintptr_t);
+extern long lx_capset(uintptr_t, uintptr_t);
+
+extern long lx_clock_nanosleep(int, int flags, struct timespec *,
+    struct timespec *);
+extern long lx_adjtimex(void *);
+extern long lx_timer_create(int, struct sigevent *, timer_t *);
+extern long lx_timer_settime(timer_t, int, struct itimerspec *,
+    struct itimerspec *);
+extern long lx_timer_gettime(timer_t, struct itimerspec *);
+extern long lx_timer_getoverrun(timer_t);
+extern long lx_timer_delete(timer_t);
+
+extern long lx_truncate(uintptr_t, uintptr_t);
+extern long lx_ftruncate(uintptr_t, uintptr_t);
+extern long lx_truncate64(uintptr_t, uintptr_t, uintptr_t);
+extern long lx_ftruncate64(uintptr_t, uintptr_t, uintptr_t);
+
+extern long lx_sysctl(uintptr_t);
+extern long lx_fsync(uintptr_t);
+extern long lx_fdatasync(uintptr_t);
+extern long lx_rmdir(uintptr_t);
+extern long lx_utime(uintptr_t, uintptr_t);
+extern long lx_sysfs(uintptr_t, uintptr_t, uintptr_t);
+
+extern long lx_uname(uintptr_t);
+extern long lx_getgroups16(uintptr_t, uintptr_t);
+extern long lx_setgroups(uintptr_t, uintptr_t);
+extern long lx_setgroups16(uintptr_t, uintptr_t);
+
+extern long lx_query_module(uintptr_t, uintptr_t, uintptr_t, uintptr_t,
+    uintptr_t);
+
+extern long lx_times(uintptr_t);
+extern long lx_setitimer(uintptr_t, uintptr_t, uintptr_t);
+
+extern long lx_clone(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+extern long lx_exit(uintptr_t);
+extern long lx_group_exit(uintptr_t);
+
+extern long lx_mlock(uintptr_t, uintptr_t);
+extern long lx_mlockall(uintptr_t);
+extern long lx_munlock(uintptr_t, uintptr_t);
+extern long lx_munlockall(void);
+extern long lx_msync(uintptr_t, uintptr_t, uintptr_t);
+extern long lx_madvise(uintptr_t, uintptr_t, uintptr_t);
+extern long lx_mprotect(uintptr_t, uintptr_t, uintptr_t);
+extern long lx_mmap(uintptr_t, uintptr_t, uintptr_t, uintptr_t,
+    uintptr_t, uintptr_t);
+extern long lx_mmap2(uintptr_t, uintptr_t, uintptr_t, uintptr_t,
+    uintptr_t, uintptr_t);
+extern long lx_remap(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+
+extern long lx_mount(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+
+extern long lx_statfs(uintptr_t, uintptr_t);
+extern long lx_fstatfs(uintptr_t, uintptr_t);
+extern long lx_statfs64(uintptr_t, uintptr_t, uintptr_t);
+extern long lx_fstatfs64(uintptr_t, uintptr_t, uintptr_t);
+
+extern long lx_sigreturn(void);
+extern long lx_rt_sigreturn(void);
+extern long lx_signal(uintptr_t, uintptr_t);
+extern long lx_sigaction(uintptr_t, uintptr_t, uintptr_t);
+extern long lx_rt_sigaction(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+extern long lx_sigaltstack(uintptr_t, uintptr_t);
+extern long lx_sigpending(uintptr_t);
+extern long lx_rt_sigpending(uintptr_t, uintptr_t);
+extern long lx_sigprocmask(uintptr_t, uintptr_t, uintptr_t);
+extern long lx_rt_sigprocmask(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+extern long lx_sigsuspend(uintptr_t);
+extern long lx_rt_sigsuspend(uintptr_t, uintptr_t);
+extern long lx_rt_sigwaitinfo(uintptr_t, uintptr_t, uintptr_t);
+extern long lx_rt_sigtimedwait(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+extern long lx_rt_sigqueueinfo(uintptr_t, uintptr_t, uintptr_t);
+extern long lx_rt_tgsigqueueinfo(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+
+extern long lx_futex(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t,
+    uintptr_t);
+
+extern long lx_tkill(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t,
+    uintptr_t);
+extern long lx_tgkill(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t,
+    uintptr_t);
+
+extern long lx_sendfile(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+extern long lx_sendfile64(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+
+extern long lx_fork(void);
+extern long lx_vfork(void);
+extern long lx_exec(uintptr_t, uintptr_t, uintptr_t);
+
+extern long lx_getpriority(uintptr_t, uintptr_t);
+extern long lx_setpriority(uintptr_t, uintptr_t, uintptr_t);
+
+extern long lx_ptrace(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+
+extern long lx_sched_getaffinity(uintptr_t, uintptr_t, uintptr_t);
+extern long lx_sched_setaffinity(uintptr_t, uintptr_t, uintptr_t);
+extern long lx_sched_getparam(uintptr_t, uintptr_t);
+extern long lx_sched_setparam(uintptr_t, uintptr_t);
+extern long lx_sched_rr_get_interval(uintptr_t pid, uintptr_t);
+extern long lx_sched_getscheduler(uintptr_t);
+extern long lx_sched_setscheduler(uintptr_t, uintptr_t, uintptr_t);
+extern long lx_sched_get_priority_min(uintptr_t);
+extern long lx_sched_get_priority_max(uintptr_t);
+
+extern long lx_xattr2(uintptr_t, uintptr_t);
+extern long lx_xattr3(uintptr_t, uintptr_t, uintptr_t);
+extern long lx_xattr4(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+
+extern long lx_keyctl(void);
+
+extern long lx_ipc(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+extern long lx_msgget(key_t, int);
+extern long lx_msgsnd(int, void *, size_t, int);
+extern long lx_msgrcv(int, void *, size_t, long, int);
+extern long lx_msgctl(int, int, void *);
+extern long lx_semget(key_t, int, int);
+extern long lx_semop(int, void *, size_t);
+extern long lx_semtimedop(int, void *, size_t, struct timespec *);
+extern long lx_semctl(int, int, int, void *);
+extern long lx_shmget(key_t, size_t, int);
+extern long lx_shmat(int, void *, int);
+extern long lx_shmctl(int, int, void *);
+
+extern long lx_close(int);
+extern long lx_eventfd(unsigned int);
+extern long lx_eventfd2(unsigned int, int);
+extern long lx_getgroups(int, gid_t *);
+extern long lx_inotify_add_watch(int, const char *, uint32_t);
+extern long lx_inotify_init(void);
+extern long lx_inotify_init1(int);
+extern long lx_inotify_rm_watch(int, int);
+extern long lx_shmdt(char *);
+extern long lx_signalfd(int, uintptr_t, size_t);
+extern long lx_signalfd4(int, uintptr_t, size_t, int);
+extern long lx_timerfd_create(int, int);
+extern long lx_timerfd_settime(int, int,
+    const struct itimerspec *, struct itimerspec *);
+extern long lx_timerfd_gettime(int, struct itimerspec *);
+extern long lx_utimes(const char *, const struct timeval *);
+
+#endif	/* !defined(_ASM) */
+
+
+#if defined(_LP64)
+#define	LX_SYS_clone		56
+#define	LX_SYS_vfork		58
+#else
+#define	LX_SYS_clone		120
+#define	LX_SYS_vfork		190
+#endif
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_LX_SYSCALL_H */
--- /dev/null	Wed Oct  5 00:31:49 2016
+++ new/usr/src/lib/brand/lx/lx_brand/sys/lx_sysv_ipc.h	Wed Oct  5 00:31:49 2016
@@ -0,0 +1,222 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.  All rights reserved.
+ */
+
+#ifndef _LX_SYSV_IPC_H
+#define	_LX_SYSV_IPC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * msg-related definitions.
+ */
+#define	LX_IPC_CREAT	00001000
+#define	LX_IPC_EXCL	00002000
+#define	LX_IPC_NOWAIT	00004000
+
+#define	LX_IPC_RMID	0
+#define	LX_IPC_SET	1
+#define	LX_IPC_STAT	2
+#define	LX_IPC_INFO	3
+
+#define	LX_IPC_64	0x0100
+
+#define	LX_SEMOP	1
+#define	LX_SEMGET	2
+#define	LX_SEMCTL	3
+#define	LX_MSGSND	11
+#define	LX_MSGRCV	12
+#define	LX_MSGGET	13
+#define	LX_MSGCTL	14
+#define	LX_SHMAT	21
+#define	LX_SHMDT	22
+#define	LX_SHMGET	23
+#define	LX_SHMCTL	24
+
+#define	LX_MSG_STAT	11
+#define	LX_MSG_INFO	12
+
+#define	LX_MSG_NOERROR	010000
+
+/*
+ * Linux hard codes the maximum msgbuf length to be 8192 bytes.  Really.
+ */
+#define	LX_MSGMAX	8192
+
+struct lx_ipc_perm {
+	key_t		key;
+	uid_t		uid;
+	uid_t		gid;
+	uid_t		cuid;
+	uid_t		cgid;
+	ushort_t	mode;
+	ushort_t	_pad1;
+	ushort_t	seq;
+	ushort_t	_pad2;
+	ulong_t		_unused1;
+	ulong_t		_unused2;
+};
+
+struct lx_msqid_ds {
+	struct lx_ipc_perm	msg_perm;
+	time_t			msg_stime;
+#if defined(_ILP32)
+	ulong_t			_unused1;
+#endif
+	time_t			msg_rtime;
+#if defined(_ILP32)
+	ulong_t			_unused2;
+#endif
+	time_t			msg_ctime;
+#if defined(_ILP32)
+	ulong_t			_unused3;
+#endif
+	ulong_t			msg_cbytes;
+	ulong_t			msg_qnum;
+	ulong_t			msg_qbytes;
+	pid_t			msg_lspid;
+	pid_t			msg_lrpid;
+	ulong_t			_unused4;
+	ulong_t			_unused5;
+};
+
+struct lx_msginfo {
+	int		msgpool;
+	int		msgmap;
+	int		msgmax;
+	int		msgmnb;
+	int		msgmni;
+	int		msgssz;
+	int		msgtql;
+	ushort_t	msgseg;
+};
+
+/*
+ * semaphore-related definitions.
+ */
+#define	LX_GETPID	11
+#define	LX_GETVAL	12
+#define	LX_GETALL	13
+#define	LX_GETNCNT	14
+#define	LX_GETZCNT	15
+#define	LX_SETVAL	16
+#define	LX_SETALL	17
+#define	LX_SEM_STAT	18
+#define	LX_SEM_INFO	19
+#define	LX_SEM_UNDO	0x1000
+#define	LX_SEMVMX	32767
+
+struct lx_semid_ds {
+	struct lx_ipc_perm	sem_perm;
+	time_t			sem_otime;
+	ulong_t			_unused1;
+	time_t			sem_ctime;
+	ulong_t			_unused2;
+	ulong_t			sem_nsems;
+	ulong_t			_unused3;
+	ulong_t			_unused4;
+};
+
+struct lx_seminfo {
+	int semmap;
+	int semmni;
+	int semmns;
+	int semmnu;
+	int semmsl;
+	int semopm;
+	int semume;
+	int semusz;
+	int semvmx;
+	int semaem;
+};
+
+union lx_semun {
+	int val;
+	struct lx_semid_ds *semds;
+	ushort_t *sems;
+	struct lx_seminfo *info;
+	uintptr_t dummy;
+};
+
+/*
+ * shm-related definitions
+ */
+#define	LX_SHM_LOCKED	02000
+#define	LX_SHM_RDONLY	010000
+#define	LX_SHM_RND	020000
+#define	LX_SHM_REMAP	040000
+
+#define	LX_SHM_LOCK	11
+#define	LX_SHM_UNLOCK	12
+#define	LX_SHM_STAT	13
+#define	LX_SHM_INFO	14
+
+struct lx_shmid_ds {
+	struct lx_ipc_perm	shm_perm;
+	size_t			shm_segsz;
+	time_t			shm_atime;
+#if defined(_ILP32)
+	ulong_t			_unused1;
+#endif
+	time_t			shm_dtime;
+#if defined(_ILP32)
+	ulong_t			_unused2;
+#endif
+	time_t			shm_ctime;
+#if defined(_ILP32)
+	ulong_t			_unused3;
+#endif
+	pid_t			shm_cpid;
+	pid_t			shm_lpid;
+	ushort_t		shm_nattch;
+	ulong_t			_unused4;
+	ulong_t			_unused5;
+};
+
+struct lx_shm_info {
+	int	used_ids;
+	ulong_t	shm_tot;
+	ulong_t	shm_rss;
+	ulong_t	shm_swp;
+	ulong_t	swap_attempts;
+	ulong_t	swap_successes;
+};
+
+struct lx_shminfo {
+	ulong_t	shmmax;
+	ulong_t	shmmin;
+	ulong_t	shmmni;
+	ulong_t	shmseg;
+	ulong_t	shmall;
+};
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _LX_SYSV_IPC_H */
--- /dev/null	Wed Oct  5 00:31:49 2016
+++ new/usr/src/lib/brand/lx/lx_brand/sys/lx_thread.h	Wed Oct  5 00:31:49 2016
@@ -0,0 +1,80 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc.	All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc
+ */
+
+#ifndef _SYS_LX_THREAD_H
+#define	_SYS_LX_THREAD_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/lx_signal.h>
+#include <thread.h>
+
+typedef enum lx_exit_type {
+	LX_ET_NONE = 0,
+	LX_ET_EXIT,
+	LX_ET_EXIT_GROUP
+} lx_exit_type_t;
+
+typedef struct lx_tsd {
+	/* per-thread flag set on parent vfork, cleared on thread resume */
+	int		lxtsd_is_vforked;
+	lx_exit_type_t	lxtsd_exit;
+	int		lxtsd_exit_status;
+	ucontext_t	lxtsd_exit_context;
+
+	/*
+	 * If this value is non-zero, we use it in lx_sigdeliver() to represent
+	 * the in-use extent of the Linux (i.e. BRAND) stack for this thread.
+	 * Access to this value must be protected by _sigoff()/_sigon().
+	 */
+	uintptr_t	lxtsd_lx_sp;
+
+	/*
+	 * Alternate stack for Linux sigaltstack emulation:
+	 */
+	lx_stack_t	lxtsd_sigaltstack;
+
+	void		*lxtsd_clone_state;
+
+	lx_sigbackup_t	*lxtsd_sigbackup;
+} lx_tsd_t;
+
+extern thread_key_t	lx_tsd_key;
+
+extern void		lx_swap_gs(long, long *);
+
+extern void		lx_exit_common(void) __NORETURN;
+
+extern lx_tsd_t		*lx_get_tsd(void);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_LX_THREAD_H */
--- /dev/null	Wed Oct  5 00:31:49 2016
+++ new/usr/src/lib/brand/lx/lx_init/Makefile	Wed Oct  5 00:31:49 2016
@@ -0,0 +1,72 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2015 Joyent, Inc.
+#
+
+PROG =		lxinit
+
+PROG_OBJS =	lxinit.o pipe_stream.o run_command.o
+LIST_OBJS =	list.o
+
+OBJS =		$(PROG_OBJS) \
+		$(LIST_OBJS)
+SRCS =		$(PROG_OBJS:%.o=%.c) \
+		$(LIST_OBJS:%.o=$(SRC)/common/list/%.c)
+
+all:		$(PROG)
+
+include ../Makefile.lx
+include $(SRC)/cmd/Makefile.cmd
+
+# override the install directory
+ROOTBIN =	$(ROOTBRANDDIR)
+CLOBBERFILES =	$(OBJS) $(ROOTPROG)
+
+UTSBASE =	$(SRC)/uts
+
+CFLAGS +=	$(CCVERBOSE)
+CPPFLAGS +=	-D_FILE_OFFSET_BITS=64 -D_REENTRANT -I$(UTSBASE)/common/brand/lx
+LDLIBS +=	-lzonecfg -lipadm -lsocket -linetutil -lnsl -lcmdutils -ldhcpagent
+LINTFLAGS +=	-xerroff=E_NAME_DEF_NOT_USED2
+
+.KEEP_STATE:
+
+install: all $(ROOTPROG)
+
+clean:
+	$(RM) $(PROG) $(OBJS)
+
+lint: lint_SRCS
+
+$(PROG): $(OBJS)
+	$(LINK.c) -o $@ $(OBJS) $(LDLIBS)
+	$(POST_PROCESS)
+
+%.o: %.c
+	$(COMPILE.c) $<
+	$(POST_PROCESS_O)
+
+%.o: $(SRC)/common/list/%.c
+	$(COMPILE.c) $<
+	$(POST_PROCESS_O)
+
+include $(SRC)/cmd/Makefile.targ
--- /dev/null	Wed Oct  5 00:31:49 2016
+++ new/usr/src/lib/brand/lx/lx_init/lxinit.c	Wed Oct  5 00:31:49 2016
@@ -0,0 +1,846 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2015 Joyent, Inc.
+ */
+
+/*
+ * lxinit performs zone-specific initialization prior to handing control to the
+ * guest Linux init.  This primarily consists of:
+ *
+ * - Starting ipmgmtd
+ * - Configuring network interfaces
+ * - Adding a default route
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <limits.h>
+#include <net/if.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <stropts.h>
+#include <sys/ioccom.h>
+#include <sys/stat.h>
+#include <sys/systeminfo.h>
+#include <sys/sockio.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/varargs.h>
+#include <unistd.h>
+#include <libintl.h>
+#include <locale.h>
+#include <libcmdutils.h>
+
+#include <netinet/dhcp.h>
+#include <dhcpagent_util.h>
+#include <dhcpagent_ipc.h>
+
+#include <arpa/inet.h>
+#include <net/route.h>
+#include <libipadm.h>
+#include <libzonecfg.h>
+#include <libinetutil.h>
+#include <sys/lx_brand.h>
+
+#include "run_command.h"
+
+static void lxi_err(char *msg, ...) __NORETURN;
+static void lxi_err(char *msg, ...);
+
+#define	IPMGMTD_PATH	"/lib/inet/ipmgmtd"
+#define	IN_NDPD_PATH	"/usr/lib/inet/in.ndpd"
+
+#define	PREFIX_LOG_WARN	"lx_init warn: "
+#define	PREFIX_LOG_ERR	"lx_init err: "
+
+#define	RTMBUFSZ	(sizeof (struct rt_msghdr) + \
+		(3 * sizeof (struct sockaddr_in)))
+
+ipadm_handle_t iph;
+
+static void
+lxi_err(char *msg, ...)
+{
+	char buf[1024];
+	int len;
+	va_list ap;
+
+	va_start(ap, msg);
+	/*LINTED*/
+	len = vsnprintf(buf, sizeof (buf), msg, ap);
+	va_end(ap);
+
+	(void) write(1, PREFIX_LOG_ERR, strlen(PREFIX_LOG_ERR));
+	(void) write(1, buf, len);
+	(void) write(1, "\n", 1);
+
+	/*
+	 * Since a non-zero exit will cause the zone to reboot, a pause here
+	 * will prevent a mis-configured zone from spinning in a reboot loop.
+	 */
+	(void) pause();
+	exit(1);
+	/*NOTREACHED*/
+}
+
+static void
+lxi_warn(char *msg, ...)
+{
+	char buf[1024];
+	int len;
+	va_list ap;
+
+	va_start(ap, msg);
+	/*LINTED*/
+	len = vsnprintf(buf, sizeof (buf), msg, ap);
+	va_end(ap);
+
+	(void) write(1, PREFIX_LOG_WARN, strlen(PREFIX_LOG_WARN));
+	(void) write(1, buf, len);
+	(void) write(1, "\n", 1);
+}
+
+static void
+lxi_log_open()
+{
+	int fd = open("/dev/console", O_WRONLY);
+
+	if (fd < 0) {
+		/* hard to log at this point... */
+		exit(1);
+	} else if (fd != 1) {
+		/*
+		 * Use stdout as the log fd.  Init should start with no files
+		 * open, so we should be required to perform this relocation
+		 * every time.
+		 */
+		if (dup2(fd, 1) != 1) {
+			exit(1);
+		}
+	}
+}
+
+static void
+lxi_log_close()
+{
+	(void) close(0);
+	(void) close(1);
+}
+
+static zone_dochandle_t
+lxi_config_open()
+{
+	zoneid_t zoneid;
+	char zonename[ZONENAME_MAX];
+	zone_dochandle_t handle;
+	zone_iptype_t iptype;
+	int res;
+
+	zoneid = getzoneid();
+	if (getzonenamebyid(zoneid, zonename, sizeof (zonename)) < 0) {
+		lxi_err("could not determine zone name");
+	}
+
+	if ((handle = zonecfg_init_handle()) == NULL)
+		lxi_err("internal libzonecfg.so.1 error", 0);
+
+	if ((res = zonecfg_get_handle(zonename, handle)) != Z_OK) {
+		zonecfg_fini_handle(handle);
+		lxi_err("could not locate zone config %d", res);
+	}
+
+	/*
+	 * Only exclusive stack is supported.
+	 */
+	if (zonecfg_get_iptype(handle, &iptype) != Z_OK ||
+	    iptype != ZS_EXCLUSIVE) {
+		zonecfg_fini_handle(handle);
+		lxi_err("lx zones do not support shared IP stacks");
+	}
+
+	return (handle);
+
+}
+
+static int
+zone_find_attr(struct zone_res_attrtab *attrs, const char *name,
+    const char **result)
+{
+	while (attrs != NULL) {
+		if (strncmp(attrs->zone_res_attr_name, name,
+		    MAXNAMELEN) == 0) {
+			*result = attrs->zone_res_attr_value;
+			return (0);
+		}
+		attrs = attrs->zone_res_attr_next;
+	}
+	return (-1);
+}
+
+static void
+lxi_svc_start(char *name, char *path, char *fmri)
+{
+	pid_t pid;
+	int status;
+	char *argv[] = {
+		NULL,
+		NULL
+	};
+	char *envp[] = {
+		NULL,
+		NULL
+	};
+	argv[0] = name;
+	envp[0] = fmri;
+
+	pid = fork();
+	if (pid == -1) {
+		lxi_err("fork() failed: %s", strerror(errno));
+	}
+
+	if (pid == 0) {
+		/* child */
+		const char *zroot = zone_get_nroot();
+		char cmd[MAXPATHLEN];
+
+		/*
+		 * Construct the full path to the binary, including the native
+		 * system root (e.g. "/native") if in use for this zone:
+		 */
+		(void) snprintf(cmd, sizeof (cmd), "%s%s", zroot != NULL ?
+		    zroot : "", path);
+
+		(void) execve(cmd, argv, envp);
+
+		lxi_err("execve(%s) failed: %s", cmd, strerror(errno));
+		/* NOTREACHED */
+	}
+
+	/* parent */
+	while (wait(&status) != pid)
+		;
+
+	if (WIFEXITED(status)) {
+		if (WEXITSTATUS(status) != 0) {
+			lxi_err("%s[%d] exited: %d", name,
+			    (int)pid, WEXITSTATUS(status));
+		}
+	} else if (WIFSIGNALED(status)) {
+		lxi_err("%s[%d] died on signal: %d", name,
+		    (int)pid, WTERMSIG(status));
+	} else {
+		lxi_err("%s[%d] failed in unknown way", name,
+		    (int)pid);
+	}
+}
+
+void
+lxi_net_ipmgmtd_start()
+{
+	lxi_svc_start("ipmgmtd", IPMGMTD_PATH,
+	    "SMF_FMRI=svc:/network/ip-interface-management:default");
+}
+
+void
+lxi_net_ndpd_start()
+{
+	lxi_svc_start("in.ndpd", IN_NDPD_PATH,
+	    "SMF_FMRI=svc:/network/routing/ndp:default");
+}
+
+
+static void
+lxi_net_ipadm_open()
+{
+	ipadm_status_t status;
+
+	if ((status = ipadm_open(&iph, IPH_LEGACY)) != IPADM_SUCCESS) {
+		lxi_err("Error opening ipadm handle: %s",
+		    ipadm_status2str(status));
+	}
+}
+
+static void
+lxi_net_ipadm_close()
+{
+	ipadm_close(iph);
+}
+
+void
+lxi_net_plumb(const char *iface)
+{
+	ipadm_status_t status;
+	char ifbuf[LIFNAMSIZ];
+
+	/* ipadm_create_if stomps on ifbuf, so create a copy: */
+	(void) strncpy(ifbuf, iface, sizeof (ifbuf));
+
+	if ((status = ipadm_create_if(iph, ifbuf, AF_INET, IPADM_OPT_ACTIVE))
+	    != IPADM_SUCCESS) {
+		lxi_err("ipadm_create_if error %d: %s/v4: %s",
+		    status, iface, ipadm_status2str(status));
+	}
+
+	if ((status = ipadm_create_if(iph, ifbuf, AF_INET6, IPADM_OPT_ACTIVE))
+	    != IPADM_SUCCESS) {
+		lxi_err("ipadm_create_if error %d: %s/v6: %s",
+		    status, iface, ipadm_status2str(status));
+	}
+}
+
+static int
+lxi_getif(int af, char *iface, int len, boolean_t first_ipv4_configured)
+{
+	struct lifreq lifr;
+	int s = socket(af, SOCK_DGRAM, 0);
+	if (s < 0) {
+		lxi_warn("socket error %d: bringing up %s: %s",
+		    errno, iface, strerror(errno));
+		return (-1);
+	}
+
+	/*
+	 * We need a new logical interface for every IP address we add, except
+	 * for the very first IPv4 address.
+	 */
+	if (af == AF_INET6 || first_ipv4_configured) {
+		(void) strncpy(lifr.lifr_name, iface, sizeof (lifr.lifr_name));
+		(void) memset(&lifr.lifr_addr, 0, sizeof (lifr.lifr_addr));
+		if (ioctl(s, SIOCLIFADDIF, (caddr_t)&lifr) < 0) {
+			if (close(s) != 0) {
+				lxi_warn("failed to close socket: %s\n",
+				    strerror(errno));
+			}
+			return (-1);
+		}
+		(void) strncpy(iface, lifr.lifr_name, len);
+	}
+
+	if (close(s) != 0) {
+		lxi_warn("failed to close socket: %s\n",
+		    strerror(errno));
+	}
+	return (0);
+}
+
+static int
+lxi_iface_ip(const char *origiface, const char *addr,
+    boolean_t *first_ipv4_configured)
+{
+	static int addrnum = 0;
+	ipadm_status_t status;
+	ipadm_addrobj_t ipaddr = NULL;
+	char iface[LIFNAMSIZ];
+	char aobjname[IPADM_AOBJSIZ];
+	int af, err = 0;
+
+	(void) strncpy(iface, origiface, sizeof (iface));
+
+	af = strstr(addr, ":") == NULL ? AF_INET : AF_INET6;
+	if (lxi_getif(af, iface, sizeof (iface), *first_ipv4_configured) != 0) {
+		lxi_warn("failed to create new logical interface "
+		    "on %s: %s", origiface, strerror(errno));
+		return (-1);
+	}
+
+	(void) snprintf(aobjname, IPADM_AOBJSIZ, "%s/addr%d", iface,
+	    addrnum++);
+
+	if ((status = ipadm_create_addrobj(IPADM_ADDR_STATIC, aobjname,
+	    &ipaddr)) != IPADM_SUCCESS) {
+		lxi_warn("ipadm_create_addrobj error %d: addr %s, "
+		    "interface %s: %s\n", status, addr, iface,
+		    ipadm_status2str(status));
+		return (-2);
+	}
+
+	if ((status = ipadm_set_addr(ipaddr, addr, AF_UNSPEC))
+	    != IPADM_SUCCESS) {
+		lxi_warn("ipadm_set_addr error %d: addr %s"
+		    ", interface %s: %s\n", status, addr,
+		    iface, ipadm_status2str(status));
+		err = -3;
+		goto done;
+	}
+
+	if ((status = ipadm_create_addr(iph, ipaddr,
+	    IPADM_OPT_ACTIVE | IPADM_OPT_UP)) != IPADM_SUCCESS) {
+		lxi_warn("ipadm_create_addr error for %s: %s\n", iface,
+		    ipadm_status2str(status));
+		err = -4;
+		goto done;
+	}
+
+	if (af == AF_INET) {
+		*first_ipv4_configured = B_TRUE;
+	}
+
+done:
+	ipadm_destroy_addrobj(ipaddr);
+	return (err);
+}
+
+static int
+lxi_iface_dhcp(const char *origiface, boolean_t *first_ipv4_configured)
+{
+	dhcp_ipc_request_t *dhcpreq = NULL;
+	dhcp_ipc_reply_t *dhcpreply = NULL;
+	int err = 0, timeout = 5;
+	char iface[LIFNAMSIZ];
+
+	(void) strncpy(iface, origiface, sizeof (iface));
+
+	if (lxi_getif(AF_INET, iface, sizeof (iface), *first_ipv4_configured)
+	    != 0) {
+		lxi_warn("failed to create new logical interface "
+		    "on %s: %s", origiface, strerror(errno));
+		return (-1);
+	}
+
+	if (dhcp_start_agent(timeout) != 0) {
+		lxi_err("Failed to start dhcpagent\n");
+		/* NOTREACHED */
+	}
+
+	dhcpreq = dhcp_ipc_alloc_request(DHCP_START, iface,
+	    NULL, 0, DHCP_TYPE_NONE);
+	if (dhcpreq == NULL) {
+		lxi_warn("Unable to allocate memory "
+		    "to start DHCP on %s\n", iface);
+		return (-1);
+	}
+
+	err = dhcp_ipc_make_request(dhcpreq, &dhcpreply, timeout);
+	if (err != 0) {
+		free(dhcpreq);
+		lxi_warn("Failed to start DHCP on %s: %s\n", iface,
+		    dhcp_ipc_strerror(err));
+		return (-1);
+	}
+	err = dhcpreply->return_code;
+	if (err != 0) {
+		lxi_warn("Failed to start DHCP on %s: %s\n", iface,
+		    dhcp_ipc_strerror(err));
+		goto done;
+	}
+
+	*first_ipv4_configured = B_TRUE;
+
+done:
+	free(dhcpreq);
+	free(dhcpreply);
+	return (err);
+}
+
+/*
+ * Initialize an IPv6 link-local address on a given interface
+ */
+static int
+lxi_iface_ipv6_link_local(const char *iface)
+{
+	struct lifreq lifr;
+	int s;
+
+	s = socket(AF_INET6, SOCK_DGRAM, 0);
+	if (s == -1) {
+		lxi_warn("socket error %d: bringing up %s: %s",
+		    errno, iface, strerror(errno));
+	}
+
+	(void) strncpy(lifr.lifr_name, iface, sizeof (lifr.lifr_name));
+	if (ioctl(s, SIOCGLIFFLAGS, (caddr_t)&lifr) < 0) {
+		lxi_warn("SIOCGLIFFLAGS error %d: bringing up %s: %s",
+		    errno, iface, strerror(errno));
+		return (-1);
+	}
+
+	lifr.lifr_flags |= IFF_UP;
+	if (ioctl(s, SIOCSLIFFLAGS, (caddr_t)&lifr) < 0) {
+		lxi_warn("SIOCSLIFFLAGS error %d: bringing up %s: %s",
+		    errno, iface, strerror(errno));
+		return (-1);
+	}
+
+	(void) close(s);
+	return (0);
+}
+
+static int
+lxi_iface_gateway(const char *iface, const char *dst, int dstpfx,
+    const char *gwaddr)
+{
+	int idx, len, sockfd;
+	/* For lint-happy alignment, use a uint32_t array... */
+	uint32_t rtbuf[RTMBUFSZ / sizeof (uint32_t)];
+	struct rt_msghdr *rtm = (struct rt_msghdr *)rtbuf;
+	struct sockaddr_in *dst_sin = (struct sockaddr_in *)(rtm + 1);
+	struct sockaddr_in *gw_sin = (struct sockaddr_in *)(dst_sin + 1);
+	struct sockaddr_in *netmask_sin = (struct sockaddr_in *)(gw_sin + 1);
+
+	(void) bzero(rtm, RTMBUFSZ);
+	rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
+	rtm->rtm_flags = RTF_UP | RTF_STATIC | RTF_GATEWAY;
+	rtm->rtm_msglen = sizeof (rtbuf);
+	rtm->rtm_pid = getpid();
+	rtm->rtm_type = RTM_ADD;
+	rtm->rtm_version = RTM_VERSION;
+
+
+	/*
+	 * The destination and netmask components have already been zeroed,
+	 * which represents the default gateway.  If we were passed a more
+	 * specific destination network, use that instead.
+	 */
+	dst_sin->sin_family = AF_INET;
+	netmask_sin->sin_family = AF_INET;
+	if (dst != NULL) {
+		struct sockaddr *mask = (struct sockaddr *)netmask_sin;
+
+		if ((inet_pton(AF_INET, dst, &(dst_sin->sin_addr))) != 1 ||
+		    plen2mask(dstpfx, AF_INET, mask) != 0) {
+			lxi_warn("bad destination network %s/%d: %s", dst,
+			    dstpfx, strerror(errno));
+			return (-1);
+		}
+	}
+
+	if ((inet_pton(AF_INET, gwaddr, &(gw_sin->sin_addr))) != 1) {
+		lxi_warn("bad gateway %s: %s", gwaddr, strerror(errno));
+		return (-1);
+	}
+
+	if (iface != NULL) {
+		if ((idx = if_nametoindex(iface)) == 0) {
+			lxi_warn("unable to get interface index for %s: %s\n",
+			    iface, strerror(errno));
+			return (-1);
+		}
+		rtm->rtm_index = idx;
+	}
+
+	if ((sockfd = socket(PF_ROUTE, SOCK_RAW, AF_INET)) < 0) {
+		lxi_warn("socket(PF_ROUTE): %s\n", strerror(errno));
+		return (-1);
+	}
+
+	if ((len = write(sockfd, rtbuf, rtm->rtm_msglen)) < 0) {
+		lxi_warn("could not write rtmsg: %s", strerror(errno));
+		(void) close(sockfd);
+		return (-1);
+	} else if (len < rtm->rtm_msglen) {
+		lxi_warn("write() rtmsg incomplete");
+		(void) close(sockfd);
+		return (-1);
+	}
+
+	(void) close(sockfd);
+	return (0);
+}
+
+static void
+lxi_net_loopback()
+{
+	const char *iface = "lo0";
+	boolean_t first_ipv4_configured = B_FALSE;
+
+	lxi_net_plumb(iface);
+	(void) lxi_iface_ip(iface, "127.0.0.1/8", &first_ipv4_configured);
+	(void) lxi_iface_ipv6_link_local(iface);
+}
+
+static void
+lxi_net_setup(zone_dochandle_t handle)
+{
+	struct zone_nwiftab lookup;
+	boolean_t do_addrconf = B_FALSE;
+
+	if (zonecfg_setnwifent(handle) != Z_OK)
+		return;
+	while (zonecfg_getnwifent(handle, &lookup) == Z_OK) {
+		const char *iface = lookup.zone_nwif_physical;
+		struct zone_res_attrtab *attrs = lookup.zone_nwif_attrp;
+		const char *ipaddrs, *primary, *gateway;
+		char ipaddrs_copy[MAXNAMELEN], /* cidraddr[BUFSIZ], */
+		    *ipaddr, *tmp, *lasts;
+		boolean_t first_ipv4_configured = B_FALSE;
+		boolean_t *ficp = &first_ipv4_configured;
+		boolean_t no_zonecfg;
+
+		/*
+		 * Regardless of whether we're configured in zonecfg(1M), or
+		 * configured by other means, make sure we plumb every
+		 * physical=<foo> for IPv4 and IPv6.
+		 */
+		lxi_net_plumb(iface);
+
+		if (zone_find_attr(attrs, "ips", &ipaddrs) != 0 /* &&
+		    lxi_get_old_ip(attrs, &ipaddrs, cidraddr, BUFSIZ) != 0*/) {
+			/*
+			 * Do not panic.  This interface has no in-zonecfg(1M)
+			 * configuration.  We keep a warning around for now.
+			 */
+			lxi_warn("Could not find zonecfg(1M) network "
+			    "configuration for the %s interface", iface);
+			no_zonecfg = B_TRUE;
+		} else {
+			no_zonecfg = B_FALSE;
+		}
+
+		if (lxi_iface_ipv6_link_local(iface) != 0) {
+			lxi_warn("unable to bring up link-local address on "
+			    "interface %s", iface);
+		}
+
+		/*
+		 * Every thing else below only happens if we have zonecfg(1M)
+		 * network configuration.
+		 */
+		if (no_zonecfg)
+			continue;
+
+		/*
+		 * If we're going to be doing DHCP, we have to do it first
+		 * since dhcpagent doesn't like to operate on non-zero logical
+		 * interfaces.
+		 */
+		if (strstr(ipaddrs, "dhcp") != NULL &&
+		    lxi_iface_dhcp(iface, ficp) != 0) {
+			lxi_warn("Failed to start DHCP on %s\n", iface);
+		}
+
+		/*
+		 * Copy the ipaddrs string, since strtok_r will write NUL
+		 * characters into it.
+		 */
+		(void) strlcpy(ipaddrs_copy, ipaddrs, MAXNAMELEN);
+		tmp = ipaddrs_copy;
+
+		/*
+		 * Iterate over each IP and then set it up on the interface.
+		 */
+		while ((ipaddr = strtok_r(tmp, ",", &lasts)) != NULL) {
+			tmp = NULL;
+			if (strcmp(ipaddr, "addrconf") == 0) {
+				do_addrconf = B_TRUE;
+			} else if (strcmp(ipaddr, "dhcp") == 0) {
+				continue;
+			} else if (lxi_iface_ip(iface, ipaddr, ficp) < 0) {
+				lxi_warn("Unable to add new IP address (%s) "
+				    "to interface %s", ipaddr, iface);
+			}
+		}
+
+		if (zone_find_attr(attrs, "primary", &primary) == 0 &&
+		    strncmp(primary, "true", MAXNAMELEN) == 0 &&
+		    zone_find_attr(attrs, "gateway", &gateway) == 0) {
+			if (lxi_iface_gateway(iface, NULL, 0, gateway) != 0) {
+				lxi_err("default route on %s -> %s failed",
+				    iface, gateway);
+			}
+		}
+	}
+
+	if (do_addrconf) {
+		lxi_net_ndpd_start();
+	}
+
+	(void) zonecfg_endnwifent(handle);
+}
+
+static void
+lxi_net_static_route(const char *line)
+{
+	/*
+	 * Each static route line is a string of the form:
+	 *
+	 *	"10.77.77.2|10.1.1.0/24|false"
+	 *
+	 * i.e. gateway address, destination network, and whether this is
+	 * a "link local" route or a next hop route.
+	 */
+	custr_t *cu = NULL;
+	char *gw = NULL, *dst = NULL;
+	int pfx = -1;
+	int i;
+
+	if (custr_alloc(&cu) != 0) {
+		lxi_err("custr_alloc failure");
+	}
+
+	for (i = 0; line[i] != '\0'; i++) {
+		if (gw == NULL) {
+			if (line[i] == '|') {
+				if ((gw = strdup(custr_cstr(cu))) == NULL) {
+					lxi_err("strdup failure");
+				}
+				custr_reset(cu);
+			} else {
+				if (custr_appendc(cu, line[i]) != 0) {
+					lxi_err("custr_appendc failure");
+				}
+			}
+			continue;
+		}
+
+		if (dst == NULL) {
+			if (line[i] == '/') {
+				if ((dst = strdup(custr_cstr(cu))) == NULL) {
+					lxi_err("strdup failure");
+				}
+				custr_reset(cu);
+			} else {
+				if (custr_appendc(cu, line[i]) != 0) {
+					lxi_err("custr_appendc failure");
+				}
+			}
+			continue;
+		}
+
+		if (pfx == -1) {
+			if (line[i] == '|') {
+				pfx = atoi(custr_cstr(cu));
+				custr_reset(cu);
+			} else {
+				if (custr_appendc(cu, line[i]) != 0) {
+					lxi_err("custr_appendc failure");
+				}
+			}
+			continue;
+		}
+
+		if (custr_appendc(cu, line[i]) != 0) {
+			lxi_err("custr_appendc failure");
+		}
+	}
+
+	/*
+	 * We currently only support "next hop" routes, so ensure that
+	 * "linklocal" is false:
+	 */
+	if (strcmp(custr_cstr(cu), "false") != 0) {
+		lxi_warn("invalid static route: %s", line);
+	}
+
+	if (lxi_iface_gateway(NULL, dst, pfx, gw) != 0) {
+		lxi_err("failed to add route: %s/%d -> %s", dst, pfx, gw);
+	}
+
+	custr_free(cu);
+	free(gw);
+	free(dst);
+}
+
+static void
+lxi_net_static_routes(void)
+{
+	const char *cmd = "/native/usr/lib/brand/lx/routeinfo";
+	char *const argv[] = { "routeinfo", NULL };
+	char *const envp[] = { NULL };
+	int code;
+	struct stat st;
+	char errbuf[512];
+
+	if (stat(cmd, &st) != 0 || !S_ISREG(st.st_mode)) {
+		/*
+		 * This binary is (potentially) shipped from another
+		 * consolidation.  If it does not exist, then the platform does
+		 * not currently support static routes for LX-branded zones.
+		 */
+		return;
+	}
+
+	/*
+	 * Run the command, firing the callback for each line that it
+	 * outputs.  When this function returns, static route processing
+	 * is complete.
+	 */
+	if (run_command(cmd, argv, envp, errbuf, sizeof (errbuf),
+	    lxi_net_static_route, &code) != 0 || code != 0) {
+		lxi_err("failed to run \"%s\": %s", cmd, errbuf);
+	}
+}
+
+static void
+lxi_config_close(zone_dochandle_t handle)
+{
+	zonecfg_fini_handle(handle);
+}
+
+static void
+lxi_init_exec(char **argv)
+{
+	const char *cmd = "/sbin/init";
+	char *const envp[] = { "container=zone", NULL };
+	int e;
+
+	argv[0] = "init";
+
+	/*
+	 * systemd uses the 'container' env var to determine it is running
+	 * inside a container. It only supports a few well-known types and
+	 * treats anything else as 'other' but this is enough to make it
+	 * behave better inside a zone. See 'detect_container' in systemd.
+	 */
+	(void) execve(cmd, argv, envp);
+	e = errno;
+
+	/*
+	 * Because stdout was closed prior to exec, it must be opened again in
+	 * the face of failure to log the error.
+	 */
+	lxi_log_open();
+	lxi_err("execve(%s) failed: %s", cmd, strerror(e));
+}
+
+/*ARGSUSED*/
+int
+main(int argc, char *argv[])
+{
+	zone_dochandle_t handle;
+
+	lxi_log_open();
+
+	lxi_net_ipmgmtd_start();
+	lxi_net_ipadm_open();
+
+	handle = lxi_config_open();
+	lxi_net_loopback();
+	lxi_net_setup(handle);
+	lxi_config_close(handle);
+
+	lxi_net_static_routes();
+
+	lxi_net_ipadm_close();
+
+	lxi_log_close();
+
+	lxi_init_exec(argv);
+
+	/* NOTREACHED */
+	return (0);
+}
--- /dev/null	Wed Oct  5 00:31:49 2016
+++ new/usr/src/lib/brand/lx/lx_init/pipe_stream.c	Wed Oct  5 00:31:49 2016
@@ -0,0 +1,325 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <port.h>
+#include <poll.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/list.h>
+
+#include "pipe_stream.h"
+
+struct pipe_stream {
+	pipe_stream_loop_t *pis_loop;
+
+	boolean_t pis_finished;
+	boolean_t pis_associated;
+
+	void *pis_arg0;
+	void *pis_arg1;
+
+	int pis_fd_write;
+	int pis_fd_read;
+	list_node_t pis_linkage;
+};
+
+struct pipe_stream_loop {
+	int psl_port;
+
+	uint8_t *psl_buf;
+	size_t psl_buf_cap;
+	size_t psl_buf_occ;
+
+	list_t psl_pipes;
+
+	pipe_stream_data_cb *psl_cb_data;
+	pipe_stream_eof_cb *psl_cb_eof;
+	pipe_stream_error_cb *psl_cb_error;
+};
+
+
+int
+pipe_stream_loop_fini(pipe_stream_loop_t *psl)
+{
+	if (psl == NULL) {
+		return (0);
+	}
+
+	VERIFY0(close(psl->psl_port));
+
+	while (!list_is_empty(&psl->psl_pipes)) {
+		(void)pipe_stream_fini(list_head(&psl->psl_pipes));
+	}
+
+	list_destroy(&psl->psl_pipes);
+	free(psl);
+
+	return (0);
+}
+
+int
+pipe_stream_loop_init(pipe_stream_loop_t **pslp, size_t bufsize,
+    pipe_stream_data_cb *data_cb, pipe_stream_eof_cb *eof_cb,
+    pipe_stream_error_cb *error_cb)
+{
+	pipe_stream_loop_t *psl;
+
+	if ((psl = calloc(1, sizeof (*psl))) == NULL) {
+		return (-1);
+	}
+
+	psl->psl_buf_cap = bufsize;
+	psl->psl_buf_occ = 0;
+	if ((psl->psl_buf = calloc(1, bufsize)) == NULL) {
+		free(psl);
+		return (-1);
+	}
+
+	if ((psl->psl_port = port_create()) == -1) {
+		free(psl->psl_buf);
+		free(psl);
+		return (-1);
+	}
+
+	psl->psl_cb_data = data_cb;
+	psl->psl_cb_eof = eof_cb;
+	psl->psl_cb_error = error_cb;
+
+	list_create(&psl->psl_pipes, sizeof (pipe_stream_t),
+	    offsetof(pipe_stream_t, pis_linkage));
+
+	*pslp = psl;
+	return (0);
+}
+
+boolean_t
+pipe_stream_loop_should_run(pipe_stream_loop_t *psl)
+{
+	pipe_stream_t *pis;
+
+	for (pis = list_head(&psl->psl_pipes); pis != NULL;
+	    pis = list_next(&psl->psl_pipes, pis)) {
+		if (!pis->pis_finished) {
+			return (B_TRUE);
+		}
+	}
+
+	return (B_FALSE);
+}
+
+int
+pipe_stream_loop_run(pipe_stream_loop_t *psl)
+{
+	pipe_stream_t *pis;
+	port_event_t pev;
+	ssize_t sz;
+
+	for (pis = list_head(&psl->psl_pipes); pis != NULL;
+	    pis = list_next(&psl->psl_pipes, pis)) {
+		if (pis->pis_finished || pis->pis_associated) {
+			/*
+			 * Skip streams that are already finished, as well as
+			 * those that have already been associated with the
+			 * port.
+			 */
+			continue;
+		}
+
+		if (port_associate(psl->psl_port, PORT_SOURCE_FD,
+		    pis->pis_fd_read, POLLIN, pis) != 0) {
+			return (-1);
+		}
+	}
+
+again:
+	if (port_get(psl->psl_port, &pev, NULL) != 0) {
+		switch (errno) {
+		case ETIME:
+			/*
+			 * Timeout expired; return to caller.
+			 */
+			return (0);
+
+		case EINTR:
+			/*
+			 * Interrupted by signal.  Try again.
+			 */
+			goto again;
+
+		default:
+			return (-1);
+		}
+	}
+
+	VERIFY(pev.portev_source == PORT_SOURCE_FD);
+	pis = (pipe_stream_t *)pev.portev_user;
+	VERIFY((int)pev.portev_object == pis->pis_fd_read);
+	pis->pis_associated = B_FALSE;
+
+read_again:
+	if ((sz = read(pis->pis_fd_read, psl->psl_buf,
+	    psl->psl_buf_cap)) == -1) {
+		if (errno == EINTR) {
+			goto read_again;
+		}
+
+		if (psl->psl_cb_error != NULL) {
+			psl->psl_cb_error(errno, pis->pis_arg0, pis->pis_arg1);
+		}
+
+		VERIFY0(close(pis->pis_fd_read));
+		pis->pis_fd_read = -1;
+		pis->pis_finished = B_TRUE;
+	}
+	psl->psl_buf_occ = sz;
+
+	if (sz == 0) {
+		/*
+		 * Stream EOF.
+		 */
+		pis->pis_finished = B_TRUE;
+		VERIFY0(close(pis->pis_fd_read));
+		pis->pis_fd_read = -1;
+		if (psl->psl_cb_eof != NULL) {
+			psl->psl_cb_eof(pis->pis_arg0, pis->pis_arg1);
+		}
+		return (0);
+	}
+
+	if (psl->psl_cb_data != NULL) {
+		int cbr = psl->psl_cb_data(psl->psl_buf, psl->psl_buf_occ,
+		    pis->pis_arg0, pis->pis_arg1);
+
+		if (cbr != 0) {
+			/*
+			 * Callback failure: close file descriptor.
+			 */
+			pis->pis_finished = B_TRUE;
+			VERIFY0(close(pis->pis_fd_read));
+			pis->pis_fd_read = -1;
+			if (psl->psl_cb_eof != NULL) {
+				psl->psl_cb_eof(pis->pis_arg0, pis->pis_arg1);
+			}
+		}
+
+		return (0);
+	}
+
+	return (0);
+}
+
+int
+pipe_stream_init(pipe_stream_loop_t *psl, pipe_stream_t **pisp, void *arg0,
+    void *arg1)
+{
+	int e = 0;
+	pipe_stream_t *pis;
+	int fds[2] = { -1, -1 };
+
+	if ((pis = calloc(1, sizeof (*pis))) == NULL) {
+		return (-1);
+	}
+
+	if (pipe(fds) != 0) {
+		e = errno;
+		goto fail;
+	}
+
+	pis->pis_fd_read = fds[0];
+	pis->pis_fd_write = fds[1];
+
+	pis->pis_arg0 = arg0;
+	pis->pis_arg1 = arg1;
+
+	pis->pis_finished = B_FALSE;
+	pis->pis_associated = B_FALSE;
+
+	pis->pis_loop = psl;
+	list_insert_tail(&psl->psl_pipes, pis);
+
+	*pisp = pis;
+	return (0);
+
+fail:
+	if (fds[0] != -1) {
+		VERIFY0(close(fds[0]));
+	}
+	if (fds[1] != -1) {
+		VERIFY0(close(fds[1]));
+	}
+	free(pis);
+	errno = e;
+	return (-1);
+}
+
+int
+pipe_stream_fini(pipe_stream_t *pis)
+{
+	if (pis == NULL) {
+		return (0);
+	}
+
+	if (pis->pis_fd_read != -1) {
+		VERIFY0(close(pis->pis_fd_read));
+	}
+	if (pis->pis_fd_write != -1) {
+		VERIFY0(close(pis->pis_fd_write));
+	}
+
+	list_remove(&pis->pis_loop->psl_pipes, pis);
+
+	free(pis);
+	return (0);
+}
+
+/*
+ * Called in the parent, after forking, to close the "write" end of the pipe.
+ */
+void
+pipe_stream_parent_afterfork(pipe_stream_t *pis)
+{
+	if (pis->pis_fd_write != -1) {
+		(void) close(pis->pis_fd_write);
+		pis->pis_fd_write = -1;
+	}
+}
+
+/*
+ * Called in the child, after forking, to close the "read" end of the
+ * pipe, and to dup the file descriptor into the right place.
+ */
+int
+pipe_stream_child_afterfork(pipe_stream_t *pis, int dup_to)
+{
+	int e = 0;
+
+	if (dup_to != -1) {
+		if (dup2(pis->pis_fd_write, dup_to) == -1) {
+			e = errno;
+		}
+		VERIFY0(close(pis->pis_fd_write));
+		pis->pis_fd_write = dup_to;
+	}
+
+	(void) close(pis->pis_fd_read);
+	pis->pis_fd_read = -1;
+
+	errno = e;
+	return (e == 0 ? 0 : -1);
+}
--- /dev/null	Wed Oct  5 00:31:49 2016
+++ new/usr/src/lib/brand/lx/lx_init/pipe_stream.h	Wed Oct  5 00:31:49 2016
@@ -0,0 +1,55 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+#ifndef _PIPE_STREAM_H
+#define	_PIPE_STREAM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef int pipe_stream_data_cb(const uint8_t *, size_t, void *, void *);
+typedef void pipe_stream_eof_cb(void *, void *);
+typedef void pipe_stream_error_cb(int, void *, void *);
+
+typedef struct pipe_stream pipe_stream_t;
+typedef struct pipe_stream_loop pipe_stream_loop_t;
+
+extern int pipe_stream_loop_fini(pipe_stream_loop_t *);
+extern int pipe_stream_loop_init(pipe_stream_loop_t **, size_t,
+    pipe_stream_data_cb *, pipe_stream_eof_cb *, pipe_stream_error_cb *);
+
+extern int pipe_stream_init(pipe_stream_loop_t *, pipe_stream_t **, void *,
+    void *);
+extern int pipe_stream_fini(pipe_stream_t *);
+
+extern void pipe_stream_parent_afterfork(pipe_stream_t *);
+extern int pipe_stream_child_afterfork(pipe_stream_t *, int);
+
+extern boolean_t pipe_stream_loop_should_run(pipe_stream_loop_t *);
+extern int pipe_stream_loop_run(pipe_stream_loop_t *);
+
+#include <assert.h>
+/*
+ * Kernel-like macros that don't trigger lint.
+ */
+#define	VERIFY0(stmt) assert((stmt) == 0)
+#define	VERIFY(stmt) assert(stmt)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PIPE_STREAM_H */
--- /dev/null	Wed Oct  5 00:31:49 2016
+++ new/usr/src/lib/brand/lx/lx_init/run_command.c	Wed Oct  5 00:31:49 2016
@@ -0,0 +1,280 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <strings.h>
+#include <unistd.h>
+#include <wait.h>
+#include <sys/types.h>
+#include <libcmdutils.h>
+
+#include "run_command.h"
+#include "pipe_stream.h"
+
+typedef struct cmd {
+	int cmd_pid;
+	int cmd_wstatus;
+	pipe_stream_t *cmd_pipe[2];
+	custr_t *cmd_err;
+	custr_t *cmd_out;
+	run_command_line_cb *cmd_func;
+	boolean_t cmd_cancel;
+} cmd_t;
+
+static int cb_data(const uint8_t *, size_t, void *, void *);
+static void cb_eof(void *, void *);
+static void cb_error(int, void *, void *);
+
+void
+post_error(cmd_t *cmd, const char *estr)
+{
+	if (cmd->cmd_cancel) {
+		return;
+	}
+	cmd->cmd_cancel = B_TRUE;
+
+	custr_reset(cmd->cmd_err);
+	(void) custr_append(cmd->cmd_err, estr);
+}
+
+int
+run_command(const char *path, char *const argv[], char *const envp[],
+    char *errbuf, size_t errlen, run_command_line_cb *func, int *status)
+{
+	pipe_stream_loop_t *psl = NULL;
+	int e = 0;
+	cmd_t cmd;
+	pid_t wpid;
+
+	bzero(&cmd, sizeof (cmd));
+
+	cmd.cmd_func = func;
+
+	/*
+	 * Allocate string buffers for stdout line buffering and error
+	 * messages:
+	 */
+	if (custr_alloc_buf(&cmd.cmd_err, errbuf, errlen) != 0 ||
+	    custr_alloc(&cmd.cmd_out) != 0) {
+		e = errno;
+		goto out;
+	}
+
+	/*
+	 * Initialise pipe stream event loop:
+	 */
+	if (pipe_stream_loop_init(&psl, 256, cb_data, cb_eof, cb_error) != 0) {
+		e = errno;
+		post_error(&cmd, "could not init pipe stream loop");
+		goto out;
+	}
+
+	/*
+	 * Create pipe streams for stdout and stderr communication with
+	 * child process:
+	 */
+	if (pipe_stream_init(psl, &cmd.cmd_pipe[0], &cmd,
+	    (void *)STDOUT_FILENO) != 0 ||
+	    pipe_stream_init(psl, &cmd.cmd_pipe[1], &cmd,
+	    (void *)STDERR_FILENO) != 0) {
+		e = errno;
+		post_error(&cmd, "could not init pipe streams");
+		goto out;
+	}
+
+	/*
+	 * Fork a child process:
+	 */
+	if ((cmd.cmd_pid = fork()) == -1) {
+		e = errno;
+		post_error(&cmd, "could not fork");
+		goto out;
+	}
+
+	if (cmd.cmd_pid == 0) {
+		/*
+		 * This is the child process.  Clean up file descriptors, and
+		 * connect stdio to the pipes we allocated:
+		 */
+		VERIFY0(close(STDIN_FILENO));
+		VERIFY0(pipe_stream_child_afterfork(cmd.cmd_pipe[0],
+		    STDOUT_FILENO));
+		VERIFY0(pipe_stream_child_afterfork(cmd.cmd_pipe[1],
+		    STDERR_FILENO));
+		closefrom(3);
+
+		(void) execve(path, argv, envp);
+		err(127, "exec(%s) failed", path);
+	}
+
+	/*
+	 * Back in the parent.  Close the remote end of the stdio pipes:
+	 */
+	pipe_stream_parent_afterfork(cmd.cmd_pipe[0]);
+	pipe_stream_parent_afterfork(cmd.cmd_pipe[1]);
+
+	/*
+	 * Run the pipe event loop until all streams are completely
+	 * consumed:
+	 */
+	while (pipe_stream_loop_should_run(psl)) {
+		if (pipe_stream_loop_run(psl) != 0) {
+			e = errno;
+			post_error(&cmd, "pipe stream loop run failure");
+			goto out;
+		}
+	}
+
+	/*
+	 * Collect exit status of child process:
+	 */
+	while ((wpid = waitpid(cmd.cmd_pid, &cmd.cmd_wstatus, 0)) !=
+	    cmd.cmd_pid) {
+		if (wpid == -1 && errno != EINTR) {
+			e = errno;
+			post_error(&cmd, "waitpid failure");
+			goto out;
+		}
+	}
+
+	/*
+	 * If the child died on a signal, fail the whole operation:
+	 */
+	if (WIFSIGNALED(cmd.cmd_wstatus)) {
+		e = ENXIO;
+		post_error(&cmd, "child process died on signal");
+		(void) custr_append_printf(cmd.cmd_err, " (pid %d signal %d)",
+		    cmd.cmd_pid, WTERMSIG(cmd.cmd_wstatus));
+		goto out;
+	}
+
+	/*
+	 * If the child did not appear to exit, fail the whole operation:
+	 */
+	if (!WIFEXITED(cmd.cmd_wstatus)) {
+		e = ENXIO;
+		post_error(&cmd, "child process did not exit");
+		(void) custr_append_printf(cmd.cmd_err, " (pid %d status %x)",
+		    cmd.cmd_pid, cmd.cmd_wstatus);
+		goto out;
+	}
+
+	/*
+	 * Report exit status to the caller:
+	 */
+	*status = WEXITSTATUS(cmd.cmd_wstatus);
+	e = 0;
+
+out:
+	VERIFY0(pipe_stream_loop_fini(psl));
+	/*
+	 * Note that freeing the static error custr_t does not touch the
+	 * underlying storage; we use this property to return the error
+	 * message (if one exists) to the caller.
+	 */
+	custr_free(cmd.cmd_err);
+	custr_free(cmd.cmd_out);
+	errno = e;
+	return (e == 0 ? 0 : -1);
+}
+
+static int
+cb_data(const uint8_t *buf, size_t sz, void *arg0, void *arg1)
+{
+	cmd_t *cmd = arg0;
+	int fd = (int)arg1;
+	unsigned int i;
+
+	if (cmd->cmd_cancel) {
+		return (-1);
+	}
+
+	switch (fd) {
+	case STDOUT_FILENO:
+		for (i = 0; i < sz; i++) {
+			if (buf[i] == '\0' || buf[i] == '\r') {
+				continue;
+			}
+
+			if (buf[i] == '\n') {
+				cmd->cmd_func(custr_cstr(cmd->cmd_out));
+				custr_reset(cmd->cmd_out);
+				continue;
+			}
+
+			if (custr_appendc(cmd->cmd_out, buf[i]) != 0) {
+				/*
+				 * Failed to allocate memory; returning
+				 * -1 here will abort the stream.
+				 */
+				post_error(cmd, "custr_appendc failure");
+				return (-1);
+			}
+		}
+		break;
+
+	case STDERR_FILENO:
+		/*
+		 * Collect as much stderr output as will fit in our static
+		 * buffer.
+		 */
+		for (i = 0; i < sz; i++) {
+			if (buf[i] == '\0') {
+				continue;
+			}
+
+			(void) custr_appendc(cmd->cmd_err, buf[i]);
+		}
+		break;
+
+	default:
+		abort();
+	}
+
+	return (0);
+}
+
+static void
+cb_eof(void *arg0, void *arg1)
+{
+	cmd_t *cmd = arg0;
+	int fd = (int)arg1;
+
+	if (cmd->cmd_cancel) {
+		return;
+	}
+
+	if (fd == STDOUT_FILENO && custr_len(cmd->cmd_out) > 0) {
+		cmd->cmd_func(custr_cstr(cmd->cmd_out));
+		custr_reset(cmd->cmd_out);
+	}
+}
+
+static void
+cb_error(int e, void *arg0, void *arg1)
+{
+	cmd_t *cmd = arg0;
+	int fd = (int)arg1;
+
+	if (cmd->cmd_cancel) {
+		return;
+	}
+
+	post_error(cmd, "stream read failure");
+	(void) custr_append_printf(cmd->cmd_err, " (pid %d fd %d): %s",
+	    cmd->cmd_pid, fd, strerror(e));
+}
--- /dev/null	Wed Oct  5 00:31:49 2016
+++ new/usr/src/lib/brand/lx/lx_init/run_command.h	Wed Oct  5 00:31:49 2016
@@ -0,0 +1,32 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+#ifndef _RUN_COMMAND_H
+#define	_RUN_COMMAND_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void run_command_line_cb(const char *);
+
+extern int run_command(const char *, char *const [], char *const [], char *,
+    size_t, run_command_line_cb *, int *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RUN_COMMAND_H */
--- /dev/null	Wed Oct  5 00:31:50 2016
+++ new/usr/src/lib/brand/lx/lx_support/Makefile	Wed Oct  5 00:31:50 2016
@@ -0,0 +1,54 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+PROG =		lx_support
+PROGS =		$(PROG)
+OBJS =		lx_support
+
+all:		$(PROG)
+
+include ../Makefile.lx
+include $(SRC)/cmd/Makefile.cmd
+
+# override the install directory
+ROOTBIN =	$(ROOTBRANDDIR)
+CLOBBERFILES =	$(OBJS) $(ROOTPROGS)
+
+UTSBASE =	$(SRC)/uts
+
+CFLAGS +=	$(CCVERBOSE)
+CPPFLAGS +=	-D_REENTRANT -I$(UTSBASE)/common/brand/lx
+LDLIBS +=	-lzonecfg
+
+.KEEP_STATE:
+
+install:	all $(ROOTPROGS)
+
+clean:
+		$(RM) $(PROG) $(OBJS)
+
+lint:		lint_PROG
+
+include $(SRC)/cmd/Makefile.targ
--- /dev/null	Wed Oct  5 00:31:50 2016
+++ new/usr/src/lib/brand/lx/lx_support/lx_support.c	Wed Oct  5 00:31:50 2016
@@ -0,0 +1,368 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.  All rights reserved.
+ */
+
+/*
+ * lx_support is a small cli utility used to perform some brand-specific
+ * tasks when booting, halting, or verifying a zone.  This utility is not
+ * intended to be called by users - it is intended to be invoked by the
+ * zones utilities.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <stropts.h>
+#include <sys/ioccom.h>
+#include <sys/stat.h>
+#include <sys/systeminfo.h>
+#include <sys/types.h>
+#include <sys/varargs.h>
+#include <unistd.h>
+#include <libintl.h>
+#include <locale.h>
+
+#include <libzonecfg.h>
+#include <sys/lx_brand.h>
+
+static void lxs_err(char *msg, ...) __NORETURN;
+static void usage(void) __NORETURN;
+
+#define	CP_CMD		"/usr/bin/cp"
+#define	MOUNT_CMD	"/sbin/mount"
+
+static char *bname = NULL;
+static char *zonename = NULL;
+static char *zoneroot = NULL;
+
+#if !defined(TEXT_DOMAIN)		/* should be defined by cc -D */
+#define	TEXT_DOMAIN	"SYS_TEST"	/* Use this only if it wasn't */
+#endif
+
+static void
+lxs_err(char *msg, ...)
+{
+	char	buf[1024];
+	va_list	ap;
+
+	va_start(ap, msg);
+	/*LINTED*/
+	(void) vsnprintf(buf, sizeof (buf), msg, ap);
+	va_end(ap);
+
+	(void) printf("%s error: %s\n", bname, buf);
+
+	exit(1);
+	/*NOTREACHED*/
+}
+
+/*
+ * The Linux init(1M) command requires communication over the /dev/initctl
+ * FIFO.  Since any attempt to create a file in /dev will fail, we must
+ * create it here.
+ */
+static void
+lxs_make_initctl()
+{
+	char		cmdbuf[ARG_MAX];
+	char		path[MAXPATHLEN];
+	char		special[MAXPATHLEN];
+	struct stat	buf;
+	int		err;
+
+	if (snprintf(special, sizeof (special), "%s/dev/initctl", zoneroot) >=
+	    sizeof (special))
+		lxs_err("%s: %s", gettext("Failed to create /dev/initctl"),
+		    gettext("zoneroot is too long"));
+
+	if (snprintf(path, sizeof (path), "%s/root/dev/initctl", zoneroot) >=
+	    sizeof (path))
+		lxs_err("%s: %s", gettext("Failed to create /dev/initctl"),
+		    gettext("zoneroot is too long"));
+
+	/* create the actual fifo as <zoneroot>/dev/initctl */
+	if (stat(special, &buf) != 0) {
+		err = errno;
+		if (err != ENOENT)
+			lxs_err("%s: %s",
+			    gettext("Failed to create /dev/initctl"),
+			    strerror(err));
+		if (mkfifo(special, 0644) < 0) {
+			err = errno;
+			lxs_err("%s: %s",
+			    gettext("Failed to create /dev/initctl"),
+			    strerror(err));
+		}
+	} else {
+		if ((buf.st_mode & S_IFIFO) == 0)
+			lxs_err("%s: %s",
+			    gettext("Failed to create /dev/initctl"),
+			    gettext("It already exists, and is not a FIFO."));
+	}
+
+	/*
+	 * now lofs mount the <zoneroot>/dev/initctl fifo onto
+	 * <zoneroot>/root/dev/initctl
+	 */
+	if (snprintf(cmdbuf, sizeof (cmdbuf), "%s -F lofs %s %s", MOUNT_CMD,
+	    special, path) >= sizeof (cmdbuf))
+		lxs_err("%s: %s", gettext("Failed to lofs mount /dev/initctl"),
+		    gettext("zoneroot is too long"));
+
+	if (system(cmdbuf) < 0) {
+		err = errno;
+		lxs_err("%s: %s", gettext("Failed to lofs mount /dev/initctl"),
+		    strerror(err));
+	}
+}
+
+/*
+ * fsck gets really confused when run inside a zone.  Removing this file
+ * prevents it from running
+ */
+static void
+lxs_remove_autofsck()
+{
+	char	path[MAXPATHLEN];
+	int	err;
+
+	if (snprintf(path, MAXPATHLEN, "%s/root/.autofsck", zoneroot) >=
+	    MAXPATHLEN)
+		lxs_err("%s: %s", gettext("Failed to remove /.autofsck"),
+		    gettext("zoneroot is too long"));
+
+	if (unlink(path) < 0) {
+		err = errno;
+		if (err != ENOENT)
+			lxs_err("%s: %s",
+			    gettext("Failed to remove /.autofsck"),
+			    strerror(err));
+	}
+}
+
+/*
+ * Extract any lx-supported attributes from the zone configuration file.
+ */
+static void
+lxs_getattrs(zone_dochandle_t zdh, char **krelease)
+{
+	struct zone_attrtab	attrtab;
+	int			err;
+
+	/* initialize the attribute iterator */
+	if (zonecfg_setattrent(zdh) != Z_OK) {
+		zonecfg_fini_handle(zdh);
+		lxs_err(gettext("error accessing zone configuration"));
+	}
+
+	*krelease = (char *)malloc(LX_KERN_RELEASE_MAX);
+	if (*krelease == NULL)
+		lxs_err(gettext("out of memory"));
+
+	bzero(*krelease, LX_KERN_RELEASE_MAX);
+	while ((err = zonecfg_getattrent(zdh, &attrtab)) == Z_OK) {
+		if ((strcmp(attrtab.zone_attr_name, "kernel-version") == 0) &&
+		    (zonecfg_get_attr_string(&attrtab, *krelease,
+		    LX_KERN_RELEASE_MAX) != Z_OK))
+			lxs_err(gettext("invalid type for zone attribute: %s"),
+			    attrtab.zone_attr_name);
+	}
+
+	if (strlen(*krelease) == 0) {
+		free(*krelease);
+		*krelease = NULL;
+	}
+
+	/* some kind of error while looking up attributes */
+	if (err != Z_NO_ENTRY)
+		lxs_err(gettext("error accessing zone configuration"));
+}
+
+static int
+lxs_boot()
+{
+	zoneid_t	zoneid;
+	zone_dochandle_t zdh;
+	char		*krelease;
+
+	lxs_make_initctl();
+	lxs_remove_autofsck();
+
+	if ((zdh = zonecfg_init_handle()) == NULL)
+		lxs_err(gettext("unable to initialize zone handle"));
+
+	if (zonecfg_get_handle((char *)zonename, zdh) != Z_OK) {
+		zonecfg_fini_handle(zdh);
+		lxs_err(gettext("unable to load zone configuration"));
+	}
+
+	/* Extract any relevant attributes from the config file. */
+	lxs_getattrs(zdh, &krelease);
+	zonecfg_fini_handle(zdh);
+
+	/*
+	 * Let the kernel know whether or not this zone's init process
+	 * should be automatically restarted on its death.
+	 */
+	if ((zoneid = getzoneidbyname(zonename)) < 0)
+		lxs_err(gettext("unable to get zoneid"));
+
+	if (krelease != NULL) {
+		/* Backward compatability with incomplete version attr */
+		if (strcmp(krelease, "2.4") == 0) {
+			krelease = "2.4.21";
+		} else if (strcmp(krelease, "2.6") == 0) {
+			krelease = "2.6.18";
+		}
+
+		if (zone_setattr(zoneid, LX_ATTR_KERN_RELEASE, krelease,
+		    strlen(krelease)) < 0)
+			lxs_err(gettext("unable to set kernel version"));
+	}
+
+	return (0);
+}
+
+static int
+lxs_halt()
+{
+	return (0);
+}
+
+static int
+lxs_verify(char *xmlfile)
+{
+	zone_dochandle_t	handle;
+	char			*krelease;
+	char			hostidp[HW_HOSTID_LEN];
+	zone_iptype_t		iptype;
+
+	if ((handle = zonecfg_init_handle()) == NULL)
+		lxs_err(gettext("internal libzonecfg.so.1 error"), 0);
+
+	if (zonecfg_get_xml_handle(xmlfile, handle) != Z_OK) {
+		zonecfg_fini_handle(handle);
+		lxs_err(gettext("zonecfg provided an invalid XML file"));
+	}
+
+	/*
+	 * Check to see whether the zone has hostid emulation enabled.
+	 */
+	if (zonecfg_get_hostid(handle, hostidp, sizeof (hostidp)) == Z_OK) {
+		zonecfg_fini_handle(handle);
+		lxs_err(gettext("lx zones do not support hostid emulation"));
+	}
+
+	/*
+	 * Only exclusive stack is supported.
+	 */
+	if (zonecfg_get_iptype(handle, &iptype) != Z_OK ||
+	    iptype != ZS_EXCLUSIVE) {
+		zonecfg_fini_handle(handle);
+		lxs_err(gettext("lx zones do not support shared IP stacks"));
+	}
+
+	/* Extract any relevant attributes from the config file. */
+	lxs_getattrs(handle, &krelease);
+	zonecfg_fini_handle(handle);
+
+	if (krelease) {
+		char *pdot, *ep;
+		long major_ver;
+
+		pdot = strchr(krelease, '.');
+		if (pdot != NULL)
+			*pdot = '\0';
+		errno = 0;
+		major_ver = strtol(krelease, &ep, 10);
+		if (major_ver < 2 || errno != 0 || *ep != '\0')
+			lxs_err(gettext("invalid value for zone attribute: %s"),
+			    "kernel-version");
+		if (pdot != NULL)
+			*pdot = '.';
+
+	}
+	return (0);
+}
+
+static void
+usage()
+{
+
+	(void) fprintf(stderr,
+	    gettext("usage:\t%s boot <zoneroot> <zonename>\n"), bname);
+	(void) fprintf(stderr,
+	    gettext("      \t%s halt <zoneroot> <zonename>\n"), bname);
+	(void) fprintf(stderr,
+	    gettext("      \t%s verify <xml file>\n\n"), bname);
+	exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+	(void) setlocale(LC_ALL, "");
+	(void) textdomain(TEXT_DOMAIN);
+
+	bname = basename(argv[0]);
+
+	if (argc < 3)
+		usage();
+
+	if (strcmp(argv[1], "boot") == 0) {
+		if (argc != 4)
+			lxs_err(gettext("usage: %s %s <zoneroot> <zonename>"),
+			    bname, argv[1]);
+		zoneroot = argv[2];
+		zonename = argv[3];
+		return (lxs_boot());
+	}
+
+	if (strcmp(argv[1], "halt") == 0) {
+		if (argc != 4)
+			lxs_err(gettext("usage: %s %s <zoneroot> <zonename>"),
+			    bname, argv[1]);
+		zoneroot = argv[2];
+		zonename = argv[3];
+		return (lxs_halt());
+	}
+
+	if (strcmp(argv[1], "verify") == 0) {
+		if (argc != 3)
+			lxs_err(gettext("usage: %s verify <xml file>"),
+			    bname);
+		return (lxs_verify(argv[2]));
+	}
+
+	usage();
+	/*NOTREACHED*/
+}
--- /dev/null	Wed Oct  5 00:31:50 2016
+++ new/usr/src/lib/brand/lx/lx_vdso/Makefile	Wed Oct  5 00:31:50 2016
@@ -0,0 +1,39 @@
+#
+# 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 Joyent, Inc.
+#
+
+include ../../../Makefile.lib
+
+SUBDIRS = tools $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+all :=          TARGET= all
+clean :=        TARGET= clean
+clobber :=      TARGET= clobber
+install :=      TARGET= install
+lint :=         TARGET= lint
+
+.KEEP_STATE:
+
+all install clean clobber: $(SUBDIRS)
+
+lint: $(LINT_SUBDIRS)
+
+$(MACH): tools
+$(MACH64): tools
+
+$(SUBDIRS): FRC
+	@cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
--- /dev/null	Wed Oct  5 00:31:50 2016
+++ new/usr/src/lib/brand/lx/lx_vdso/Makefile.com	Wed Oct  5 00:31:50 2016
@@ -0,0 +1,85 @@
+#
+# 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 Joyent, Inc.
+#
+
+LIBRARY	=	lx_vdso.a
+VERS =		.1
+
+include $(SRC)/lib/commpage/Makefile.shared.com
+
+COBJS =		vdso_main.o vdso_subr.o
+OBJECTS =	$(COBJS) $(COMMPAGE_OBJS)
+
+include ../../../../Makefile.lib
+include ../../Makefile.lx
+
+#
+# Since our name doesn't start with "lib", Makefile.lib incorrectly
+# calculates LIBNAME. Therefore, we set it here.
+#
+LIBNAME =	lx_vdso
+
+MAPFILES =	../common/mapfile-vers
+MAPOPTS =	$(MAPFILES:%=-M%)
+
+ASOBJS  =	vdso_subr.o
+COBJS  =	vdso_main.o
+OBJECTS =	$(ASOBJS) $(COBJS) $(COMMPAGE_OBJS)
+
+SRCDIR =	../common
+
+ASSRCS =	$(ASOBJS:%.o=$(ISASRCDIR)/%.s)
+CSRCS =		$(COBJS:%.o=$(SRCDIR)/%.c)
+SRCS =		$(ASSRCS) $(CSRCS)
+
+LIBS =		$(DYNLIB)
+DYNFLAGS +=	$(DYNFLAGS_$(CLASS))
+DYNFLAGS +=	$(MAPOPTS)
+LDLIBS +=
+ASFLAGS =	-P $(ASFLAGS_$(CURTYPE)) -D_ASM
+
+
+LIBS =		$(DYNLIB)
+
+CLEANFILES +=	$(DYNLIB)
+ROOTLIBDIR =	$(ROOT)/usr/lib/brand/lx
+ROOTLIBDIR64 =	$(ROOT)/usr/lib/brand/lx/$(MACH64)
+
+VDSO_TOOL =	../tools/vdso_tool
+
+.KEEP_STATE:
+
+#
+# While $(VDSO_TOOL) performs most of the transformations required to
+# construct a correct VDSO object, we still make use of $(ELFEDIT).  To
+# remove the $(ELFEDIT) requirement would mean shouldering the burden of
+# becoming a link-editor; this dark lore is best left to the linker aliens.
+#
+all: $(LIBS)
+	$(ELFEDIT) -e "dyn:value -add VERSYM $$($(ELFEDIT) \
+	    -e 'shdr:dump .SUNW_versym' $(DYNLIB) | \
+	    $(AWK) '{ if ($$1 == "sh_addr:") { print $$2 } }')" $(DYNLIB)
+	$(VDSO_TOOL) -f $(DYNLIB)
+
+lint: $(LINTLIB) lintcheck
+
+include ../../../../Makefile.targ
+include $(SRC)/lib/commpage/Makefile.shared.targ
+
+pics/%.o: $(ISASRCDIR)/%.s
+	$(COMPILE.s) -o $@ $<
+	$(POST_PROCESS_O)
+
+pics/vdso_main.o := CPPFLAGS += $(COMMPAGE_CPPFLAGS) -I$(SRCDIR)
+pics/vdso_subr.o := ASFLAGS += -I$(SRC)/uts/common/brand/lx -I$(SRCDIR)
--- /dev/null	Wed Oct  5 00:31:50 2016
+++ new/usr/src/lib/brand/lx/lx_vdso/amd64/Makefile	Wed Oct  5 00:31:50 2016
@@ -0,0 +1,42 @@
+#
+# 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 Joyent, Inc.
+#
+
+ISASRCDIR=.
+TARGET_ARCH=$(MACH64)
+
+include ../Makefile.com
+include $(SRC)/lib/Makefile.lib.64
+
+ASFLAGS += -D__$(MACH64)
+
+SONAME = linux-vdso.so.1
+
+# Disable save-args since some vDSO consumers are sensitive to stack usage.
+SAVEARGS =
+
+#
+# You might ask, why aren't we overriding BUILD.SO in Makefile.com.
+# That's a sad story. The answer is that Makefile.lib.64 includes
+# Makefile.master.64 which redefines BUILD.SO, leaving us in an
+# unfortunate jumble. Therefore we have to redefine it in the
+# lower-level Makefile.
+#
+BUILD.SO =	$(LD) -o $@ $(GSHARED) $(DYNFLAGS) $(PICS) $(LDLIBS)
+
+ASSYMDEP_OBJS = lx_vdso.o
+
+CLOBBERFILES	= $(ROOTLIBDIR64)/$(DYNLIB) $(ROOTLIBDIR64)/$(LINTLIB)
+
+install: all $(ROOTLIBS64)
--- /dev/null	Wed Oct  5 00:31:50 2016
+++ new/usr/src/lib/brand/lx/lx_vdso/amd64/vdso_subr.s	Wed Oct  5 00:31:50 2016
@@ -0,0 +1,131 @@
+/*
+ *
+ * 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 Joyent, Inc.
+ */
+
+
+#include <sys/asm_linkage.h>
+#include <sys/lx_syscalls.h>
+#include <vdso_defs.h>
+
+#if defined(lint)
+
+comm_page_t *
+__vdso_find_commpage()
+{}
+
+long
+__vdso_sys_clock_gettime(uint_t clock_id, timespec_t *tp)
+{}
+
+int
+__vdso_sys_gettimeofday(timespec_t *tp, struct lx_timezone *tz)
+{}
+
+time_t
+__vdso_sys_time(timespec_t *tp)
+{}
+
+#else /* lint */
+
+	ENTRY_NP(__vdso_find_commpage)
+	leaq	0x0(%rip), %rax
+	andq	$LX_VDSO_ADDR_MASK, %rax
+	addq	$LX_VDSO_SIZE, %rax
+	ret
+	SET_SIZE(__vdso_find_commpage)
+
+	ENTRY_NP(__vdso_sys_clock_gettime)
+	movl	$LX_SYS_clock_gettime, %eax
+	syscall
+	ret
+	SET_SIZE(__vdso_sys_clock_gettime)
+
+	ENTRY_NP(__vdso_sys_gettimeofday)
+	movl	$LX_SYS_gettimeofday, %eax
+	syscall
+	ret
+	SET_SIZE(__vdso_sys_gettimeofday)
+
+	ENTRY_NP(__vdso_sys_time)
+	movl	$LX_SYS_time, %eax
+	syscall
+	ret
+	SET_SIZE(__vdso_sys_time)
+
+/*
+ * long
+ * __vdso_clock_gettime(uint_t, timespec_t *)
+ */
+	ENTRY_NP(__vdso_clock_gettime)
+	subq	$0x18, %rsp
+	movl	%edi, (%rsp)
+	movq	%rsi, 0x8(%rsp)
+
+	call	__vdso_find_commpage
+	movq	%rax, 0x10(%rsp)
+
+	movq	%rax, %rdi
+	call	__cp_can_gettime
+	cmpl	$0, %eax
+	je	5f
+
+	/*
+	 * Restore the original args/stack (with commpage pointer in rdx)
+	 * This enables the coming tail-call to the desired function, be it
+	 * __cp_clock_gettime_* or __vdso_sys_clock_gettime.
+	 */
+	movl	(%rsp), %edi
+	movq	0x8(%rsp), %rsi
+	movq	0x10(%rsp), %rdx
+	addq	$0x18, %rsp
+
+	cmpl	$LX_CLOCK_REALTIME, %edi
+	jne	2f
+1:
+	movq	%rdx, %rdi
+	jmp	__cp_clock_gettime_realtime
+
+2:
+	cmpl	$LX_CLOCK_MONOTONIC, %edi
+	jne	4f
+3:
+	movq	%rdx, %rdi
+	jmp	__cp_clock_gettime_monotonic
+
+4:
+	cmpl	$LX_CLOCK_REALTIME_COARSE, %edi
+	je	1b
+	cmpl	$LX_CLOCK_MONOTONIC_RAW, %edi
+	je	3b
+	cmpl	$LX_CLOCK_MONOTONIC_COARSE, %edi
+	je	3b
+	jmp	6f
+
+5:
+	/*
+	 * When falling through from a failed cp_can_gettime, the stack
+	 * allocation must be released before a tail-call is made to the
+	 * fallback syscall function.
+	 */
+	addq	$0x18, %rsp
+
+6:
+	/* Let the real syscall handle all other cases */
+	jmp	__vdso_sys_clock_gettime
+	SET_SIZE(__vdso_clock_gettime)
+
+
+#endif /* lint */
--- /dev/null	Wed Oct  5 00:31:50 2016
+++ new/usr/src/lib/brand/lx/lx_vdso/common/mapfile-vers	Wed Oct  5 00:31:50 2016
@@ -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 2014 Joyent, Inc.  All rights reserved.
+#
+
+#
+# MAPFILE HEADER START
+#
+# WARNING:  STOP NOW.  DO NOT MODIFY THIS FILE.
+# Object versioning must comply with the rules detailed in
+#
+#	usr/src/lib/README.mapfiles
+#
+# You should not be making modifications here until you've read the most current
+# copy of that file. If you need help, contact a gatekeeper for guidance.
+#
+# MAPFILE HEADER END
+#
+
+$mapfile_version 2
+
+SYMBOL_VERSION LINUX_2.6 {
+    global:
+	__vdso_gettimeofday;
+	__vdso_clock_gettime;
+	__vdso_getcpu;
+	__vdso_time;
+    local:
+        *;
+};
+
+#
+# The vDSO module in GNU/Linux must only have a single PT_LOAD section.
+# Further, we should not have any data sections at all. Therefore, we go
+# through and explicitly disable several of the writeable sections that
+# might commonly show up.
+#
+LOAD_SEGMENT data {
+        disable;
+};
+
+LOAD_SEGMENT ldata {
+        disable;
+};
+
+LOAD_SEGMENT bss {
+        disable;
+};
+
--- /dev/null	Wed Oct  5 00:31:50 2016
+++ new/usr/src/lib/brand/lx/lx_vdso/common/vdso_defs.h	Wed Oct  5 00:31:50 2016
@@ -0,0 +1,42 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#ifndef	_VDSO_DEFS_H_
+#define	_VDSO_DEFS_H_
+
+#define	LX_CLOCK_REALTIME		0	/* CLOCK_REALTIME	*/
+#define	LX_CLOCK_MONOTONIC		1	/* CLOCK_HIGHRES	*/
+#define	LX_CLOCK_PROCESS_CPUTIME_ID	2	/* Emulated		*/
+#define	LX_CLOCK_THREAD_CPUTIME_ID	3	/* Emulated		*/
+#define	LX_CLOCK_MONOTONIC_RAW		4	/* CLOCK_HIGHRES	*/
+#define	LX_CLOCK_REALTIME_COARSE	5	/* CLOCK_REALTIME	*/
+#define	LX_CLOCK_MONOTONIC_COARSE	6	/* CLOCK_HIGHRES	*/
+
+#if !defined(_ASM)
+
+struct lx_timezone {
+	int tz_minuteswest;	/* minutes W of Greenwich */
+	int tz_dsttime;		/* type of dst correction */
+};
+
+/* Functions provided by the mach-specific vdso_subr.s */
+extern comm_page_t *__vdso_find_commpage();
+extern int __vdso_sys_gettimeofday(timespec_t *, struct lx_timezone *);
+extern time_t __vdso_sys_time(time_t *);
+extern long __vdso_sys_clock_gettime(uint_t, timespec_t *);
+
+#endif /* !defined(_ASM) */
+
+#endif /* _VDSO_DEFS_H_ */
--- /dev/null	Wed Oct  5 00:31:50 2016
+++ new/usr/src/lib/brand/lx/lx_vdso/common/vdso_main.c	Wed Oct  5 00:31:50 2016
@@ -0,0 +1,139 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#include <cp_defs.h>
+#include <vdso_defs.h>
+
+
+#if defined(__i386)
+
+long
+__vdso_clock_gettime(uint_t clock_id, timespec_t *tp)
+{
+	comm_page_t *cp = __vdso_find_commpage();
+
+	if (__cp_can_gettime(cp) == 0) {
+		return (__vdso_sys_clock_gettime(clock_id, tp));
+	}
+
+	switch (clock_id) {
+	case LX_CLOCK_REALTIME:
+	case LX_CLOCK_REALTIME_COARSE:
+		return (__cp_clock_gettime_realtime(cp, tp));
+
+	case LX_CLOCK_MONOTONIC:
+	case LX_CLOCK_MONOTONIC_RAW:
+	case LX_CLOCK_MONOTONIC_COARSE:
+		return (__cp_clock_gettime_monotonic(cp, tp));
+
+	case LX_CLOCK_PROCESS_CPUTIME_ID:
+	case LX_CLOCK_THREAD_CPUTIME_ID:
+	default:
+		return (__vdso_sys_clock_gettime(clock_id, tp));
+	}
+}
+
+/*
+ * On i386, the implementation of __cp_clock_gettime_monotonic expects that an
+ * hrt2ts function is provided.  It is provided below since the vDSO is
+ * operating on its own, without native libc.
+ */
+void
+hrt2ts(hrtime_t hrt, timespec_t *tsp)
+{
+	uint32_t sec, nsec, tmp;
+
+	tmp = (uint32_t)(hrt >> 30);
+	sec = tmp - (tmp >> 2);
+	sec = tmp - (sec >> 5);
+	sec = tmp + (sec >> 1);
+	sec = tmp - (sec >> 6) + 7;
+	sec = tmp - (sec >> 3);
+	sec = tmp + (sec >> 1);
+	sec = tmp + (sec >> 3);
+	sec = tmp + (sec >> 4);
+	tmp = (sec << 7) - sec - sec - sec;
+	tmp = (tmp << 7) - tmp - tmp - tmp;
+	tmp = (tmp << 7) - tmp - tmp - tmp;
+	nsec = (uint32_t)hrt - (tmp << 9);
+	while (nsec >= NANOSEC) {
+		nsec -= NANOSEC;
+		sec++;
+	}
+	tsp->tv_sec = (time_t)sec;
+	tsp->tv_nsec = nsec;
+}
+
+#else
+
+/*
+ * On amd64, the __vdso_clock_gettime function is implemented in asm to stay
+ * within the allowed stack budget.
+ */
+
+#endif /* defined(__i386) */
+
+
+int
+__vdso_gettimeofday(timespec_t *tp, struct lx_timezone *tz)
+{
+	if (tz != NULL) {
+		tz->tz_minuteswest = 0;
+		tz->tz_dsttime = 0;
+	}
+
+	if (tp != NULL) {
+		comm_page_t *cp = __vdso_find_commpage();
+
+		if (__cp_can_gettime(cp) == 0) {
+			return (__vdso_sys_gettimeofday(tp, tz));
+		}
+
+		__cp_clock_gettime_realtime(cp, tp);
+		tp->tv_nsec /= 1000;
+	}
+	return (0);
+}
+
+time_t
+__vdso_time(time_t *tp)
+{
+	comm_page_t *cp = __vdso_find_commpage();
+	timespec_t ts;
+
+	if (__cp_can_gettime(cp) == 0) {
+		return (__vdso_sys_time(tp));
+	}
+
+	__cp_clock_gettime_realtime(cp, &ts);
+	if (tp != NULL) {
+		*tp = ts.tv_sec;
+	}
+	return (ts.tv_sec);
+}
+
+int
+__vdso_getcpu(uint_t *cpu, uint_t *node, void *tcache)
+{
+	comm_page_t *cp = __vdso_find_commpage();
+
+	if (cpu != NULL) {
+		*cpu = __cp_getcpu(cp);
+	}
+	if (node != NULL) {
+		*node = 0;
+	}
+	return (0);
+}
--- /dev/null	Wed Oct  5 00:31:50 2016
+++ new/usr/src/lib/brand/lx/lx_vdso/i386/Makefile	Wed Oct  5 00:31:50 2016
@@ -0,0 +1,43 @@
+#
+# 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 Joyent, Inc.
+#
+
+ISASRCDIR=.
+TARGET_ARCH=$(MACH)
+
+include ../Makefile.com
+
+ASFLAGS += -D__$(MACH)
+
+SONAME = linux-gate.so.1
+
+#
+# You might ask, why aren't we overriding BUILD.SO in Makefile.com.
+# See the amd64 Makefile for more answers
+#
+BUILD.SO =	$(LD) -o $@ $(GSHARED) $(DYNFLAGS) $(PICS) $(LDLIBS)
+
+ASSYMDEP_OBJS = lx_vdso.o
+
+CLOBBERFILES	= $(ROOTLIBDIR)/$(DYNLIB) $(ROOTLIBDIR)/$(LINTLIB)
+
+# Set the object entry point for __vsyscall-ers
+entryfix: $(DYNLIB)
+	$(ELFEDIT) -e "ehdr:e_entry \
+	    $$($(ELFEDIT) -re 'sym:st_value -osimple __vsyscall' $(DYNLIB))" \
+	    $(DYNLIB)
+
+all: entryfix
+
+install: all $(ROOTLIBS)
--- /dev/null	Wed Oct  5 00:31:50 2016
+++ new/usr/src/lib/brand/lx/lx_vdso/i386/vdso_subr.s	Wed Oct  5 00:31:50 2016
@@ -0,0 +1,92 @@
+/*
+ *
+ * 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 Joyent, Inc.
+ */
+
+
+#include <sys/asm_linkage.h>
+#include <sys/lx_syscalls.h>
+
+
+#if defined(lint)
+
+comm_page_t *
+__vdso_find_commpage()
+{}
+
+long
+__vdso_sys_clock_gettime(uint_t clock_id, timespec_t *tp)
+{}
+
+int
+__vdso_sys_gettimeofday(timespec_t *tp, struct lx_timezone *tz)
+{}
+
+time_t
+__vdso_sys_time(timespec_t *tp)
+{}
+
+#else /* lint */
+
+	ENTRY_NP(__vdso_find_commpage)
+	call	1f
+1:	popl	%eax
+	andl	$LX_VDSO_ADDR_MASK, %eax
+	addl	$LX_VDSO_SIZE, %eax
+	ret
+	SET_SIZE(__vdso_find_commpage)
+
+	ENTRY_NP(__vdso_sys_clock_gettime)
+	movl	$LX_SYS_clock_gettime, %eax
+	movl	0x4(%esp), %ebx
+	movl	0x8(%esp), %ecx
+	int	$0x80
+	ret
+	SET_SIZE(__vdso_sys_clock_gettime)
+
+	ENTRY_NP(__vdso_sys_gettimeofday)
+	movl	$LX_SYS_gettimeofday, %eax
+	movl	0x4(%esp), %ebx
+	movl	0x8(%esp), %ecx
+	int	$0x80
+	ret
+	SET_SIZE(__vdso_sys_gettimeofday)
+
+	ENTRY_NP(__vdso_sys_time)
+	movl	$LX_SYS_time, %eax
+	movl	0x4(%esp), %ebx
+	int	$0x80
+	ret
+	SET_SIZE(__vdso_sys_time)
+
+	ENTRY_NP(__vsyscall)
+	/*
+	 * On 32-bit Linux, the VDSO entry point (specified by e_entry)
+	 * provides a potentially accelerated means to vector into the kernel.
+	 * Normally this means using 'sysenter' with a Linux-custom calling
+	 * convention so programs expecting int80 behavior are not required to
+	 * change how arguments are passed.
+	 *
+	 * The SunOS sysenter entry point does _not_ tolerate such a departure
+	 * from convention, so if this function is updated to use sysenter, it
+	 * must properly marshal arguments onto the stack from the int80 style.
+	 * Such an enhancement can only occur once sysenter receives the same
+	 * branding hooks as syscall and int80.
+	 */
+	int	$0x80
+	ret
+	SET_SIZE(__vsyscall)
+
+#endif /* lint */
--- /dev/null	Wed Oct  5 00:31:50 2016
+++ new/usr/src/lib/brand/lx/lx_vdso/tools/Makefile	Wed Oct  5 00:31:50 2016
@@ -0,0 +1,42 @@
+#
+# 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 2015 Joyent, Inc.
+#
+
+PROG =			vdso_tool
+
+include ../../../../../cmd/Makefile.cmd
+
+OBJS =			vdso_tool.o
+
+CLOBBERFILES +=		$(PROG)
+
+NATIVECC_CFLAGS +=	$(CFLAGS) $(CCVERBOSE)
+NATIVECC_LDLIBS +=	-lelf
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install: all
+
+lint:	lint_PROG
+
+clean:
+	$(RM) $(OBJS)
+
+$(PROG): $(OBJS)
+	$(NATIVECC) $(NATIVECC_CFLAGS) $(NATIVECC_LDLIBS) $(OBJS) -o $@
+	$(POST_PROCESS)
+
+include ../../../../../cmd/Makefile.targ
--- /dev/null	Wed Oct  5 00:31:50 2016
+++ new/usr/src/lib/brand/lx/lx_vdso/tools/vdso_tool.c	Wed Oct  5 00:31:50 2016
@@ -0,0 +1,388 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+/*
+ * vdso_tool: a build-time tool for adjusting properties of the "lx_vdso.so.1"
+ * object we build for VDSO emulation in the LX brand.
+ *
+ * This tool ensures that the shared object contains only one loadable program
+ * header (PT_LOAD), and extends the size of that program header to induce the
+ * loading of all sections into memory.  It also sets a few attributes in the
+ * ELF header.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <strings.h>
+#include <fcntl.h>
+#include <err.h>
+#include <errno.h>
+#include <libelf.h>
+#include <gelf.h>
+
+#define	PROG	"vdso_tool"
+
+typedef enum vdso_flags {
+	VDSO_UNLINK = 0x0001,
+	VDSO_UPDATE = 0x0002
+} vdso_flags_t;
+
+typedef struct vdso {
+	int v_fd;
+	char *v_path;
+	Elf *v_elf;
+	vdso_flags_t v_flags;
+	int v_ptload_phdr;
+	Elf64_Off v_max_offset;
+} vdso_t;
+
+static int
+open_vdso(vdso_t **vp, char *path)
+{
+	vdso_t *v;
+
+	if ((v = calloc(1, sizeof (vdso_t))) == NULL ||
+	    (v->v_path = strdup(path)) == NULL) {
+		err(1, "could not allocate memory");
+	}
+	v->v_ptload_phdr = -1;
+	v->v_fd = -1;
+	*vp = v;
+
+	/*
+	 * Open shared object file.
+	 */
+	if ((v->v_fd = open(v->v_path, O_RDWR)) == -1) {
+		(void) fprintf(stderr, "could not open: %s: %s\n", v->v_path,
+		    strerror(errno));
+		return (-1);
+	}
+
+	/*
+	 * Attach libelf.
+	 */
+	if ((v->v_elf = elf_begin(v->v_fd, ELF_C_RDWR, NULL)) == NULL) {
+		(void) fprintf(stderr, "could not attach libelf: %s\n",
+		    elf_errmsg(-1));
+		return (-1);
+	}
+
+	if (elf_kind(v->v_elf) != ELF_K_ELF) {
+		(void) fprintf(stderr, "wrong elf type\n");
+		return (-1);
+	}
+
+	return (0);
+}
+
+static int
+close_vdso(vdso_t *v)
+{
+	int rval = 0;
+
+	if (v == NULL) {
+		return (0);
+	}
+
+	if (v->v_elf != NULL) {
+		/*
+		 * If we want to write to the file, do so now.
+		 */
+		if (v->v_flags & VDSO_UPDATE) {
+			if (elf_update(v->v_elf, ELF_C_WRITE) == -1) {
+				(void) fprintf(stderr, "ERROR: elf_update "
+				    "failed: %s\n", elf_errmsg(-1));
+				v->v_flags |= VDSO_UNLINK;
+				rval = -1;
+			}
+		}
+
+		/*
+		 * Close the libelf handle for this file.
+		 */
+		if (elf_end(v->v_elf) == -1) {
+			(void) fprintf(stderr, "ERROR: elf_end failed: %s\n",
+			    elf_errmsg(-1));
+			v->v_flags |= VDSO_UNLINK;
+			rval = -1;
+		}
+	}
+
+	if (v->v_fd > 0) {
+		(void) close(v->v_fd);
+	}
+
+	if (v->v_flags & VDSO_UNLINK) {
+		(void) fprintf(stderr, "unlinking file: %s\n", v->v_path);
+		if (unlink(v->v_path) != 0) {
+			(void) fprintf(stderr, "unlink failed: %s\n",
+			    strerror(errno));
+			rval = -1;
+		}
+	}
+
+	free(v->v_path);
+	free(v);
+
+	return (rval);
+}
+
+static int
+adjust_elf_ehdr(vdso_t *v)
+{
+	GElf_Ehdr ehdr;
+	boolean_t dirty = B_FALSE;
+
+	if (gelf_getehdr(v->v_elf, &ehdr) == NULL) {
+		(void) fprintf(stderr, "could not get ehdr: %s\n",
+		    elf_errmsg(-1));
+		goto errout;
+	}
+
+	if (ehdr.e_ident[EI_OSABI] != ELFOSABI_NONE) {
+		(void) fprintf(stdout, "set EI_OSABI = ELFOSABI_NONE\n");
+		ehdr.e_ident[EI_OSABI] = ELFOSABI_NONE;
+		dirty = B_TRUE;
+	}
+
+	if (ehdr.e_ident[EI_ABIVERSION] != 0) {
+		(void) fprintf(stdout, "set EI_ABIVERSION = 0\n");
+		ehdr.e_ident[EI_ABIVERSION] = 0;
+		dirty = B_TRUE;
+	}
+
+	if (dirty && gelf_update_ehdr(v->v_elf, &ehdr) == 0) {
+		(void) fprintf(stderr, "could not update ehdr: %s\n",
+		    elf_errmsg(-1));
+		goto errout;
+	}
+
+	v->v_flags |= VDSO_UPDATE;
+	return (0);
+
+errout:
+	v->v_flags |= VDSO_UNLINK;
+	return (-1);
+}
+
+static int
+find_pt_load_phdr(vdso_t *v)
+{
+	size_t nphdr, nloadable = 0;
+	int i;
+
+	if (elf_getphdrnum(v->v_elf, &nphdr) != 0) {
+		(void) fprintf(stderr, "could not get phdr count: %s\n",
+		    elf_errmsg(-1));
+		goto errout;
+	}
+	(void) fprintf(stdout, "phdr count: %d\n", nphdr);
+
+	for (i = 0; i < nphdr; i++) {
+		GElf_Phdr phdr;
+
+		if (gelf_getphdr(v->v_elf, i, &phdr) == NULL) {
+			(void) fprintf(stderr, "could not get phdr[%d] count: "
+			    "%s\n", i, elf_errmsg(-1));
+			goto errout;
+		}
+
+		if (phdr.p_type == PT_LOAD) {
+			if (nloadable++ != 0) {
+				(void) fprintf(stderr, "multiple PT_LOAD "
+				    "phdrs\n");
+				goto errout;
+			}
+
+			(void) fprintf(stdout, "PT_LOAD header is phdr[%d]\n",
+			    i);
+			v->v_ptload_phdr = i;
+
+			if (phdr.p_filesz != phdr.p_memsz) {
+				(void) fprintf(stderr, "mismatched filesz "
+				    "(%llx) and memsz (%llx)\n", phdr.p_filesz,
+				    phdr.p_memsz);
+				goto errout;
+			}
+
+			if (phdr.p_filesz == 0) {
+				(void) fprintf(stderr, "filesz was zero\n");
+				goto errout;
+			}
+		}
+	}
+
+	return (0);
+
+errout:
+	v->v_flags |= VDSO_UNLINK;
+	return (-1);
+}
+
+static int
+find_maximum_offset(vdso_t *v)
+{
+	size_t nshdr;
+	int i;
+
+	if (elf_getshdrnum(v->v_elf, &nshdr) != 0) {
+		(void) fprintf(stderr, "could not get shdr count: %s\n",
+		    elf_errmsg(-1));
+		v->v_flags |= VDSO_UNLINK;
+		return (-1);
+	}
+	(void) fprintf(stdout, "shdr count: %d\n", nshdr);
+
+	for (i = 0; i < nshdr; i++) {
+		Elf_Scn *scn = elf_getscn(v->v_elf, i);
+		GElf_Shdr shdr;
+		Elf64_Off end;
+
+		if (gelf_getshdr(scn, &shdr) == NULL) {
+			(void) fprintf(stderr, "could not get shdr[%d] "
+			    "count: %s\n", i, elf_errmsg(-1));
+			goto errout;
+		}
+
+		end = shdr.sh_offset + shdr.sh_size;
+
+		if (end > v->v_max_offset) {
+			v->v_max_offset = end;
+		}
+	}
+
+	(void) fprintf(stdout, "maximum offset: %llx\n", v->v_max_offset);
+
+	return (0);
+
+errout:
+	v->v_flags |= VDSO_UNLINK;
+	return (-1);
+}
+
+static int
+update_pt_load_size(vdso_t *v)
+{
+	GElf_Phdr phdr;
+
+	if (gelf_getphdr(v->v_elf, v->v_ptload_phdr, &phdr) == NULL) {
+		(void) fprintf(stderr, "could not get phdr[%d] count: %s\n",
+		    v->v_ptload_phdr, elf_errmsg(-1));
+		goto errout;
+	}
+
+	(void) fprintf(stdout, "PT_LOAD size is currently %llx\n",
+	    phdr.p_filesz);
+	if (phdr.p_filesz < v->v_max_offset) {
+		(void) fprintf(stdout, "extending PT_LOAD size from %llx "
+		    "to %llx\n", phdr.p_filesz, v->v_max_offset);
+
+		phdr.p_memsz = phdr.p_filesz = v->v_max_offset;
+
+		if (gelf_update_phdr(v->v_elf, v->v_ptload_phdr, &phdr) ==
+		    NULL) {
+			(void) fprintf(stderr, "could not update PT_LOAD "
+			    "phdr: %s", elf_errmsg(-1));
+			goto errout;
+		}
+
+		v->v_flags |= VDSO_UPDATE;
+	}
+
+	return (0);
+
+errout:
+	v->v_flags |= VDSO_UNLINK;
+	return (-1);
+}
+
+int
+main(int argc, char **argv)
+{
+	vdso_t *v;
+	char *filen = NULL;
+	int errflg = 0;
+	int c;
+	int status = 0;
+	boolean_t do_update = B_TRUE;
+
+	while ((c = getopt(argc, argv, ":nf:")) != -1) {
+		switch (c) {
+		case 'n':
+			do_update = B_FALSE;
+			break;
+		case 'f':
+			filen = optarg;
+			break;
+		case ':':
+			(void) fprintf(stderr, "option -%c requires an "
+			    "operand\n", optopt);
+			errflg++;
+			break;
+		case '?':
+			(void) fprintf(stderr, "unrecognised option: -%c\n",
+			    optopt);
+			errflg++;
+			break;
+		}
+	}
+
+	if (errflg != 0 || optind != argc || filen == NULL) {
+		(void) fprintf(stderr, "usage: %s -f <vdso.so>\n", PROG);
+		return (1);
+	}
+
+	(void) fprintf(stdout, "vdso file: %s\n", filen);
+
+	if (elf_version(EV_CURRENT) == EV_NONE) {
+		(void) fprintf(stderr, "libelf mismatch: %s\n", elf_errmsg(-1));
+		return (2);
+	}
+
+	status = 3;
+	if (open_vdso(&v, filen) == -1) {
+		goto out;
+	}
+
+	status++;
+	if (adjust_elf_ehdr(v) == -1) {
+		goto out;
+	}
+
+	status++;
+	if (find_pt_load_phdr(v) == -1) {
+		goto out;
+	}
+
+	status++;
+	if (find_maximum_offset(v) == -1) {
+		goto out;
+	}
+
+	status++;
+	if (do_update && update_pt_load_size(v) == -1) {
+		goto out;
+	}
+
+out:
+	status++;
+	if (close_vdso(v) == 0) {
+		status = 0;
+	}
+
+	return (status);
+}
--- /dev/null	Wed Oct  5 00:31:50 2016
+++ new/usr/src/lib/brand/lx/netfiles/Makefile	Wed Oct  5 00:31:50 2016
@@ -0,0 +1,47 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+# Copyright 2015 Joyent, Inc.
+#
+
+TXTS = etc_default_nfs
+NFS_DFL = ../../../../cmd/fs.d/nfs/etc/nfs.dfl
+
+all: $(TXTS)
+
+include ../Makefile.lx
+
+lint:
+
+install: $(ROOTTXTS)
+
+clean:
+	-$(RM) etc_default_nfs
+
+clobber: clean
+	-$(RM) $(ROOTXMLDOCS) $(ROOTTXTS)
+
+etc_default_nfs: $(NFS_DFL)
+	$(RM) $@
+	$(CP) $(NFS_DFL) $@
--- /dev/null	Wed Oct  5 00:31:50 2016
+++ new/usr/src/lib/brand/lx/testing/Makefile	Wed Oct  5 00:31:50 2016
@@ -0,0 +1,36 @@
+#
+# 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 2015 Joyent, Inc. All rights reserved.
+#
+
+TXTS = ltp_skiplist ltp_tests
+
+all:
+
+include $(SRC)/cmd/Makefile.cmd
+include ../Makefile.lx
+
+all :=		TARGET= all
+install :=	TARGET= install
+clobber :=	TARGET= clobber
+
+install: $(ROOTTXTS)
+
+_msg:
+
+lint:
+
+clean:
+
+clobber:
+	-$(RM) $(ROOTTXTS)
--- /dev/null	Wed Oct  5 00:31:50 2016
+++ new/usr/src/lib/brand/lx/testing/Readme_ltp	Wed Oct  5 00:31:50 2016
@@ -0,0 +1,291 @@
+The Linux Test Project (LTP) provides the basis for testing the lx API
+implementation. The project webpage is at http://linux-test-project.github.io/
+and the source is available on GitHub at
+https://github.com/linux-test-project/ltp.git.
+
+LTP should be built and run from within an lx zone.
+
+To build
+--------
+As root first make sure you have the tools installed:
+    apt-get install build-essential autoconf automake git quota
+
+Additional prerequisites are required activate some tests:
+    apt-get install attr-dev libaio-dev
+
+As a normal user:
+    git clone https://github.com/linux-test-project/ltp.git
+    cd ltp
+    make autotools
+    ./configure
+    make all
+
+As root:
+    make install
+
+The tests can be built in both a zone that has been installed with a 32-bit
+version of Linux and another zone that has been installed with a 64-bit version
+of the same release. When testing the 64-bit zone, a copy of the 32-bit build
+can be run in the 64-bit zone to ensure that 32-bit applications work
+correctly on a 64-bit install.
+
+Running the tests
+-----------------
+The LTP source tree provides detailed documentation on using the test suite, so
+this readme only give a short summary.
+
+Because many of the tests are targetted at kernel functionality which does not
+apply to Illumos, or test capabilities which are not available from within a
+zone, or test system call functionality which has not yet been completed, a
+skip list is used during the test run to bypass tests which are known to fail.
+
+The skip list lives in this directory and is delivered on the system. It is
+available from within the lx zone as /native/usr/lib/brand/lx/ltp_skiplist. As
+new functionality is completed, the skip list should be updated to remove tests
+which now work.
+
+As root:
+    cd /opt/ltp
+    /opt/ltp/runltp -f `cat /native/usr/lib/brand/lx/ltp_tests` \
+        -S /native/usr/lib/brand/lx/ltp_skiplist -p >/tmp/test.log
+
+When the test run has finished, the results will be logged in a date/time
+stamped file under /opt/ltp/results. The summary at the end of the log file
+should show "Total Failures: 0". If not, something is wrong.
+
+Running tests for development
+-----------------------------
+The source for the tests can be found under the testcases directory. The
+largest and most useful set for lx live under testcases/kernel/syscalls.
+
+For development purposes, an individual test (or tests) can be run by listing
+them in a command file, one per line. For example, with a command file named
+~/tcmds, to run the read01 test you setup the file so it looks like this:
+    read01 read01
+
+You can run that specific test as follows:
+    /opt/ltp/runltp -f ~/tcmds -p -l ~/read.log
+
+Test status
+-----------
+This section provides a short summary of the rationale for why tests are being
+skipped.
+
+LTP groups tests into command files (i.e. syscalls, nptl, etc. provided with
+the -f option in the runltp command shown above). A complete list of the groups
+can be seen in LTP source tree under the runtest directory. Some of these
+groups are obviously not applicable when running in an lx zone. The remaining
+groups still need work before they can be run. The groups shown in the runltp
+command above are expected to work when the skip list is used. The 'syscalls'
+command file runs the majority of the actual system call tests which we are
+interested in.
+
+The following table indicates why specific subtests are being skipped. Also
+note that the following tests pass in a 64-bit lx zone, but fail in a zone
+installed with a 32-bit Linux build: mmap15, open12, openat02 and sendfile09.
+
+    Legend:
+	x = never in a zone
+	* = fails on kvm and bare metal too
+	# = emulation not implemented yet
+	- = could enable with a test zone config change
+
+- access06	wants a block device
+x acct01	enables bsd process accounting
+# add_key01
+# add_key02
+x adjtimex01
+x adjtimex02
+x bdflush01
+x cacheflush01
+x chmod03	need PRIV_SYS_CONFIG to set sticky bit on reg file
+- chmod06	needs dev
+x chmod07	need PRIV_SYS_CONFIG to set sticky bit on reg file
+- chown04	needs dev
+- chown04_16	needs dev
+# clone02
+# clone08
+- creat06	wants to mount a ro fs
+x creat07	we don't behave this way for ETXTBSY
+x creat08	sets euid to 'nobody', loses PRIV_FILE_SETID to set sgid
+x execve04	we don't behave this way for ETXTBSY
+# fallocate01
+# fallocate02
+# fallocate03
+x fchmod02	need PRIV_SYS_CONFIG to set sticky bit on reg file
+x fchmod03	need PRIV_SYS_CONFIG to set sticky bit on reg file
+- fchmod06	needs dev
+# fchown04	mounts
+# fchown04_16
+# fcntl06	not supported on linux
+# fcntl06_64
+# fcntl23	leases not implemented
+# fcntl23_64	"
+# fcntl24	"
+# fcntl24_64	"
+# fcntl25	"
+# fcntl25_64	"
+# fcntl26	"
+# fcntl26_64	"
+# fcntl30
+# fcntl30_64
+# fcntl31	setown/getown not impl
+# fcntl31_64
+# fcntl32	F_SETLEASE not impl
+# fcntl32_64
+# fcntl33	F_SETLEASE not impl
+# fcntl33_64
+# fork05	asm into %fs reg
+- fork09	needs a swap cap of ~9GB
+# fork13	decided not to support this
+# fork14	"
+# ftruncate04	need a mnt with mandatory locking
+# ftruncate04_64
+# getdents02	wrong errno on test 4 - perf. impact too high
+# getdents02_64
+# get_mempolicy01
+x getrusage03	we don't fill in the ru_maxrss field
+- getxattr01	need attr/xattr.h at build time
+- getxattr02
+- getxattr03
+# ioctl03	needs /dev/net/tun
+# io_cancel01	libaio stuff not done
+# io_destroy01
+# io_getevents01
+# io_setup01
+# io_submit01
+- inotify03	needs dev
+# fanotify01	don't have fanotify
+# fanotify02
+# fanotify03
+# fanotify04
+# fanotify05
+# keyctl01	no kernel keyring support
+- lchown03	needs to mount ro fs
+- lchown03_16
+- linkat02	needs dev
+- link08	needs dev
+x mem01		crashme test which expects OOM killer to save the day
+- mkdir03	needs dev
+- mkdirat02	needs dev
+x mknod01	makes block and chr devs
+- mknod07	needs dev
+- mknodat02	needs dev
+# mmap13	expects "invalid access" to SIGBUS
+- mount01	needs dev
+- mount02	needs dev
+x mount03	mounts ext2
+- mount04	needs dev
+x mount05	mounts ext2
+x mount06	mounts ext2
+# mq_notify01
+# mq_notify02
+# mq_open01
+# mq_timedreceive01
+# mq_timedsend01
+# mq_unlink01
+x mremap01
+x mremap02
+x mremap03
+x mremap04
+x mremap05
+# msgctl12	uses MSG_STAT
+# msgrcv07	MSG_EXCEPT subtest - not avail.
+x open01	need PRIV_SYS_CONFIG to set sticky bit on reg file
+# open02	expects NOATIME to cause err for unpriv user
+# open10	setgid on sgid subdir behavior
+x open11	makes device
+# ppoll01
+# process_vm_readv01
+# process_vm_readv02
+# process_vm_readv03
+# process_vm_writev01
+# process_vm_writev02
+# prot_hsymlinks	/proc/sys/fs/protected_hardlinks
+x ptrace04	not supp on our arch
+# ptrace05	OS-3307
+# read02	checks errno for O_DIRECT
+# readahead01
+# readahead02
+# readdir21	dir perf. issue
+- rename11	needs dev
+- renameat01	needs dev
+- rmdir02	needs dev
+x sched_getparam01	assumes Linux SCHED_OTHER return value
+x sched_getparam02	assumes Linux SCHED_OTHER return value
+# sched_rr_get_interval01
+# sched_rr_get_interval02
+# sched_rr_get_interval03
+x sched_setparam02	tries to set Linux policies
+x sched_setparam03	tries to set Linux policies
+# sched_getscheduler01
+# sched_getscheduler02
+x sched_setscheduler01
+x sched_setaffinity01
+x sched_getaffinity01
+# semctl01	all pass but SEM_STAT - linux specific
+# semop02	last test fails errno - expensive
+# sendfile02	OS-3296
+# sendfile02_64	"
+# sendfile04	"
+# sendfile04_64	"
+# sendfile06	"
+# sendfile06_64	"
+# sendfile07	"
+# sendfile07_64	"
+sendmsg01	OS-3295 - tests actually pass
+x setfsuid04	no real equiv. and only for NFS server
+x setfsuid04_16
+# sgetmask01	obsolete
+# setgroups04_16	expects sig11 for certain err
+# setns01
+# setns02
+# setpgid02	all pass but one, expects pid 0 to be there
+* setregid02	fails on bare metal, expects to lookup group "nobody" which
+* setregid02_16	doesn't exist. it is "nogroup" on ubuntu at least
+# setrlimit01	all pass but one, expects to set proc limit
+x settimeofday01
+# setxattr01
+# setxattr02
+# setxattr03
+# shmget05	OS-3326
+# splice01
+# splice02
+# splice03
+# tee01
+# tee02
+# ssetmask01	obsolete
+x stime01
+x switch01
+# sync_file_range01
+# sysconf01	most pass but see OS-3305
+# sysctl01	the build compiled this out,
+# sysctl03	this syscall is basically obsolete
+# sysctl04	obsolete
+# sysctl05
+# syslog01
+# syslog02
+# syslog03
+# syslog04
+# syslog05
+# syslog06
+# syslog07
+# syslog08
+# syslog09
+# syslog10
+# syslog11
+# syslog12
+# unshare01
+# unshare02
+- umount01	needs dev
+- umount02	needs dev
+- umount03	needs dev
+x ustat01	obsolete call to stat FS
+x ustat02	obsolete call to stat FS
+- utime06	needs dev
+- utimes01	needs dev
+# utimensat01	many subtests pass but see OS-3328 for the rest
+# vmsplice01
+# vmsplice02
+# perf_event_open01
+# perf_event_open02
--- /dev/null	Wed Oct  5 00:31:50 2016
+++ new/usr/src/lib/brand/lx/testing/ltp_skiplist	Wed Oct  5 00:31:50 2016
@@ -0,0 +1,276 @@
+#
+# 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 Joyent, Inc.
+
+# Broken tests
+poll02		# OS-3997
+
+# tests functionality not allowed in a zone
+accept4_01
+acct01
+adjtimex01
+adjtimex02
+bdflush01
+cacheflush01
+chmod03
+chmod07
+creat07
+creat08
+execve04
+fchmod02
+fchmod03
+getrusage03
+ioperm01
+ioperm02
+iopl01
+iopl02
+isofs
+mbind01
+mem01
+migrate_pages01
+migrate_pages02
+mknod01
+mlockall02
+mlockall03
+mlock02
+mmap12
+mount03
+mount05
+mount06
+modify_ldt01
+modify_ldt02
+move_pages01
+move_pages02
+move_pages03
+move_pages04
+move_pages05
+move_pages06
+move_pages07
+move_pages08
+move_pages09
+move_pages10
+move_pages11
+mremap01
+mremap02
+mremap03
+mremap04
+mremap05
+open01
+open11
+ptrace04
+quotactl01
+quotactl02
+remap_file_pages01
+remap_file_pages02
+sched_getparam01
+sched_getparam02
+sched_setscheduler01
+sched_setscheduler03	# OS-5524
+sched_setaffinity01
+sched_getaffinity01
+sched_setparam02
+sched_setparam03
+setfsuid04
+setfsuid04_16
+settimeofday01
+stime01
+swapoff01
+swapoff02
+swapon01
+swapon02
+swapon03
+switch01
+ustat01
+ustat02
+
+# needs a config with a dev or mounting
+chmod06
+chown04
+chown04_16
+creat06
+fchmod06
+fchown04
+fchown04_16
+inotify03
+lchown03
+lchown03_16
+linkat02
+link08
+mkdir03
+mkdirat02
+mknod07
+mknodat02
+mount01
+mount02
+mount04
+rename11
+renameat01
+rmdir02
+umount01
+umount02
+umount03
+utime06
+utimes01
+
+# These tests are broken on 32-bit (including on "real" Linux)
+preadv01
+preadv02
+pwritev01
+pwritev02
+
+# tests functionality not implemented yet
+access06
+add_key01
+add_key02
+clone02
+clone08
+crash02
+fallocate04		# needs SEEK_HOLE
+fanotify01
+fanotify02
+fanotify03
+fanotify04
+fanotify05
+fanotify06
+fcntl06
+fcntl23
+fcntl23_64
+fcntl24
+fcntl24_64
+fcntl25
+fcntl25_64
+fcntl26
+fcntl26_64
+fcntl31
+fcntl31_64
+fcntl32
+fcntl32_64
+fcntl33
+fcntl33_64
+fork05
+fork09
+fork13
+fork14
+ftruncate04
+ftruncate04_64
+futex_wake04		# OS-4471
+getdents02
+getdents02_64
+get_mempolicy01
+getxattr01
+getxattr02
+getxattr03
+ioctl03
+io_cancel01
+io_destroy01
+io_getevents01
+io_setup01
+io_submit01
+keyctl01
+kcmp01
+kcmp02
+kcmp03
+lgetxattr01
+lgetxattr02
+llistxattr01
+llistxattr02
+llistxattr03
+mmap13
+mq_notify01
+mq_notify02
+mq_open01
+mq_timedreceive01
+mq_timedsend01
+mq_unlink01
+msgctl12
+msgrcv07
+open02
+open10
+perf_event_open01
+perf_event_open02
+ppoll01
+process_vm_readv01
+process_vm_readv02
+process_vm_readv03
+process_vm_writev01
+process_vm_writev02
+prot_hsymlinks
+pty01			# OS-5437
+read02
+readahead01
+readahead02
+readdir2
+readdir21
+removexattr01
+removexattr02
+request_key01
+request_key02
+sched_rr_get_interval01
+sched_rr_get_interval02
+sched_rr_get_interval03
+sched_getscheduler01
+sched_getscheduler02
+sched_getattr01
+sched_getattr02
+sched_setattr01
+semctl01
+semop02
+sendfile02		# OS-3296
+sendfile02_64
+sendfile04
+sendfile04_64
+sendfile06
+sendfile06_64
+sendfile07
+sendfile07_64
+sendmsg01
+setgroups04_16
+setns01
+setns02
+setpgid02
+setregid02
+setregid02_16
+setrlimit01
+setxattr01
+setxattr02
+setxattr03
+sgetmask01
+shmget05		# OS-3326
+splice01
+splice02
+splice03
+ssetmask01
+sync_file_range01
+sysconf01		# OS-3305
+sysctl01
+sysctl03
+sysctl04
+sysctl05
+syslog01
+syslog02
+syslog03
+syslog04
+syslog05
+syslog06
+syslog07
+syslog08
+syslog09
+syslog10
+syslog11
+syslog12
+tee01
+tee02
+unshare01
+utimensat01		# OS-3328
+unshare02
+vmsplice01
+vmsplice02
--- /dev/null	Wed Oct  5 00:31:51 2016
+++ new/usr/src/lib/brand/lx/testing/ltp_tests	Wed Oct  5 00:31:51 2016
@@ -0,0 +1,1 @@
+syscalls,nptl,ipc,pipes,pty,crashme,math
--- /dev/null	Wed Oct  5 00:31:51 2016
+++ new/usr/src/lib/brand/lx/zone/Makefile	Wed Oct  5 00:31:51 2016
@@ -0,0 +1,71 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Copyright 2015 Joyent, Inc.  All rights reserved.
+# Copyright 2016 OmniTI Computer Consulting, Inc. All rights reserved.
+#
+
+PROGS =	lx_install lx_boot
+PROGS += lx_boot_zone_redhat lx_boot_zone_ubuntu lx_boot_zone_debian
+PROGS += lx_boot_zone_busybox
+XMLDOCS =	config.xml platform.xml
+TEMPLATES =	SUNWlx.xml SUNWlx26.xml
+
+all:	$(PROGS)
+
+include $(SRC)/cmd/Makefile.cmd
+include ../Makefile.lx
+
+all :=		TARGET= all
+install :=	TARGET= install
+clobber :=	TARGET= clobber
+
+POFILES=	$(PROGS:%=%.po)
+POFILE=		lx_zone.po
+
+$(POFILE): $(POFILES)
+	$(RM) $@
+	$(BUILDPO.pofiles)
+
+_msg: $(MSGDOMAINPOFILE)
+
+install: $(PROGS) $(ROOTXMLDOCS) $(ROOTTEMPLATES) $(ROOTPROGS)
+	mkdir -p $(ROOT)/usr/lib/brand/lx/ld/64
+	crle -c $(ROOT)/usr/lib/brand/lx/ld/ld.config \
+		-l /native/lib:/native/usr/lib \
+		-s /native/lib/secure:/native/usr/lib/secure
+	crle -64 -c $(ROOT)/usr/lib/brand/lx/ld/64/ld.config \
+		-l /native/lib/64:/native/usr/lib/64 \
+		-s /native/lib/secure/64:/native/usr/lib/secure/64
+
+lint:
+
+clean:
+	-$(RM) $(PROGS)
+
+clobber: clean
+	-$(RM) $(ROOTXMLDOCS) $(ROOTPROGS) $(ROOTTEMPLATES)
+
+FRC:
+
+include $(SRC)/Makefile.msg.targ
--- /dev/null	Wed Oct  5 00:31:51 2016
+++ new/usr/src/lib/brand/lx/zone/SUNWlx.xml	Wed Oct  5 00:31:51 2016
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+
+<!--
+ Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License (the "License").
+ You may not use this file except in compliance with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+    ident	"%Z%%M%	%I%	%E% SMI"
+
+    DO NOT EDIT THIS FILE.  Use zonecfg(1M) instead.
+-->
+
+<!DOCTYPE zone PUBLIC "-//Sun Microsystems Inc//DTD Zones//EN" "file:///usr/share/lib/xml/dtd/zonecfg.dtd.1">
+
+<zone name="default" zonepath="" autoboot="false" brand="lx">
+</zone>
--- /dev/null	Wed Oct  5 00:31:51 2016
+++ new/usr/src/lib/brand/lx/zone/SUNWlx26.xml	Wed Oct  5 00:31:51 2016
@@ -0,0 +1,35 @@
+<?xml version="1.0"?>
+
+<!--
+ Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License (the "License").
+ You may not use this file except in compliance with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+    ident	"%Z%%M%	%I%	%E% SMI"
+
+    DO NOT EDIT THIS FILE.  Use zonecfg(1M) instead.
+-->
+
+<!DOCTYPE zone PUBLIC "-//Sun Microsystems Inc//DTD Zones//EN" "file:///usr/share/lib/xml/dtd/zonecfg.dtd.1">
+
+<zone name="default" zonepath="" autoboot="false" brand="lx">
+  <attr name="kernel-version" type="string" value="2.6"/>
+</zone>
--- /dev/null	Wed Oct  5 00:31:51 2016
+++ new/usr/src/lib/brand/lx/zone/config.xml	Wed Oct  5 00:31:51 2016
@@ -0,0 +1,105 @@
+<?xml version="1.0"?>
+
+<!--
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License (the "License").
+ You may not use this file except in compliance with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ Copyright 2015, Joyent, Inc.
+
+ DO NOT EDIT THIS FILE.
+-->
+
+<!DOCTYPE brand PUBLIC "-//Sun Microsystems Inc//DTD Brands//EN"
+    "file:///usr/share/lib/xml/dtd/brand.dtd.1">
+
+<brand name="lx">
+	<modname>lx_brand</modname>
+
+	<initname>/native/usr/lib/brand/lx/lxinit</initname>
+	<login_cmd>/bin/login -h zone:%Z %u</login_cmd>
+	<forcedlogin_cmd>/bin/login -h zone:%Z -f %u</forcedlogin_cmd>
+	<user_cmd>/usr/bin/getent passwd %u</user_cmd>
+
+	<install>/usr/lib/brand/lx/lx_install -z %z -R %R</install>
+	<installopts>s:</installopts>
+	<boot>/usr/lib/brand/lx/lx_boot %z %R</boot>
+	<halt>/usr/lib/brand/lx/lx_support halt %R %z</halt>
+	<verify_cfg>/usr/lib/brand/lx/lx_support verify</verify_cfg>
+	<verify_adm></verify_adm>
+	<postclone></postclone>
+	<postinstall></postinstall>
+
+	<privilege set="default" name="contract_event" />
+	<privilege set="default" name="contract_identity" />
+	<privilege set="default" name="contract_observer" />
+	<privilege set="default" name="dtrace_proc" />
+	<privilege set="default" name="dtrace_user" />
+	<privilege set="default" name="file_chown" />
+	<privilege set="default" name="file_chown_self" />
+	<privilege set="default" name="file_dac_execute" />
+	<privilege set="default" name="file_dac_read" />
+	<privilege set="default" name="file_dac_search" />
+	<privilege set="default" name="file_dac_write" />
+	<privilege set="default" name="file_owner" />
+	<privilege set="default" name="file_setid" />
+	<privilege set="default" name="ipc_dac_read" />
+	<privilege set="default" name="ipc_dac_write" />
+	<privilege set="default" name="ipc_owner" />
+	<privilege set="default" name="net_bindmlp" />
+	<privilege set="default" name="net_icmpaccess" />
+	<privilege set="default" name="net_mac_aware" />
+	<privilege set="default" name="net_privaddr" />
+	<privilege set="default" name="net_rawaccess" ip-type="exclusive" />
+	<privilege set="default" name="proc_chroot" />
+	<privilege set="default" name="sys_audit" />
+	<privilege set="default" name="proc_audit" />
+	<privilege set="default" name="proc_clock_highres" />
+	<privilege set="default" name="proc_lock_memory" />
+	<privilege set="default" name="proc_owner" />
+	<privilege set="default" name="proc_setid" />
+	<privilege set="default" name="proc_prioup" />
+	<privilege set="default" name="proc_taskid" />
+	<privilege set="default" name="sys_acct" />
+	<privilege set="default" name="sys_admin" />
+	<privilege set="default" name="sys_ip_config" ip-type="exclusive" />
+	<privilege set="default" name="sys_iptun_config" ip-type="exclusive" />
+	<privilege set="default" name="sys_mount" />
+	<privilege set="default" name="sys_nfs" />
+	<privilege set="default" name="sys_resource" />
+	<privilege set="default" name="sys_ppp_config" ip-type="exclusive" />
+
+	<privilege set="prohibited" name="dtrace_kernel" />
+	<privilege set="prohibited" name="proc_zone" />
+	<privilege set="prohibited" name="sys_config" />
+	<privilege set="prohibited" name="sys_devices" />
+	<privilege set="prohibited" name="sys_ip_config" ip-type="shared" />
+	<privilege set="prohibited" name="sys_linkdir" />
+	<privilege set="prohibited" name="sys_net_config" />
+	<privilege set="prohibited" name="sys_ppp_config" ip-type="shared" />
+	<privilege set="prohibited" name="sys_res_config" />
+	<privilege set="prohibited" name="sys_suser_compat" />
+	<privilege set="prohibited" name="xvm_control" />
+	<privilege set="prohibited" name="virt_manage" />
+
+	<privilege set="required" name="proc_exec" />
+	<privilege set="required" name="proc_fork" />
+	<privilege set="required" name="sys_ip_config" ip-type="exclusive" />
+	<privilege set="required" name="sys_mount" />
+</brand>
--- /dev/null	Wed Oct  5 00:31:51 2016
+++ new/usr/src/lib/brand/lx/zone/lx_boot.ksh	Wed Oct  5 00:31:51 2016
@@ -0,0 +1,92 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#    
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#    
+# CDDL HEADER END
+#
+#
+# Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2015, Joyent, Inc.
+#
+# lx boot script.
+#
+# The arguments to this script are the zone name and the zonepath.
+#
+
+. /usr/lib/brand/shared/common.ksh
+
+ZONENAME=$1
+ZONEPATH=$2
+ZONEROOT=$ZONEPATH/root
+
+w_missing=$(gettext "Warning: \"%s\" is not installed in the global zone")
+
+arch=`uname -p`
+if [ "$arch" = "i386" ]; then
+	ARCH32=i86
+        ARCH64=amd64
+else
+        echo "Unsupported architecture: $arch"
+        exit 2
+fi
+
+#
+# Run the lx_support boot hook.
+#
+/usr/lib/brand/lx/lx_support boot $ZONEPATH $ZONENAME
+if (( $? != 0 )) ; then
+        exit 1
+fi
+
+BRANDDIR=/native/usr/lib/brand/lx;
+EXIT_CODE=1
+
+#
+# Before we boot we validate and fix, if necessary, the required files within
+# the zone.  These modifications can be lost if a patch or upgrade is applied
+# within the zone, so we validate and fix the zone every time it boots.
+#
+
+#
+# Determine the distro.
+#
+distro=""
+if [[ $(zonecfg -z $ZONENAME info attr name=docker) =~ "value: true" ]]; then
+	distro="docker"
+elif [[ -f $ZONEROOT/etc/redhat-release ]]; then
+	distro="redhat"
+elif [[ -f $ZONEROOT/etc/lsb-release ]]; then
+	if egrep -s Ubuntu $ZONEROOT/etc/lsb-release; then
+		distro="ubuntu"
+	elif [[ -f $ZONEROOT/etc/debian_version ]]; then
+		distro="debian"
+	fi
+elif [[ -f $ZONEROOT/etc/debian_version ]]; then
+	distro="debian"
+elif [[ -f $ZONEROOT/etc/alpine-release ]]; then
+	distro="busybox"
+fi
+
+[[ -z $distro ]] && fatal "Unsupported distribution!"
+
+#
+# Perform distro-specific customization.
+#
+. $(dirname $0)/lx_boot_zone_${distro}
+
+exit 0
--- /dev/null	Wed Oct  5 00:31:51 2016
+++ new/usr/src/lib/brand/lx/zone/lx_boot_zone_busybox.ksh	Wed Oct  5 00:31:51 2016
@@ -0,0 +1,182 @@
+#!/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 2015 Joyent, Inc.
+#
+
+#
+# Customisation for busybox-init-based distributions.  Assumes to have been
+# sourced from lx_boot.
+#
+
+tmpfile=/tmp/lx-busybox.$$
+
+# Check that the directories we're writing to aren't symlinks outside the zone
+safe_dir /etc
+safe_dir /etc/init.d
+
+# Generate network setup script
+#
+cat > $tmpfile <<EOF
+#!/sbin/runscript
+depend() {
+    need localmount
+    after bootmisc hwdrivers modules
+    provide net
+    keyword nojail noprefix novserver
+}
+start() {
+EOF
+# Only alter resolve.conf if we're getting info from zonecfg(1M).
+zonecfg -z $ZONENAME info attr name=resolvers | grep -q resolvers
+if [[ $? == 0 ]]; then
+    cat > $tmpfile <<EOF
+    if [ ! -e /etc/resolv.conf ]; then
+        echo "# AUTOMATIC ZONE CONFIG" > /etc/resolv.conf
+EOF
+    zonecfg -z $ZONENAME info attr name=resolvers |
+    awk '
+    {
+        if ($1 == "value:") {
+            nres = split($2, resolvers, ",")
+        }
+    }
+    END {
+        for (i = 1; i <= nres; i++) {
+            printf("        echo \"nameserver %s\" >> %s\n", resolvers[i],
+                "/etc/resolv.conf")
+        }
+    }
+    ' >> $tmpfile
+    zonecfg -z $ZONENAME info attr name=dns-domain |
+    awk '
+    {
+        if ($1 == "value:") {
+            dom = $2
+        }
+    }
+    END {
+        printf("        echo \"search %s\" >> %s\n", dom, "/etc/resolv.conf")
+    }
+    ' >> $tmpfile
+    cat >> $tmpfile <<EOF
+    fi
+    return 0
+    }
+EOF
+else
+    cat >> $tmpfile <<EOF
+    return 0
+    }
+EOF
+fi
+
+cat >> $tmpfile <<EOF
+    stop() {
+        return 0
+    }
+EOF
+fnm=$ZONEROOT/etc/init.d/networking
+if [[ -f $fnm || -h $fnm ]]; then
+	mv -f $tmpfile $fnm
+	chmod 755 $fnm
+fi
+
+
+#
+# The default /etc/inittab might spawn mingetty on each of the virtual consoles
+# as well as xdm on the X console.  Since we don't have virtual consoles nor
+# an X console, spawn a single mingetty on /dev/console instead.
+#
+# Don't bother changing the file if it looks like we already did.
+#
+fnm=$ZONEROOT/etc/inittab
+if ! egrep -s "Modified by lx brand" $fnm; then
+	sed 's/^tty[1-6]:/# Disabled by lx brand: &/' \
+	    $fnm > $tmpfile
+	echo "console::respawn:/sbin/getty 38400 console" >> $tmpfile
+	echo "# Modified by lx brand" >> $tmpfile
+
+	if [[ ! -h $fnm ]]; then
+		mv -f $tmpfile $fnm
+		chmod 644 $fnm
+	fi
+fi
+
+#
+# The following scripts attempt to start services or otherwise configure the
+# system in ways incompatible with zones, so replace them with stubs.
+#
+
+fnm=$ZONEROOT/etc/init.d/fsck
+[[ ! -h $fnm && -f $fnm ]] && cat <<DONE > $fnm
+#!/sbin/runscript
+
+depend() {
+    use dev clock modules
+}
+
+start() {
+    return 0
+}
+DONE
+
+fnm=$ZONEROOT/etc/init.d/hwclock
+[[ ! -h $fnm && -f $fnm ]] && cat <<DONE > $fnm
+#!/sbin/runscript
+
+depend() {
+    provide clock
+}
+
+start() {
+    return 0
+}
+DONE
+
+fnm=$ZONEROOT/etc/init.d/klogd
+[[ ! -h $fnm && -f $fnm ]] && cat <<DONE > $fnm
+#!/sbin/runscript
+
+depend() {
+    need clock hostname localmount
+    before net
+}
+
+start() {
+    return 0
+}
+DONE
+
+fnm=$ZONEROOT/etc/init.d/sysfs
+[[ ! -h $fnm && -f $fnm ]] && cat <<DONE > $fnm
+#!/sbin/runscript
+
+depend() {
+}
+
+start() {
+    return 0
+}
+DONE
+
+#
+# Setup for the /dev/shm mount.
+#
+fnm=$ZONEROOT/etc/fstab
+entry=$(awk '{if ($2 == "/dev/shm") print $2}' $fnm)
+if [[ -z "$entry" && ! -h $fnm ]]; then
+    echo "swapfs    /dev/shm    tmpfs    defaults    0 0" >> $fnm
+fi
+
+# Hand control back to lx_boot
--- /dev/null	Wed Oct  5 00:31:51 2016
+++ new/usr/src/lib/brand/lx/zone/lx_boot_zone_debian.ksh	Wed Oct  5 00:31:51 2016
@@ -0,0 +1,175 @@
+#!/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 2015 Joyent, Inc.
+#
+
+#
+# Customisation for Debian-based distributions.  Assumes to have been
+# sourced from lx_boot.
+#
+
+tmpfile=/tmp/lx-debian.$$
+
+
+# Check that the directories we're writing to aren't symlinks outside the zone
+safe_dir /etc
+safe_dir /etc/init.d
+safe_dir /etc/network
+safe_dir /etc/rc0.d
+safe_dir /etc/rc1.d
+safe_dir /etc/rc2.d
+safe_dir /etc/rc3.d
+safe_dir /etc/rc4.d
+safe_dir /etc/rc5.d
+safe_dir /etc/rc6.d
+safe_dir /etc/rcS.d
+safe_opt_dir /etc/selinux
+
+# Populate resolve.conf setup files IF we have resolvers information.
+zonecfg -z $ZONENAME info attr name=resolvers | grep -q resolvers
+if [[ $? == 0 ]]; then
+    zonecfg -z $ZONENAME info attr name=resolvers | awk '
+    BEGIN {
+	print("# AUTOMATIC ZONE CONFIG")
+    }
+    $1 == "value:" {
+	nres = split($2, resolvers, ",");
+	for (i = 1; i <= nres; i++) {
+		print("nameserver", resolvers[i]);
+	}
+    }
+    ' > $tmpfile
+    zonecfg -z $ZONENAME info attr name=dns-domain | awk '
+    $1 == "value:" {
+	dom = $2
+    }
+    END {
+	print("search", dom);
+    }
+    ' >> $tmpfile
+    fnm=$ZONEROOT/etc/resolv.conf
+    if [[ -f $fnm || -h $fnm ]]; then
+	mv -f $tmpfile $fnm
+    fi
+fi
+
+# Override network configuration
+zonecfg -z $ZONENAME info net | awk '
+BEGIN {
+	print("# AUTOMATIC ZONE CONFIG")
+	print("iface lo inet manual");
+}
+$1 == "physical:" {
+	print("iface", $2, "inet manual");
+}
+' > $tmpfile
+fnm=$ZONEROOT/etc/network/interfaces
+if [[ -f $fnm || -h $fnm ]]; then
+	mv -f $tmpfile $fnm
+fi
+
+#
+# The default /etc/inittab might spawn mingetty on each of the virtual consoles
+# as well as xdm on the X console.  Since we don't have virtual consoles nor
+# an X console, spawn a single mingetty on /dev/console instead.
+#
+# Don't bother changing the file if it looks like we already did.
+#
+fnm=$ZONEROOT/etc/inittab
+if ! egrep -s "Modified by lx brand" $fnm; then
+	sed 's/^[1-6]:/# Disabled by lx brand: &/' \
+	    $fnm > $tmpfile
+	echo "1:2345:respawn:/sbin/getty 38400 console" >> $tmpfile
+	echo "# Modified by lx brand" >> $tmpfile
+
+	if [[ ! -h $fnm ]]; then
+		mv -f $tmpfile $fnm
+		chmod 644 $fnm
+	fi
+fi
+
+# The Debian init uses a combination of traditional rc-style service
+# definitions and upstart-style definitions.
+
+#
+# The following rc-style scripts attempt to start services or otherwise
+# configure the system in ways incompatible with zones, so don't execute them
+# at boot time.
+#
+unsupported_rc_services="
+	checkfs.sh
+	checkroot.sh
+	hwclock.sh
+	kmod
+	mtab.sh
+	procps
+	udev
+	udev-mtab
+"
+
+for file in $unsupported_rc_services; do
+	rm -f $ZONEROOT/etc/init.d/$file
+
+	rc_files="$(echo $ZONEROOT/etc/rc[0-6S].d/[SK]+([0-9])$file)"
+
+	if [[ "$rc_files" != \
+	    "$ZONEROOT/etc/rc[0-6S].d/[SK]+([0-9])$file" ]]; then
+		for file in $rc_files; do
+			rm -f "$file"
+		done
+	fi
+done
+
+disable_svc()
+{
+	fnm=$ZONEROOT/etc/init/$1.override
+	[[ -h $fnm || -f $fnm ]] && return
+	echo "manual" > $fnm
+}
+
+
+#
+# Now customize upstart
+#
+
+RMSVCS="
+	network-interface-security
+	udev
+	udevmonitor
+	udevtrigger
+	udev-fallback-graphics
+	udev-finish
+"
+for f in $RMSVCS
+do
+	disable_svc $f
+done
+
+#
+# We need to setup for the /dev/shm mount. Unlike some other distros, Debian
+# can handle it as either /dev/shm or /run/shm. For simplicity we create an
+# fstab entry to force it into the /dev/shm style.
+#
+fnm=$ZONEROOT/etc/fstab
+entry=$(awk '{if ($2 == "/dev/shm") print $2}' $fnm)
+if [[ -z "$entry" && ! -h $fnm ]]; then
+    echo "swapfs    /dev/shm    tmpfs    defaults    0 0" >> $fnm
+fi
+
+#
+# upstart modifications are complete
+#
+rm -f $tmpfile
+
+# Hand control back to lx_boot
--- /dev/null	Wed Oct  5 00:31:51 2016
+++ new/usr/src/lib/brand/lx/zone/lx_boot_zone_redhat.ksh	Wed Oct  5 00:31:51 2016
@@ -0,0 +1,369 @@
+#!/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 2015 Joyent, Inc.
+# Copyright 2016 OmniTI Computer Consulting, Inc. All rights reserved.
+#
+
+#
+# Since CentOS, Red Hat Enterprise Linux, and Fedora all use approximately
+# the same source, this file should be good for all three.
+#
+# Currently, this file assumed a pre-systemd existence, so this should be
+# CentOS 6.x or earlier.  Testing has been done on CentOS 6.6.
+#
+
+tmpfile=/tmp/lx-redhat.$$
+
+
+# Before doing anything else, make sure some Centos-specific dirs are safe.
+# /etc/init.d is normally a symlink so we can't easily tell if it's safe so
+# check rc.d/init.d instead.
+
+safe_dir /etc/sysconfig
+safe_dir /etc/rc.d
+safe_dir /etc/rc.d/init.d
+safe_dir /etc/rc.d/rc0.d
+safe_dir /etc/rc.d/rc1.d
+safe_dir /etc/rc.d/rc2.d
+safe_dir /etc/rc.d/rc3.d
+safe_dir /etc/rc.d/rc4.d
+safe_dir /etc/rc.d/rc5.d
+safe_dir /etc/rc.d/rc6.d
+safe_opt_dir /etc/selinux
+
+# Generate the /etc/rc.d/init.d/network rc script
+cat > $tmpfile <<EOF
+#!/bin/bash
+# network       Bring up/down networking
+#
+### BEGIN INIT INFO
+# Provides: \$network
+# Short-Description: Bring up/down networking
+# Description: Bring up/down networking
+### END INIT INFO
+
+case "\$1" in
+    start)
+    [ "\$EUID" != "0" ] && exit 4
+
+EOF
+
+# Alter resolv.conf if we're pulling resolver information from zonecfg(1M).
+zonecfg -z $ZONENAME info attr name=resolvers | grep -q resolvers
+if [[ $? == 0 ]]; then
+    cat >> $tmpfile <<EOF
+    if [ ! -e /etc/resolv.conf ]; then
+        if [ -h /etc/resolv.conf ]; then
+            rm -f /etc/resolv.conf
+        fi
+        echo "# AUTOMATIC ZONE CONFIG" > /etc/resolv.conf
+EOF
+    zonecfg -z $ZONENAME info attr name=resolvers |
+    awk '
+    {
+        if ($1 == "value:") {
+            nres = split($2, resolvers, ",")
+        }
+    }
+    END {
+        for (i = 1; i <= nres; i++) {
+            printf("        echo \"nameserver %s\" >> %s\n", resolvers[i],
+                "/etc/resolv.conf")
+        }
+    }
+    ' >> $tmpfile
+    zonecfg -z $ZONENAME info attr name=dns-domain |
+    awk '
+    {
+        if ($1 == "value:") {
+            dom = $2
+        }
+    }
+    END {
+        printf("        echo \"search %s\" >> %s\n", dom, "/etc/resolv.conf")
+    }
+    ' >> $tmpfile
+    echo fi >> $tmpfile
+fi
+
+cat >> $tmpfile <<EOF
+    touch /var/lock/subsys/network
+    rc=0
+    ;;
+  stop)
+    [ "\$EUID" != "0" ] && exit 4
+
+    rm -f /var/lock/subsys/network
+    rc=0
+    ;;
+  status)
+    echo "Configured devices:"
+    echo "lo \$(cd /dev/net; ls)"
+    echo "Currently active devices:"
+    echo \$(/sbin/ip -o link show up | awk -F ": " '{ print \$2 }')
+    rc=0
+    ;;
+  restart|reload|force-reload)
+    cd "\$CWD"
+    \$0 stop
+    \$0 start
+    rc=\$?
+    ;;
+  *)
+    echo "Usage: \$0 {start|stop|status|restart|reload|force-reload}"
+    exit 2
+esac
+
+exit \$rc
+EOF
+fnm=$ZONEROOT/etc/rc.d/init.d/network
+if [[ -f $fnm || -h $fnm ]]; then
+	mv -f $tmpfile $fnm
+	chmod 755 $fnm
+fi
+
+# This is specific to a systemd-based image
+sysdir=$ZONEROOT/etc/systemd/system
+if [[ -d $ZONEROOT/etc && ! -h $ZONEROOT/etc && -d $ZONEROOT/etc/systemd &&
+    ! -h $ZONEROOT/etc/systemd && -d $sysdir && ! -h $sysdir ]]; then
+    # don't use NetworkManager
+    rm -f $sysdir/dbus-org.freedesktop.nm-dispatcher.service
+    rm -f $sysdir/multi-user.target.wants/NetworkManager.service
+    rm -f $sysdir/dbus-org.freedesktop.NetworkManager.service
+    # our network setup needs to run
+    fnm=$sysdir/multi-user.target.wants/network.service
+    if [[ ! -f $fnm ]]; then
+        ln -s /etc/rc.d/init.d/network \
+            $sysdir/multi-user.target.wants/network.service
+    fi
+fi
+
+#
+# The default /etc/inittab only sets the runlevel. Make sure it's runlevel 3
+# and not runlevel 5 (X11).
+# Don't bother changing the file if it looks like we already did.
+#
+fnm=$ZONEROOT/etc/inittab
+if ! egrep -s "Modified by lx brand" $fnm; then
+	sed 's/^id:5:initdefault:/id:3:initdefault: &/' \
+	    $fnm > $tmpfile
+	echo "# Modified by lx brand" >> $tmpfile
+
+	if [[ ! -h $fnm ]]; then
+		mv -f $tmpfile $fnm
+		chmod 644 $fnm
+	fi
+fi
+
+#
+# Ensure svcs depending on $network will start.
+#
+fnm=$ZONEROOT/etc/sysconfig/network
+if ! egrep -s "NETWORKING=yes" $fnm; then
+	cfghnm=$(zonecfg -z $ZONENAME info attr name=hostname | \
+	    awk '{if ($1 == "value:") print $2}')
+	if [[ -z "$cfghnm" ]]; then
+		cfghnm=$ZONENAME
+	fi
+	if [[ ! -h $fnm ]]; then
+		cat > $fnm <<- EOF
+		NETWORKING=yes
+		HOSTNAME=$cfghnm
+		EOF
+	fi
+fi
+
+#
+# SELinux must be disabled otherwise we won't get past init.
+#
+fnm=$ZONEROOT/etc/selinux/config
+if egrep -s "^SELINUX=enforcing|^SELINUX=permissive" $fnm; then
+	tmpfile=/tmp/selinux_config.$$
+
+	sed 's/^SELINUX=.*$/SELINUX=disabled/' $fnm > $tmpfile
+	if [[ ! -h $fnm ]]; then
+		mv -f $tmpfile $fnm
+		chmod 644 $fnm
+	fi
+fi
+
+#
+# /etc/rc.d/init.d/keytable tries to load a physical keyboard map, which won't
+# work in a zone. If we remove etc/sysconfig/keyboard, it won't try this at all.
+#
+fnm=$ZONEROOT/etc/sysconfig/keyboard
+if [[ ! -h $fnm ]]; then
+	rm -f $ZONEROOT/etc/sysconfig/keyboard
+fi
+
+# The Centos init uses a combination of traditional rc-style service
+# definitions and upstart-style definitions.
+
+#
+# The following rc-style scripts attempt to start services or otherwise
+# configure the system in ways incompatible with zones, so don't execute them
+# at boot time.
+#
+unsupported_rc_services="
+	acpid
+	auditd
+	gpm
+	hpoj
+	ip6tables
+	iptables
+	irda
+	irqbalance
+	iscsi
+	isdn
+	kdump
+	kudzu
+	mdmpd
+	mdmonitor
+	microcode_ctl
+	netdump
+	ntpd
+	ntpdate
+	pcmcia
+	psacct
+	quota_nld
+	random
+	rawdevices
+	smartd
+"
+
+for file in $unsupported_rc_services; do
+	rm -f $ZONEROOT/etc/rc.d/init.d/$file
+
+	rc_files="$(echo $ZONEROOT/etc/rc.d/rc[0-6].d/[SK]+([0-9])$file)"
+
+	if [[ "$rc_files" != \
+	    "$ZONEROOT/etc/rc.d/rc[0-6].d/[SK]+([0-9])$file" ]]; then
+		for file in $rc_files; do
+			rm -f "$file"
+		done
+	fi
+done
+
+disable_svc()
+{
+	# XXX - TBD does this work like on Ubuntu?
+	#
+	fnm=$ZONEROOT/etc/init/$1.override
+	[[ -h $fnm || -f $fnm ]] && return
+	echo "manual" > $fnm
+
+	# fnm=$ZONEROOT/etc/init/$1.conf
+	# rm -f $fnm
+}
+
+RMSVCS="control-alt-delete
+	ttyS0"
+
+#
+# Now customize upstart services
+#
+
+for f in $RMSVCS
+do
+	disable_svc $f
+done
+
+if [[ ! -f $ZONEROOT/etc/init/tty.override ]]; then
+	cat > $ZONEROOT/etc/init/tty.override <<- EOF
+	# tty - getty
+	#
+	# This service maintains a getty on the console.
+
+	stop on runlevel [S016]
+
+	respawn
+	instance console
+	exec /sbin/mingetty console
+	EOF
+fi
+
+if [[ ! -f $ZONEROOT/etc/init/start-ttys.override ]]; then
+	cat > $ZONEROOT/etc/init/start-ttys.override <<- EOF
+	# This service starts the configured number of gettys.
+	#
+
+	start on stopped rc RUNLEVEL=[2345]
+
+	task
+	script
+		initctl start tty
+	end script
+	EOF
+fi
+
+#
+# There is a lot of stuff in the standard halt and reboot scripts that we
+# have no business running in a zone.  Fortunately, the stuff we want to
+# skip is all in one contiguous chunk.
+#
+# Don't bother to modify the file if it looks like we already did.
+#
+fnm=$ZONEROOT/etc/rc.d/init.d/halt
+if ! egrep -s "Disabled by lx brand" $fnm; then
+	awk 'BEGIN {skip = ""}
+	    /^# Save mixer/ {skip = "# Disabled by lx brand: "}
+	    /halt.local/ {skip = ""}
+	    /./ {print skip $0}' $fnm > /tmp/halt.$$
+
+	if [[ $? -eq 0 && ! -h $fnm ]]; then
+		mv -f /tmp/halt.$$ $fnm
+		chmod 755 $fnm
+	fi
+fi
+
+#
+# Fix up /etc/rc.d/rc.sysinit:
+#
+# 1) /sbin/hwclock requires the iopl() system call, which BrandZ won't support.
+#    Since the hardware clock cannot be set from within a zone, we comment out
+#    the line.
+#
+# 2) Disable dmesg commands, since we don't implement klogctl
+#
+# 3) Disable initlog and the mount of /dev/pts
+#
+# 4) Don't touch /dev/tty* in order to start virtual terminals, as that won't
+#    work from within a zone.
+#
+# 5) Don't try to check the root filesystem (/) as there is no associated
+#    physical device, and any attempt to run fsck will fail.
+#
+fnm=$ZONEROOT/etc/rc.d/rc.sysinit
+tmpfile=/tmp/lx_rc.sysinit.$$
+
+sed 's@^/sbin/hwclock@# lx: &@
+    s@^/bin/dmesg -n@# lx: &@
+    s@^dmesg -s@# lx: &@
+    s@^initlog -c \"fsck@# lx: &@
+    s@^mount -n -o remount /dev/shm @mount -t tmpfs tmpfs /dev/shm @
+    s@^mount .* /dev/pts@# lx: &@
+    /^#remount \/dev\/shm/d' \
+    $fnm > $tmpfile
+
+if [[ ! -h $fnm ]]; then
+	mv -f $tmpfile $fnm
+	chmod 755 $fnm
+fi
+
+#
+# sysinit modifications are complete
+#
+rm -f $tmpfile
+
+# Hand control back to lx_boot
--- /dev/null	Wed Oct  5 00:31:51 2016
+++ new/usr/src/lib/brand/lx/zone/lx_boot_zone_ubuntu.ksh	Wed Oct  5 00:31:51 2016
@@ -0,0 +1,142 @@
+#!/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 Joyent, Inc.
+#
+
+#
+# Customisation for Ubuntu-based distributions.  Assumes to have been
+# sourced from lx_boot.
+#
+tmpfile=/tmp/lx-ubuntu.$$
+
+# Check that the directories we're writing to aren't symlinks outside the zone
+safe_dir /etc
+safe_dir /etc/init
+safe_dir /etc/resolvconf
+safe_dir /etc/resolvconf/resolv.conf.d
+safe_dir /etc/network
+safe_dir /etc/network/interfaces.d
+safe_dir /etc/network/interfaces.d/smartos
+
+# Populate resolve.conf setup files IF we have resolvers information.
+zonecfg -z $ZONENAME info attr name=resolvers | grep -q resolvers
+if [[ $? == 0 ]]; then
+    zonecfg -z $ZONENAME info attr name=resolvers | awk '
+    BEGIN {
+	print("# AUTOMATIC ZONE CONFIG")
+    }
+    $1 == "value:" {
+	nres = split($2, resolvers, ",");
+	for (i = 1; i <= nres; i++) {
+		print("nameserver", resolvers[i]);
+	}
+    }
+    ' > $tmpfile
+    zonecfg -z $ZONENAME info attr name=dns-domain | awk '
+    $1 == "value:" {
+	dom = $2
+    }
+    END {
+	print("search", dom);
+    }
+    ' >> $tmpfile
+    fnm=$ZONEROOT/etc/resolvconf/resolv.conf.d/tail
+    if [[ -f $fnm || -h $fnm || ! -e $fnm ]]; then
+	mv -f $tmpfile $fnm
+    fi
+fi
+
+# Override network configuration
+zonecfg -z $ZONENAME info net | awk '
+BEGIN {
+	print("# AUTOMATIC ZONE CONFIG")
+}
+$1 == "physical:" {
+	print("iface", $2, "inet manual");
+}
+' > $tmpfile
+fnm=$ZONEROOT/etc/network/interfaces.d/smartos
+if [[ -f $fnm || -h $fnm ]]; then
+	mv -f $tmpfile $fnm
+fi
+
+src_fnm=$ZONEROOT/etc/init/console.conf
+tgt_fnm=$ZONEROOT/etc/init/console.override
+if [[ -f $src_fnm && ! -f $tgt_fnm && ! -h $tgt_fnm ]] then
+	sed -e 's/lxc/smartos/' $src_fnm > /tmp/console.conf.$$
+	mv /tmp/console.conf.$$ $tgt_fnm
+fi
+
+fnm=$ZONEROOT/etc/init/container-detect.override
+if [[ ! -f $fnm && ! -h $fnm ]] then
+	cat <<'DONE' > $fnm
+description "Track if upstart is running in a container"
+
+start on mounted MOUNTPOINT=/run
+
+env container
+env LIBVIRT_LXC_UUID
+
+emits container
+
+pre-start script
+    container=smartos
+    echo "$container" > /run/container_type || true
+    initctl emit --no-wait container CONTAINER=$container
+    exit 0
+end script
+DONE
+fi
+
+# XXX need to add real mounting into this svc definition
+
+fnm=$ZONEROOT/etc/init/mountall.override
+if [[ ! -h $fnm ]] then
+	cat <<DONE > $fnm
+description	"Mount filesystems on boot"
+
+start on startup
+
+task
+
+emits virtual-filesystems
+emits local-filesystems
+emits remote-filesystems
+emits all-swaps
+emits filesystem
+emits mounted
+
+script
+    echo "/dev/zfsds0 / zfs rw 0 0" > /etc/mtab
+    echo "proc /proc proc rw,noexec,nosuid,nodev 0 0" >> /etc/mtab
+
+    /sbin/initctl emit --no-wait virtual-filesystems
+    /bin/mount -t tmpfs tmpfs /dev/shm || true
+    /bin/mount -t tmpfs tmpfs /run || true
+    /bin/mkdir -p /run/lock || true
+    /bin/ln -s /dev/shm /run/shm || true
+    /sbin/initctl emit --no-wait mounted MOUNTPOINT=/run TYPE=tmpfs
+    /sbin/initctl emit --no-wait local-filesystems
+    /sbin/initctl emit --no-wait all-swaps
+    /sbin/initctl emit --no-wait filesystem
+end script
+DONE
+fi
+
+#
+# upstart modifications are complete
+#
+rm -f $tmpfile
+
+# Hand control back to lx_boot
--- /dev/null	Wed Oct  5 00:31:51 2016
+++ new/usr/src/lib/brand/lx/zone/lx_install.ksh	Wed Oct  5 00:31:51 2016
@@ -0,0 +1,137 @@
+#!/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 Joyent, Inc.  All rights reserved.
+# Copyright 2016 OmniTI Computer Consulting, Inc.  All rights reserved.
+#
+
+PATH=/bin:/usr/bin:/usr/sbin
+export PATH
+
+. /usr/lib/brand/shared/common.ksh
+
+ZFS_SEED=""
+TAR_SEED=""
+
+bad_usage() {
+	echo "LX zone install bad option"
+	echo "Available options are:"
+	echo "	-s <absolute-pathname>	Path to ZFS send stream or gzip thereof"
+	echo "	-t <absolute-pathname>	Path to tar archive or gzip thereof"
+	exit $ZONE_SUBPROC_USAGE
+}
+
+while getopts "R:s:t:z:" opt
+do
+	case "$opt" in
+		R)	ZONEPATH="$OPTARG";;
+		z)	ZONENAME="$OPTARG";;
+		s)	ZFS_SEED="$OPTARG";;
+		t)	TAR_SEED="$OPTARG";;
+		*)	bad_usage ;;
+	esac
+done
+shift OPTIND-1
+
+if [[ $ZFS_SEED == "" && $TAR_SEED == "" ]]; then
+    echo "The -s <absolute-pathname> argument or the -t <absolute-pathname>"
+    echo "argument is required for LX installation."
+    bad_usage
+fi
+
+if [[ $ZFS_SEED != "" && $TAR_SEED != "" ]]; then
+    echo "You must only specify one of -s or -t for LX installation."
+    bad_usage
+fi
+
+# Set the ZONEPATH_DS variable so we know the zone's dataset.
+get_zonepath_ds $ZONEPATH
+
+# Do something based on whatever ZFS_SEED is.
+
+if [[ -f $TAR_SEED ]]; then
+    type=`file -b $TAR_SEED | awk '{print $1}'`
+    if [[ $type == "gzip" ]]; then
+	args="-xzf"
+    else
+	args="-xf"
+    fi
+    cd $ZONEPATH
+    # Be very precise about permissions and ownership.
+    mkdir -m 0755 dev
+    chgrp sys dev
+    mkdir -m 0755 root
+    chgrp root sys
+    cd root
+    gtar $args $TAR_SEED
+    exit 0
+elif [[ ! -f $ZFS_SEED ]]; then
+    # Try and eat a snapshot or a filesystem.
+    outstr=`zfs list -Ht filesystem $ZFS_SEED 2>/dev/null | awk '{print $1}'`
+    if [[ $outstr == $ZFS_SEED ]]; then
+	# We have a zfs filesystem name.
+	# Snapshot it using today's date/time
+	snapname=`date -u "+%Y-%m-%d:%H:%M:%S"`
+	ZFS_SEED=$ZFS_SEED@$snapname
+	zfs snapshot $ZFS_SEED
+	if [[ $? != 0 ]]; then
+	    echo "ZFS snapshot ($ZFS_SEED) command failed ($?)."
+	    exit $ZONE_SUBPROC_FATAL
+	fi
+	# else continue on with the new snapshot...
+    fi
+
+    outstr=`zfs list -Ht snapshot $ZFS_SEED 2>/dev/null | awk '{print $1}'`
+    if [[ $outstr == $ZFS_SEED ]]; then
+	# Hmmm, we found a snapshot name!
+	echo "Cloning from snapshot $ZFS_SEED"
+	# zoneadm already created $ZONEPATH_DS, destroy it before we clone.
+	zfs destroy $ZONEPATH_DS
+	zfs clone $ZFS_SEED $ZONEPATH_DS
+	if [[ $? != 0 ]]; then
+	    echo "ZFS clone ($ZFS_SEED to $ZONEPATH_DS) failed ($?)."
+	    exit $ZONE_SUBPROC_FAIL
+	fi
+	# zfs promote $ZONEPATH_DS
+	# if [[ $? != 0 ]]; then
+	#    echo "ZFS promote ($ZONEPATH_DS) failed ($?)."
+	#    exit $ZONE_SUBPROC_FAIL
+	# fi
+    else 
+	echo "Seed file $ZFS_SEED $TAR_SEED not found."
+	bad_usage
+    fi
+else
+    type=`file -b $ZFS_SEED | awk '{print $1}'`
+
+    # For now, we are dependent on the output of file(1).
+    # I'm being cheesy in checking the first word of file(1)'s output.
+    if [[ $type == "ZFS" ]]; then
+	zfs recv -F $ZONEPATH_DS < $ZFS_SEED
+    elif [[ $type == "gzip" ]]; then
+	gunzip -c $ZFS_SEED | zfs recv -F $ZONEPATH_DS
+    else
+	echo "Seed file $ZFS_SEED not a ZFS receive (or compressed) one."
+	bad_usage
+    fi
+
+    if [[ $? != 0 ]]; then
+	echo "ZFS receive command failed ($?)."
+	exit $ZONE_SUBPROC_FATAL
+    fi
+fi
+
+# One Joyent-ism we need to clean up.
+rmdir $ZONEPATH/cores
+# And one we should probably adopt.
+zfs set devices=off $ZONEPATH_DS
+
+exit 0
--- /dev/null	Wed Oct  5 00:31:51 2016
+++ new/usr/src/lib/brand/lx/zone/platform.xml	Wed Oct  5 00:31:51 2016
@@ -0,0 +1,161 @@
+<?xml version="1.0"?>
+
+<!--
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License (the "License").
+ You may not use this file except in compliance with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ Use is subject to license terms.
+ Copyright 2016 Joyent, Inc.
+
+ DO NOT EDIT THIS FILE.
+-->
+
+<!DOCTYPE platform PUBLIC "-//Sun Microsystems Inc//Zones Platform//EN"
+    "file:///usr/share/lib/xml/dtd/zone_platform.dtd.1">
+
+<platform name="lx" allow-exclusive-ip="true">
+	<!-- Global filesystems to mount when booting the zone -->
+	<global_mount special="/dev" directory="/native/dev" type="dev"
+	    opt="attrdir=%R/dev" />
+
+	<!--
+	    Local filesystems to mount when booting the zone. The /native/sbin
+	    entry is needed so that we can replace ifconfig with its native
+	    counterpart (this is required for exclusive-stack zones to work).
+	    We also need dladm from /native/sbin.
+	-->
+	<global_mount special="/lib" directory="/native/lib"
+	    opt="ro" type="lofs" />
+	<global_mount special="/usr" directory="/native/usr"
+	    opt="ro" type="lofs" />
+	<global_mount special="/usr/lib/brand/lx/etc_default_nfs"
+	    directory="/native/etc/default/nfs" type="lofs" opt="ro" />
+	<global_mount special="/etc/default/dhcpagent"
+	    directory="/native/etc/default/dhcpagent" type="lofs" opt="ro" />
+	<global_mount special="/etc/netconfig"
+	    directory="/native/etc/netconfig" type="lofs" opt="ro" />
+	<global_mount special="/etc/nfssec.conf"
+	    directory="/native/etc/nfssec.conf" type="lofs" opt="ro" />
+	<global_mount special="/sbin"
+	    directory="/native/sbin" opt="ro,nodevices" type="lofs" />
+	<global_mount special="/usr/lib/brand/lx/ld" directory="/var/ld"
+	    opt="ro" type="lofs" />
+	<global_mount special="/etc/zones/%z.xml"
+	    directory="/native/etc/zones/%z.xml" opt="ro" type="lofs" />
+
+	<!-- Local filesystems to mount when booting the zone -->
+	<mount special="/native/dev" directory="/dev" type="lx_devfs" />
+	<mount special="proc" directory="/native/proc" type="proc" />
+	<mount special="swap" directory="/native/etc/svc/volatile"
+	    type="tmpfs" />
+	<mount special="swap" directory="/native/tmp" type="tmpfs" />
+	<mount special="objfs" directory="/system/object" type="objfs" />
+	<mount special="ctfs" directory="/system/contract" type="ctfs" />
+	<mount special="mnttab" directory="/etc/mnttab" type="mntfs" />
+	<mount special="lxproc" directory="/proc" type="lx_proc" />
+
+	<!-- Devices to create under /dev -->
+	<device match="arp" />
+	<device match="dtrace/*" />
+	<device match="dtrace/provider/*" />
+	<device match="eventfd" />
+	<device match="inotify" />
+	<device match="ipnet" />
+	<device match="kstat" />
+	<device match="lo0" />
+	<device match="null" />
+	<device match="poll" />
+	<device match="pts/*" />
+	<device match="random" />
+	<device match="signalfd" />
+	<device match="tcp" />
+	<device match="tcp6" />
+	<device match="timerfd" />
+	<device match="tty" />
+	<device match="udp" />
+	<device match="udp6" />
+	<device match="urandom" />
+	<device match="zero" />
+	<device match="zfs" />
+	<device match="zvol/dsk/%P/%z/*" />
+	<device match="zvol/rdsk/%P/%z/*" />
+
+	<!-- Devices to create in exclusive IP zone only -->
+	<device match="dld" ip-type="exclusive" />
+	<device match="icmp" ip-type="exclusive" />
+	<device match="icmp6" ip-type="exclusive" />
+	<device match="ip" ip-type="exclusive" />
+	<device match="ip6" ip-type="exclusive" />
+	<device match="ipauth" ip-type="exclusive" />
+	<device match="ipf" ip-type="exclusive" />
+	<device match="ipl" ip-type="exclusive" />
+	<device match="iplookup" ip-type="exclusive" />
+	<device match="ipmpstub" ip-type="exclusive" />
+	<device match="ipnat" ip-type="exclusive" />
+	<device match="ipscan" ip-type="exclusive" />
+	<device match="ipsecah" ip-type="exclusive" />
+	<device match="ipsecesp" ip-type="exclusive" />
+	<device match="ipstate" ip-type="exclusive" />
+	<device match="ipsync" ip-type="exclusive" />
+	<device match="keysock" ip-type="exclusive" />
+	<device match="net/*" ip-type="exclusive" />
+	<device match="rawip" ip-type="exclusive" />
+	<device match="rawip6" ip-type="exclusive" />
+	<device match="rts" ip-type="exclusive" />
+	<device match="sad/admin" ip-type="exclusive" />
+	<device match="sctp" ip-type="exclusive" />
+	<device match="sctp6" ip-type="exclusive" />
+	<device match="spdsock" ip-type="exclusive" />
+	<device match="sppp" ip-type="exclusive" />
+	<device match="sppptun" ip-type="exclusive" />
+	<device match="vni" ip-type="exclusive" />
+
+	<!-- Renamed devices to create under /dev -->
+	<device match="brand/lx/autofs" name="autofs" />
+	<device match="brand/lx/ptmx" name="ptmx" />
+	<device match="zcons/%z/zoneconsole" name="console" />
+	<device match="zfd/%z/slave/0" name="zfd/0" />
+	<device match="zfd/%z/slave/1" name="zfd/1" />
+	<device match="zfd/%z/slave/2" name="zfd/2" />
+	<device match="zfd/%z/slave/3" name="zfd/3" />
+	<device match="zfd/%z/slave/4" name="zfd/4" />
+
+	<!-- Audio devices to create under /dev -->
+	<device match="brand/lx/dsp" name="dsp" />
+	<device match="brand/lx/mixer" name="mixer" />
+
+	<!-- Symlinks to create under /dev -->
+	<symlink source="fd" target="../proc/self/fd" />
+	<symlink source="stderr" target="../proc/self/fd/2" />
+	<symlink source="stdin" target="../proc/self/fd/0" />
+	<symlink source="stdout" target="../proc/self/fd/1" />
+	<symlink source="systty" target="console" />
+	<symlink source="kmsg" target="console" />
+	<symlink source="conslog" target="console" />
+
+	<!-- Create a mount point for the /dev/initctl fifo -->
+	<device match="null" name="initctl" />
+	<!-- Create a mount point for /dev/shm tmpfs (see shm_overview(7)) -->
+	<!-- We need to force a dir for the Linux mount to work ok -->
+	<symlink source="shm/loop" target="." />
+	<!-- Create a dummy /dev/xconsole -->
+	<device match="null" name="xconsole" />
+
+</platform>
--- old/usr/src/lib/brand/shared/zone/common.ksh	Wed Oct  5 00:31:51 2016
+++ new/usr/src/lib/brand/shared/zone/common.ksh	Wed Oct  5 00:31:51 2016
@@ -19,6 +19,7 @@
 # CDDL HEADER END
 #
 # Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2014, Joyent, Inc. All rights reserved.
 #
 
 #
@@ -96,9 +97,27 @@
 safe_dir()
 {
 	typeset dir="$1"
+	typeset pwd_dir=""
 
-	if [[ -h $ZONEROOT/$dir || ! -d $ZONEROOT/$dir ]]; then
-		fatal "$e_baddir" "$dir"
+	if [[ -d $ZONEROOT/$dir ]]; then
+		if [[ -h $ZONEROOT/$dir ]]; then
+			#
+			# When dir is a symlink to a directory, we 'cd' to that
+			# directory to ensure that's under $ZONEROOT. We use pwd
+			# from /usr/bin instead of built-in because they give
+			# different results.
+			#
+			pwd_dir=$(cd $ZONEROOT/$dir && /usr/bin/pwd)
+			if [[ $pwd_dir =~ "^$ZONEROOT" ]]; then
+				return;
+			else
+				fatal \
+				    "$e_baddir: symlink out of zoneroot" "$dir"
+			fi
+		else
+			# it's a dir and not a symlink, so that's ok.
+			return
+		fi
 	fi
 }
 
@@ -109,9 +128,7 @@
 
 	[[ ! -e $ZONEROOT/$dir ]] && return
 
-	if [[ -h $ZONEROOT/$dir || ! -d $ZONEROOT/$dir ]]; then
-		fatal "$e_baddir" "$dir"
-	fi
+	safe_dir $dir
 }
 
 # Only make a copy if we haven't already done so.
@@ -187,7 +204,7 @@
 	fi
 
 	cat <<-END >$filename || exit 1
-	#!/bin/sh -p
+	#!/bin/sh
 	#
 	# Solaris Brand Replacement
 	#
--- /dev/null	Wed Oct  5 00:31:51 2016
+++ new/usr/src/lib/commpage/Makefile.shared.com	Wed Oct  5 00:31:51 2016
@@ -0,0 +1,33 @@
+#
+# 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 Joyent, Inc.
+#
+
+#
+# This Makefile is shared between both libc and other consumers
+#
+
+COMMPAGE_OBJS = \
+	cp_subr.o \
+	cp_main.o
+
+COMMPAGE_OFFSETS_SRC = $(SRC)/lib/commpage/common/offsets.in
+COMMPAGE_OFFSETS_H = cp_offsets.h
+
+CLEANFILES += $(COMMPAGE_OFFSETS_H)
+
+pics/cp_main.o := CPPFLAGS += -I$(SRC)/uts/i86pc
+pics/cp_subr.o := ASFLAGS += -I$(SRC)/uts/i86pc -I./
+$(COMMPAGE_OFFSETS_H) := CPPFLAGS += -I$(SRC)/uts/i86pc
+
+COMMPAGE_CPPFLAGS = -I$(SRC)/lib/commpage/common
--- /dev/null	Wed Oct  5 00:31:51 2016
+++ new/usr/src/lib/commpage/Makefile.shared.targ	Wed Oct  5 00:31:51 2016
@@ -0,0 +1,29 @@
+#
+# 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 Joyent, Inc.
+#
+
+#
+# This Makefile is shared between both libc and other consumers
+#
+
+pics/%.o: $(SRC)/lib/commpage/common/%.c
+	$(COMPILE.c) -o $@ $<
+	$(POST_PROCESS_O)
+
+pics/%.o: $(SRC)/lib/commpage/$(TARGET_ARCH)/%.s $(COMMPAGE_OFFSETS_H)
+	$(COMPILE.s) -o $@ $<
+	$(POST_PROCESS_O)
+
+$(COMMPAGE_OFFSETS_H): $(COMMPAGE_OFFSETS_SRC)
+	$(OFFSETS_CREATE) <$(COMMPAGE_OFFSETS_SRC) >$@
--- /dev/null	Wed Oct  5 00:31:51 2016
+++ new/usr/src/lib/commpage/amd64/cp_subr.s	Wed Oct  5 00:31:51 2016
@@ -0,0 +1,406 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#include <sys/asm_linkage.h>
+#include <sys/segments.h>
+#include <sys/time_impl.h>
+#include <sys/tsc.h>
+#include <cp_offsets.h>
+
+#define	GETCPU_GDT_OFFSET	SEL_GDT(GDT_CPUID, SEL_UPL)
+
+	.file	"cp_subr.s"
+
+/*
+ * These are cloned from TSC and time related code in the kernel.  They should
+ * be kept in sync in the case that the source values are changed.
+ * See: uts/i86pc/os/timestamp.c
+ */
+#define	NSEC_SHIFT	5
+#define	ADJ_SHIFT	4
+#define	NANOSEC		0x3b9aca00
+
+/*
+ * hrtime_t
+ * __cp_tsc_read(comm_page_t *cp)
+ *
+ * Stack usage: 0 bytes
+ */
+	ENTRY_NP(__cp_tsc_read)
+	movl	CP_TSC_TYPE(%rdi), %esi
+	movl	CP_TSC_NCPU(%rdi), %r8d
+	leaq	CP_TSC_SYNC_TICK_DELTA(%rdi), %r9
+
+	cmpl	$TSC_TSCP, %esi
+	jne	2f
+	rdtscp
+	/*
+	 * When the TSC is read, the low 32 bits are placed in %eax while the
+	 * high 32 bits are placed in %edx.  They are shifted and ORed together
+	 * to obtain the full 64-bit value.
+	 */
+	shlq	$0x20, %rdx
+	orq	%rdx, %rax
+	cmpl	$0, %esi
+	jne	1f
+	ret
+1:
+	/*
+	 * When cp_tsc_ncpu is non-zero, it indicates the length of the
+	 * cp_tsc_sync_tick_delta array, which contains per-CPU offsets for the
+	 * TSC.  The CPU ID furnished by the IA32_TSC_AUX register via rdtscp
+	 * is used to look up an offset value in that array and apply it to the
+	 * TSC reading.
+	 */
+	movq	(%r9, %rcx, 8), %rdx
+	addq	%rdx, %rax
+	ret
+
+2:
+	/*
+	 * Without rdtscp, there is no way to perform a TSC reading and
+	 * simultaneously query the current CPU.  If tsc_ncpu indicates that
+	 * per-CPU TSC offsets are present, the ID of the current CPU is
+	 * queried before performing a TSC reading.  It will be later compared
+	 * to a second CPU ID lookup to catch CPU migrations.
+	 *
+	 * This method will catch all but the most pathological scheduling.
+	 */
+	cmpl	$0, %r8d
+	je	3f
+	movl	$GETCPU_GDT_OFFSET, %edx
+	lsl	%dx, %edx
+
+3:
+	/* Save the most recently queried CPU ID for later comparison. */
+	movl	%edx, %r10d
+
+	cmpl	$TSC_RDTSC_MFENCE, %esi
+	jne	4f
+	mfence
+	rdtsc
+	jmp	7f
+
+4:
+	cmpl	$TSC_RDTSC_LFENCE, %esi
+	jne	5f
+	lfence
+	rdtsc
+	jmp	7f
+
+5:
+	cmpl	$TSC_RDTSC_CPUID, %esi
+	jne	6f
+	/*
+	 * Since the amd64 ABI dictates that %rbx is callee-saved, it must be
+	 * preserved here.  Its contents will be overwritten when cpuid is used
+	 * as a serializing instruction.
+	 */
+	movq	%rbx, %r11
+	xorl	%eax, %eax
+	cpuid
+	rdtsc
+	movq	%r11, %rbx
+	jmp	7f
+
+6:
+	/*
+	 * Other protections should have prevented this function from being
+	 * called in the first place.  The only sane action is to abort.
+	 * The easiest means in this context is via SIGILL.
+	 */
+	ud2a
+
+7:
+	shlq	$0x20, %rdx
+	orq	%rdx, %rax
+
+	/*
+	 * Query the current CPU again if a per-CPU offset is being applied to
+	 * the TSC reading.  If the result differs from the earlier reading,
+	 * then a migration has occured and the TSC must be read again.
+	 */
+	cmpl	$0, %r8d
+	je	8f
+	movl	$GETCPU_GDT_OFFSET, %edx
+	lsl	%dx, %edx
+	cmpl	%edx, %r10d
+	jne	3b
+	movq	(%r9, %rdx, 8), %rdx
+	addq	%rdx, %rax
+8:
+	ret
+	SET_SIZE(__cp_tsc_read)
+
+
+/*
+ * uint_t
+ * __cp_getcpu(comm_page_t *)
+ *
+ * Stack usage: 0 bytes
+ */
+	ENTRY_NP(__cp_getcpu)
+	movl	CP_TSC_TYPE(%rdi), %edi
+	/*
+	 * If RDTSCP is available, it is a quick way to grab the cpu_id which
+	 * is stored in the TSC_AUX MSR by the kernel.
+	 */
+	cmpl	$TSC_TSCP, %edi
+	jne	1f
+	rdtscp
+	movl	%ecx, %eax
+	ret
+1:
+	mov	$GETCPU_GDT_OFFSET, %eax
+	lsl	%ax, %eax
+	ret
+	SET_SIZE(__cp_getcpu)
+
+/*
+ * hrtime_t
+ * __cp_gethrtime(comm_page_t *cp)
+ *
+ * Stack usage: 0x20 local + 0x8 call = 0x28 bytes
+ *
+ * %rsp+0x00 - hrtime_t tsc_last
+ * %rsp+0x08 - hrtime_t hrtime_base
+ * %rsp+0x10 - commpage_t *cp
+ * %rsp+0x18 - int hres_lock
+ */
+	ENTRY_NP(__cp_gethrtime)
+	subq	$0x20, %rsp
+	movq	%rdi, 0x10(%rsp)
+1:
+	movl	CP_HRES_LOCK(%rdi), %r9d
+	movl	%r9d, 0x18(%rsp)
+
+	movq	CP_TSC_LAST(%rdi), %rax
+	movq	CP_TSC_HRTIME_BASE(%rdi), %rdx
+	movq	%rax, (%rsp)
+	movq	%rdx, 0x8(%rsp)
+
+	call	__cp_tsc_read
+	movq	0x10(%rsp), %rdi
+
+	movl	0x18(%rsp), %r9d
+	movl	CP_HRES_LOCK(%rdi), %edx
+	andl	$0xfffffffe, %r9d
+	cmpl	%r9d, %edx
+	jne	1b
+
+	/*
+	 * The in-kernel logic for calculating hrtime performs several checks
+	 * to protect against edge cases.  That logic is summarized as:
+	 * if (tsc >= tsc_last) {
+	 *         delta -= tsc_last;
+	 * } else if (tsc >= tsc_last - 2*tsc_max_delta) {
+	 *         delta = 0;
+	 * } else {
+	 *         delta = MIN(tsc, tsc_resume_cap);
+	 * }
+	 *
+	 * The below implementation achieves the same result, although it is
+	 * structured for speed and optimized for the fast path:
+	 *
+	 * delta = tsc - tsc_last;
+	 * if (delta < 0) {
+	 *         delta += (tsc_max_delta << 1);
+	 *         if (delta >= 0) {
+	 *                 delta = 0;
+	 *         } else {
+	 *                 delta = MIN(tsc, tsc_resume_cap);
+	 *         }
+	 * }
+	 */
+	movq	(%rsp), %rdx
+	subq	%rdx, %rax		/* delta = tsc - tsc_last */
+	jbe	3f			/* if (delta < 0) */
+
+2:
+	/*
+	 * Optimized TSC_CONVERT_AND_ADD:
+	 * hrtime_base += (tsc_delta * nsec_scale) >> (32 - NSEC_SHIFT)
+	 *
+	 * Since the multiply and shift are done in 128-bit, there is no need
+	 * to worry about overflow.
+	 */
+	movl	CP_NSEC_SCALE(%rdi), %ecx
+	mulq	%rcx
+	shrdq	$_CONST(32 - NSEC_SHIFT), %rdx, %rax
+	movq	0x8(%rsp), %r8
+	addq	%r8, %rax
+
+	addq	$0x20, %rsp
+	ret
+
+3:
+	movq	%rax, %r9		/* save (tsc - tsc_last) in r9 */
+	movl	CP_TSC_MAX_DELTA(%rdi), %ecx
+	sall	$1, %ecx
+	addq	%rcx, %rax		/* delta += (tsc_max_delta << 1) */
+	jae	4f			/* delta < 0 */
+	xorq	%rax, %rax
+	jmp	2b
+
+4:
+	/*
+	 * Repopulate %rax with the TSC reading by adding tsc_last to %r9
+	 * (which holds tsc - tsc_last)
+	 */
+	movq	(%rsp), %rax
+	addq	%r9, %rax
+
+	/* delta = MIN(tsc, resume_cap) */
+	movq	CP_TSC_RESUME_CAP(%rdi), %rcx
+	cmpq	%rcx, %rax
+	jbe	5f
+	movq	%rcx, %rax
+5:
+	jmp	2b
+
+	SET_SIZE(__cp_gethrtime)
+
+/*
+ * int
+ * __cp_clock_gettime_monotonic(comm_page_t *cp, timespec_t *tsp)
+ *
+ * Stack usage: 0x8 local + 0x8 call + 0x28 called func. = 0x38 bytes
+ *
+ * %rsp+0x00 - timespec_t *tsp
+ */
+	ENTRY_NP(__cp_clock_gettime_monotonic)
+	subq	$0x8, %rsp
+	movq	%rsi, (%rsp)
+
+	call	__cp_gethrtime
+
+	/*
+	 * Convert from hrtime_t (int64_t in nanoseconds) to timespec_t.
+	 * This uses the same approach as hrt2ts, although it has been updated
+	 * to utilize 64-bit math.
+	 * 1 / 1,000,000,000 =
+	 * 1000100101110000010111110100000100110110101101001010110110011B-26
+	 * = 0x112e0be826d694b3 * 2^-26
+	 *
+	 * secs = (nsecs * 0x112e0be826d694b3) >> 26
+	 *
+	 * In order to account for the 2s-compliment of negative inputs, a
+	 * final operation completes the process:
+	 *
+	 * secs -= (nsecs >> 63)
+	 */
+	movq	%rax, %r11
+	movq	$0x112e0be826d694b3, %rdx
+	imulq	%rdx
+	sarq	$0x1a, %rdx
+	movq	%r11, %rax
+	sarq	$0x3f, %rax
+	subq	%rax, %rdx
+	movq	(%rsp), %rsi
+	movq	%rdx, (%rsi)
+	/*
+	 * Populating tv_nsec is easier:
+	 * tv_nsec = nsecs - (secs * NANOSEC)
+	 */
+	imulq	$NANOSEC, %rdx, %rdx
+	subq	%rdx, %r11
+	movq	%r11, 0x8(%rsi)
+
+	xorl	%eax, %eax
+	addq	$0x8, %rsp
+	ret
+	SET_SIZE(__cp_clock_gettime_monotonic)
+
+/*
+ * int
+ * __cp_clock_gettime_realtime(comm_page_t *cp, timespec_t *tsp)
+ *
+ * Stack usage: 0x18 local + 0x8 call + 0x28 called func. = 0x48 bytes
+ *
+ * %rsp+0x00 - commpage_t *cp
+ * %rsp+0x08 - timespec_t *tsp
+ * %rsp+0x10 - int hres_lock
+ */
+	ENTRY_NP(__cp_clock_gettime_realtime)
+	subq	$0x18, %rsp
+	movq	%rdi, (%rsp)
+	movq	%rsi, 0x8(%rsp)
+
+1:
+	movl	CP_HRES_LOCK(%rdi), %eax
+	movl	%eax, 0x10(%rsp)
+
+	call	__cp_gethrtime
+	movq	(%rsp), %rdi
+	movq	CP_HRES_LAST_TICK(%rdi), %rdx
+	subq	%rdx, %rax			/* nslt = hrtime - last_tick */
+	jb	1b
+	movq	CP_HRESTIME(%rdi), %r9
+	movq	_CONST(CP_HRESTIME + CP_HRESTIME_INCR)(%rdi), %r10
+	movl	CP_HRESTIME_ADJ(%rdi), %r11d
+
+	addq	%rax, %r10			/* now.tv_nsec += nslt */
+
+	cmpl	$0, %r11d
+	jb	4f				/* hres_adj > 0 */
+	ja	6f				/* hres_adj < 0 */
+
+2:
+	cmpq	$NANOSEC, %r10
+	jae	8f				/* tv_nsec >= NANOSEC */
+
+3:
+	movl	0x10(%rsp), %eax
+	movl	CP_HRES_LOCK(%rdi), %edx
+	andl	$0xfffffffe, %edx
+	cmpl	%eax, %edx
+	jne	1b
+
+	movq	0x8(%rsp), %rsi
+	movq	%r9, (%rsi)
+	movq	%r10, 0x8(%rsi)
+
+	xorl	%eax, %eax
+	addq	$0x18, %rsp
+	ret
+
+
+4:						/* hres_adj > 0 */
+	sarq	$ADJ_SHIFT, %rax
+	cmpl	%r11d, %eax
+	jbe	5f
+	movl	%r11d, %eax
+5:
+	addq	%rax, %r10
+	jmp	2b
+
+6:						/* hres_adj < 0 */
+	sarq	$ADJ_SHIFT, %rax
+	negl	%r11d
+	cmpl	%r11d, %eax
+	jbe	7f
+	movl	%r11d, %eax
+7:
+	subq	%rax, %r10
+	jmp	2b
+
+8:						/* tv_nsec >= NANOSEC */
+	subq	$NANOSEC, %r10
+	incq	%r9
+	cmpq	$NANOSEC, %r10
+	jae	8b
+	jmp	3b
+
+	SET_SIZE(__cp_clock_gettime_realtime)
--- /dev/null	Wed Oct  5 00:31:51 2016
+++ new/usr/src/lib/commpage/common/cp_defs.h	Wed Oct  5 00:31:51 2016
@@ -0,0 +1,37 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#ifndef	_CP_DEFS_H_
+#define	_CP_DEFS_H_
+
+#include <sys/types.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+struct comm_page;
+typedef struct comm_page comm_page_t;
+
+extern uint_t __cp_can_gettime(comm_page_t *);
+extern hrtime_t __cp_gethrtime(comm_page_t *);
+extern int __cp_clock_gettime_realtime(comm_page_t *, timespec_t *);
+extern int __cp_clock_gettime_monotonic(comm_page_t *, timespec_t *);
+extern uint_t __cp_getcpu(comm_page_t *cp);
+
+#ifdef	__cplusplus
+}
+#endif
+#endif /* _CP_DEFS_H_ */
--- /dev/null	Wed Oct  5 00:31:52 2016
+++ new/usr/src/lib/commpage/common/cp_main.c	Wed Oct  5 00:31:52 2016
@@ -0,0 +1,184 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#include <sys/comm_page.h>
+#include <sys/tsc.h>
+
+
+/*
+ * Interrogate if querying the clock via the comm page is possible.
+ */
+int
+__cp_can_gettime(comm_page_t *cp)
+{
+	switch (cp->cp_tsc_type) {
+	case TSC_TSCP:
+	case TSC_RDTSC_MFENCE:
+	case TSC_RDTSC_LFENCE:
+	case TSC_RDTSC_CPUID:
+		return (1);
+	default:
+		break;
+	}
+	return (0);
+}
+
+#ifdef __amd64
+
+/*
+ * The functions used for calculating time (both monotonic and wall-clock) are
+ * implemented in assembly on amd64.  This is primarily for stack conservation.
+ */
+
+#else /* i386 below */
+
+/*
+ * ASM-defined functions.
+ */
+extern hrtime_t __cp_tsc_read(comm_page_t *);
+
+/*
+ * These are cloned from TSC and time related code in the kernel.  The should
+ * be kept in sync in the case that the source values are changed.
+ */
+#define	NSEC_SHIFT	5
+#define	ADJ_SHIFT	4
+#define	NANOSEC		1000000000LL
+
+#define	TSC_CONVERT_AND_ADD(tsc, hrt, scale) do {		\
+	uint32_t *_l = (uint32_t *)&(tsc); 			\
+	uint64_t sc = (uint32_t)(scale);			\
+	(hrt) += (uint64_t)(_l[1] * sc) << NSEC_SHIFT;		\
+	(hrt) += (uint64_t)(_l[0] * sc) >> (32 - NSEC_SHIFT);	\
+} while (0)
+
+/*
+ * Userspace version of tsc_gethrtime.
+ * See: uts/i86pc/os/timestamp.c
+ */
+hrtime_t
+__cp_gethrtime(comm_page_t *cp)
+{
+	uint32_t old_hres_lock;
+	hrtime_t tsc, hrt, tsc_last;
+
+	/*
+	 * Several precautions must be taken when collecting the data necessary
+	 * to perform an accurate gethrtime calculation.
+	 *
+	 * While much of the TSC state stored in the comm page is unchanging
+	 * after boot, portions of it are periodically updated during OS ticks.
+	 * Changes to hres_lock during the course of the copy indicates a
+	 * potentially inconsistent snapshot, necessitating a loop.
+	 *
+	 * Even more complicated is the handling for TSCs which require sync
+	 * offsets between different CPUs.  Since userspace lacks the luxury of
+	 * disabling interrupts, a validation loop checking for CPU migrations
+	 * is used.  Pathological scheduling could, in theory, "outwit"
+	 * this check.  Such a possibility is considered an acceptable risk.
+	 *
+	 */
+	do {
+		old_hres_lock = cp->cp_hres_lock;
+		tsc_last = cp->cp_tsc_last;
+		hrt = cp->cp_tsc_hrtime_base;
+		tsc = __cp_tsc_read(cp);
+	} while ((old_hres_lock & ~1) != cp->cp_hres_lock);
+
+	if (tsc >= tsc_last) {
+		tsc -= tsc_last;
+	} else if (tsc >= tsc_last - (2 * cp->cp_tsc_max_delta)) {
+		tsc = 0;
+	} else if (tsc > cp->cp_tsc_resume_cap) {
+		tsc = cp->cp_tsc_resume_cap;
+	}
+	TSC_CONVERT_AND_ADD(tsc, hrt, cp->cp_nsec_scale);
+
+	return (hrt);
+}
+
+/*
+ * Userspace version of pc_gethrestime.
+ * See: uts/i86pc/os/machdep.c
+ */
+int
+__cp_clock_gettime_realtime(comm_page_t *cp, timespec_t *tsp)
+{
+	int lock_prev, nslt;
+	timespec_t now;
+	int64_t hres_adj;
+
+loop:
+	lock_prev = cp->cp_hres_lock;
+	now.tv_sec = cp->cp_hrestime[0];
+	now.tv_nsec = cp->cp_hrestime[1];
+	nslt = (int)(__cp_gethrtime(cp) - cp->cp_hres_last_tick);
+	hres_adj = cp->cp_hrestime_adj;
+	if (nslt < 0) {
+		/*
+		 * Tick came between sampling hrtime and hres_last_tick;
+		 */
+		goto loop;
+	}
+	now.tv_nsec += nslt;
+
+	/*
+	 * Apply hres_adj skew, if needed.
+	 */
+	if (hres_adj > 0) {
+		nslt = (nslt >> ADJ_SHIFT);
+		if (nslt > hres_adj)
+			nslt = (int)hres_adj;
+		now.tv_nsec += nslt;
+	} else if (hres_adj < 0) {
+		nslt = -(nslt >> ADJ_SHIFT);
+		if (nslt < hres_adj)
+			nslt = (int)hres_adj;
+		now.tv_nsec += nslt;
+	}
+
+	/*
+	 * Rope in tv_nsec from any excessive adjustments.
+	 */
+	while ((unsigned long)now.tv_nsec >= NANOSEC) {
+		now.tv_nsec -= NANOSEC;
+		now.tv_sec++;
+	}
+
+	if ((cp->cp_hres_lock & ~1) != lock_prev)
+		goto loop;
+
+	*tsp = now;
+	return (0);
+}
+
+/*
+ * The __cp_clock_gettime_monotonic function expects that hrt2ts be present
+ * when the code is finally linked.
+ * (The amd64 version has no such requirement.)
+ */
+extern void hrt2ts(hrtime_t, timespec_t *);
+
+int
+__cp_clock_gettime_monotonic(comm_page_t *cp, timespec_t *tsp)
+{
+	hrtime_t hrt;
+
+	hrt = __cp_gethrtime(cp);
+	hrt2ts(hrt, tsp);
+	return (0);
+}
+
+#endif /* __amd64 */
--- /dev/null	Wed Oct  5 00:31:52 2016
+++ new/usr/src/lib/commpage/common/offsets.in	Wed Oct  5 00:31:52 2016
@@ -0,0 +1,35 @@
+\
+\ 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 Joyent, Inc.
+\
+
+
+\
+\ offsets.in: input file to produce assym.h using the ctfstabs program
+\
+
+#include <sys/comm_page.h>
+
+
+comm_page_s	COMM_PAGE_S_SIZE
+	cp_tsc_last
+	cp_tsc_hrtime_base
+	cp_tsc_resume_cap
+	cp_tsc_type
+	cp_tsc_max_delta
+	cp_hres_lock
+	cp_nsec_scale
+	cp_hrestime_adj
+	cp_hres_last_tick
+	cp_tsc_ncpu
+	cp_hrestime
+	cp_tsc_sync_tick_delta
--- /dev/null	Wed Oct  5 00:31:52 2016
+++ new/usr/src/lib/commpage/i386/cp_subr.s	Wed Oct  5 00:31:52 2016
@@ -0,0 +1,157 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#include <sys/asm_linkage.h>
+#include <sys/segments.h>
+#include <sys/time_impl.h>
+#include <sys/tsc.h>
+#include <cp_offsets.h>
+
+#define	GETCPU_GDT_OFFSET	SEL_GDT(GDT_CPUID, SEL_UPL)
+
+	.file	"cp_subr.s"
+
+/*
+ * hrtime_t
+ * __cp_tsc_read(uint_t cp_tsc_type)
+ *
+ * Stack usage: 0x18 bytes
+ */
+	ENTRY_NP(__cp_tsc_read)
+	pushl	%ebp
+	movl	%esp, %ebp
+	pushl	%edi
+	pushl	%esi
+	subl	$0x4, %esp
+
+	movl	0x8(%ebp), %edi
+	movl	CP_TSC_TYPE(%edi), %eax
+	movl	CP_TSC_NCPU(%edi), %esi
+	cmpl	$TSC_TSCP, %eax
+	jne	3f
+	rdtscp
+	cmpl	$0, %esi
+	jne	2f
+1:
+	addl	$0x4, %esp
+	popl	%esi
+	popl	%edi
+	leave
+	ret
+2:
+	/*
+	 * When cp_tsc_ncpu is non-zero, it indicates the length of the
+	 * cp_tsc_sync_tick_delta array, which contains per-CPU offsets for the
+	 * TSC.  The CPU ID furnished by the IA32_TSC_AUX register via rdtscp
+	 * is used to look up an offset value in that array and apply it to the
+	 * TSC reading.
+	 */
+	leal	CP_TSC_SYNC_TICK_DELTA(%edi), %esi
+	leal	(%esi, %ecx, 8), %ecx
+	addl	(%ecx), %eax
+	adcl	0x4(%ecx), %edx
+	jmp	1b
+
+3:
+	cmpl	$0, %esi
+	je	4f
+	mov	$GETCPU_GDT_OFFSET, %eax
+	lsl	%ax, %eax
+	movl	%eax, (%esp)
+	movl	CP_TSC_TYPE(%edi), %eax
+
+4:
+	cmpl	$TSC_RDTSC_MFENCE, %eax
+	jne	5f
+	mfence
+	rdtsc
+	jmp	8f
+
+5:
+	cmpl	$TSC_RDTSC_LFENCE, %eax
+	jne	6f
+	lfence
+	rdtsc
+	jmp	8f
+
+6:
+	cmpl	$TSC_RDTSC_CPUID, %eax
+	jne	7f
+	pushl	%ebx
+	xorl	%eax, %eax
+	cpuid
+	rdtsc
+	popl	%ebx
+	jmp	8f
+
+7:
+	/*
+	 * Other protections should have prevented this function from being
+	 * called in the first place.  The only sane action is to abort.
+	 * The easiest means in this context is via SIGILL.
+	 */
+	ud2a
+
+8:
+
+	cmpl	$0, %esi
+	je	1b
+	/*
+	 * With a TSC reading in-hand, confirm that the thread has not migrated
+	 * since the cpu_id was first checked.
+	 */
+	movl	$GETCPU_GDT_OFFSET, %ecx
+	lsl	%cx, %ecx
+	movl	(%esp), %esi
+	cmpl	%ecx, %esi
+	je	9f
+	/*
+	 * There was a CPU migration, perform another reading.
+	 */
+	movl	%eax, (%esp)
+	movl	CP_TSC_NCPU(%edi), %esi
+	movl	CP_TSC_TYPE(%edi), %eax
+	jmp	4b
+
+9:
+	/* Grab the per-cpu offset and add it to the TSC result */
+	leal	CP_TSC_SYNC_TICK_DELTA(%edi), %esi
+	leal	(%esi, %ecx, 8), %ecx
+	addl	(%ecx), %eax
+	adcl	0x4(%ecx), %edx
+	jmp	1b
+	SET_SIZE(__cp_tsc_read)
+
+/*
+ * uint_t
+ * __cp_getcpu(uint_t cp_tsc_type)
+ */
+	ENTRY_NP(__cp_getcpu)
+	/*
+	 * If RDTSCP is available, it is a quick way to grab the cpu_id which
+	 * is stored in the TSC_AUX MSR by the kernel.
+	 */
+	movl	4(%esp), %eax
+	movl	CP_TSC_TYPE(%eax), %eax
+	cmpl	$TSC_TSCP, %eax
+	jne	1f
+	rdtscp
+	movl	%ecx, %eax
+	ret
+1:
+	mov	$GETCPU_GDT_OFFSET, %eax
+	lsl	%ax, %eax
+	ret
+	SET_SIZE(__cp_getcpu)
--- old/usr/src/lib/libbc/inc/include/sys/socket.h	Wed Oct  5 00:31:52 2016
+++ new/usr/src/lib/libbc/inc/include/sys/socket.h	Wed Oct  5 00:31:52 2016
@@ -3,8 +3,6 @@
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /*
  * Copyright (c) 1982, 1985, 1986 Regents of the University of California.
  * All rights reserved.  The Berkeley software License Agreement
@@ -169,6 +167,4 @@
 #define	MSG_PEEK	0x2		/* peek at incoming message */
 #define	MSG_DONTROUTE	0x4		/* send without using routing tables */
 
-#define	MSG_MAXIOVLEN	16
-
 #endif /*!_sys_socket_h*/
--- old/usr/src/lib/libbrand/common/libbrand.c	Wed Oct  5 00:31:52 2016
+++ new/usr/src/lib/libbrand/common/libbrand.c	Wed Oct  5 00:31:52 2016
@@ -817,6 +817,7 @@
 	xmlNodePtr node;
 	xmlChar *special, *dir, *type, *opt;
 	char special_exp[MAXPATHLEN];
+	char dir_exp[MAXPATHLEN];
 	char opt_exp[MAXPATHLEN];
 	int ret;
 
@@ -843,6 +844,10 @@
 		    special_exp, sizeof (special_exp),
 		    zonename, zonepath, NULL, NULL)) != 0)
 			goto next;
+		if ((ret = i_substitute_tokens((char *)dir,
+		    dir_exp, sizeof (dir_exp),
+		    zonename, zonepath, NULL, NULL)) != 0)
+			goto next;
 
 		/* opt might not be defined */
 		if (strlen((const char *)opt) == 0) {
@@ -855,7 +860,7 @@
 				goto next;
 		}
 
-		ret = func(data, (char *)special_exp, (char *)dir,
+		ret = func(data, (char *)special_exp, (char *)dir_exp,
 		    (char *)type, ((opt != NULL) ? opt_exp : NULL));
 
 next:
--- old/usr/src/lib/libc/Makefile.targ	Wed Oct  5 00:31:52 2016
+++ new/usr/src/lib/libc/Makefile.targ	Wed Oct  5 00:31:52 2016
@@ -21,6 +21,7 @@
 #
 # Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
+# Copyright 2016 Joyent, Inc.
 #
 #
 
@@ -93,6 +94,10 @@
 	$(BUILD.s)
 	$(POST_PROCESS_O)
 
+pics/%.o: $(LIBCBASE)/../$(MACH)/sys/%.c
+	$(COMPILE.c) -o $@ $<
+	$(POST_PROCESS_O)
+
 pics/%.o: $(LIBCBASE)/../$(TARGET_ARCH)/sys/%.c
 	$(COMPILE.c) -o $@ $<
 	$(POST_PROCESS_O)
--- old/usr/src/lib/libc/amd64/Makefile	Wed Oct  5 00:31:52 2016
+++ new/usr/src/lib/libc/amd64/Makefile	Wed Oct  5 00:31:52 2016
@@ -36,6 +36,10 @@
 CPP=		/usr/lib/cpp
 TARGET_ARCH=	amd64
 
+# include comm page definitions
+include $(SRC)/lib/commpage/Makefile.shared.com
+include $(SRC)/lib/commpage/Makefile.shared.targ
+
 # objects are grouped by source directory
 
 # local objects
@@ -106,6 +110,7 @@
 	strtoull.o
 
 GENOBJS=			\
+	$(COMMPAGE_OBJS)	\
 	_getsp.o		\
 	abs.o			\
 	alloca.o		\
@@ -281,6 +286,7 @@
 
 SYSOBJS=			\
 	__clock_gettime.o	\
+	__clock_gettime_sys.o	\
 	__getcontext.o		\
 	__uadmin.o		\
 	_lwp_mutex_unlock.o	\
@@ -876,6 +882,7 @@
 	fcntl.o			\
 	getpagesizes.o		\
 	getpeerucred.o		\
+	inotify.o		\
 	inst_sync.o		\
 	issetugid.o		\
 	label.o			\
@@ -1059,7 +1066,7 @@
 	$(AR) q $@ `$(LORDER) $(MOSTOBJS:%=$(DIR)/%) | $(GREP) -v ' L ' | $(TSORT)`
 
 # extra files for the clean target
-CLEANFILES=			\
+CLEANFILES+=			\
 	$(LIBCDIR)/port/gen/errlst.c	\
 	$(LIBCDIR)/port/gen/new_list.c	\
 	assym.h			\
@@ -1183,6 +1190,8 @@
 
 pics/arc4random.o :=	CPPFLAGS += -I$(SRC)/common/crypto/chacha
 
+pics/__clock_gettime.o := CPPFLAGS += $(COMMPAGE_CPPFLAGS)
+
 .KEEP_STATE:
 
 all: $(LIBS) $(LIB_PIC)
--- old/usr/src/lib/libc/amd64/gen/siginfolst.c	Wed Oct  5 00:31:52 2016
+++ new/usr/src/lib/libc/amd64/gen/siginfolst.c	Wed Oct  5 00:31:52 2016
@@ -22,6 +22,7 @@
 /*
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2015, Joyent, Inc.
  */
 
 /*	Copyright (c) 1988 AT&T	*/
@@ -188,6 +189,7 @@
 	0,		0,
 	0,		0,
 	0,		0,			/* SIGRTMIN+15 */
+	0,		0,			/* SIGRTMIN+16 */
 	0,		0,			/* SIGRTMAX-15 */
 	0,		0,
 	0,		0,
--- old/usr/src/lib/libc/i386/Makefile.com	Wed Oct  5 00:31:52 2016
+++ new/usr/src/lib/libc/i386/Makefile.com	Wed Oct  5 00:31:52 2016
@@ -34,6 +34,10 @@
 CPP=		/usr/lib/cpp
 TARGET_ARCH=	i386
 
+# include comm page definitions
+include $(SRC)/lib/commpage/Makefile.shared.com
+include $(SRC)/lib/commpage/Makefile.shared.targ
+
 VALUES=		values-Xa.o
 
 # objects are grouped by source directory
@@ -110,6 +114,7 @@
 	dtrace_data.o
 
 GENOBJS=			\
+	$(COMMPAGE_OBJS)	\
 	_div64.o		\
 	_divdi3.o		\
 	_getsp.o		\
@@ -305,6 +310,7 @@
 
 SYSOBJS=			\
 	__clock_gettime.o	\
+	__clock_gettime_sys.o	\
 	__getcontext.o		\
 	__uadmin.o		\
 	_lwp_mutex_unlock.o	\
@@ -916,6 +922,7 @@
 	fcntl.o			\
 	getpagesizes.o		\
 	getpeerucred.o		\
+	inotify.o		\
 	inst_sync.o		\
 	issetugid.o		\
 	label.o			\
@@ -1120,7 +1127,7 @@
 	$(AR) q $@ `$(LORDER) $(MOSTOBJS:%=$(DIR)/%) | $(GREP) -v ' L ' | $(TSORT)`
 
 # extra files for the clean target
-CLEANFILES=			\
+CLEANFILES+=			\
 	$(LIBCDIR)/port/gen/errlst.c	\
 	$(LIBCDIR)/port/gen/new_list.c	\
 	assym.h			\
@@ -1251,6 +1258,8 @@
 
 pics/arc4random.o :=	CPPFLAGS += -I$(SRC)/common/crypto/chacha
 
+pics/__clock_gettime.o := CPPFLAGS += $(COMMPAGE_CPPFLAGS)
+
 .KEEP_STATE:
 
 all: $(LIBS) $(LIB_PIC)
--- old/usr/src/lib/libc/i386/gen/siginfolst.c	Wed Oct  5 00:31:53 2016
+++ new/usr/src/lib/libc/i386/gen/siginfolst.c	Wed Oct  5 00:31:53 2016
@@ -22,6 +22,7 @@
 /*
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2015, Joyent, Inc.
  */
 
 /*	Copyright (c) 1988 AT&T	*/
@@ -188,6 +189,7 @@
 	0,		0,
 	0,		0,
 	0,		0,			/* SIGRTMIN+15 */
+	0,		0,			/* SIGRTMIN+16 */
 	0,		0,			/* SIGRTMAX-15 */
 	0,		0,
 	0,		0,
--- /dev/null	Wed Oct  5 00:31:53 2016
+++ new/usr/src/lib/libc/i386/sys/__clock_gettime.c	Wed Oct  5 00:31:53 2016
@@ -0,0 +1,43 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+
+
+#include "thr_uberdata.h"
+#include <cp_defs.h>
+
+extern int __clock_gettime_sys(clockid_t, timespec_t *);
+
+int
+__clock_gettime(clockid_t clock_id, timespec_t *tp)
+{
+	comm_page_t *cp = (comm_page_t *)__uberdata.ub_comm_page;
+
+	if (cp != NULL && __cp_can_gettime(cp) != 0) {
+		switch (clock_id) {
+		case __CLOCK_REALTIME0:
+		case CLOCK_REALTIME:
+			return (__cp_clock_gettime_realtime(cp, tp));
+
+		case CLOCK_MONOTONIC:
+			return (__cp_clock_gettime_monotonic(cp, tp));
+
+		default:
+			/* Fallback */
+			break;
+		}
+	}
+	return (__clock_gettime_sys(clock_id, tp));
+}
--- old/usr/src/lib/libc/i386/sys/__clock_gettime.s	Wed Oct  5 00:31:53 2016
+++ /dev/null	Wed Oct  5 00:31:53 2016
@@ -1,52 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-/*
- * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
- */
-
-	.file	"__clock_gettime.s"
-
-#include <sys/time_impl.h>
-#include "SYS.h"
-
-/*
- * int
- * __clock_gettime(clockid_t clock_id, timespec_t *tp)
- */
-
-	ENTRY(__clock_gettime)
-	movl	4(%esp), %eax
-	cmpl	$__CLOCK_REALTIME0, %eax	/ if (clock_id)
-	je	2f				/ equal to __CLOCK_REALTIME0
-	cmpl	$CLOCK_REALTIME, %eax		/ or if (clock_id)
-	jne	1f				/ equal to CLOCK_REALTIME
-2:
-	SYSFASTTRAP(GETHRESTIME)
-	movl	8(%esp), %ecx
-	movl	%eax, (%ecx)
-	movl	%edx, 4(%ecx)
-	RETC
-1:
-	SYSTRAP_RVAL1(clock_gettime)
-	SYSCERROR
-	RETC
-	SET_SIZE(__clock_gettime)
--- old/usr/src/lib/libc/inc/thr_uberdata.h	Wed Oct  5 00:31:53 2016
+++ new/usr/src/lib/libc/inc/thr_uberdata.h	Wed Oct  5 00:31:53 2016
@@ -965,6 +965,7 @@
 	int	ndaemons;	/* total number of THR_DAEMON threads/lwps */
 	pid_t	pid;		/* the current process's pid */
 	void	(*sigacthandler)(int, siginfo_t *, void *);
+	int	(*setctxt)(const ucontext_t *);
 	ulwp_t	*lwp_stacks;
 	ulwp_t	*lwp_laststack;
 	int	nfreestack;
@@ -977,6 +978,8 @@
 	robust_t	**robustlocks;	/* table of registered robust locks */
 	robust_t	*robustlist;	/* list of registered robust locks */
 	char	*progname;	/* the basename of the program, from argv[0] */
+	char	*ub_broot;	/* the root of the native code in the brand */
+	void	*ub_comm_page;	/* arch-specific comm page of kernel data */
 	struct uberdata **tdb_bootstrap;
 	tdb_t	tdb;		/* thread debug interfaces (for libc_db) */
 } uberdata_t;
@@ -1178,6 +1181,7 @@
 	int		ndaemons;
 	int		pid;
 	caddr32_t	sigacthandler;
+	caddr32_t	setctxt;
 	caddr32_t	lwp_stacks;
 	caddr32_t	lwp_laststack;
 	int		nfreestack;
@@ -1190,6 +1194,8 @@
 	caddr32_t	robustlocks;
 	caddr32_t	robustlist;
 	caddr32_t	progname;
+	caddr32_t	ub_broot;
+	caddr32_t	ub_comm_page;
 	caddr32_t	tdb_bootstrap;
 	tdb32_t		tdb;
 } uberdata32_t;
@@ -1279,6 +1285,7 @@
 extern	void	heldlock_exit(void);
 extern	void	heldlock_free(ulwp_t *);
 extern	void	sigacthandler(int, siginfo_t *, void *);
+extern	int	setctxt(const ucontext_t *);
 extern	void	signal_init(void);
 extern	int	sigequalset(const sigset_t *, const sigset_t *);
 extern	void	mutex_setup(void);
--- old/usr/src/lib/libc/port/gen/getauxv.c	Wed Oct  5 00:31:53 2016
+++ new/usr/src/lib/libc/port/gen/getauxv.c	Wed Oct  5 00:31:53 2016
@@ -24,9 +24,8 @@
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include "lint.h"
+#include "thr_uberdata.h"
 #include <libc.h>
 #include <fcntl.h>
 #include <stdlib.h>
@@ -38,6 +37,7 @@
 #include <thread.h>
 #include <synch.h>
 #include <atomic.h>
+#include <limits.h>
 
 static mutex_t auxlock = DEFAULTMUTEX;
 
@@ -59,11 +59,20 @@
 	if (auxb == NULL) {
 		lmutex_lock(&auxlock);
 		if (auxb == NULL) {
+			uberdata_t *udp = curthread->ul_uberdata;
 			struct stat statb;
 			auxv_t *buf = NULL;
+			char *path = "/proc/self/auxv";
+			char pbuf[PATH_MAX];
 			int fd;
 
-			if ((fd = open("/proc/self/auxv", O_RDONLY)) != -1 &&
+			if (udp->ub_broot != NULL) {
+				(void) snprintf(pbuf, sizeof (pbuf),
+				    "%s/proc/self/auxv", udp->ub_broot);
+				path = pbuf;
+			}
+
+			if ((fd = open(path, O_RDONLY)) != -1 &&
 			    fstat(fd, &statb) != -1)
 				buf = libc_malloc(
 				    statb.st_size + sizeof (auxv_t));
--- old/usr/src/lib/libc/port/gen/siglist.c	Wed Oct  5 00:31:53 2016
+++ new/usr/src/lib/libc/port/gen/siglist.c	Wed Oct  5 00:31:53 2016
@@ -22,6 +22,7 @@
 /*
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2015, Joyent, Inc.
  */
 
 /*	Copyright (c) 1988 AT&T	*/
@@ -128,6 +129,7 @@
 		"Fourteenth Realtime Signal",		/* SIGRTMIN+13	*/
 		"Fifteenth Realtime Signal",		/* SIGRTMIN+14	*/
 		"Sixteenth Realtime Signal",		/* SIGRTMIN+15	*/
+		"Seventeenth Realtime Signal",		/* SIGRTMIN+16	*/
 		"Sixteenth Last Realtime Signal",	/* SIGRTMAX-15	*/
 		"Fifteenth Last Realtime Signal",	/* SIGRTMAX-14	*/
 		"Fourteenth Last Realtime Signal",	/* SIGRTMAX-13	*/
--- old/usr/src/lib/libc/port/gen/str2sig.c	Wed Oct  5 00:31:53 2016
+++ new/usr/src/lib/libc/port/gen/str2sig.c	Wed Oct  5 00:31:53 2016
@@ -22,7 +22,7 @@
 /*
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
- * Copyright (c) 2014, Joyent, Inc.  All rights reserved.
+ * Copyright 2015, Joyent, Inc.
  */
 
 /*	Copyright (c) 1988 AT&T	*/
@@ -102,6 +102,7 @@
 	{ "RTMIN+13",	_SIGRTMIN+13 },
 	{ "RTMIN+14",	_SIGRTMIN+14 },
 	{ "RTMIN+15",	_SIGRTMIN+15 },
+	{ "RTMIN+16",	_SIGRTMIN+16 },
 	{ "RTMAX-15",	_SIGRTMAX-15 },
 	{ "RTMAX-14",	_SIGRTMAX-14 },
 	{ "RTMAX-13",	_SIGRTMAX-13 },
--- old/usr/src/lib/libc/port/mapfile-vers	Wed Oct  5 00:31:53 2016
+++ new/usr/src/lib/libc/port/mapfile-vers	Wed Oct  5 00:31:53 2016
@@ -2986,6 +2986,10 @@
 	__idmap_unreg;
 	__init_daemon_priv;
 	__init_suid_priv;
+	inotify_init;
+	inotify_init1;
+	inotify_add_watch;
+	inotify_rm_watch;
 	_insert;
 	inst_sync;
 	_iswctype;
@@ -3067,7 +3071,9 @@
 	scrwidth;
 	semctl64;
 	_semctl64;
+	set_escaped_context_cleanup;
 	set_setcontext_enforcement;
+	setcontext_sigmask;
 	_setbufend;
 	__set_errno;
 	setprojrctl;
@@ -3184,6 +3190,7 @@
 	zone_list;
 	zone_list_datalink;
 	zonept;
+	zone_get_nroot;
 	zone_remove_datalink;
 	zone_setattr;
 	zone_shutdown;
--- old/usr/src/lib/libc/port/stdio/system.c	Wed Oct  5 00:31:54 2016
+++ new/usr/src/lib/libc/port/stdio/system.c	Wed Oct  5 00:31:54 2016
@@ -22,6 +22,7 @@
 /*
  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
  */
 
 /*	Copyright (c) 1988 AT&T	*/
@@ -43,6 +44,7 @@
 #include <synch.h>
 #include <spawn.h>
 #include <paths.h>
+#include <zone.h>
 #include "libc.h"
 
 extern const char **_environ;
@@ -125,11 +127,18 @@
 	int error;
 	sigset_t mask;
 	struct stat64 buf;
-	const char *shpath = _PATH_BSHELL;
+	char shpath[MAXPATHLEN];
+	const char *zroot = zone_get_nroot();
 	char *argv[4];
 	posix_spawnattr_t attr;
 	static const char *shell = "sh";
 
+	/*
+	 * If executing in brand use native root.
+	 */
+	(void) snprintf(shpath, sizeof (shpath), "%s%s",
+	    zroot != NULL ? zroot : "", _PATH_BSHELL);
+
 	if (cmd == NULL) {
 		if (stat64(shpath, &buf) != 0) {
 			return (0);
--- old/usr/src/lib/libc/port/sys/epoll.c	Wed Oct  5 00:31:54 2016
+++ new/usr/src/lib/libc/port/sys/epoll.c	Wed Oct  5 00:31:54 2016
@@ -10,7 +10,7 @@
  */
 
 /*
- * Copyright (c) 2014, Joyent, Inc.  All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 
 #include <sys/types.h>
@@ -114,7 +114,7 @@
 {
 	dvpoll_epollfd_t epoll[2];
 	uint32_t events, ev = 0;
-	int i = 0;
+	int i = 0, res;
 
 	epoll[i].dpep_pollfd.fd = fd;
 
@@ -165,8 +165,29 @@
 	}
 
 	epoll[i].dpep_pollfd.events = ev;
+retry:
+	res = write(epfd, epoll, sizeof (epoll[0]) * (i + 1));
 
-	return (write(epfd, epoll, sizeof (epoll[0]) * (i + 1)) == -1 ? -1 : 0);
+	if (res == -1) {
+		if (errno == EINTR) {
+			/*
+			 * Linux does not document EINTR as an allowed error
+			 * for epoll_ctl.  The write must be retried if it is
+			 * not done automatically via SA_RESTART.
+			 */
+			goto retry;
+		}
+		if (errno == ELOOP) {
+			/*
+			 * Convert the specific /dev/poll error about an fd
+			 * loop into what is expected from the Linux epoll
+			 * interface.
+			 */
+			errno = EINVAL;
+		}
+		return (-1);
+	}
+	return (0);
 }
 
 int
--- /dev/null	Wed Oct  5 00:31:54 2016
+++ new/usr/src/lib/libc/port/sys/inotify.c	Wed Oct  5 00:31:54 2016
@@ -0,0 +1,142 @@
+/*
+ * 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 (c) 2014, Joyent, Inc.  All rights reserved.
+ */
+
+#include <sys/inotify.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <dirent.h>
+
+int
+inotify_init()
+{
+	return (open("/dev/inotify", O_RDWR));
+}
+
+int
+inotify_init1(int flags)
+{
+	int oflags = O_RDWR;
+
+	if (flags & IN_NONBLOCK)
+		oflags |= O_NONBLOCK;
+
+	if (flags & IN_CLOEXEC)
+		oflags |= O_CLOEXEC;
+
+	return (open("/dev/inotify", oflags));
+}
+
+int
+inotify_add_watch(int fd, const char *pathname, uint32_t mask)
+{
+	inotify_addwatch_t ioc;
+	inotify_addchild_t cioc;
+	struct stat buf;
+	int dirfd, wd;
+	DIR *dir;
+	struct dirent *dp;
+	int oflags = O_RDONLY;
+
+	if (mask & IN_DONT_FOLLOW)
+		oflags |= O_NOFOLLOW;
+
+	if ((dirfd = open(pathname, oflags)) < 0)
+		return (-1);
+
+	if (fstat(dirfd, &buf) != 0) {
+		(void) close(dirfd);
+		return (-1);
+	}
+
+	if ((mask & IN_ONLYDIR) && !(buf.st_mode & S_IFDIR)) {
+		(void) close(dirfd);
+		errno = ENOTDIR;
+		return (-1);
+	}
+
+	bzero(&ioc, sizeof (ioc));
+	ioc.inaw_fd = dirfd;
+	ioc.inaw_mask = mask;
+
+	if ((wd = ioctl(fd, INOTIFYIOC_ADD_WATCH, &ioc)) < 0) {
+		(void) close(dirfd);
+		return (-1);
+	}
+
+	if (!(buf.st_mode & S_IFDIR) || !(mask & IN_CHILD_EVENTS)) {
+		(void) close(dirfd);
+		(void) ioctl(fd, INOTIFYIOC_ACTIVATE, wd);
+		return (wd);
+	}
+
+	/*
+	 * If we have a directory and we have a mask that denotes child events,
+	 * we need to manually add a child watch to every directory entry.
+	 * (Because our watch is in place, it will automatically be added to
+	 * files that are newly created after this point.)
+	 */
+	if ((dir = fdopendir(dirfd)) == NULL) {
+		(void) inotify_rm_watch(fd, wd);
+		(void) close(dirfd);
+		return (-1);
+	}
+
+	bzero(&cioc, sizeof (cioc));
+	cioc.inac_fd = dirfd;
+
+	while ((dp = readdir(dir)) != NULL) {
+		if (strcmp(dp->d_name, ".") == 0)
+			continue;
+
+		if (strcmp(dp->d_name, "..") == 0)
+			continue;
+
+		cioc.inac_name = dp->d_name;
+
+		if (ioctl(fd, INOTIFYIOC_ADD_CHILD, &cioc) != 0) {
+			/*
+			 * If we get an error that indicates clear internal
+			 * malfunctioning, we propagate the error.  Otherwise
+			 * we eat it:  this could be a file that no longer
+			 * exists or a symlink or something else that we
+			 * can't lookup.
+			 */
+			switch (errno) {
+			case ENXIO:
+			case EFAULT:
+			case EBADF:
+				(void) closedir(dir);
+				(void) inotify_rm_watch(fd, wd);
+				return (-1);
+			default:
+				break;
+			}
+		}
+	}
+
+	(void) closedir(dir);
+	(void) ioctl(fd, INOTIFYIOC_ACTIVATE, wd);
+
+	return (wd);
+}
+
+int
+inotify_rm_watch(int fd, int wd)
+{
+	return (ioctl(fd, INOTIFYIOC_RM_WATCH, wd));
+}
--- old/usr/src/lib/libc/port/sys/zone.c	Wed Oct  5 00:31:54 2016
+++ new/usr/src/lib/libc/port/sys/zone.c	Wed Oct  5 00:31:54 2016
@@ -25,6 +25,7 @@
  */
 
 #include "lint.h"
+#include "thr_uberdata.h"
 #include <sys/types.h>
 #include <sys/syscall.h>
 #include <sys/zone.h>
@@ -240,4 +241,11 @@
 zone_list_datalink(zoneid_t zoneid, int *dlnump, datalink_id_t *linkids)
 {
 	return (syscall(SYS_zone, ZONE_LIST_DATALINK, zoneid, dlnump, linkids));
+}
+
+const char *
+zone_get_nroot()
+{
+	uberdata_t *udp = curthread->ul_uberdata;
+	return (udp->ub_broot);
 }
--- old/usr/src/lib/libc/port/threads/sigaction.c	Wed Oct  5 00:31:54 2016
+++ new/usr/src/lib/libc/port/threads/sigaction.c	Wed Oct  5 00:31:54 2016
@@ -22,6 +22,7 @@
 /*
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
  */
 
 #include "lint.h"
@@ -284,7 +285,25 @@
 		thr_panic("take_deferred_signal(): __sigresend() failed");
 }
 
+/*
+ * sigacthandler() attempts to clean up dangling uc_link pointers in
+ * signal handling contexts when libc believes us to have escaped
+ * a signal handler incorrectly in the past.
+ *
+ * Branded processes have a legitimate use for a chain including contexts
+ * other than those used for signal handling when tracking emulation
+ * requests from the kernel.  We allow them to disable this cleanup
+ * behaviour.
+ */
+static int escaped_context_cleanup = 1;
+
 void
+set_escaped_context_cleanup(int on)
+{
+	escaped_context_cleanup = on;
+}
+
+void
 sigacthandler(int sig, siginfo_t *sip, void *uvp)
 {
 	ucontext_t *ucp = uvp;
@@ -306,7 +325,7 @@
 	 * we are actually executing at main level (self->ul_siglink == NULL).
 	 * See the code for setjmp()/longjmp() for more details.
 	 */
-	if (self->ul_siglink == NULL)
+	if (escaped_context_cleanup && self->ul_siglink == NULL)
 		ucp->uc_link = NULL;
 
 	/*
@@ -458,11 +477,12 @@
 }
 
 /*
- * This is a private interface for the linux brand interface.
+ * This is a private interface for the lx brand.
  */
 void
 setsigacthandler(void (*nsigacthandler)(int, siginfo_t *, void *),
-    void (**osigacthandler)(int, siginfo_t *, void *))
+    void (**osigacthandler)(int, siginfo_t *, void *),
+    int (*brsetctxt)(const ucontext_t *))
 {
 	ulwp_t *self = curthread;
 	uberdata_t *udp = self->ul_uberdata;
@@ -471,6 +491,9 @@
 		*osigacthandler = udp->sigacthandler;
 
 	udp->sigacthandler = nsigacthandler;
+
+	if (brsetctxt != NULL)
+		udp->setctxt = brsetctxt;
 }
 
 /*
@@ -517,11 +540,39 @@
 	setcontext_enforcement = on;
 }
 
+/*
+ * The LX brand emulation library implements an operation that is analogous to
+ * setcontext(), but takes a different path in to the kernel.  So that it can
+ * correctly restore a signal mask, we expose just the signal mask handling
+ * part of the regular setcontext() routine as a private interface.
+ */
+void
+setcontext_sigmask(ucontext_t *ucp)
+{
+	ulwp_t *self = curthread;
+
+	if (ucp->uc_flags & UC_SIGMASK) {
+		block_all_signals(self);
+		delete_reserved_signals(&ucp->uc_sigmask);
+		self->ul_sigmask = ucp->uc_sigmask;
+		if (self->ul_cursig) {
+			/*
+			 * We have a deferred signal present.
+			 * The signal mask will be set when the
+			 * signal is taken in take_deferred_signal().
+			 */
+			ASSERT(self->ul_critical + self->ul_sigdefer != 0);
+			ucp->uc_flags &= ~UC_SIGMASK;
+		}
+	}
+}
+
 #pragma weak _setcontext = setcontext
 int
 setcontext(const ucontext_t *ucp)
 {
 	ulwp_t *self = curthread;
+	uberdata_t *udp = self->ul_uberdata;
 	int ret;
 	ucontext_t uc;
 
@@ -536,20 +587,7 @@
 	/*
 	 * Restore previous signal mask and context link.
 	 */
-	if (uc.uc_flags & UC_SIGMASK) {
-		block_all_signals(self);
-		delete_reserved_signals(&uc.uc_sigmask);
-		self->ul_sigmask = uc.uc_sigmask;
-		if (self->ul_cursig) {
-			/*
-			 * We have a deferred signal present.
-			 * The signal mask will be set when the
-			 * signal is taken in take_deferred_signal().
-			 */
-			ASSERT(self->ul_critical + self->ul_sigdefer != 0);
-			uc.uc_flags &= ~UC_SIGMASK;
-		}
-	}
+	setcontext_sigmask(&uc);
 	self->ul_siglink = uc.uc_link;
 
 	/*
@@ -578,7 +616,7 @@
 	 */
 	set_parking_flag(self, 0);
 	self->ul_sp = 0;
-	ret = __setcontext(&uc);
+	ret = udp->setctxt(&uc);
 
 	/*
 	 * It is OK for setcontext() to return if the user has not specified
--- old/usr/src/lib/libc/port/threads/thr.c	Wed Oct  5 00:31:54 2016
+++ new/usr/src/lib/libc/port/threads/thr.c	Wed Oct  5 00:31:54 2016
@@ -125,6 +125,7 @@
 	0,			/* ndaemons */
 	0,			/* pid */
 	sigacthandler,		/* sigacthandler */
+	__setcontext,		/* setctxt */
 	NULL,			/* lwp_stacks */
 	NULL,			/* lwp_laststack */
 	0,			/* nfreestack */
@@ -137,6 +138,8 @@
 	NULL,			/* robustlocks */
 	NULL,			/* robustlist */
 	NULL,			/* progname */
+	NULL,			/* ub_broot */
+	NULL,			/* ub_comm_page */
 	NULL,			/* __tdb_bootstrap */
 	{			/* tdb */
 		NULL,		/* tdb_sync_addr_hash */
@@ -1220,6 +1223,29 @@
 extern void __proc64id(void);
 #endif
 
+static void
+init_auxv_data(uberdata_t *udp)
+{
+	Dl_argsinfo_t args;
+
+	udp->ub_broot = NULL;
+	udp->ub_comm_page = NULL;
+	if (dlinfo(RTLD_SELF, RTLD_DI_ARGSINFO, &args) < 0)
+		return;
+
+	while (args.dla_auxv->a_type != AT_NULL) {
+		switch (args.dla_auxv->a_type) {
+		case AT_SUN_BRAND_NROOT:
+			udp->ub_broot = args.dla_auxv->a_un.a_ptr;
+			break;
+		case AT_SUN_COMMPAGE:
+			udp->ub_comm_page = args.dla_auxv->a_un.a_ptr;
+			break;
+		}
+		args.dla_auxv++;
+	}
+}
+
 /*
  * libc_init() is called by ld.so.1 for library initialization.
  * We perform minimal initialization; enough to work with the main thread.
@@ -1256,6 +1282,14 @@
 	(void) _atexit(__cleanup);
 
 	/*
+	 * Every libc, regardless of link map, needs to go through and check
+	 * its aux vectors.  Doing so will indicate whether or not this has
+	 * been given a brand root (used to qualify various other data) or a
+	 * comm page (to optimize certain system actions).
+	 */
+	init_auxv_data(udp);
+
+	/*
 	 * We keep our uberdata on one of (a) the first alternate link map
 	 * or (b) the primary link map.  We switch to the primary link map
 	 * and stay there once we see it.  All intermediate link maps are
--- old/usr/src/lib/libc/sparc/Makefile.com	Wed Oct  5 00:31:54 2016
+++ new/usr/src/lib/libc/sparc/Makefile.com	Wed Oct  5 00:31:54 2016
@@ -950,6 +950,7 @@
 	fcntl.o			\
 	getpagesizes.o		\
 	getpeerucred.o		\
+	inotify.o		\
 	inst_sync.o		\
 	issetugid.o		\
 	label.o			\
--- old/usr/src/lib/libc/sparc/gen/siginfolst.c	Wed Oct  5 00:31:54 2016
+++ new/usr/src/lib/libc/sparc/gen/siginfolst.c	Wed Oct  5 00:31:54 2016
@@ -22,6 +22,7 @@
 /*
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2015, Joyent, Inc.
  */
 
 /*	Copyright (c) 1988 AT&T	*/
@@ -188,6 +189,7 @@
 	0,		0,
 	0,		0,
 	0,		0,			/* SIGRTMIN+15 */
+	0,		0,			/* SIGRTMIN+16 */
 	0,		0,			/* SIGRTMAX-15 */
 	0,		0,
 	0,		0,
--- old/usr/src/lib/libc/sparcv9/gen/siginfolst.c	Wed Oct  5 00:31:54 2016
+++ new/usr/src/lib/libc/sparcv9/gen/siginfolst.c	Wed Oct  5 00:31:54 2016
@@ -22,6 +22,7 @@
 /*
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2015, Joyent, Inc.
  */
 
 /*	Copyright (c) 1988 AT&T	*/
@@ -188,6 +189,7 @@
 	0,		0,
 	0,		0,
 	0,		0,			/* SIGRTMIN+15 */
+	0,		0,			/* SIGRTMIN+16 */
 	0,		0,			/* SIGRTMAX-15 */
 	0,		0,
 	0,		0,
--- old/usr/src/lib/libcurses/screen/setupterm.c	Wed Oct  5 00:31:55 2016
+++ new/usr/src/lib/libcurses/screen/setupterm.c	Wed Oct  5 00:31:55 2016
@@ -37,8 +37,6 @@
  * contributors.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /*LINTLIBRARY*/
 
 #include	<stdio.h>
@@ -48,6 +46,7 @@
 #include	<string.h>
 #include	<unistd.h>
 #include	<errno.h>
+#include	<zone.h>
 #include	"curses_inc.h"
 
 #define	TERMPATH	"/usr/share/lib/terminfo/"
@@ -272,9 +271,11 @@
 	}
 
 	if (tfd < 0) {
+		const char *zroot = zone_get_nroot();
 		/* /usr/share/lib/terminfo/?/$TERM */
 		if (snprintf(fname, sizeof (fname),
-			"%s/%c/%s", TERMPATH, *term, term) >= sizeof (fname)) {
+			"%s/%s/%c/%s", zroot == NULL ? "" : zroot, TERMPATH,
+			*term, term) >= sizeof (fname)) {
 			term_errno = TERMINFO_TOO_LONG;
 			goto out_err;
 		}
--- old/usr/src/lib/libdhcpagent/common/dhcpagent_util.c	Wed Oct  5 00:31:55 2016
+++ new/usr/src/lib/libdhcpagent/common/dhcpagent_util.c	Wed Oct  5 00:31:55 2016
@@ -36,6 +36,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <zone.h>
 
 #include "dhcpagent_ipc.h"
 #include "dhcpagent_util.h"
@@ -125,7 +126,13 @@
 	int			ctfd;
 	pid_t			childpid;
 	ctid_t			ct;
+	char			dhcpcmd[MAXPATHLEN];
+	const char		*zroot = zone_get_nroot();
 
+	/* Prepend the root of the native code in the brand to the command */
+	(void) snprintf(dhcpcmd, sizeof (dhcpcmd), "%s%s", zroot != NULL ?
+	    zroot : "", DHCP_AGENT_PATH);
+
 	/*
 	 * just send a dummy request to the agent to find out if it's
 	 * up.  we do this instead of directly connecting to it since
@@ -160,7 +167,7 @@
 		goto fail;
 
 	case  0:
-		(void) execl(DHCP_AGENT_PATH, DHCP_AGENT_PATH, (char *)0);
+		(void) execl(dhcpcmd, dhcpcmd, (char *)0);
 		_exit(EXIT_FAILURE);
 
 	default:
--- old/usr/src/lib/libdladm/common/linkprop.c	Wed Oct  5 00:31:55 2016
+++ new/usr/src/lib/libdladm/common/linkprop.c	Wed Oct  5 00:31:55 2016
@@ -1496,12 +1496,17 @@
 }
 
 typedef int (*zone_get_devroot_t)(char *, char *, size_t);
+typedef int (*zone_get_brand_t)(char *, char *, size_t);
 
 static int
 i_dladm_get_zone_dev(char *zone_name, char *dev, size_t devlen)
 {
 	char			root[MAXPATHLEN];
+	char			brand[MAXPATHLEN]; /* Overkill, for sure. */
+	static char		*full_native_path = "/native/dev";
+	char			*native_dev_path = full_native_path;
 	zone_get_devroot_t	real_zone_get_devroot;
+	zone_get_brand_t	real_zone_get_brand;
 	void			*dlhandle;
 	void			*sym;
 	int			ret;
@@ -1513,11 +1518,28 @@
 		(void) dlclose(dlhandle);
 		return (-1);
 	}
-
 	real_zone_get_devroot = (zone_get_devroot_t)sym;
 
+	if ((sym = dlsym(dlhandle, "zone_get_brand")) == NULL) {
+		(void) dlclose(dlhandle);
+		return (-1);
+	}
+	real_zone_get_brand = (zone_get_devroot_t)sym;
+
+	/* Have "/dev" be LX-agile for possibility of "/native/dev". */
+	ret = real_zone_get_brand(zone_name, brand, sizeof (brand));
+	if (ret != 0) {
+		(void) dlclose(dlhandle);
+		return (ret);
+	}
+	/* Can use strcmp with constant string... */
+	if (strcmp(brand, "lx") != 0) {
+		/* Non-LX zone, don't use "/native/dev" */
+		native_dev_path += 7;	/* strlen("/native") */
+	}
+
 	if ((ret = real_zone_get_devroot(zone_name, root, sizeof (root))) == 0)
-		(void) snprintf(dev, devlen, "%s%s", root, "/dev");
+		(void) snprintf(dev, devlen, "%s%s", root, native_dev_path);
 	(void) dlclose(dlhandle);
 	return (ret);
 }
--- old/usr/src/lib/libdtrace/common/dt_open.c	Wed Oct  5 00:31:55 2016
+++ new/usr/src/lib/libdtrace/common/dt_open.c	Wed Oct  5 00:31:55 2016
@@ -40,6 +40,7 @@
 #include <fcntl.h>
 #include <errno.h>
 #include <assert.h>
+#include <zone.h>
 
 #define	_POSIX_PTHREAD_SEMANTICS
 #include <dirent.h>
@@ -685,8 +686,8 @@
 { DTRACE_STABILITY_UNSTABLE, DTRACE_STABILITY_UNSTABLE, DTRACE_CLASS_COMMON },
 };
 
-const char *_dtrace_defcpp = "/usr/ccs/lib/cpp"; /* default cpp(1) to invoke */
-const char *_dtrace_defld = "/usr/ccs/bin/ld";   /* default ld(1) to invoke */
+const char *_dtrace_defcpp = "/usr/lib/cpp"; /* default cpp(1) to invoke */
+const char *_dtrace_defld = "/usr/bin/ld";   /* default ld(1) to invoke */
 
 const char *_dtrace_libdir = "/usr/lib/dtrace"; /* default library directory */
 const char *_dtrace_provdir = "/dev/dtrace/provider"; /* provider directory */
@@ -826,6 +827,8 @@
 	dt_provmod_t *provmod = NULL;
 	int i, err;
 	struct rlimit rl;
+	const char *zroot;
+	char *libpath = NULL;
 
 	const dt_intrinsic_t *dinp;
 	const dt_typedef_t *dtyp;
@@ -958,11 +961,19 @@
 	dtp->dt_provs = calloc(dtp->dt_provbuckets, sizeof (dt_provider_t *));
 	dt_proc_init(dtp);
 	dtp->dt_vmax = DT_VERS_LATEST;
-	dtp->dt_cpp_path = strdup(_dtrace_defcpp);
+	zroot = zone_get_nroot();
+	if (zroot != NULL) {
+		(void) asprintf(&dtp->dt_ld_path, "%s/%s", zroot,
+		    _dtrace_defld);
+		(void) asprintf(&dtp->dt_cpp_path, "%s/%s", zroot,
+		    _dtrace_defcpp);
+	} else {
+		dtp->dt_ld_path = strdup(_dtrace_defld);
+		dtp->dt_cpp_path = strdup(_dtrace_defcpp);
+	}
 	dtp->dt_cpp_argv = malloc(sizeof (char *));
 	dtp->dt_cpp_argc = 1;
 	dtp->dt_cpp_args = 1;
-	dtp->dt_ld_path = strdup(_dtrace_defld);
 	dtp->dt_provmod = provmod;
 	dtp->dt_vector = vector;
 	dtp->dt_varg = arg;
@@ -1307,9 +1318,15 @@
 	 * compile, and to provide better error reporting (because the full
 	 * reporting of compiler errors requires dtrace_open() to succeed).
 	 */
-	if (dtrace_setopt(dtp, "libdir", _dtrace_libdir) != 0)
+	if (zroot != NULL)
+		(void) asprintf(&libpath, "%s/%s", zroot, _dtrace_libdir);
+	if (dtrace_setopt(dtp, "libdir",
+	    libpath != NULL ? libpath : _dtrace_libdir) != 0)
 		return (set_open_errno(dtp, errp, dtp->dt_errno));
 
+	if (libpath != NULL)
+		free(libpath);
+
 	return (dtp);
 }
 
--- old/usr/src/lib/libipadm/common/libipadm.c	Wed Oct  5 00:31:55 2016
+++ new/usr/src/lib/libipadm/common/libipadm.c	Wed Oct  5 00:31:55 2016
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
  */
 
@@ -285,11 +286,19 @@
 boolean_t
 ipadm_check_auth(void)
 {
+	int		uid;
 	struct passwd	pwd;
 	char		buf[NSS_BUFLEN_PASSWD];
 
+	/*
+	 * Branded zones may have different kinds of auth, but root always
+	 * allowed.
+	 */
+	if ((uid = getuid()) == 0)
+		return (B_TRUE);
+
 	/* get the password entry for the given user ID */
-	if (getpwuid_r(getuid(), &pwd, buf, sizeof (buf)) == NULL)
+	if (getpwuid_r(uid, &pwd, buf, sizeof (buf)) == NULL)
 		return (B_FALSE);
 
 	/* check for presence of given authorization */
@@ -897,9 +906,21 @@
 
 reopen:
 	(void) pthread_mutex_lock(&iph->iph_lock);
-	/* The door descriptor is opened if it isn't already */
+	/*
+	 * The door descriptor is opened if it isn't already.
+	 */
 	if (iph->iph_door_fd == -1) {
-		if ((iph->iph_door_fd = open(IPMGMT_DOOR, O_RDONLY)) < 0) {
+		char door[MAXPATHLEN];
+		const char *zroot = zone_get_nroot();
+
+		/*
+		 * If this is a branded zone, make sure we use the "/native"
+		 * prefix for the door path:
+		 */
+		(void) snprintf(door, sizeof (door), "%s%s", zroot != NULL ?
+		    zroot : "", IPMGMT_DOOR);
+
+		if ((iph->iph_door_fd = open(door, O_RDONLY)) < 0) {
 			err = errno;
 			(void) pthread_mutex_unlock(&iph->iph_lock);
 			return (err);
--- old/usr/src/lib/libnsl/common/mapfile-vers	Wed Oct  5 00:31:55 2016
+++ new/usr/src/lib/libnsl/common/mapfile-vers	Wed Oct  5 00:31:55 2016
@@ -21,6 +21,7 @@
 #
 # Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
 # Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
+# Copyright 2015 Joyent, Inc.
 #
 
 #
@@ -467,6 +468,7 @@
 
 SYMBOL_VERSION SUNWprivate_1.5 {
     global:
+ 	_nsl_brand_set_hooks;
 	clnt_create_service_timed;
 	inet_matchaddr;
 } SUNWprivate_1.4;
--- old/usr/src/lib/libnsl/netselect/netselect.c	Wed Oct  5 00:31:56 2016
+++ new/usr/src/lib/libnsl/netselect/netselect.c	Wed Oct  5 00:31:56 2016
@@ -22,6 +22,7 @@
 /*
  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
  */
 
 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
@@ -32,8 +33,6 @@
  * under license from the Regents of the University of California.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include "mt.h"
 #include "../rpc/rpc_mt.h"		/* for MT declarations only */
 #include <rpc/types.h>
@@ -45,6 +44,7 @@
 #include <malloc.h>
 #include <libintl.h>
 #include <syslog.h>
+#include <zone.h>
 #include "netcspace.h"
 
 #define	FAILURE  (unsigned)(-1)
@@ -70,6 +70,9 @@
 
 extern const char __nsl_dom[];
 
+static int (*brand_get_sz)(void) = NULL;
+static struct netconfig *(*brand_get_net_ent)(int) = NULL;
+
 /*
  *	Static global variables used by the library procedures:
  *
@@ -255,6 +258,14 @@
 	netconfig_free(netp);
 }
 
+void
+_nsl_brand_set_hooks(int (*set_sz_func)(void),
+    struct netconfig *(*get_ent_func)(int))
+{
+	brand_get_sz = set_sz_func;
+	brand_get_net_ent = get_ent_func;
+}
+
 /*
  *	getnetlist() reads the netconfig file and creates a
  *	NULL-terminated list of entries.
@@ -265,52 +276,82 @@
 static struct netconfig **
 getnetlist(void)
 {
-	char line[BUFSIZ];	/* holds each line of NETCONFIG */
-	FILE *fp;		/* file stream for NETCONFIG */
+	FILE *fp = NULL;	/* file stream for NETCONFIG */
 	struct netconfig **listpp; /* the beginning of the netconfig list */
 	struct netconfig **tpp;	/* used to traverse the netconfig list */
 	int count;		/* the number of entries in file */
+	char nc_path[MAXPATHLEN];
+	const char *zroot = zone_get_nroot();
 
-	if ((fp = fopen(NETCONFIG, "rF")) == NULL) {
-		nc_error = NC_OPENFAIL;
-		return (NULL);
-	}
+	/*
+	 * If we are running in a branded zone, ensure we use the "/native"
+	 * prefix when opening the netconfig file:
+	 */
+	(void) snprintf(nc_path, sizeof (nc_path), "%s%s", zroot != NULL ?
+	    zroot : "", NETCONFIG);
 
-	count = 0;
-	while (fgets(line, BUFSIZ, fp)) {
-		if (!(blank(line) || comment(line))) {
-			++count;
+	if (brand_get_sz != NULL) {
+		count = brand_get_sz();
+	} else {
+		char line[BUFSIZ];	/* holds each line of NETCONFIG */
+
+		if ((fp = fopen(nc_path, "rF")) == NULL) {
+			nc_error = NC_OPENFAIL;
+			return (NULL);
 		}
+
+		count = 0;
+		while (fgets(line, BUFSIZ, fp)) {
+			if (!(blank(line) || comment(line))) {
+				++count;
+			}
+		}
+		rewind(fp);
 	}
-	rewind(fp);
 
 	if (count == 0) {
 		nc_error = NC_NOTFOUND;
-		(void) fclose(fp);
+		if (fp != NULL)
+			(void) fclose(fp);
 		return (NULL);
 	}
+
 	if ((listpp = malloc((count + 1) *
 	    sizeof (struct netconfig *))) == NULL) {
 		nc_error = NC_NOMEM;
-		(void) fclose(fp);
+		if (fp != NULL)
+			(void) fclose(fp);
 		return (NULL);
 	}
 
-	/*
-	 *	The following loop fills in the list (loops until
-	 *	fgetnetconfig() returns a NULL) and counts the
-	 *	number of entries placed in the list.  Note that
-	 *	when the loop is completed, the last entry in the
-	 *	list will contain a NULL (signifying the end of
-	 *	the list).
-	 */
-	linenum = 0;
-	for (tpp = listpp; *tpp = fgetnetconfig(fp, NULL); tpp++)
-		;
-	(void) fclose(fp);
+	if (brand_get_net_ent != NULL) {
+		int i;
 
-	if (nc_error != NC_NOMOREENTRIES) /* Something is screwed up */
-		netlist_free(&listpp);
+		tpp = listpp;
+		for (i = 0; i < count; i++) {
+			*tpp = brand_get_net_ent(i);
+			tpp++;
+		}
+		*tpp = NULL;
+		nc_error = NC_NOMOREENTRIES;
+	} else {
+		/*
+		 *	The following loop fills in the list (loops until
+		 *	fgetnetconfig() returns a NULL) and counts the
+		 *	number of entries placed in the list.  Note that
+		 *	when the loop is completed, the last entry in the
+		 *	list will contain a NULL (signifying the end of
+		 *	the list).
+		 */
+		linenum = 0;
+		for (tpp = listpp; *tpp = fgetnetconfig(fp, NULL); tpp++)
+			;
+		(void) fclose(fp);
+
+		if (nc_error != NC_NOMOREENTRIES) /* Something is screwed up */
+			netlist_free(&listpp);
+	}
+
 	return (listpp);
 }
 
--- old/usr/src/lib/libproc/common/Pcontrol.c	Wed Oct  5 00:31:56 2016
+++ new/usr/src/lib/libproc/common/Pcontrol.c	Wed Oct  5 00:31:56 2016
@@ -338,10 +338,16 @@
 void
 _libproc_init(void)
 {
+	const char *root;
+
 	_libproc_debug = getenv("LIBPROC_DEBUG") != NULL;
 	_libproc_no_qsort = getenv("LIBPROC_NO_QSORT") != NULL;
 	_libproc_incore_elf = getenv("LIBPROC_INCORE_ELF") != NULL;
 
+	if ((root = zone_get_nroot()) != NULL)
+		(void) snprintf(procfs_path, sizeof (procfs_path), "%s/proc",
+		    root);
+
 	(void) sigfillset(&blockable_sigs);
 	(void) sigdelset(&blockable_sigs, SIGKILL);
 	(void) sigdelset(&blockable_sigs, SIGSTOP);
@@ -1760,6 +1766,9 @@
 	case PR_SUSPENDED:
 		dprintf("%s: SUSPENDED\n", caller);
 		break;
+	case PR_BRAND:
+		dprintf("%s: BRANDPRIVATE (%d)\n", caller, lsp->pr_what);
+		break;
 	default:
 		dprintf("%s: Unknown\n", caller);
 		break;
@@ -1939,6 +1948,7 @@
 	case PR_FAULTED:
 	case PR_JOBCONTROL:
 	case PR_SUSPENDED:
+	case PR_BRAND:
 		break;
 	default:
 		errno = EPROTO;
@@ -3513,6 +3523,7 @@
 	case PR_FAULTED:
 	case PR_JOBCONTROL:
 	case PR_SUSPENDED:
+	case PR_BRAND:
 		break;
 	default:
 		errno = EPROTO;
--- old/usr/src/lib/libproc/common/Pcontrol.h	Wed Oct  5 00:31:56 2016
+++ new/usr/src/lib/libproc/common/Pcontrol.h	Wed Oct  5 00:31:56 2016
@@ -24,7 +24,7 @@
  */
 /*
  * Copyright 2012 DEY Storage Systems, Inc.  All rights reserved.
- * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2015, Joyent, Inc. All rights reserved.
  * Copyright (c) 2013 by Delphix. All rights reserved.
  */
 
@@ -92,10 +92,11 @@
 
 typedef struct file_info {	/* symbol information for a mapped file */
 	plist_t	file_list;	/* linked list */
-	char	file_pname[PRMAPSZ];	/* name from prmap_t */
+	char	file_pname[PATH_MAX];	/* name from prmap_t */
 	struct map_info *file_map;	/* primary (text) mapping */
 	int	file_ref;	/* references from map_info_t structures */
 	int	file_fd;	/* file descriptor for the mapped file */
+	int	file_dbgfile;	/* file descriptor for the debug file */
 	int	file_init;	/* 0: initialization yet to be performed */
 	GElf_Half file_etype;	/* ELF e_type from ehdr */
 	GElf_Half file_class;	/* ELF e_ident[EI_CLASS] from ehdr */
@@ -105,6 +106,7 @@
 	char	*file_rname;	/* resolved on-disk object pathname */
 	char	*file_rbase;	/* pointer to basename of file_rname */
 	Elf	*file_elf;	/* ELF handle so we can close */
+	Elf	*file_dbgelf;	/* Debug ELF handle so we can close */
 	void	*file_elfmem;	/* data for faked-up ELF handle */
 	sym_tbl_t file_symtab;	/* symbol table */
 	sym_tbl_t file_dynsym;	/* dynamic symbol table */
--- old/usr/src/lib/libproc/common/Pcore.c	Wed Oct  5 00:31:56 2016
+++ new/usr/src/lib/libproc/common/Pcore.c	Wed Oct  5 00:31:56 2016
@@ -2699,6 +2699,7 @@
 
 			fp->file_ref = 1;
 			fp->file_fd = -1;
+			fp->file_dbgfile = -1;
 
 			fp->file_lo = malloc(sizeof (rd_loadobj_t));
 			fp->file_lname = strdup(execname);
--- old/usr/src/lib/libproc/common/Pidle.c	Wed Oct  5 00:31:56 2016
+++ new/usr/src/lib/libproc/common/Pidle.c	Wed Oct  5 00:31:56 2016
@@ -226,6 +226,7 @@
 	}
 
 	fp->file_fd = fd;
+	fp->file_dbgfile = -1;
 	fp->file_lo->rl_lmident = LM_ID_BASE;
 	if ((fp->file_lname = strdup(fp->file_pname)) == NULL) {
 		*perr = G_STRANGE;
--- old/usr/src/lib/libproc/common/Psymtab.c	Wed Oct  5 00:31:56 2016
+++ new/usr/src/lib/libproc/common/Psymtab.c	Wed Oct  5 00:31:56 2016
@@ -21,7 +21,7 @@
 
 /*
  * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  * Copyright (c) 2013 by Delphix. All rights reserved.
  */
 
@@ -43,6 +43,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/sysmacros.h>
+#include <sys/crc32.h>
 
 #include "libproc.h"
 #include "Pcontrol.h"
@@ -61,6 +62,7 @@
 static int read_ehdr64(struct ps_prochandle *, Elf64_Ehdr *, uint_t *,
     uintptr_t);
 #endif
+static uint32_t psym_crc32[] = { CRC32_TABLE };
 
 #define	DATA_TYPES	\
 	((1 << STT_OBJECT) | (1 << STT_FUNC) | \
@@ -69,6 +71,22 @@
 
 #define	MA_RWX	(MA_READ | MA_WRITE | MA_EXEC)
 
+/*
+ * Minimum and maximum length of a build-id that we'll accept. Generally it's a
+ * 20 byte SHA1 and it's expected that the first byte (which is two ascii
+ * characters) indicates a directory and the remaining bytes become the file
+ * name. Therefore, our minimum length is at least 2 bytes (one for the
+ * directory and one for the name) and the max is a bit over the minimum -- 64,
+ * just in case folks do something odd. The string length is three times the max
+ * length. This accounts for the fact that each byte is two characters, a null
+ * terminator, and the directory '/' character.
+ */
+#define	MINBUILDID	2
+#define	MAXBUILDID	64
+#define	BUILDID_STRLEN	(3*MAXBUILDID)
+#define	BUILDID_NAME	".note.gnu.build-id"
+#define	DBGLINK_NAME	".gnu_debuglink"
+
 typedef enum {
 	PRO_NATURAL,
 	PRO_BYADDR,
@@ -184,6 +202,7 @@
 	mptr->map_file = fptr;
 	fptr->file_ref = 1;
 	fptr->file_fd = -1;
+	fptr->file_dbgfile = -1;
 	P->num_files++;
 
 	/*
@@ -274,6 +293,10 @@
 			free(fptr->file_elfmem);
 		if (fptr->file_fd >= 0)
 			(void) close(fptr->file_fd);
+		if (fptr->file_dbgelf)
+			(void) elf_end(fptr->file_dbgelf);
+		if (fptr->file_dbgfile >= 0)
+			(void) close(fptr->file_dbgfile);
 		if (fptr->file_ctfp) {
 			ctf_close(fptr->file_ctfp);
 			free(fptr->file_ctf_buf);
@@ -819,15 +842,29 @@
 Plmid_to_ctf(struct ps_prochandle *P, Lmid_t lmid, const char *name)
 {
 	map_info_t *mptr;
-	file_info_t *fptr;
+	file_info_t *fptr = NULL;
 
 	if (name == PR_OBJ_EVERY)
 		return (NULL);
 
-	if ((mptr = object_name_to_map(P, lmid, name)) == NULL ||
-	    (fptr = mptr->map_file) == NULL)
-		return (NULL);
+	/*
+	 * While most idle files are all ELF objects, not all of them have
+	 * mapping information available. There's nothing which would make
+	 * sense to fake up for ET_REL. Instead, if we're being asked for their
+	 * executable object and we know that the information is valid and they
+	 * only have a single file, we jump straight to that file pointer.
+	 */
+	if (P->state == PS_IDLE && name == PR_OBJ_EXEC && P->info_valid == 1 &&
+	    P->num_files == 1 && P->mappings == NULL) {
+		fptr = list_next(&P->file_head);
+	}
 
+	if (fptr == NULL) {
+		if ((mptr = object_name_to_map(P, lmid, name)) == NULL ||
+		    (fptr = mptr->map_file) == NULL)
+			return (NULL);
+	}
+
 	return (Pbuild_file_ctf(P, fptr));
 }
 
@@ -1544,7 +1581,7 @@
 
 static Elf *
 build_fake_elf(struct ps_prochandle *P, file_info_t *fptr, GElf_Ehdr *ehdr,
-	size_t *nshdrs, Elf_Data **shdata)
+    size_t *nshdrs, Elf_Data **shdata)
 {
 	size_t shstrndx;
 	Elf_Scn *scn;
@@ -1567,6 +1604,225 @@
 }
 
 /*
+ * Try and find the file described by path in the file system and validate that
+ * it matches our CRC before we try and process it for symbol information. If we
+ * instead have an ELF data section, then that means we're checking a build-id
+ * section instead. In that case we just need to find and bcmp the corresponding
+ * section.
+ *
+ * Before we validate if it's a valid CRC or data section, we check to ensure
+ * that it's a normal file and not anything else.
+ */
+static boolean_t
+build_alt_debug(file_info_t *fptr, const char *path, uint32_t crc,
+    Elf_Data *data)
+{
+	int fd;
+	struct stat st;
+	Elf *elf;
+	Elf_Scn *scn;
+	GElf_Shdr symshdr, strshdr;
+	Elf_Data *symdata, *strdata;
+	boolean_t valid;
+	uint32_t c = -1U;
+
+	if ((fd = open(path, O_RDONLY)) < 0)
+		return (B_FALSE);
+
+	if (fstat(fd, &st) != 0) {
+		(void) close(fd);
+		return (B_FALSE);
+	}
+
+	if (S_ISREG(st.st_mode) == 0) {
+		(void) close(fd);
+		return (B_FALSE);
+	}
+
+	/*
+	 * Only check the CRC if we've come here through a GNU debug link
+	 * section as opposed to the build id. This is indicated by having the
+	 * value of data be NULL.
+	 */
+	if (data == NULL) {
+		for (;;) {
+			char buf[4096];
+			ssize_t ret = read(fd, buf, sizeof (buf));
+			if (ret == -1) {
+				if (ret == EINTR)
+					continue;
+				(void) close(fd);
+				return (B_FALSE);
+			}
+			if (ret == 0) {
+				c = ~c;
+				if (c != crc) {
+					dprintf("crc mismatch, found: 0x%x "
+					    "expected 0x%x\n", c, crc);
+					(void) close(fd);
+					return (B_FALSE);
+				}
+				break;
+			}
+			CRC32(c, buf, ret, c, psym_crc32);
+		}
+	}
+
+	elf = elf_begin(fd, ELF_C_READ, NULL);
+	if (elf == NULL) {
+		(void) close(fd);
+		return (B_FALSE);
+	}
+
+	if (elf_kind(elf) != ELF_K_ELF) {
+		goto fail;
+	}
+
+	/*
+	 * If we have a data section, that indicates we have a build-id which
+	 * means we need to find the corresponding build-id section and compare
+	 * it.
+	 */
+	scn = NULL;
+	valid = B_FALSE;
+	for (scn = elf_nextscn(elf, scn); data != NULL && scn != NULL;
+	    scn = elf_nextscn(elf, scn)) {
+		GElf_Shdr hdr;
+		Elf_Data *ntdata;
+
+		if (gelf_getshdr(scn, &hdr) == NULL)
+			goto fail;
+
+		if (hdr.sh_type != SHT_NOTE)
+			continue;
+
+		if ((ntdata = elf_getdata(scn, NULL)) == NULL)
+			goto fail;
+
+		/*
+		 * First verify the data section sizes are equal, then the
+		 * section name. If that's all true, then we can just do a bcmp.
+		 */
+		if (data->d_size != ntdata->d_size)
+			continue;
+
+		dprintf("found corresponding section in alternate file\n");
+		if (bcmp(ntdata->d_buf, data->d_buf, data->d_size) != 0)
+			goto fail;
+
+		valid = B_TRUE;
+		break;
+	}
+	if (data != NULL && valid == B_FALSE) {
+		dprintf("failed to find a matching %s section in %s\n",
+		    BUILDID_NAME, path);
+		goto fail;
+	}
+
+
+	/*
+	 * Do two passes, first see if we have a symbol header, then see if we
+	 * can find the corresponding linked string table.
+	 */
+	scn = NULL;
+	for (scn = elf_nextscn(elf, scn); scn != NULL;
+	    scn = elf_nextscn(elf, scn)) {
+
+		if (gelf_getshdr(scn, &symshdr) == NULL)
+			goto fail;
+
+		if (symshdr.sh_type != SHT_SYMTAB)
+			continue;
+
+		if ((symdata = elf_getdata(scn, NULL)) == NULL)
+			goto fail;
+
+		break;
+	}
+	if (scn == NULL)
+		goto fail;
+
+	if ((scn = elf_getscn(elf, symshdr.sh_link)) == NULL)
+		goto fail;
+
+	if (gelf_getshdr(scn, &strshdr) == NULL)
+		goto fail;
+
+	if ((strdata = elf_getdata(scn, NULL)) == NULL)
+		goto fail;
+
+	fptr->file_symtab.sym_data_pri = symdata;
+	fptr->file_symtab.sym_symn += symshdr.sh_size / symshdr.sh_entsize;
+	fptr->file_symtab.sym_strs = strdata->d_buf;
+	fptr->file_symtab.sym_strsz = strdata->d_size;
+	fptr->file_symtab.sym_hdr_pri = symshdr;
+	fptr->file_symtab.sym_strhdr = strshdr;
+
+	dprintf("successfully loaded additional debug symbols for %s from %s\n",
+	    fptr->file_rname, path);
+
+	fptr->file_dbgfile = fd;
+	fptr->file_dbgelf = elf;
+	return (B_TRUE);
+fail:
+	(void) elf_end(elf);
+	(void) close(fd);
+	return (B_FALSE);
+}
+
+/*
+ * We're here because the object in question has no symbol information, that's a
+ * bit unfortunate. However, we've found that there's a .gnu_debuglink sitting
+ * around. By convention that means that given the current location of the
+ * object on disk, and the debug name that we found in the binary we need to
+ * search the following locations for a matching file.
+ *
+ * <dirname>/.debug/<debug-name>
+ * /usr/lib/debug/<dirname>/<debug-name>
+ *
+ * In the future, we should consider supporting looking in the prefix's
+ * lib/debug directory for a matching object or supporting an arbitrary user
+ * defined set of places to look.
+ */
+static void
+find_alt_debuglink(file_info_t *fptr, const char *name, uint32_t crc)
+{
+	boolean_t r;
+	char *dup = NULL, *path = NULL, *dname;
+
+	dprintf("find_alt_debug: looking for %s, crc 0x%x\n", name, crc);
+	if (fptr->file_rname == NULL) {
+		dprintf("find_alt_debug: encountered null file_rname\n");
+		return;
+	}
+
+	dup = strdup(fptr->file_rname);
+	if (dup == NULL)
+		return;
+
+	dname = dirname(dup);
+	if (asprintf(&path, "%s/.debug/%s", dname, name) != -1) {
+		dprintf("attempting to load alternate debug information "
+		    "from %s\n", path);
+		r = build_alt_debug(fptr, path, crc, NULL);
+		free(path);
+		if (r == B_TRUE)
+			goto out;
+	}
+
+	if (asprintf(&path, "/usr/lib/debug/%s/%s", dname, name) != -1) {
+		dprintf("attempting to load alternate debug information "
+		    "from %s\n", path);
+		r = build_alt_debug(fptr, path, crc, NULL);
+		free(path);
+		if (r == B_TRUE)
+			goto out;
+	}
+out:
+	free(dup);
+}
+
+/*
  * Build the symbol table for the given mapped file.
  */
 void
@@ -1587,7 +1843,8 @@
 		GElf_Shdr c_shdr;
 		Elf_Data *c_data;
 		const char *c_name;
-	} *cp, *cache = NULL, *dyn = NULL, *plt = NULL, *ctf = NULL;
+	} *cp, *cache = NULL, *dyn = NULL, *plt = NULL, *ctf = NULL,
+	*dbglink = NULL, *buildid = NULL;
 
 	if (fptr->file_init)
 		return;	/* We've already processed this file */
@@ -1813,10 +2070,154 @@
 				continue;
 			}
 			ctf = cp;
+		} else if (strcmp(cp->c_name, BUILDID_NAME) == 0) {
+			dprintf("Found a %s section for %s\n", BUILDID_NAME,
+			    fptr->file_rname);
+			/* The ElfXX_Nhdr is 32/64-bit neutral */
+			if (cp->c_shdr.sh_type == SHT_NOTE &&
+			    cp->c_data->d_buf != NULL &&
+			    cp->c_data->d_size >= sizeof (Elf32_Nhdr)) {
+				Elf32_Nhdr *hdr = cp->c_data->d_buf;
+				if (hdr->n_type != 3)
+					continue;
+				if (hdr->n_namesz != 4)
+					continue;
+				if (hdr->n_descsz < MINBUILDID)
+					continue;
+				/* Set a reasonable upper bound */
+				if (hdr->n_descsz > MAXBUILDID) {
+					dprintf("Skipped %s as too large "
+					    "(%ld)\n", BUILDID_NAME,
+					    (unsigned long)hdr->n_descsz);
+					continue;
+				}
+
+				if (cp->c_data->d_size < sizeof (hdr) +
+				    hdr->n_namesz + hdr->n_descsz)
+					continue;
+				buildid = cp;
+			}
+		} else if (strcmp(cp->c_name, DBGLINK_NAME) == 0) {
+			dprintf("found %s section for %s\n", DBGLINK_NAME,
+			    fptr->file_rname);
+			/*
+			 * Let's make sure of a few things before we do this.
+			 */
+			if (cp->c_shdr.sh_type == SHT_PROGBITS &&
+			    cp->c_data->d_buf != NULL &&
+			    cp->c_data->d_size) {
+				dbglink = cp;
+			}
 		}
 	}
 
 	/*
+	 * If we haven't found any symbol table information and we have found
+	 * either a .note.gnu.build-id or a .gnu_debuglink, it's time to try and
+	 * figure out where we might find this. Originally, GNU used the
+	 * .gnu_debuglink solely, but then they added a .note.gnu.build-id. The
+	 * build-id is some size, usually 16 or 20 bytes, often a SHA1 sum of
+	 * the old, but not present file. All that you have to do to compare
+	 * things is see if the sections are less, in theory saving you from
+	 * doing lots of expensive I/O.
+	 *
+	 * For the .note.gnu.build-id, we're going to check a few things before
+	 * using it, first that the name is 4 bytes, and is GNU and that the
+	 * type is 3, which they say is the build-id identifier.
+	 *
+	 * To verify that the elf data for the .gnu_debuglink seems somewhat
+	 * sane, eg. the elf data should be a string, so we want to verify we
+	 * have a null-terminator.
+	 */
+	if (fptr->file_symtab.sym_data_pri == NULL && buildid != NULL) {
+		int i, bo;
+		uint8_t *dp;
+		char buf[BUILDID_STRLEN], *path;
+		Elf32_Nhdr *hdr = buildid->c_data->d_buf;
+
+		/*
+		 * This was checked for validity when assigning the buildid
+		 * variable.
+		 */
+		bzero(buf, sizeof (buf));
+		dp = (uint8_t *)((uintptr_t)hdr + sizeof (*hdr) +
+		    hdr->n_namesz);
+		for (i = 0, bo = 0; i < hdr->n_descsz; i++, bo += 2, dp++) {
+			assert(sizeof (buf) - bo > 0);
+
+			/*
+			 * Recall that the build-id is structured as a series of
+			 * bytes. However, the first two characters are supposed
+			 * to represent a directory. Hence, once we reach offset
+			 * two, we insert a '/' character.
+			 */
+			if (bo == 2) {
+				buf[bo] = '/';
+				bo++;
+			}
+			(void) snprintf(buf + bo, sizeof (buf) - bo, "%2x",
+			    *dp);
+		}
+
+		if (asprintf(&path, "/usr/lib/debug/.build-id/%s.debug",
+		    buf) != -1) {
+			boolean_t r;
+			dprintf("attempting to find build id alternate debug "
+			    "file at %s\n", path);
+			r = build_alt_debug(fptr, path, 0, buildid->c_data);
+			dprintf("attempt %s\n", r == B_TRUE ?
+			    "succeeded" : "failed");
+			free(path);
+		} else {
+			dprintf("failed to construct build id path: %s\n",
+			    strerror(errno));
+		}
+	}
+
+	if (fptr->file_symtab.sym_data_pri == NULL && dbglink != NULL) {
+		char *c = dbglink->c_data->d_buf;
+		size_t i;
+		boolean_t found = B_FALSE;
+		Elf_Data *ed = dbglink->c_data;
+		uint32_t crc;
+
+		for (i = 0; i < ed->d_size; i++) {
+			if (c[i] == '\0') {
+				uintptr_t off;
+				dprintf("got .gnu_debuglink terminator at "
+				    "offset %lu\n", (unsigned long)i);
+				/*
+				 * After the null terminator, there should be
+				 * padding, followed by a 4 byte CRC of the
+				 * file. If we don't see this, we're going to
+				 * assume this is bogus.
+				 */
+				if ((i % sizeof (uint32_t)) == 0) {
+					i += 4;
+				} else {
+					i += sizeof (uint32_t) -
+					    (i % sizeof (uint32_t));
+				}
+				if (i + sizeof (uint32_t) ==
+				    dbglink->c_data->d_size) {
+					found = B_TRUE;
+					off = (uintptr_t)ed->d_buf + i;
+					crc = *(uint32_t *)off;
+				} else {
+					dprintf(".gnu_debuglink size mismatch, "
+					    "expected: %lu, found: %lu\n",
+					    (unsigned long)i,
+					    (unsigned long)ed->d_size);
+				}
+				break;
+			}
+		}
+
+		if (found == B_TRUE)
+			find_alt_debuglink(fptr, dbglink->c_data->d_buf, crc);
+	}
+
+	/*
 	 * At this point, we've found all the symbol tables we're ever going
 	 * to find: the ones in the loop above and possibly the symtab that
 	 * was included in the core file. Before we perform any lookups, we
@@ -1943,7 +2344,13 @@
 		fptr->file_elfmem = NULL;
 	}
 	(void) close(fptr->file_fd);
+	if (fptr->file_dbgelf != NULL)
+		(void) elf_end(fptr->file_dbgelf);
+	fptr->file_dbgelf = NULL;
+	if (fptr->file_dbgfile >= 0)
+		(void) close(fptr->file_dbgfile);
 	fptr->file_fd = -1;
+	fptr->file_dbgfile = -1;
 }
 
 /*
@@ -2579,7 +2986,7 @@
  */
 int
 Plookup_by_name(struct ps_prochandle *P, const char *object,
-	const char *symbol, GElf_Sym *symp)
+    const char *symbol, GElf_Sym *symp)
 {
 	return (Pxlookup_by_name(P, PR_LMID_EVERY, object, symbol, symp, NULL));
 }
@@ -2677,7 +3084,7 @@
 
 static char *
 i_Pobjname(struct ps_prochandle *P, boolean_t lmresolve, uintptr_t addr,
-	char *buffer, size_t bufsize)
+    char *buffer, size_t bufsize)
 {
 	map_info_t *mptr;
 	file_info_t *fptr;
@@ -2710,7 +3117,7 @@
  */
 char *
 Pobjname(struct ps_prochandle *P, uintptr_t addr,
-	char *buffer, size_t bufsize)
+    char *buffer, size_t bufsize)
 {
 	return (i_Pobjname(P, B_FALSE, addr, buffer, bufsize));
 }
@@ -2725,7 +3132,7 @@
  */
 char *
 Pobjname_resolved(struct ps_prochandle *P, uintptr_t addr,
-	char *buffer, size_t bufsize)
+    char *buffer, size_t bufsize)
 {
 	return (i_Pobjname(P, B_TRUE, addr, buffer, bufsize));
 }
--- old/usr/src/lib/libzonecfg/Makefile.com	Wed Oct  5 00:31:57 2016
+++ new/usr/src/lib/libzonecfg/Makefile.com	Wed Oct  5 00:31:57 2016
@@ -20,11 +20,14 @@
 #
 #
 # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2015 Joyent, Inc.
 #
 
 LIBRARY=	libzonecfg.a
 VERS=		.1
-OBJECTS=	libzonecfg.o getzoneent.o scratchops.o
+LIB_OBJS=	libzonecfg.o getzoneent.o scratchops.o
+XML_OBJS=	os_dtd.o
+OBJECTS=	$(LIB_OBJS) $(XML_OBJS)
 
 include ../../Makefile.lib
 
@@ -35,11 +38,19 @@
 $(DYNLIB) :=	LDLIBS += -lxml2
 
 SRCDIR =	../common
+
+XMLDIR =	$(SRC)/lib/xml
+SRCS = \
+		$(LIB_OBJS:%.o=$(SRCDIR)/%.c) \
+		$(XML_OBJS:%.o=$(XMLDIR)/%.c) \
+
 CPPFLAGS +=	-I$(ADJUNCT_PROTO)/usr/include/libxml2 -I$(SRCDIR) -D_REENTRANT
 CERRWARN +=	-_gcc=-Wno-uninitialized
 CERRWARN +=	-_gcc=-Wno-parentheses
 $(LINTLIB) := SRCS=	$(SRCDIR)/$(LINTSRC)
 
+CPPFLAGS +=	-I$(XMLDIR)
+
 .KEEP_STATE:
 
 all:	$(LIBS)
@@ -46,4 +57,8 @@
 
 lint:	lintcheck
 
+pics/%.o: $(XMLDIR)/%.c
+	$(COMPILE.c) -o $@ $<
+	$(POST_PROCESS_O)
+
 include ../../Makefile.targ
--- old/usr/src/lib/libzonecfg/common/getzoneent.c	Wed Oct  5 00:31:57 2016
+++ new/usr/src/lib/libzonecfg/common/getzoneent.c	Wed Oct  5 00:31:57 2016
@@ -20,6 +20,7 @@
  */
 /*
  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
  */
 
 
@@ -170,18 +171,40 @@
 }
 
 static boolean_t
-get_index_path(char *path)
+path_common(char *path, size_t path_size, const char *stem)
 {
-	return (snprintf(path, MAXPATHLEN, "%s%s", zonecfg_root,
-	    ZONE_INDEX_FILE) < MAXPATHLEN);
+	const char *native_root = zone_get_nroot();
+
+	if (native_root == NULL || zonecfg_in_alt_root()) {
+		/*
+		 * Do not prepend the native system root (e.g. "/native") if an
+		 * alternative configuration root has been selected.
+		 */
+		native_root = "";
+	}
+
+	return (snprintf(path, path_size, "%s%s%s", native_root, zonecfg_root,
+	    stem) < path_size);
 }
 
+static boolean_t
+get_index_path(char *path, size_t path_size)
+{
+	return (path_common(path, path_size, ZONE_INDEX_FILE));
+}
+
+static boolean_t
+get_temp_path(char *path, size_t path_size)
+{
+	return (path_common(path, path_size, _PATH_TMPFILE));
+}
+
 FILE *
 setzoneent(void)
 {
 	char path[MAXPATHLEN];
 
-	if (!get_index_path(path)) {
+	if (!get_index_path(path, sizeof (path))) {
 		errno = EINVAL;
 		return (NULL);
 	}
@@ -202,8 +225,7 @@
 	struct flock lock;
 	char path[MAXPATHLEN];
 
-	if (snprintf(path, sizeof (path), "%s%s", zonecfg_root,
-	    ZONE_INDEX_LOCK_DIR) >= sizeof (path))
+	if (!path_common(path, sizeof (path), ZONE_INDEX_LOCK_DIR))
 		return (-1);
 	if ((mkdir(path, S_IRWXU) == -1) && errno != EEXIST)
 		return (-1);
@@ -269,13 +291,14 @@
 putzoneent(struct zoneent *ze, zoneent_op_t operation)
 {
 	FILE *index_file, *tmp_file;
-	char *tmp_file_name, buf[MAX_INDEX_LEN];
+	char buf[MAX_INDEX_LEN];
 	int tmp_file_desc, lock_fd, err;
 	boolean_t exist, need_quotes;
 	char *cp;
+	char tmp_path[MAXPATHLEN];
 	char path[MAXPATHLEN];
 	char uuidstr[UUID_PRINTABLE_STRING_LENGTH];
-	size_t tlen, namelen;
+	size_t namelen;
 	const char *zone_name, *zone_state, *zone_path, *zone_uuid;
 
 	assert(ze != NULL);
@@ -299,20 +322,14 @@
 	if ((lock_fd = lock_index_file()) == -1)
 		return (Z_LOCKING_FILE);
 
-	/* using sizeof gives us room for the terminating NUL byte as well */
-	tlen = sizeof (_PATH_TMPFILE) + strlen(zonecfg_root);
-	tmp_file_name = malloc(tlen);
-	if (tmp_file_name == NULL) {
+	if (!get_temp_path(tmp_path, sizeof (tmp_path))) {
 		(void) unlock_index_file(lock_fd);
 		return (Z_NOMEM);
 	}
-	(void) snprintf(tmp_file_name, tlen, "%s%s", zonecfg_root,
-	    _PATH_TMPFILE);
 
-	tmp_file_desc = mkstemp(tmp_file_name);
+	tmp_file_desc = mkstemp(tmp_path);
 	if (tmp_file_desc == -1) {
-		(void) unlink(tmp_file_name);
-		free(tmp_file_name);
+		(void) unlink(tmp_path);
 		(void) unlock_index_file(lock_fd);
 		return (Z_TEMP_FILE);
 	}
@@ -323,7 +340,7 @@
 		err = Z_MISC_FS;
 		goto error;
 	}
-	if (!get_index_path(path)) {
+	if (!get_index_path(path, sizeof (path))) {
 		err = Z_MISC_FS;
 		goto error;
 	}
@@ -464,11 +481,10 @@
 		goto error;
 	}
 	tmp_file = NULL;
-	if (rename(tmp_file_name, path) == -1) {
+	if (rename(tmp_path, path) == -1) {
 		err = errno == EACCES ? Z_ACCES : Z_MISC_FS;
 		goto error;
 	}
-	free(tmp_file_name);
 	if (unlock_index_file(lock_fd) != Z_OK)
 		return (Z_UNLOCKING_FILE);
 	return (Z_OK);
@@ -478,8 +494,7 @@
 		(void) fclose(index_file);
 	if (tmp_file != NULL)
 		(void) fclose(tmp_file);
-	(void) unlink(tmp_file_name);
-	free(tmp_file_name);
+	(void) unlink(tmp_path);
 	(void) unlock_index_file(lock_fd);
 	return (err);
 }
--- old/usr/src/lib/libzonecfg/common/libzonecfg.c	Wed Oct  5 00:31:57 2016
+++ new/usr/src/lib/libzonecfg/common/libzonecfg.c	Wed Oct  5 00:31:57 2016
@@ -23,6 +23,7 @@
  * Copyright 2014 Gary Mills
  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2015 Joyent Inc.
  */
 
 #include <libsysevent.h>
@@ -59,6 +60,8 @@
 #include <secdb.h>
 #include <user_attr.h>
 #include <prof_attr.h>
+#include <sys/debug.h>
+#include <os_dtd.h>
 
 #include <arpa/inet.h>
 #include <netdb.h>
@@ -79,6 +82,8 @@
 #define	ZONE_EVENT_PING_SUBCLASS	"ping"
 #define	ZONE_EVENT_PING_PUBLISHER	"solaris"
 
+#define	DEBUGID_FILE	"/etc/zones/did.txt"
+
 /* Hard-code the DTD element/attribute/entity names just once, here. */
 #define	DTD_ELEM_ATTR		(const xmlChar *) "attr"
 #define	DTD_ELEM_COMMENT	(const xmlChar *) "comment"
@@ -86,6 +91,7 @@
 #define	DTD_ELEM_FS		(const xmlChar *) "filesystem"
 #define	DTD_ELEM_FSOPTION	(const xmlChar *) "fsoption"
 #define	DTD_ELEM_NET		(const xmlChar *) "network"
+#define	DTD_ELEM_NETATTR	(const xmlChar *) "net-attr"
 #define	DTD_ELEM_RCTL		(const xmlChar *) "rctl"
 #define	DTD_ELEM_RCTLVALUE	(const xmlChar *) "rctl-value"
 #define	DTD_ELEM_ZONE		(const xmlChar *) "zone"
@@ -105,10 +111,12 @@
 #define	DTD_ATTR_IPTYPE		(const xmlChar *) "ip-type"
 #define	DTD_ATTR_DEFROUTER	(const xmlChar *) "defrouter"
 #define	DTD_ATTR_DIR		(const xmlChar *) "directory"
+#define	DTD_ATTR_GNIC		(const xmlChar *) "global-nic"
 #define	DTD_ATTR_LIMIT		(const xmlChar *) "limit"
 #define	DTD_ATTR_LIMITPRIV	(const xmlChar *) "limitpriv"
 #define	DTD_ATTR_BOOTARGS	(const xmlChar *) "bootargs"
 #define	DTD_ATTR_SCHED		(const xmlChar *) "scheduling-class"
+#define	DTD_ATTR_MAC		(const xmlChar *) "mac-addr"
 #define	DTD_ATTR_MATCH		(const xmlChar *) "match"
 #define	DTD_ATTR_NAME		(const xmlChar *) "name"
 #define	DTD_ATTR_PHYSICAL	(const xmlChar *) "physical"
@@ -118,6 +126,7 @@
 #define	DTD_ATTR_SPECIAL	(const xmlChar *) "special"
 #define	DTD_ATTR_TYPE		(const xmlChar *) "type"
 #define	DTD_ATTR_VALUE		(const xmlChar *) "value"
+#define	DTD_ATTR_VLANID		(const xmlChar *) "vlan-id"
 #define	DTD_ATTR_ZONEPATH	(const xmlChar *) "zonepath"
 #define	DTD_ATTR_NCPU_MIN	(const xmlChar *) "ncpu_min"
 #define	DTD_ATTR_NCPU_MAX	(const xmlChar *) "ncpu_max"
@@ -130,6 +139,7 @@
 #define	DTD_ATTR_MODE		(const xmlChar *) "mode"
 #define	DTD_ATTR_ACL		(const xmlChar *) "acl"
 #define	DTD_ATTR_BRAND		(const xmlChar *) "brand"
+#define	DTD_ATTR_DID		(const xmlChar *) "debugid"
 #define	DTD_ATTR_HOSTID		(const xmlChar *) "hostid"
 #define	DTD_ATTR_USER		(const xmlChar *) "user"
 #define	DTD_ATTR_AUTHS		(const xmlChar *) "auths"
@@ -176,6 +186,8 @@
 	{ALIAS_MAXSEMIDS, "zone.max-sem-ids", "privileged", "deny", 0},
 	{ALIAS_MAXLOCKEDMEM, "zone.max-locked-memory", "privileged", "deny", 0},
 	{ALIAS_MAXSWAP, "zone.max-swap", "privileged", "deny", 0},
+	{ALIAS_MAXPHYSMEM, "zone.max-physical-memory", "privileged", "deny",
+	    1048576},
 	{ALIAS_SHARES, "zone.cpu-shares", "privileged", "none", 0},
 	{ALIAS_CPUCAP, "zone.cpu-cap", "privileged", "deny", 0},
 	{ALIAS_MAXPROCS, "zone.max-processes", "privileged", "deny", 100},
@@ -269,19 +281,37 @@
  */
 
 static boolean_t
-config_file_path(const char *zonename, char *answer)
+file_path_common(const char *zonename, const char *subdir, const char *stem,
+    char *answer, size_t answer_size)
 {
-	return (snprintf(answer, MAXPATHLEN, "%s%s/%s.xml", zonecfg_root,
-	    ZONE_CONFIG_ROOT, zonename) < MAXPATHLEN);
+	const char *native_root = zone_get_nroot();
+
+	if (native_root == NULL || zonecfg_in_alt_root()) {
+		/*
+		 * Do not prepend the native system root (e.g. "/native") if an
+		 * alternative configuration root has been selected.
+		 */
+		native_root = "";
+	}
+
+	return (snprintf(answer, answer_size, "%s%s%s/%s.%s", native_root,
+	    zonecfg_root, subdir, zonename, stem) < answer_size);
 }
 
 static boolean_t
-snap_file_path(const char *zonename, char *answer)
+config_file_path(const char *zonename, char *answer, size_t answer_size)
 {
-	return (snprintf(answer, MAXPATHLEN, "%s%s/%s.snapshot.xml",
-	    zonecfg_root, ZONE_SNAPSHOT_ROOT, zonename) < MAXPATHLEN);
+	return (file_path_common(zonename, ZONE_CONFIG_ROOT, "xml", answer,
+	    answer_size));
 }
 
+static boolean_t
+snap_file_path(const char *zonename, char *answer, size_t answer_size)
+{
+	return (file_path_common(zonename, ZONE_SNAPSHOT_ROOT, "snapshot.xml",
+	    answer, answer_size));
+}
+
 /*ARGSUSED*/
 static void
 zonecfg_error_func(void *ctx, const char *msg, ...)
@@ -350,7 +380,7 @@
 	int err, state_err;
 	zone_state_t state;
 
-	if (!config_file_path(zonename, path))
+	if (!config_file_path(zonename, path, sizeof (path)))
 		return (Z_MISC_FS);
 
 	state_err = zone_get_state((char *)zonename, &state);
@@ -407,7 +437,7 @@
 {
 	char path[MAXPATHLEN];
 
-	if (!snap_file_path(zonename, path))
+	if (!snap_file_path(zonename, path, sizeof (path)))
 		return (Z_MISC_FS);
 	return (zonecfg_destroy_impl(path));
 }
@@ -574,9 +604,8 @@
 zonecfg_get_handle_impl(const char *zonename, const char *filename,
     zone_dochandle_t handle)
 {
-	xmlValidCtxtPtr cvp;
 	struct stat statbuf;
-	int valid;
+	boolean_t valid;
 
 	if (zonename == NULL)
 		return (Z_NO_ZONE);
@@ -587,14 +616,13 @@
 			return (Z_INVALID_DOCUMENT);
 		return (Z_NO_ZONE);
 	}
-	if ((cvp = xmlNewValidCtxt()) == NULL)
+
+	if (os_dtd_validate(handle->zone_dh_doc, B_FALSE, &valid) != 0) {
 		return (Z_NOMEM);
-	cvp->error = zonecfg_error_func;
-	cvp->warning = zonecfg_error_func;
-	valid = xmlValidateDocument(cvp, handle->zone_dh_doc);
-	xmlFreeValidCtxt(cvp);
-	if (valid == 0)
+	}
+	if (!valid) {
 		return (Z_INVALID_DOCUMENT);
+	}
 
 	/* delete any comments such as inherited Sun copyright / ident str */
 	stripcomments(handle);
@@ -606,7 +634,7 @@
 {
 	char path[MAXPATHLEN];
 
-	if (!config_file_path(zonename, path))
+	if (!config_file_path(zonename, path, sizeof (path)))
 		return (Z_MISC_FS);
 	handle->zone_dh_newzone = B_FALSE;
 
@@ -650,7 +678,7 @@
 {
 	char path[MAXPATHLEN];
 
-	if (!snap_file_path(zonename, path))
+	if (!snap_file_path(zonename, path, sizeof (path)))
 		return (Z_MISC_FS);
 	handle->zone_dh_newzone = B_FALSE;
 	return (zonecfg_get_handle_impl(zonename, path, handle));
@@ -663,7 +691,7 @@
 	char path[MAXPATHLEN];
 	int err;
 
-	if (!config_file_path(template, path))
+	if (!config_file_path(template, path, sizeof (path)))
 		return (Z_MISC_FS);
 
 	if ((err = zonecfg_get_handle_impl(template, path, handle)) != Z_OK)
@@ -697,21 +725,19 @@
 zonecfg_attach_manifest(int fd, zone_dochandle_t local_handle,
     zone_dochandle_t rem_handle)
 {
-	xmlValidCtxtPtr cvp;
-	int valid;
+	boolean_t valid;
 
 	/* load the manifest into the handle for the remote system */
 	if ((rem_handle->zone_dh_doc = xmlReadFd(fd, NULL, NULL, 0)) == NULL) {
 		return (Z_INVALID_DOCUMENT);
 	}
-	if ((cvp = xmlNewValidCtxt()) == NULL)
+
+	if (os_dtd_validate(rem_handle->zone_dh_doc, B_FALSE, &valid) != 0) {
 		return (Z_NOMEM);
-	cvp->error = zonecfg_error_func;
-	cvp->warning = zonecfg_error_func;
-	valid = xmlValidateDocument(cvp, rem_handle->zone_dh_doc);
-	xmlFreeValidCtxt(cvp);
-	if (valid == 0)
+	}
+	if (!valid) {
 		return (Z_INVALID_DOCUMENT);
+	}
 
 	/* delete any comments such as inherited Sun copyright / ident str */
 	stripcomments(rem_handle);
@@ -732,14 +758,12 @@
 	 * We need to re-run xmlValidateDocument on local_handle to properly
 	 * update the in-core representation of the configuration.
 	 */
-	if ((cvp = xmlNewValidCtxt()) == NULL)
+	if (os_dtd_validate(local_handle->zone_dh_doc, B_FALSE, &valid) != 0) {
 		return (Z_NOMEM);
-	cvp->error = zonecfg_error_func;
-	cvp->warning = zonecfg_error_func;
-	valid = xmlValidateDocument(cvp, local_handle->zone_dh_doc);
-	xmlFreeValidCtxt(cvp);
-	if (valid == 0)
+	}
+	if (!valid) {
 		return (Z_INVALID_DOCUMENT);
+	}
 
 	strip_sw_inv(local_handle);
 
@@ -1193,9 +1217,9 @@
 {
 	char tmpfile[MAXPATHLEN];
 	char bakdir[MAXPATHLEN], bakbase[MAXPATHLEN], bakfile[MAXPATHLEN];
-	int tmpfd, err, valid;
-	xmlValidCtxt cvp = { NULL };
+	int tmpfd, err;
 	boolean_t backup;
+	boolean_t valid;
 
 	(void) strlcpy(tmpfile, filename, sizeof (tmpfile));
 	(void) dirname(tmpfile);
@@ -1208,16 +1232,13 @@
 	}
 	(void) close(tmpfd);
 
-	cvp.error = zonecfg_error_func;
-	cvp.warning = zonecfg_error_func;
-
 	/*
 	 * We do a final validation of the document.  Since the library has
 	 * malfunctioned if it fails to validate, we follow-up with an
 	 * assert() that the doc is valid.
 	 */
-	valid = xmlValidateDocument(&cvp, handle->zone_dh_doc);
-	assert(valid != 0);
+	VERIFY0(os_dtd_validate(handle->zone_dh_doc, B_FALSE, &valid));
+	VERIFY(valid == B_TRUE);
 
 	if (xmlSaveFormatFile(tmpfile, handle->zone_dh_doc, 1) <= 0)
 		goto err;
@@ -1272,7 +1293,6 @@
 			/*
 			 * Try to restore from our backup.
 			 */
-			(void) unlink(filename);
 			(void) rename(bakfile, filename);
 		} else {
 			/*
@@ -1315,7 +1335,7 @@
 	if ((err = zonecfg_get_name(handle, zname, sizeof (zname))) != Z_OK)
 		return (err);
 
-	if (!config_file_path(zname, path))
+	if (!config_file_path(zname, path, sizeof (path)))
 		return (Z_MISC_FS);
 
 	addcomment(handle, "\n    DO NOT EDIT THIS "
@@ -1336,8 +1356,10 @@
 	handle->zone_dh_newzone = B_FALSE;
 
 	if (is_renaming(handle)) {
-		if (config_file_path(handle->zone_dh_delete_name, delpath))
+		if (config_file_path(handle->zone_dh_delete_name, delpath,
+		    sizeof (delpath))) {
 			(void) unlink(delpath);
+		}
 		handle->zone_dh_delete_name[0] = '\0';
 	}
 
@@ -1347,23 +1369,18 @@
 int
 zonecfg_verify_save(zone_dochandle_t handle, char *filename)
 {
-	int valid;
+	boolean_t valid;
 
-	xmlValidCtxt cvp = { NULL };
-
 	if (zonecfg_check_handle(handle) != Z_OK)
 		return (Z_BAD_HANDLE);
 
-	cvp.error = zonecfg_error_func;
-	cvp.warning = zonecfg_error_func;
-
 	/*
 	 * We do a final validation of the document.  Since the library has
 	 * malfunctioned if it fails to validate, we follow-up with an
 	 * assert() that the doc is valid.
 	 */
-	valid = xmlValidateDocument(&cvp, handle->zone_dh_doc);
-	assert(valid != 0);
+	VERIFY0(os_dtd_validate(handle->zone_dh_doc, B_FALSE, &valid));
+	VERIFY(valid == B_TRUE);
 
 	if (xmlSaveFormatFile(filename, handle->zone_dh_doc, 1) <= 0)
 		return (Z_SAVING_FILE);
@@ -1377,9 +1394,8 @@
 	char zname[ZONENAME_MAX];
 	char path[MAXPATHLEN];
 	char migpath[MAXPATHLEN];
-	xmlValidCtxt cvp = { NULL };
 	int err = Z_SAVING_FILE;
-	int valid;
+	boolean_t valid;
 
 	if (zonecfg_check_handle(handle) != Z_OK)
 		return (Z_BAD_HANDLE);
@@ -1406,16 +1422,13 @@
 	addcomment(handle, "\n    DO NOT EDIT THIS FILE.  "
 	    "Use zonecfg(1M) and zoneadm(1M) attach.\n");
 
-	cvp.error = zonecfg_error_func;
-	cvp.warning = zonecfg_error_func;
-
 	/*
 	 * We do a final validation of the document.  Since the library has
 	 * malfunctioned if it fails to validate, we follow-up with an
 	 * assert() that the doc is valid.
 	 */
-	valid = xmlValidateDocument(&cvp, handle->zone_dh_doc);
-	assert(valid != 0);
+	VERIFY0(os_dtd_validate(handle->zone_dh_doc, B_FALSE, &valid));
+	VERIFY(valid == B_TRUE);
 
 	if (xmlSaveFormatFile(migpath, handle->zone_dh_doc, 1) <= 0)
 		return (Z_SAVING_FILE);
@@ -1488,7 +1501,7 @@
 {
 	char path[MAXPATHLEN];
 
-	if (!config_file_path(zonename, path))
+	if (!config_file_path(zonename, path, sizeof (path)))
 		return (Z_INVAL);
 	if (access(path, amode) == 0)
 		return (Z_OK);
@@ -1553,7 +1566,7 @@
 		goto out;
 	}
 
-	if (!snap_file_path(zonename, path)) {
+	if (!snap_file_path(zonename, path, sizeof (path))) {
 		error = Z_MISC_FS;
 		goto out;
 	}
@@ -2084,13 +2097,17 @@
 int
 zonecfg_lookup_nwif(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
 {
-	xmlNodePtr cur;
+	xmlNodePtr cur, val;
 	xmlNodePtr firstmatch;
 	int err;
 	char address[INET6_ADDRSTRLEN];
 	char physical[LIFNAMSIZ];
+	char mac[MAXMACADDRLEN];
+	char gnic[LIFNAMSIZ];
 	size_t addrspec;		/* nonzero if tabptr has IP addr */
 	size_t physspec;		/* nonzero if tabptr has interface */
+	size_t macspec;			/* nonzero if tabptr has mac addr */
+	size_t gnicspec;		/* nonzero if tabptr has gnic */
 	size_t defrouterspec;		/* nonzero if tabptr has def. router */
 	size_t allowed_addrspec;
 	zone_iptype_t iptype;
@@ -2102,17 +2119,20 @@
 	 * Determine the fields that will be searched.  There must be at least
 	 * one.
 	 *
-	 * zone_nwif_address, zone_nwif_physical, and zone_nwif_defrouter are
+	 * zone_nwif_address, zone_nwif_physical, zone_nwif_defrouter,
+	 * zone_nwif_mac, zone_nwif_vlan_id and zone_nwif_gnic  are
 	 * arrays, so no NULL checks are necessary.
 	 */
 	addrspec = strlen(tabptr->zone_nwif_address);
 	physspec = strlen(tabptr->zone_nwif_physical);
+	macspec = strlen(tabptr->zone_nwif_mac);
+	gnicspec = strlen(tabptr->zone_nwif_gnic);
 	defrouterspec = strlen(tabptr->zone_nwif_defrouter);
 	allowed_addrspec = strlen(tabptr->zone_nwif_allowed_address);
 	if (addrspec != 0 && allowed_addrspec != 0)
 		return (Z_INVAL); /* can't specify both */
 	if (addrspec == 0 && physspec == 0 && defrouterspec == 0 &&
-	    allowed_addrspec == 0)
+	    allowed_addrspec == 0 && macspec == 0 && gnicspec == 0)
 		return (Z_INSUFFICIENT_SPEC);
 
 	if ((err = operation_prep(handle)) != Z_OK)
@@ -2139,6 +2159,15 @@
 		    physical, sizeof (physical)) != Z_OK ||
 		    strcmp(tabptr->zone_nwif_physical, physical) != 0))
 			continue;
+		if (iptype == ZS_EXCLUSIVE && macspec != 0 &&
+		    (fetchprop(cur, DTD_ATTR_MAC, mac, sizeof (mac)) != Z_OK ||
+		    strcmp(tabptr->zone_nwif_mac, mac) != 0))
+			continue;
+		if (iptype == ZS_EXCLUSIVE && gnicspec != 0 &&
+		    (fetchprop(cur, DTD_ATTR_GNIC, gnic,
+		    sizeof (gnic)) != Z_OK ||
+		    strcmp(tabptr->zone_nwif_gnic, gnic) != 0))
+			continue;
 		if (iptype == ZS_SHARED && addrspec != 0 &&
 		    (fetchprop(cur, DTD_ATTR_ADDRESS, address,
 		    sizeof (address)) != Z_OK ||
@@ -2181,6 +2210,21 @@
 		return (err);
 
 	if (iptype == ZS_EXCLUSIVE &&
+	    (err = fetchprop(cur, DTD_ATTR_MAC, tabptr->zone_nwif_mac,
+	    sizeof (tabptr->zone_nwif_mac))) != Z_OK)
+		return (err);
+
+	if (iptype == ZS_EXCLUSIVE &&
+	    (err = fetchprop(cur, DTD_ATTR_VLANID, tabptr->zone_nwif_vlan_id,
+	    sizeof (tabptr->zone_nwif_vlan_id))) != Z_OK)
+		return (err);
+
+	if (iptype == ZS_EXCLUSIVE &&
+	    (err = fetchprop(cur, DTD_ATTR_GNIC, tabptr->zone_nwif_gnic,
+	    sizeof (tabptr->zone_nwif_gnic))) != Z_OK)
+		return (err);
+
+	if (iptype == ZS_EXCLUSIVE &&
 	    (err = fetchprop(cur, DTD_ATTR_ALLOWED_ADDRESS,
 	    tabptr->zone_nwif_allowed_address,
 	    sizeof (tabptr->zone_nwif_allowed_address))) != Z_OK)
@@ -2191,6 +2235,32 @@
 	    sizeof (tabptr->zone_nwif_defrouter))) != Z_OK)
 		return (err);
 
+	tabptr->zone_nwif_attrp = NULL;
+	for (val = cur->xmlChildrenNode; val != NULL; val = val->next) {
+		struct zone_res_attrtab *valptr;
+
+		valptr = (struct zone_res_attrtab *)malloc(
+		    sizeof (struct zone_res_attrtab));
+		if (valptr == NULL)
+			return (Z_NOMEM);
+
+		valptr->zone_res_attr_name[0] =
+		    valptr->zone_res_attr_value[0] = '\0';
+		if (zonecfg_add_res_attr(&(tabptr->zone_nwif_attrp), valptr)
+		    != Z_OK) {
+			free(valptr);
+			break;
+		}
+
+		if ((fetchprop(val, DTD_ATTR_NAME, valptr->zone_res_attr_name,
+		    sizeof (valptr->zone_res_attr_name)) != Z_OK))
+			break;
+		if ((fetchprop(val, DTD_ATTR_VALUE,
+		    valptr->zone_res_attr_value,
+		    sizeof (valptr->zone_res_attr_value)) != Z_OK))
+			break;
+	}
+
 	return (Z_OK);
 }
 
@@ -2197,7 +2267,8 @@
 static int
 zonecfg_add_nwif_core(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
 {
-	xmlNodePtr newnode, cur = handle->zone_dh_cur;
+	xmlNodePtr newnode, cur = handle->zone_dh_cur, valnode;
+	struct zone_res_attrtab *valptr;
 	int err;
 
 	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_NET, NULL);
@@ -2213,13 +2284,40 @@
 	    tabptr->zone_nwif_physical)) != Z_OK)
 		return (err);
 	/*
-	 * Do not add this property when it is not set, for backwards
-	 * compatibility and because it is optional.
+	 * Do not add these properties when they are not set, for backwards
+	 * compatibility and because they are optional.
 	 */
 	if ((strlen(tabptr->zone_nwif_defrouter) > 0) &&
 	    ((err = newprop(newnode, DTD_ATTR_DEFROUTER,
 	    tabptr->zone_nwif_defrouter)) != Z_OK))
 		return (err);
+	if (strlen(tabptr->zone_nwif_mac) > 0 &&
+	    (err = newprop(newnode, DTD_ATTR_MAC,
+	    tabptr->zone_nwif_mac)) != Z_OK)
+		return (err);
+	if (strlen(tabptr->zone_nwif_vlan_id) > 0 &&
+	    (err = newprop(newnode, DTD_ATTR_VLANID,
+	    tabptr->zone_nwif_vlan_id)) != Z_OK)
+		return (err);
+	if (strlen(tabptr->zone_nwif_gnic) > 0 &&
+	    (err = newprop(newnode, DTD_ATTR_GNIC,
+	    tabptr->zone_nwif_gnic)) != Z_OK)
+		return (err);
+
+	for (valptr = tabptr->zone_nwif_attrp; valptr != NULL;
+	    valptr = valptr->zone_res_attr_next) {
+		valnode = xmlNewTextChild(newnode, NULL, DTD_ELEM_NETATTR,
+		    NULL);
+		err = newprop(valnode, DTD_ATTR_NAME,
+		    valptr->zone_res_attr_name);
+		if (err != Z_OK)
+			return (err);
+		err = newprop(valnode, DTD_ATTR_VALUE,
+		    valptr->zone_res_attr_value);
+		if (err != Z_OK)
+			return (err);
+	}
+
 	return (Z_OK);
 }
 
@@ -2244,7 +2342,8 @@
 zonecfg_delete_nwif_core(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
 {
 	xmlNodePtr cur = handle->zone_dh_cur;
-	boolean_t addr_match, phys_match, allowed_addr_match;
+	boolean_t addr_match, phys_match, allowed_addr_match, mac_match,
+	    gnic_match;
 
 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
 		if (xmlStrcmp(cur->name, DTD_ELEM_NET))
@@ -2256,8 +2355,13 @@
 		    tabptr->zone_nwif_allowed_address);
 		phys_match = match_prop(cur, DTD_ATTR_PHYSICAL,
 		    tabptr->zone_nwif_physical);
+		mac_match = match_prop(cur, DTD_ATTR_MAC,
+		    tabptr->zone_nwif_mac);
+		gnic_match = match_prop(cur, DTD_ATTR_GNIC,
+		    tabptr->zone_nwif_gnic);
 
-		if (addr_match && allowed_addr_match && phys_match) {
+		if ((addr_match || allowed_addr_match || mac_match ||
+		    gnic_match) && phys_match) {
 			xmlUnlinkNode(cur);
 			xmlFreeNode(cur);
 			return (Z_OK);
@@ -2306,6 +2410,58 @@
 	return (Z_OK);
 }
 
+void
+zonecfg_free_res_attr_list(struct zone_res_attrtab *valtab)
+{
+	if (valtab == NULL)
+		return;
+	zonecfg_free_res_attr_list(valtab->zone_res_attr_next);
+	free(valtab);
+}
+
+int
+zonecfg_add_res_attr(struct zone_res_attrtab **headptr,
+    struct zone_res_attrtab *valtabptr)
+{
+	struct zone_res_attrtab *last, *old, *new;
+
+	last = *headptr;
+	for (old = last; old != NULL; old = old->zone_res_attr_next)
+		last = old;	/* walk to the end of the list */
+	new = valtabptr;	/* alloc'd by caller */
+	new->zone_res_attr_next = NULL;
+	if (last == NULL)
+		*headptr = new;
+	else
+		last->zone_res_attr_next = new;
+	return (Z_OK);
+}
+
+int
+zonecfg_remove_res_attr(struct zone_res_attrtab **headptr,
+    struct zone_res_attrtab *valtabptr)
+{
+	struct zone_res_attrtab *last, *this, *next;
+
+	last = *headptr;
+	for (this = last; this != NULL; this = this->zone_res_attr_next) {
+		if (strcmp(this->zone_res_attr_name,
+		    valtabptr->zone_res_attr_name) == 0 &&
+		    strcmp(this->zone_res_attr_value,
+		    valtabptr->zone_res_attr_value) == 0) {
+			next = this->zone_res_attr_next;
+			if (this == *headptr)
+				*headptr = next;
+			else
+				last->zone_res_attr_next = next;
+			free(this);
+			return (Z_OK);
+		} else
+			last = this;
+	}
+	return (Z_NO_PROPERTY_ID);
+}
+
 /*
  * Must be a comma-separated list of alpha-numeric file system names.
  */
@@ -2455,7 +2611,7 @@
 int
 zonecfg_lookup_dev(zone_dochandle_t handle, struct zone_devtab *tabptr)
 {
-	xmlNodePtr cur, firstmatch;
+	xmlNodePtr cur, val, firstmatch;
 	int err;
 	char match[MAXPATHLEN];
 
@@ -2500,6 +2656,32 @@
 	    sizeof (tabptr->zone_dev_match))) != Z_OK)
 		return (err);
 
+	tabptr->zone_dev_attrp = NULL;
+	for (val = cur->xmlChildrenNode; val != NULL; val = val->next) {
+		struct zone_res_attrtab *valptr;
+
+		valptr = (struct zone_res_attrtab *)malloc(
+		    sizeof (struct zone_res_attrtab));
+		if (valptr == NULL)
+			return (Z_NOMEM);
+
+		valptr->zone_res_attr_name[0] =
+		    valptr->zone_res_attr_value[0] = '\0';
+		if (zonecfg_add_res_attr(&(tabptr->zone_dev_attrp), valptr)
+		    != Z_OK) {
+			free(valptr);
+			break;
+		}
+
+		if ((fetchprop(val, DTD_ATTR_NAME, valptr->zone_res_attr_name,
+		    sizeof (valptr->zone_res_attr_name)) != Z_OK))
+			break;
+		if ((fetchprop(val, DTD_ATTR_VALUE,
+		    valptr->zone_res_attr_value,
+		    sizeof (valptr->zone_res_attr_value)) != Z_OK))
+			break;
+	}
+
 	return (Z_OK);
 }
 
@@ -2506,7 +2688,8 @@
 static int
 zonecfg_add_dev_core(zone_dochandle_t handle, struct zone_devtab *tabptr)
 {
-	xmlNodePtr newnode, cur = handle->zone_dh_cur;
+	xmlNodePtr newnode, cur = handle->zone_dh_cur, valnode;
+	struct zone_res_attrtab *valptr;
 	int err;
 
 	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_DEVICE, NULL);
@@ -2515,6 +2698,21 @@
 	    tabptr->zone_dev_match)) != Z_OK)
 		return (err);
 
+	for (valptr = tabptr->zone_dev_attrp; valptr != NULL;
+	    valptr = valptr->zone_res_attr_next) {
+		valnode = xmlNewTextChild(newnode, NULL, DTD_ELEM_NETATTR,
+		    NULL);
+		err = newprop(valnode, DTD_ATTR_NAME,
+		    valptr->zone_res_attr_name);
+		if (err != Z_OK)
+			return (err);
+		err = newprop(valnode, DTD_ATTR_VALUE,
+		    valptr->zone_res_attr_value);
+		if (err != Z_OK)
+			return (err);
+	}
+
+
 	return (Z_OK);
 }
 
@@ -4712,7 +4910,8 @@
 int
 zonecfg_getnwifent(zone_dochandle_t handle, struct zone_nwiftab *tabptr)
 {
-	xmlNodePtr cur;
+	xmlNodePtr cur, val;
+	struct zone_res_attrtab *valptr;
 	int err;
 
 	if (handle == NULL)
@@ -4748,6 +4947,24 @@
 		return (err);
 	}
 
+	if ((err = fetchprop(cur, DTD_ATTR_MAC, tabptr->zone_nwif_mac,
+	    sizeof (tabptr->zone_nwif_mac))) != Z_OK) {
+		handle->zone_dh_cur = handle->zone_dh_top;
+		return (err);
+	}
+
+	if ((err = fetchprop(cur, DTD_ATTR_VLANID, tabptr->zone_nwif_vlan_id,
+	    sizeof (tabptr->zone_nwif_vlan_id))) != Z_OK) {
+		handle->zone_dh_cur = handle->zone_dh_top;
+		return (err);
+	}
+
+	if ((err = fetchprop(cur, DTD_ATTR_GNIC, tabptr->zone_nwif_gnic,
+	    sizeof (tabptr->zone_nwif_gnic))) != Z_OK) {
+		handle->zone_dh_cur = handle->zone_dh_top;
+		return (err);
+	}
+
 	if ((err = fetchprop(cur, DTD_ATTR_DEFROUTER,
 	    tabptr->zone_nwif_defrouter,
 	    sizeof (tabptr->zone_nwif_defrouter))) != Z_OK) {
@@ -4755,6 +4972,29 @@
 		return (err);
 	}
 
+	tabptr->zone_nwif_attrp = NULL;
+	for (val = cur->xmlChildrenNode; val != NULL; val = val->next) {
+		valptr = (struct zone_res_attrtab *)malloc(
+		    sizeof (struct zone_res_attrtab));
+		if (valptr == NULL)
+			return (Z_NOMEM);
+
+		valptr->zone_res_attr_name[0] =
+		    valptr->zone_res_attr_value[0] = '\0';
+		if (zonecfg_add_res_attr(&(tabptr->zone_nwif_attrp), valptr)
+		    != Z_OK) {
+			free(valptr);
+			break;
+		}
+
+		if (fetchprop(val, DTD_ATTR_NAME, valptr->zone_res_attr_name,
+		    sizeof (valptr->zone_res_attr_name)) != Z_OK)
+			break;
+		if (fetchprop(val, DTD_ATTR_VALUE, valptr->zone_res_attr_value,
+		    sizeof (valptr->zone_res_attr_value)) != Z_OK)
+			break;
+	}
+
 	handle->zone_dh_cur = cur->next;
 	return (Z_OK);
 }
@@ -4774,7 +5014,7 @@
 int
 zonecfg_getdevent(zone_dochandle_t handle, struct zone_devtab *tabptr)
 {
-	xmlNodePtr cur;
+	xmlNodePtr cur, val;
 	int err;
 
 	if (handle == NULL)
@@ -4797,6 +5037,31 @@
 		return (err);
 	}
 
+	tabptr->zone_dev_attrp = NULL;
+	for (val = cur->xmlChildrenNode; val != NULL; val = val->next) {
+		struct zone_res_attrtab *valptr;
+
+		valptr = (struct zone_res_attrtab *)malloc(
+		    sizeof (struct zone_res_attrtab));
+		if (valptr == NULL)
+			return (Z_NOMEM);
+
+		valptr->zone_res_attr_name[0] =
+		    valptr->zone_res_attr_value[0] = '\0';
+		if (zonecfg_add_res_attr(&(tabptr->zone_dev_attrp), valptr)
+		    != Z_OK) {
+			free(valptr);
+			break;
+		}
+
+		if ((fetchprop(val, DTD_ATTR_NAME, valptr->zone_res_attr_name,
+		    sizeof (valptr->zone_res_attr_name)) != Z_OK))
+			break;
+		if ((fetchprop(val, DTD_ATTR_VALUE, valptr->zone_res_attr_value,
+		    sizeof (valptr->zone_res_attr_value)) != Z_OK))
+			break;
+	}
+
 	handle->zone_dh_cur = cur->next;
 	return (Z_OK);
 }
@@ -5525,6 +5790,164 @@
 }
 
 /*
+ * Atomically get a new zone_did value.  The currently allocated value
+ * is stored in /etc/zones/did.txt.  Lock the file, read the current value,
+ * increment, save the new value and unlock the file.  Return the new value
+ * or -1 if there was an error.  The ID namespace is large enough that we
+ * don't worry about recycling an ID when a zone is deleted.
+ */
+static zoneid_t
+new_zone_did()
+{
+	int fd;
+	int len;
+	int val;
+	struct flock lck;
+	char buf[80];
+
+	if ((fd = open(DEBUGID_FILE, O_RDWR | O_CREAT,
+	    S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) {
+		perror("new_zone_did open failed");
+		return (-1);
+	}
+
+	/* Initialize the lock. */
+	lck.l_whence = SEEK_SET;
+	lck.l_start = 0;
+	lck.l_len = 0;
+
+	/* Wait until we acquire an exclusive lock on the file. */
+	lck.l_type = F_WRLCK;
+	if (fcntl(fd, F_SETLKW, &lck) == -1) {
+		perror("new_zone_did lock failed");
+		(void) close(fd);
+		return (-1);
+	}
+
+	/* Get currently allocated value */
+	len = read(fd, buf, sizeof (buf));
+	if (len == -1) {
+		perror("new_zone_did read failed");
+		val = -1;
+	} else {
+		if (lseek(fd, 0L, SEEK_SET) == -1) {
+			perror("new_zone_did seek failed");
+			val = -1;
+		} else {
+			if (len == 0) {
+				/* Just created the file, initialize at 1 */
+				val = 1;
+			} else {
+				val = atoi(buf);
+				val++;
+			}
+
+			(void) snprintf(buf, sizeof (buf), "%d\n", val);
+			len = strlen(buf);
+
+			/* Save newly allocated value */
+			if (write(fd, buf, len) == -1) {
+				perror("new_zone_did write failed");
+				val = -1;
+			}
+		}
+	}
+
+	/* Release the file lock. */
+	lck.l_type = F_UNLCK;
+	if (fcntl(fd, F_SETLK, &lck) == -1) {
+		perror("new_zone_did unlock failed");
+		val = -1;
+	}
+
+	if (close(fd) != 0)
+		perror("new_zone_did close failed");
+
+	return (val);
+}
+
+/*
+ * Called by zoneadmd to get the zone's debug ID.
+ * If the zone doesn't already have an ID, a new one is generated and
+ * persistently saved onto the zone.  Normally either zoneadm or zonecfg
+ * will assign a new ID for the zone, so zoneadmd should never have to
+ * generate one, but we also handle that here just to be paranoid.
+ */
+zoneid_t
+zone_get_did(char *zone_name)
+{
+	int res;
+	zoneid_t new_did;
+	zone_dochandle_t handle;
+	char did_str[80];
+
+	if ((handle = zonecfg_init_handle()) == NULL)
+		return (getpid());
+
+	if (zonecfg_get_handle((char *)zone_name, handle) != Z_OK)
+		return (getpid());
+
+	res = getrootattr(handle, DTD_ATTR_DID, did_str, sizeof (did_str));
+
+	/* If the zone already has an assigned debug ID, return it. */
+	if (res == Z_OK && did_str[0] != '\0') {
+		zonecfg_fini_handle(handle);
+		return (atoi(did_str));
+	}
+
+	/*
+	 * The zone doesn't have an assigned debug ID yet, generate one and
+	 * save it as part of the zone definition.
+	 */
+	if ((new_did = new_zone_did()) == -1) {
+		/*
+		 * We should really never hit this block of code.
+		 * Generating a new ID failed for some reason.  Use the current
+		 * pid as a temporary ID so that the zone can continue to boot
+		 * but we don't persistently save this temporary ID on the zone.
+		 */
+		zonecfg_fini_handle(handle);
+		return (getpid());
+	}
+
+	/* Now persistently save this new ID onto the zone. */
+	(void) snprintf(did_str, sizeof (did_str), "%d", new_did);
+	(void) setrootattr(handle, DTD_ATTR_DID, did_str);
+	(void) zonecfg_save(handle);
+
+	zonecfg_fini_handle(handle);
+	return (new_did);
+}
+
+zoneid_t
+zonecfg_get_did(zone_dochandle_t handle)
+{
+	char did_str[80];
+	int err;
+	zoneid_t did;
+
+	err = getrootattr(handle, DTD_ATTR_DID, did_str, sizeof (did_str));
+	if (err == Z_OK && did_str[0] != '\0')
+		did = atoi(did_str);
+	else
+		did = -1;
+
+	return (did);
+}
+
+void
+zonecfg_set_did(zone_dochandle_t handle)
+{
+	zoneid_t new_did;
+	char did_str[80];
+
+	if ((new_did = new_zone_did()) == -1)
+		return;
+	(void) snprintf(did_str, sizeof (did_str), "%d", new_did);
+	(void) setrootattr(handle, DTD_ATTR_DID, did_str);
+}
+
+/*
  * Return the appropriate root for the active /dev.
  * For normal zone, the path is $ZONEPATH/root;
  * for scratch zone, the dev path is $ZONEPATH/lu.
@@ -6839,133 +7262,51 @@
 	return (err);
 }
 
-static int
-add_mcap(zone_dochandle_t handle, struct zone_mcaptab *tabptr)
-{
-	xmlNodePtr newnode, cur = handle->zone_dh_cur;
-	int err;
-
-	newnode = xmlNewTextChild(cur, NULL, DTD_ELEM_MCAP, NULL);
-	if ((err = newprop(newnode, DTD_ATTR_PHYSCAP, tabptr->zone_physmem_cap))
-	    != Z_OK)
-		return (err);
-
-	return (Z_OK);
-}
-
+/*
+ * Cleanup obsolete constructs in the configuration.
+ * Return true of the config has been updated and must be commited.
+ */
 int
-zonecfg_delete_mcap(zone_dochandle_t handle)
+zonecfg_fix_obsolete(zone_dochandle_t handle)
 {
-	int err;
-	xmlNodePtr cur = handle->zone_dh_cur;
-
-	if ((err = operation_prep(handle)) != Z_OK)
-		return (err);
-
-	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
-		if (xmlStrcmp(cur->name, DTD_ELEM_MCAP) != 0)
-			continue;
-
-		xmlUnlinkNode(cur);
-		xmlFreeNode(cur);
-		return (Z_OK);
-	}
-	return (Z_NO_RESOURCE_ID);
-}
-
-int
-zonecfg_modify_mcap(zone_dochandle_t handle, struct zone_mcaptab *tabptr)
-{
-	int err;
-
-	if (tabptr == NULL)
-		return (Z_INVAL);
-
-	err = zonecfg_delete_mcap(handle);
-	/* it is ok if there is no mcap entry */
-	if (err != Z_OK && err != Z_NO_RESOURCE_ID)
-		return (err);
-
-	if ((err = add_mcap(handle, tabptr)) != Z_OK)
-		return (err);
-
-	return (Z_OK);
-}
-
-int
-zonecfg_lookup_mcap(zone_dochandle_t handle, struct zone_mcaptab *tabptr)
-{
+	int res = 0;
+	int add_physmem_rctl = 0;
 	xmlNodePtr cur;
-	int err;
+	char	zone_physmem_cap[MAXNAMELEN];
 
-	if (tabptr == NULL)
-		return (Z_INVAL);
+	if (operation_prep(handle) != Z_OK)
+		return (res);
 
-	if ((err = operation_prep(handle)) != Z_OK)
-		return (err);
-
+	/*
+	 * If an obsolete mcap entry exists, convert it to the rctl.
+	 */
 	cur = handle->zone_dh_cur;
 	for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
 		if (xmlStrcmp(cur->name, DTD_ELEM_MCAP) != 0)
 			continue;
-		if ((err = fetchprop(cur, DTD_ATTR_PHYSCAP,
-		    tabptr->zone_physmem_cap,
-		    sizeof (tabptr->zone_physmem_cap))) != Z_OK) {
-			handle->zone_dh_cur = handle->zone_dh_top;
-			return (err);
+
+		if (fetchprop(cur, DTD_ATTR_PHYSCAP,
+		    zone_physmem_cap, sizeof (zone_physmem_cap)) == Z_OK) {
+			res = 1;
+			add_physmem_rctl = 1;
 		}
 
-		return (Z_OK);
+		xmlUnlinkNode(cur);
+		xmlFreeNode(cur);
+		break;
 	}
 
-	return (Z_NO_ENTRY);
-}
+	if (add_physmem_rctl) {
+		uint64_t cap;
+		char *endp;
 
-static int
-getmcapent_core(zone_dochandle_t handle, struct zone_mcaptab *tabptr)
-{
-	xmlNodePtr cur;
-	int err;
-
-	if (handle == NULL)
-		return (Z_INVAL);
-
-	if ((cur = handle->zone_dh_cur) == NULL)
-		return (Z_NO_ENTRY);
-
-	for (; cur != NULL; cur = cur->next)
-		if (xmlStrcmp(cur->name, DTD_ELEM_MCAP) == 0)
-			break;
-	if (cur == NULL) {
-		handle->zone_dh_cur = handle->zone_dh_top;
-		return (Z_NO_ENTRY);
+		cap = strtoull(zone_physmem_cap, &endp, 10);
+		(void) zonecfg_set_aliased_rctl(handle, ALIAS_MAXPHYSMEM, cap);
 	}
 
-	if ((err = fetchprop(cur, DTD_ATTR_PHYSCAP, tabptr->zone_physmem_cap,
-	    sizeof (tabptr->zone_physmem_cap))) != Z_OK) {
-		handle->zone_dh_cur = handle->zone_dh_top;
-		return (err);
-	}
-
-	handle->zone_dh_cur = cur->next;
-	return (Z_OK);
+	return (res);
 }
 
-int
-zonecfg_getmcapent(zone_dochandle_t handle, struct zone_mcaptab *tabptr)
-{
-	int err;
-
-	if ((err = zonecfg_setent(handle)) != Z_OK)
-		return (err);
-
-	err = getmcapent_core(handle, tabptr);
-
-	(void) zonecfg_endent(handle);
-
-	return (err);
-}
-
 /*
  * Get the full tree of pkg metadata in a set of nested AVL trees.
  * pkgs_avl is an AVL tree of pkgs.
@@ -7631,7 +7972,7 @@
 		(void) fclose(uaf);
 		return (Z_MISC_FS);
 	}
-	if (!config_file_path(zonename, config_file)) {
+	if (!config_file_path(zonename, config_file, sizeof (config_file))) {
 		(void) fclose(uaf);
 		return (Z_MISC_FS);
 	}
--- old/usr/src/lib/libzonecfg/common/mapfile-vers	Wed Oct  5 00:31:58 2016
+++ new/usr/src/lib/libzonecfg/common/mapfile-vers	Wed Oct  5 00:31:58 2016
@@ -20,6 +20,7 @@
 #
 #
 # Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, Joyent Inc. All rights reserved.
 #
 
 #
@@ -53,6 +54,7 @@
 	zonecfg_add_fs_option;
 	zonecfg_add_admin;
 	zonecfg_add_nwif;
+	zonecfg_add_res_attr;
 	zonecfg_add_pkg;
 	zonecfg_add_pset;
 	zonecfg_add_rctl;
@@ -79,7 +81,6 @@
 	zonecfg_delete_dev;
 	zonecfg_delete_ds;
 	zonecfg_delete_filesystem;
-	zonecfg_delete_mcap;
 	zonecfg_delete_nwif;
 	zonecfg_delete_pset;
 	zonecfg_delete_rctl;
@@ -104,7 +105,9 @@
 	zonecfg_find_mounts;
 	zonecfg_find_scratch;
 	zonecfg_fini_handle;
+	zonecfg_fix_obsolete;
 	zonecfg_free_fs_option_list;
+	zonecfg_free_res_attr_list;
 	zonecfg_free_rctl_value_list;
 	zonecfg_get_aliased_rctl;
 	zonecfg_get_attach_handle;
@@ -118,6 +121,7 @@
 	zonecfg_get_bootargs;
 	zonecfg_get_brand;
 	zonecfg_get_dflt_sched_class;
+	zonecfg_get_did;
 	zonecfg_getdevent;
 	zonecfg_getdevperment;
 	zonecfg_getdsent;
@@ -127,7 +131,6 @@
 	zonecfg_get_hostid;
 	zonecfg_get_iptype;
 	zonecfg_get_limitpriv;
-	zonecfg_getmcapent;
 	zonecfg_get_name;
 	zonecfg_get_name_by_uuid;
 	zonecfg_getnwifent;
@@ -160,7 +163,6 @@
 	zonecfg_lookup_dev;
 	zonecfg_lookup_ds;
 	zonecfg_lookup_filesystem;
-	zonecfg_lookup_mcap;
 	zonecfg_lookup_nwif;
 	zonecfg_lookup_pset;
 	zonecfg_lookup_rctl;
@@ -169,7 +171,6 @@
 	zonecfg_modify_dev;
 	zonecfg_modify_ds;
 	zonecfg_modify_filesystem;
-	zonecfg_modify_mcap;
 	zonecfg_modify_nwif;
 	zonecfg_modify_pset;
 	zonecfg_modify_rctl;
@@ -183,6 +184,7 @@
 	zonecfg_ping_zoneadmd;
 	zonecfg_release_lock_file;
 	zonecfg_remove_fs_option;
+	zonecfg_remove_res_attr;
 	zonecfg_remove_rctl_value;
 	zonecfg_remove_userauths;
 	zonecfg_reverse_scratch;
@@ -196,6 +198,7 @@
 	zonecfg_set_autoboot;
 	zonecfg_set_bootargs;
 	zonecfg_set_brand;
+	zonecfg_set_did;
 	zonecfg_setdevent;
 	zonecfg_setdevperment;
 	zonecfg_setdsent;
@@ -229,6 +232,7 @@
 	zonecfg_verify_save;
 	zonecfg_warn_poold;
 	zone_get_brand;
+	zone_get_did;
 	zone_get_devroot;
 	zone_get_id;
 	zone_get_rootpath;
--- old/usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1	Wed Oct  5 00:31:58 2016
+++ new/usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1	Wed Oct  5 00:31:58 2016
@@ -21,6 +21,7 @@
  CDDL HEADER END
 
  Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2011, Joyent Inc. All rights reserved.
 
 -->
 
@@ -46,14 +47,21 @@
 
 <!ATTLIST inherited-pkg-dir	directory	CDATA #REQUIRED>
 
-<!ELEMENT network	EMPTY>
+<!ELEMENT net-attr	EMPTY>
+<!ATTLIST net-attr	name		CDATA #REQUIRED
+			value		CDATA #REQUIRED>
 
+<!ELEMENT network	(net-attr)*>
+
 <!ATTLIST network	address		CDATA ""
 			allowed-address	CDATA ""
 			defrouter	CDATA ""
-			physical	CDATA #REQUIRED>
+			global-nic	CDATA ""
+			mac-addr	CDATA ""
+			physical	CDATA #REQUIRED
+			vlan-id		CDATA "">
 
-<!ELEMENT device	EMPTY>
+<!ELEMENT device	(net-attr)*>
 
 <!ATTLIST device	match		CDATA #REQUIRED>
 
@@ -156,6 +164,7 @@
 			limitpriv	CDATA ""
 			bootargs	CDATA ""
 			brand		CDATA ""
+			debugid		CDATA ""
 			scheduling-class	CDATA ""
 			fs-allowed	CDATA ""
 			version		NMTOKEN #FIXED '1'>
--- /dev/null	Wed Oct  5 00:31:58 2016
+++ new/usr/src/lib/xml/os_dtd.c	Wed Oct  5 00:31:58 2016
@@ -0,0 +1,238 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+/*
+ * Utility functions for working with XML documents that are validated against
+ * Document Type Definitions (DTD) shipped in the operating system.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+#include <zone.h>
+
+#include <libxml/parser.h>
+#include <libxml/xmlmemory.h>
+
+#include "os_dtd.h"
+
+struct os_dtd_path {
+	os_dtd_id_t odp_id;
+	const char *odp_name;
+	const char *odp_public_ident;
+	const char *odp_path;
+};
+
+static os_dtd_path_t os_dtd_paths[] = {
+	/*
+	 * DTDs for Zones infrastructure:
+	 */
+	{ OS_DTD_ZONES_BRAND, "brand",
+	    "-//Sun Microsystems Inc//DTD Brands//EN",
+	    "/usr/share/lib/xml/dtd/brand.dtd.1" },
+	{ OS_DTD_ZONES_ZONE, "zone",
+	    "-//Sun Microsystems Inc//DTD Zones//EN",
+	    "/usr/share/lib/xml/dtd/zonecfg.dtd.1" },
+	{ OS_DTD_ZONES_PLATFORM, "platform",
+	    "-//Sun Microsystems Inc//Zones Platform//EN",
+	    "/usr/share/lib/xml/dtd/zone_platform.dtd.1" },
+
+	/*
+	 * DTDs for smf(5):
+	 */
+	{ OS_DTD_SMF_SERVICE_BUNDLE, "service_bundle",
+	    NULL,
+	    "/usr/share/lib/xml/dtd/service_bundle.dtd.1" },
+
+	{ OS_DTD_UNKNOWN, NULL, NULL, NULL }
+};
+
+/*
+ * Check this document to see if it references the public identifier of a
+ * well-known DTD that we ship with the operating system.  If there is no DTD,
+ * or the public identifier is unknown to us, return OS_DTD_UNKNOWN.
+ */
+os_dtd_id_t
+os_dtd_identify(xmlDocPtr doc)
+{
+	xmlDtdPtr dp;
+	int i;
+
+	if ((dp = xmlGetIntSubset(doc)) == NULL) {
+		/*
+		 * This document does not have an internal subset pointing
+		 * to a DTD.
+		 */
+		errno = EIO;
+		return (OS_DTD_UNKNOWN);
+	}
+
+	/*
+	 * The use of a symbolic name via the public identifier is preferred.
+	 * Check to see if the document refers to a public identifier for any
+	 * well-known DTD:
+	 */
+	for (i = 0; os_dtd_paths[i].odp_id != OS_DTD_UNKNOWN; i++) {
+		os_dtd_path_t *odp = &os_dtd_paths[i];
+		const xmlChar *pubid = (const xmlChar *)odp->odp_public_ident;
+
+		if (dp->ExternalID == NULL || odp->odp_public_ident == NULL) {
+			continue;
+		}
+
+		if (xmlStrEqual(pubid, dp->ExternalID)) {
+			return (odp->odp_id);
+		}
+	}
+
+	/*
+	 * If a public identifier is not present, or does not match any known
+	 * DTD, fall back to inspecting the system identifier.
+	 */
+	for (i = 0; os_dtd_paths[i].odp_id != OS_DTD_UNKNOWN; i++) {
+		os_dtd_path_t *odp = &os_dtd_paths[i];
+		char uri[sizeof ("file://") + MAXPATHLEN];
+		const xmlChar *path = (const xmlChar *)odp->odp_path;
+
+		if (dp->SystemID == NULL || odp->odp_path == NULL) {
+			continue;
+		}
+
+		/*
+		 * The system identifier may be a regular path.
+		 */
+		if (xmlStrEqual(path, dp->SystemID)) {
+			return (odp->odp_id);
+		}
+
+		/*
+		 * The system identifier may also be a "file://"
+		 * URI referring to a path:
+		 */
+		(void) snprintf(uri, sizeof (uri), "file://%s", odp->odp_path);
+		if (xmlStrEqual((const xmlChar *)uri, dp->SystemID)) {
+			return (odp->odp_id);
+		}
+	}
+
+	errno = ENOENT;
+	return (OS_DTD_UNKNOWN);
+}
+
+static os_dtd_path_t *
+os_dtd_lookup(os_dtd_id_t id)
+{
+	int i;
+
+	for (i = 0; os_dtd_paths[i].odp_id != OS_DTD_UNKNOWN; i++) {
+		os_dtd_path_t *odp = &os_dtd_paths[i];
+
+		if (odp->odp_id == id) {
+			return (odp);
+		}
+	}
+
+	errno = ENOENT;
+	return (NULL);
+}
+
+/*
+ * If this document references a DTD, remove that reference (the "internal
+ * subset").  Install a new internal subset reference to the well-known
+ * operating system DTD passed by the caller.  The URI in this reference will
+ * respect the current native system prefix (e.g. "/native") if there is one,
+ * such as when running in a branded zone.
+ */
+int
+os_dtd_attach(xmlDocPtr doc, os_dtd_id_t id)
+{
+	xmlDtdPtr dp;
+	os_dtd_path_t *odp;
+	const char *zroot = zone_get_nroot();
+	char uri[sizeof ("file://") + MAXPATHLEN];
+
+	if ((odp = os_dtd_lookup(id)) == NULL) {
+		return (-1);
+	}
+
+	if ((dp = xmlGetIntSubset(doc)) != NULL) {
+		/*
+		 * This document already has an internal subset.  Remove it
+		 * before attaching the new one.
+		 */
+		xmlUnlinkNode((xmlNodePtr)dp);
+		xmlFreeNode((xmlNodePtr)dp);
+	}
+
+	/*
+	 * The "system identifier" of this internal subset must refer to the
+	 * path in the filesystem where the DTD file (the external subset) is
+	 * stored.  If we are running in a branded zone, that file may be at a
+	 * different path (e.g. under "/native").
+	 */
+	(void) snprintf(uri, sizeof (uri), "file://%s%s", zroot != NULL ?
+	    zroot : "", odp->odp_path);
+
+	if (xmlCreateIntSubset(doc, (const xmlChar *)odp->odp_name,
+	    (const xmlChar *)odp->odp_public_ident,
+	    (const xmlChar *)uri) == NULL) {
+		errno = EIO;
+		return (-1);
+	}
+
+	return (0);
+}
+
+/*ARGSUSED*/
+static void
+os_dtd_print_nothing(void *ctx, const char *msg, ...)
+{
+}
+
+int
+os_dtd_validate(xmlDocPtr doc, boolean_t emit_messages, boolean_t *valid)
+{
+	int ret;
+	xmlValidCtxtPtr cvp;
+	os_dtd_id_t dtdid;
+
+	if ((dtdid = os_dtd_identify(doc)) != OS_DTD_UNKNOWN) {
+		/*
+		 * This document refers to a well-known DTD shipped with
+		 * the operating system.  Ensure that it points to the
+		 * correct local path for validation in the current context.
+		 */
+		if (os_dtd_attach(doc, dtdid) != 0) {
+			return (-1);
+		}
+	}
+
+	if ((cvp = xmlNewValidCtxt()) == NULL) {
+		return (-1);
+	}
+
+	if (!emit_messages) {
+		cvp->error = os_dtd_print_nothing;
+		cvp->warning = os_dtd_print_nothing;
+	}
+
+	ret = xmlValidateDocument(cvp, doc);
+	xmlFreeValidCtxt(cvp);
+
+	*valid = (ret == 1 ? B_TRUE : B_FALSE);
+	return (0);
+}
--- /dev/null	Wed Oct  5 00:31:58 2016
+++ new/usr/src/lib/xml/os_dtd.h	Wed Oct  5 00:31:58 2016
@@ -0,0 +1,49 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+#ifndef _OS_DTD_H
+#define	_OS_DTD_H
+
+/*
+ * Utility functions for working with XML documents that are validated against
+ * Document Type Definitions (DTD) shipped in the operating system.
+ */
+
+#include <libxml/parser.h>
+#include <libxml/xmlmemory.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum os_dtd_id {
+	OS_DTD_UNKNOWN = 0,
+	OS_DTD_ZONES_BRAND,
+	OS_DTD_ZONES_ZONE,
+	OS_DTD_ZONES_PLATFORM,
+	OS_DTD_SMF_SERVICE_BUNDLE
+} os_dtd_id_t;
+
+typedef struct os_dtd_path os_dtd_path_t;
+
+extern os_dtd_id_t os_dtd_identify(xmlDocPtr);
+extern int os_dtd_attach(xmlDocPtr, os_dtd_id_t);
+extern int os_dtd_validate(xmlDocPtr, boolean_t, boolean_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _OS_DTD_H */
--- old/usr/src/man/man1/pfexec.1	Wed Oct  5 00:31:58 2016
+++ new/usr/src/man/man1/pfexec.1	Wed Oct  5 00:31:58 2016
@@ -53,6 +53,11 @@
 union of the privileges specified using the "\fBprivs\fR" keyword in
 \fBprof_attr\fR(4) for all the user's profiles and added to the inheritable set
 before executing the command.
+.sp
+.LP
+For \fBpfexec\fR to function correctly, the \fBpfexecd\fR daemon must be running
+in the current zone. This is normally managed by the
+"\fBsvc:/system/pfexec:default\fR" SMF service (see \fBsmf\fR(5)).
 .SH USAGE
 .sp
 .LP
@@ -104,4 +109,4 @@
 .sp
 .LP
 \fBcsh\fR(1), \fBksh\fR(1), \fBprofiles\fR(1), \fBsh\fR(1), \fBexec_attr\fR(4),
-\fBprof_attr\fR(4), \fBuser_attr\fR(4), \fBattributes\fR(5)
+\fBprof_attr\fR(4), \fBuser_attr\fR(4), \fBattributes\fR(5), \fBsmf\fR(5)
--- old/usr/src/man/man1/zlogin.1	Wed Oct  5 00:31:59 2016
+++ new/usr/src/man/man1/zlogin.1	Wed Oct  5 00:31:59 2016
@@ -13,24 +13,24 @@
 .\" Portions Copyright [yyyy] [name of copyright owner]
 .\" Copyright 2013 DEY Storage Systems, Inc.
 .\" Copyright (c) 2014 Gary Mills
+.\" Copyright (c) 2015, Joyent, Inc. All Rights Reserved
 .\" Copyright 2015 Nexenta Systems, Inc. All rights reserved.
-.TH ZLOGIN 1 "Mar 17, 2015"
+.TH ZLOGIN 1 "Mar 30, 2015"
 .SH NAME
 zlogin \- enter a zone
 .SH SYNOPSIS
 .LP
 .nf
-\fBzlogin\fR [\fB-dCEQ\fR] [\fB-e\fR \fIc\fR] [\fB-l\fR \fIusername\fR] \fIzonename\fR
+\fBzlogin\fR [\fB-dCEINQ\fR] [\fB-e\fR \fIc\fR] [\fB-l\fR \fIusername\fR] \fIzonename\fR
 .fi
 
 .LP
 .nf
-\fBzlogin\fR [\fB-nEQS\fR] [\fB-e\fR \fIc\fR] [\fB-l\fR \fIusername\fR] \fIzonename\fR \fIutility\fR
+\fBzlogin\fR [\fB-inEQS\fR] [\fB-e\fR \fIc\fR] [\fB-l\fR \fIusername\fR] \fIzonename\fR \fIutility\fR
      [\fIargument\fR]...
 .fi
 
 .SH DESCRIPTION
-.sp
 .LP
 The \fBzlogin\fR utility is used by the administrator to enter an operating
 system zone. Only a superuser operating in the global system zone can use this
@@ -37,7 +37,7 @@
 utility.
 .sp
 .LP
-\fBzlogin\fR operates in one of three modes:
+\fBzlogin\fR operates in one of four modes:
 .sp
 .ne 2
 .na
@@ -44,12 +44,12 @@
 \fBInteractive Mode\fR
 .ad
 .RS 24n
-If no utility argument is given and the stdin file descriptor for the
-\fBzlogin\fR process is a tty device, \fBzlogin\fR operates in \fBinteractive
-mode\fR. In this mode, \fBzlogin\fR creates a new pseudo terminal for use
-within the login session. Programs requiring a tty device, for example,
-\fBvi\fR(1), work properly in this mode. In this mode, \fBzlogin\fR invokes
-\fBlogin\fR(1) to provide a suitable login session.
+If no utility argument is given or if the \fB-i\fR option is specified, and the
+stdin file descriptor for the \fBzlogin\fR process is a tty device, \fBzlogin\fR
+operates in \fBinteractive mode\fR. In this mode, \fBzlogin\fR creates a new
+pseudo terminal for use within the login session. Programs requiring a tty
+device, for example, \fBvi\fR(1), work properly in this mode. In this mode,
+\fBzlogin\fR invokes \fBlogin\fR(1) to provide a suitable login session.
 .RE
 
 .sp
@@ -58,11 +58,12 @@
 \fBNon-Interactive Mode\fR
 .ad
 .RS 24n
-If a utility is specified, \fBzlogin\fR operates in \fBnon-interactive mode\fR.
-This mode can be useful for script authors since stdin, stdout, and stderr are
-preserved and the exit status of \fIutility\fR is returned upon termination. In
-this mode, \fBzlogin\fR invokes \fBsu\fR(1M) in order to set up the user's
-environment and to provide a login environment.
+If a utility is specified and the \fB-i\fR option is not specified, \fBzlogin\fR
+operates in \fBnon-interactive mode\fR.  This mode can be useful for script
+authors since stdin, stdout, and stderr are preserved and the exit status of
+\fIutility\fR is returned upon termination. In this mode, \fBzlogin\fR invokes
+\fBsu\fR(1M) in order to set up the user's environment and to provide a login
+environment.
 .sp
 The specified command is passed as a string and interpreted by a shell running
 in the non-global zone. See \fBrsh\fR(1).
@@ -80,8 +81,17 @@
 are persistent across reboot of the zone.
 .RE
 
-.SH OPTIONS
 .sp
+.ne 2
+.na
+\fBStandalone-processs Interactive Mode\fR
+.ad
+.RS 24n
+If the \fB-I\fR option is specified the user is connected to the zone's stdin,
+stdout and stderr \fBzfd(7D)\fR devices.
+.RE
+
+.SH OPTIONS
 .LP
 The following options are supported:
 .sp
@@ -127,6 +137,25 @@
 .sp
 .ne 2
 .na
+\fB\fB-i\fR\fR
+.ad
+.RS 15n
+Forces interactive mode when a utility argument is specified.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB-I\fR\fR
+.ad
+.RS 15n
+Connects to the zone's \fBzfd(7D)\fR devices.
+.RE
+
+.sp
+.sp
+.ne 2
+.na
 \fB\fB-l\fR \fIusername\fR\fR
 .ad
 .RS 15n
@@ -149,6 +178,17 @@
 .sp
 .ne 2
 .na
+\fB-N\fR
+.ad
+.RS 15n
+Nohup. This may only be used with the -I option to avoid sending EOF to the zfd
+device when zlogin's stdin receives EOF. It can also be toggled by sending
+\fBSIGUSR1\fR to an attached zlogin process.
+.RE
+
+.sp
+.ne 2
+.na
 \fB-Q\fR
 .ad
 .RS 15n
@@ -172,7 +212,6 @@
 .RE
 
 .SS "Escape Sequences"
-.sp
 .LP
 Lines that you type that start with the tilde character (\fB~\fR) are "escape
 sequences". The escape character can be changed using the \fB-e\fR option.
@@ -187,12 +226,10 @@
 .RE
 
 .SH SECURITY
-.sp
 .LP
 Once a process has been placed in a zone other than the global zone, the
 process cannot change zone again, nor can any of its children.
 .SH OPERANDS
-.sp
 .LP
 The following operands are supported:
 .sp
@@ -223,7 +260,6 @@
 .RE
 
 .SH EXIT STATUS
-.sp
 .LP
 In interactive and non-interactive modes, the \fBzlogin\fR utility exits when
 the command or shell in the non-global zone exits. In non-interactive mode, the
@@ -268,7 +304,6 @@
 .RE
 
 .SH ATTRIBUTES
-.sp
 .LP
 See \fBattributes\fR(5) for descriptions of the following attributes:
 .sp
@@ -284,12 +319,10 @@
 .TE
 
 .SH SEE ALSO
-.sp
 .LP
 \fBlogin\fR(1), \fBrsh\fR(1), \fBvi\fR(1), \fBsu\fR(1M), \fBzoneadm\fR(1M),
 \fBzonecfg\fR(1M), \fBattributes\fR(5), \fBzones\fR(5)
 .SH NOTES
-.sp
 .LP
 \fBzlogin\fR fails if its open files or any portion of its address space
 corresponds to an NFS file. This includes the executable itself or the shared
--- old/usr/src/man/man1m/mount_tmpfs.1m	Wed Oct  5 00:31:59 2016
+++ new/usr/src/man/man1m/mount_tmpfs.1m	Wed Oct  5 00:31:59 2016
@@ -1,10 +1,10 @@
 '\" te
 .\"  Copyright (c) 2003, Sun Microsystems, Inc.  All Rights Reserved
-.\"  Copyright (c) 2011, Joyent, Inc.  All Rights Reserved
+.\"  Copyright 2015 Joyent, Inc.
 .\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
 .\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
 .\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH MOUNT_TMPFS 1M "Dec 2, 2011"
+.TH MOUNT_TMPFS 1M "Mar 18, 2015"
 .SH NAME
 mount_tmpfs \- mount tmpfs file systems
 .SH SYNOPSIS
@@ -26,7 +26,8 @@
 (mode, owner, and group) of the root of the \fBtmpfs\fR filesystem are
 inherited from the underlying \fImount_point\fR, provided that those attributes
 are determinable. If not, the root's attributes are set to their default
-values.
+values. The mode may also be overriden by the \fBmode\fR mount option, which
+takes precedence if set.
 .sp
 .LP
 The \fIspecial\fR argument is usually specified as \fBswap\fR but is in fact
@@ -55,6 +56,20 @@
 .sp
 .ne 2
 .na
+\fBmode=\fIoctalmode\fR\fR
+.ad
+.RS 19n
+The \fImode\fR argument controls the permissions of the \fBtmpfs\fR mount
+point.  The argument must be an octal number, of the form passed to
+\fBchmod\fR(1).  Only the access mode, setuid, setgid, and sticky bits (a mask
+of \fB07777\fR) may be set.  If this option is not provided then the default
+mode behaviour, as described above, applies.
+.RE
+
+.sp
+.sp
+.ne 2
+.na
 \fBsize=\fIsz\fR\fR
 .ad
 .RS 19n
@@ -61,7 +76,9 @@
 The \fIsz\fR argument controls the size of this particular \fBtmpfs\fR file
 system. If the argument is has a `k' suffix, the number will be interpreted as
 a number of kilobytes. An `m' suffix will be interpreted as a number of
-megabytes. No suffix is interpreted as bytes. In all cases, the actual size of
+megabytes. A `g' suffix will be interpreted as a number of gigabytes. A `%'
+suffix will be interpreted as a percentage of the swap space available to the
+zone. No suffix is interpreted as bytes. In all cases, the actual size of
 the file system is the number of bytes specified, rounded up to the physical
 pagesize of the system.
 .RE
--- old/usr/src/man/man1m/zonecfg.1m	Wed Oct  5 00:31:59 2016
+++ new/usr/src/man/man1m/zonecfg.1m	Wed Oct  5 00:31:59 2016
@@ -1,10 +1,10 @@
 '\" te
 .\" Copyright (c) 2004, 2009 Sun Microsystems, Inc. All Rights Reserved.
-.\" Copyright 2013 Joyent, Inc. All Rights Reserved.
+.\" Copyright 2015 Joyent, Inc.
 .\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License"). You may not use this file except in compliance with the License. You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.
 .\" See the License for the specific language governing permissions and limitations under the License. When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE. If applicable, add the following below this CDDL HEADER, with the
 .\" fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH ZONECFG 1M "Feb 28, 2014"
+.TH ZONECFG 1M "Nov 4, 2015"
 .SH NAME
 zonecfg \- set up zone configuration
 .SH SYNOPSIS
@@ -29,7 +29,6 @@
 .fi
 
 .SH DESCRIPTION
-.sp
 .LP
 The \fBzonecfg\fR utility creates and modifies the configuration of a zone.
 Zone configuration consists of a number of resources and properties.
@@ -72,7 +71,6 @@
 more details on each brand. For an overview of brands, see the \fBbrands\fR(5)
 man page.
 .SS "Resources"
-.sp
 .LP
 The following resource types are supported:
 .sp
@@ -166,7 +164,6 @@
 .RE
 
 .SS "Properties"
-.sp
 .LP
 Each resource type has one or more properties. There are also some global
 properties, that is, properties of the configuration as a whole, rather than of
@@ -784,7 +781,7 @@
 .ie t \(bu
 .el o
 If the \fBcpu-shares\fR property or equivalent rctl is set, the scheduling
-class FSS is used.
+class \fBFSS\fR is used.
 .RE
 .RS +4
 .TP
@@ -800,10 +797,15 @@
 .el o
 Under any other conditions, the system default scheduling class is used.
 .RE
+.sp
+If the \fBFX\fR scheduling class is specified, then the optional
+\fBfixed-hi-pri\fR attribute can be set to \fBtrue\fR. This causes all of the
+processes in the zone to run at the highest \fBFX\fR priority. By default
+processes under \fBFX\fR run at the lowest priority. See \fBpriocntl\fR(2)
+for details on each scheduling class.
 .RE
 
 
-
 .sp
 .ne 2
 .na
@@ -944,7 +946,6 @@
 use by the system. Finally, the "autoboot" global property must have a value of
 "true" or "false".
 .SS "Using Kernel Statistics to Monitor CPU Caps"
-.sp
 .LP
 Using the kernel statistics (\fBkstat\fR(3KSTAT)) module \fBcaps\fR, the system
 maintains information for all capped projects and zones. You can access this
@@ -1097,7 +1098,6 @@
 .LP
 See \fBEXAMPLES\fR for sample output from a \fBkstat\fR command.
 .SH OPTIONS
-.sp
 .LP
 The following options are supported:
 .sp
@@ -1126,7 +1126,6 @@
 .RE
 
 .SH SUBCOMMANDS
-.sp
 .LP
 You can use the \fBadd\fR and \fBselect\fR subcommands to select a specific
 resource, at which point the scope changes to that resource. The \fBend\fR and
@@ -1803,7 +1802,6 @@
 .sp
 
 .SH EXIT STATUS
-.sp
 .LP
 The following exit values are returned:
 .sp
@@ -1837,7 +1835,6 @@
 .RE
 
 .SH ATTRIBUTES
-.sp
 .LP
 See \fBattributes\fR(5) for descriptions of the following attributes:
 .sp
@@ -1853,12 +1850,11 @@
 .TE
 
 .SH SEE ALSO
-.sp
 .LP
 \fBppriv\fR(1), \fBprctl\fR(1), \fBzlogin\fR(1), \fBkstat\fR(1M),
 \fBmount\fR(1M), \fBpooladm\fR(1M), \fBpoolcfg\fR(1M), \fBpoold\fR(1M),
 \fBrcapd\fR(1M), \fBrctladm\fR(1M), \fBsvcadm\fR(1M), \fBsysidtool\fR(1M),
-\fBzfs\fR(1M), \fBzoneadm\fR(1M), \fBpriv_str_to_set\fR(3C),
+\fBzfs\fR(1M), \fBzoneadm\fR(1M), \fBpriocntl\fR(2), \fBpriv_str_to_set\fR(3C),
 \fBkstat\fR(3KSTAT), \fBvfstab\fR(4), \fBattributes\fR(5), \fBbrands\fR(5),
 \fBfnmatch\fR(5), \fBlx\fR(5), \fBprivileges\fR(5), \fBresource_controls\fR(5),
 \fBzones\fR(5)
@@ -1867,6 +1863,5 @@
 \fISystem Administration Guide: Solaris Containers-Resource Management, and
 Solaris Zones\fR
 .SH NOTES
-.sp
 .LP
 All character data used by \fBzonecfg\fR must be in US-ASCII encoding.
--- old/usr/src/man/man2/meminfo.2	Wed Oct  5 00:31:59 2016
+++ new/usr/src/man/man2/meminfo.2	Wed Oct  5 00:31:59 2016
@@ -156,7 +156,13 @@
 \fBPRIV_PROC_MEMINFO\fR privilege.
 
 .SH RETURN VALUES
+.sp
 .LP
+All but \fBMEMINFO_VLGRP\fR and \fBMEMINFO_VPAGESIZE\fR require the
+\fBPRIV_PROC_MEMINFO\fR privilege.
+
+.SH RETURN VALUES
+.LP
 Upon successful completion \fBmeminfo()\fR returns 0. Otherwise \(mi1 is
 returned and \fBerrno\fR is set to indicate the error.
 .SH ERRORS
--- old/usr/src/man/man3c/Makefile	Wed Oct  5 00:32:00 2016
+++ new/usr/src/man/man3c/Makefile	Wed Oct  5 00:32:00 2016
@@ -210,6 +210,9 @@
 	 	imaxdiv.3c					\
 	 	index.3c					\
 	 	initgroups.3c					\
+	 	inotify_init.3c					\
+	 	inotify_add_watch.3c				\
+	 	inotify_rm_watch.3c				\
 	 	insque.3c					\
 	 	is_system_labeled.3c				\
 	 	isaexec.3c					\
--- /dev/null	Wed Oct  5 00:32:00 2016
+++ new/usr/src/man/man3c/inotify_add_watch.3c	Wed Oct  5 00:32:00 2016
@@ -0,0 +1,120 @@
+'\" te
+.\"  Copyright (c) 2014, Joyent, Inc. All Rights Reserved.
+.\"  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.
+.TH INOTIFY_ADD_WATCH 3C "Sep 17, 2014"
+.SH NAME
+inotify_add_watch \- add a watch to an inotify instance
+.SH SYNOPSIS
+
+.LP
+.nf
+#include <sys/inotify.h>
+
+\fBint\fR \fBinotify_add_watch\fR(\fBint\fR \fIfd\fR, \fBconst char *\fR\fIpathname\fR, \fBuint32_t\fR \fImask\fR);
+.fi
+
+.SH DESCRIPTION
+.sp
+.LP
+The \fBinotify_add_watch()\fR function adds a watch for the file or
+directory specified by \fIpathname\fR to the inotify instance 
+specified by \fIfd\fR for the events specified by \fImask\fR.  See
+\fBinotify\fR(5) for details on the meaning of \fImask\fR, how
+it affects the interpretation of \fIpathname\fR, and how 
+events for the watched file or directory are subsequently 
+retrieved via \fBread\fR(2).
+
+.SH RETURN VALUES
+.sp
+.LP
+Upon succesful completion, \fBinotify_add_watch()\fR returns the
+watch descriptor associated with the new watch.
+If an error occurs, -1 is returned and errno is set to indicate
+the error.
+
+.SH ERRORS
+.sp
+.LP
+\fBinotify_add_watch()\fR will fail if:
+.sp
+.ne 2
+.na
+\fB\fBEACCES\fR\fR
+.ad
+.RS 10n
+\fIpathname\fR could not be opened for reading.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBEBADF\fR\fR
+.ad
+.RS 10n
+The \fIfd\fR argument is not a valid open file descriptor.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBEFAULT\fR\fR
+.ad
+.RS 10n
+The memory associated with \fIpathname\fR was not mapped.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBEINVAL\fR\fR
+.ad
+.RS 10n
+The \fIfd\fR argument does not correspond to an
+\fBinotify\fR(5) instance as initialized with 
+\fBinotify_init\fR(3C) or \fBinotify_init1\fR(3C).
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBENOSPC\fR\fR
+.ad
+.RS 10n
+The number of watches on the specified instance would exceed the
+maximum number of watches per \fBinotify\fR(5) instance.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBENOTDIR\fR\fR
+.ad
+.RS 10n
+\fIpathname\fR does not correspond to a directory and 
+\fBIN_ONLYDIR\fR was specified in \fImask\fR.
+.RE
+
+.sp
+.SH NOTES
+.sp
+.LP
+
+While the \fBinotify\fR(5) facility is implemented for purposes of
+offering compatibility for Linux-borne applications, native
+applications may opt to use it instead of (or in addition to) the 
+\fBPORT_SOURCE_FILE\fR capability of event ports.  See
+\fBinotify\fR(5) for details and restrictions.
+
+.SH SEE ALSO
+.sp
+.LP
+\fBinotify_init\fR(3C), \fBinotify_init1\fR(3C),
+\fBport_create\fR(3C), \fBport_associate\fR(3C), \fBport_get\fR(3C),
+\fBinotify\fR(5)
--- /dev/null	Wed Oct  5 00:32:00 2016
+++ new/usr/src/man/man3c/inotify_init.3c	Wed Oct  5 00:32:00 2016
@@ -0,0 +1,107 @@
+'\" te
+.\"  Copyright (c) 2014, Joyent, Inc. All Rights Reserved.
+.\"  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.
+.TH INOTIFY_INIT 3C "Sep 17, 2014"
+.SH NAME
+inotify_init, inotify_init1 \- initialize an inotify instance
+.SH SYNOPSIS
+
+.LP
+.nf
+#include <sys/inotify.h>
+
+\fBint\fR \fBinotify_init\fR(\fBvoid\fR);
+.fi
+
+.LP
+.nf
+\fBint\fR \fBinotify_init1\fR(\fBint\fR \fIflags\fR);
+.fi
+
+.SH DESCRIPTION
+.sp
+.LP
+The \fBinotify_init()\fR and \fBinotify_init1()\fR functions both create an
+\fBinotify\fR(5) instance that can be operated upon via
+\fBinotify_add_watch\fR(3C), \fBinotify_rm_watch\fR(3C) and \fBread\fR(2).
+\fBinotify\fR instances are
+represented as file descriptors, and should be closed via \fBclose\fR(2).
+
+The only difference between the two functions is their signature;
+\fBinotify_init()\fR takes no arguments,
+while \fBinotify_init1()\fR takes a \fIflags\fR argument that can have
+any of the following values:
+
+.sp
+.ne 2
+.na
+\fBIN_CLOEXEC\fR
+.ad
+.RS 12n
+Instance should be closed upon an
+\fBexec\fR(2); see \fBopen\fR(2)'s description of \fBO_CLOEXEC\fR.
+.RE
+
+.sp
+.ne 2
+.na
+\fBIN_NONBLOCK\fR
+.ad
+.RS 12n
+Instance will be set to be non-blocking.  A \fBread\fR(2) on an
+\fBinotify\fR instance that has been initialized with
+\fBIN_NONBLOCK\fR will return \fBEAGAIN\fR if there are
+no events enqueued in lieu of blocking.
+.RE
+
+.SH RETURN VALUES
+.sp
+.LP
+Upon succesful completion, 0 is returned. Otherwise, -1 is returned and errno
+is set to indicate the error.
+.SH ERRORS
+.sp
+.LP
+The \fBinotify_init()\fR and \fBinotify_init1()\fR functions will fail if:
+.sp
+.ne 2
+.na
+\fB\fBEINVAL\fR\fR
+.ad
+.RS 10n
+The \fIflags\fR are invalid (\fBinotify_init1()\fR).
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBEMFILE\fR\fR
+.ad
+.RS 10n
+There are currently {\fBOPEN_MAX\fR} file descriptors open in the calling
+process, or the maximum number of \fBinotify\fR instances for the user 
+would be exceeded.
+.RE
+
+.sp
+.SH NOTES
+.sp
+.LP
+
+While the \fBinotify\fR(5) facility is implemented for purposes of
+offering compatibility for Linux-borne applications, native
+applications may opt to use it instead of (or in addition to) the 
+\fBPORT_SOURCE_FILE\fR capability of event ports.  See
+\fBinotify\fR(5) for details and restrictions.
+
+.SH SEE ALSO
+.sp
+.LP
+\fBinotiy_add_watch\fR(3C), \fBinotify_rm_watch\fR(3C), \fBinotify\fR(5)
--- /dev/null	Wed Oct  5 00:32:00 2016
+++ new/usr/src/man/man3c/inotify_rm_watch.3c	Wed Oct  5 00:32:00 2016
@@ -0,0 +1,81 @@
+'\" te
+.\"  Copyright (c) 2014, Joyent, Inc. All Rights Reserved.
+.\"  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.
+.TH INOTIFY_RM_WATCH 3C "Sep 17, 2014"
+.SH NAME
+inotify_rm_watch \- remove a watch from an inotify instance
+.SH SYNOPSIS
+
+.LP
+.nf
+#include <sys/inotify.h>
+
+\fBint\fR \fBinotify_rm_watch\fR(\fBint\fR \fIfd\fR, \fBint\fR \fIwd\fR);
+.fi
+
+.SH DESCRIPTION
+.sp
+.LP
+The \fBinotify_rm_watch()\fR function removes the watch specified
+by \fIwd\fR from the inotify instance associated with \fIfd\fR.
+Removing a watch will induce an \fBIN_IGNORED\fR event; see
+\fBinotify\fR(5) for details.
+
+.SH RETURN VALUES
+.sp
+.LP
+Upon succesful completion, \fBinotify_add_watch()\fR returns the
+watch descriptor associated with the new watch.
+If an error occurs, -1 is returned and errno is set to indicate
+the error.
+
+.SH ERRORS
+.sp
+.LP
+\fBinotify_rm_watch()\fR will fail if:
+.sp
+.ne 2
+.na
+\fB\fBEBADF\fR\fR
+.ad
+.RS 10n
+The \fIfd\fR argument is not a valid open file descriptor.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fBEINVAL\fR\fR
+.ad
+.RS 10n
+The \fIfd\fR argument does not correspond to an
+\fBinotify\fR(5) instance as initialized with 
+\fBinotify_init\fR(3C) or \fBinotify_init1\fR(3C), or
+\fIwd\fR is not a valid watch for the specified inotify
+instance.
+.RE
+
+.sp
+.SH NOTES
+.sp
+.LP
+
+While the \fBinotify\fR(5) facility is implemented for purposes of
+offering compatibility for Linux-borne applications, native
+applications may opt to use it instead of (or in addition to) the 
+\fBPORT_SOURCE_FILE\fR capability of event ports.  See
+\fBinotify\fR(5) for details and restrictions.
+
+.SH SEE ALSO
+.sp
+.LP
+\fBinotify_init\fR(3C), \fBinotify_init1\fR(3C),
+\fBport_create\fR(3C), \fBport_associate\fR(3C), \fBport_get\fR(3C),
+\fBinotify\fR(5)
--- old/usr/src/man/man3c/signalfd.3c	Wed Oct  5 00:32:00 2016
+++ new/usr/src/man/man3c/signalfd.3c	Wed Oct  5 00:32:00 2016
@@ -8,9 +8,9 @@
 .\"  source.  A copy of the CDDL is also available via the Internet at
 .\"  http://www.illumos.org/license/CDDL.
 .\"
-.\"  Copyright 2015, Joyent, Inc.
+.\"  Copyright 2016, Joyent, Inc.
 .\"
-.Dd "April 9, 2016"
+.Dd "May 05, 2016"
 .Dt SIGNALFD 3C
 .Os
 .Sh NAME
@@ -150,6 +150,20 @@
         uint8_t  ssi_pad[48];   /* pad size to 128 bytes */
 } signalfd_siginfo_t;
 .Ed
+.Sh NOTES
+File descriptor duplication during fork presents a challenge to the
+.Sy signalfd
+facility since signal data and events are dependent on the process from which
+they are queried.  Its use with caching event systems such as
+.Xr epoll 5 ,
+.Sy /dev/poll ,
+or
+.Xr port_create 3C
+can result in undefined behavior if signalfd and polling descriptors are used
+together after being shared across a fork.  Such restrictions do not apply if
+the child only calls
+.Xr close 2
+on the descriptors.
 .Sh RETURN VALUES
 Upon successful completion, a file descriptor associated with the instance
 is returned. Otherwise, -1 is returned and errno is set to indicate the error.
@@ -187,6 +201,8 @@
 .Sh SEE ALSO
 .Xr poll 2 ,
 .Xr sigwait 2 ,
+.Xr port_create 3C ,
 .Xr sigsetops 3C ,
 .Xr sigwaitinfo 3C ,
-.Xr signal.h 3HEAD
+.Xr signal.h 3HEAD ,
+.Xr epoll 5
--- old/usr/src/man/man4/proc.4	Wed Oct  5 00:32:00 2016
+++ new/usr/src/man/man4/proc.4	Wed Oct  5 00:32:00 2016
@@ -1,15 +1,14 @@
 '\" te
 .\" Copyright 1989 AT&T
 .\" Copyright (c) 2006, Sun Microsystems, Inc. All Rights Reserved.
-.\" Copyright (c) 2013, Joyent, Inc. All rights reserved.
+.\" Copyright 2015, Joyent, Inc.
 .\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
 .\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
 .\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH PROC 4 "Mar 31, 2013"
+.TH PROC 4 "May 19, 2014"
 .SH NAME
 proc \- /proc, the process file system
 .SH DESCRIPTION
-.sp
 .LP
 \fB/proc\fR is a file system that provides access to the state of each process
 and light-weight process (lwp) in the system. The name of each entry in the
@@ -174,7 +173,6 @@
 explicit 32-bit fixed-width data structures (like \fBcstruct stat32\fR) visible
 to the 64-bit program. See \fBtypes32.h\fR(3HEAD).
 .SH DIRECTORY STRUCTURE
-.sp
 .LP
 At the top level, the directory \fB/proc\fR contains entries each of which
 names an existing process in the system. These entries are themselves
@@ -201,7 +199,6 @@
 of the system and it is not legitimate for a program to assume that they will
 not.
 .SH STRUCTURE OF \fB/proc/\fR\fIpid\fR
-.sp
 .LP
 A given directory \fB/proc/\fR\fIpid\fR contains the following entries. A
 process can use the invisible alias \fB/proc/self\fR if it wishes to open one
@@ -209,13 +206,11 @@
 does not appear in a directory listing of \fB/proc\fR obtained from
 \fBls\fR(1), \fBgetdents\fR(2), or \fBreaddir\fR(3C)).
 .SS "contracts"
-.sp
 .LP
 A directory containing references to the contracts held by the process. Each
 entry is a symlink to the contract's directory under \fB/system/contract\fR.
 See \fBcontract\fR(4).
 .SS "as"
-.sp
 .LP
 Contains the address-space image of the process; it can be opened for both
 reading and writing. \fBlseek\fR(2) is used to position the file at the virtual
@@ -223,7 +218,6 @@
 through \fBread\fR(2) or \fBwrite\fR(2) (or by using \fBpread\fR(2) or
 \fBpwrite\fR(2) for the combined operation).
 .SS "ctl"
-.sp
 .LP
 A write-only file to which structured messages are written directing the system
 to change some aspect of the process's state or control its behavior in some
@@ -235,7 +229,6 @@
 appropriate status and information files. The types of control messages are
 described in detail later. See \fBCONTROL MESSAGES\fR.
 .SS "status"
-.sp
 .LP
 Contains state information about the process and the representative lwp. The
 file contains a \fBpstatus\fR structure which contains an embedded
@@ -672,6 +665,18 @@
 .RE
 
 .sp
+.ne 2
+.na
+\fB\fBPR_BRAND\fR\fR
+.ad
+.RS 17n
+indicates that the lwp stopped for a brand-specific reason.  Interpretation
+of the value of \fBpr_what\fR depends on which zone brand is in use.  It is
+not generally expected that an lwp stopped in this state will be restarted
+by native \fBproc\fR(4) consumers.
+.RE
+
+.sp
 .LP
 \fBpr_cursig\fR names the current signal, that is, the next signal to be
 delivered to the lwp, if any. \fBpr_info\fR, when the lwp is in a
@@ -864,7 +869,6 @@
 .LP
 If the lwp is not stopped, all register values are undefined.
 .SS "psinfo"
-.sp
 .LP
 Contains miscellaneous information about the process and the representative lwp
 needed by the \fBps\fR(1) command. \fBpsinfo\fR remains accessible after a
@@ -930,6 +934,15 @@
 0x8000). \fBpr_pctcpu\fR is the summation over all lwps in the process.
 .sp
 .LP
+The \fBpr_fname\fR and \fBpr_psargs\fR are writable by the owner of the
+process. To write to them, the \fBpsinfo\fR file should be open for writing
+and the desired value for the field should be written at the file offset
+that corresponds to the member of structure.  No other entry may be written
+to; if a write is attempted to an offset that does not represent one of
+these two memers, or if the size of the write is not exactly the size of
+the member being written, no bytes will be written and zero will be returned.
+.sp
+.LP
 \fBpr_lwp\fR contains the \fBps\fR(1) information for the representative lwp.
 If the process is a \fIzombie\fR, \fBpr_nlwp\fR, \fBpr_nzomb\fR, and
 \fBpr_lwp.pr_lwpid\fR are zero and the other fields of \fBpr_lwp\fR are
@@ -983,7 +996,6 @@
 \fBpr_contract\fR is the id of the process contract of which the process is a
 member. See \fBcontract\fR(4) and \fBprocess\fR(4).
 .SS "cred"
-.sp
 .LP
 Contains a description of the credentials associated with the process:
 .sp
@@ -1010,7 +1022,6 @@
 \fBpr_ngroups\fR indicates the number of supplementary groups. (See also the
 \fBPCSCRED\fR and \fBPCSCREDX\fR control operations.)
 .SS "priv"
-.sp
 .LP
 Contains a description of the privileges associated with the process:
 .sp
@@ -1044,7 +1055,6 @@
 The full size of the structure can be computed using
 \fBPRIV_PRPRIV_SIZE\fR(\fBprpriv_t *\fR).
 .SS "sigact"
-.sp
 .LP
 Contains an array of \fBsigaction structures\fR describing the current
 dispositions of all signals associated with the traced process (see
@@ -1052,14 +1062,18 @@
 that the action for signal number \fIn\fR appears in position \fIn\fR-1 of the
 array.
 .SS "auxv"
-.sp
 .LP
 Contains the initial values of the process's aux vector in an array of
 \fBauxv_t\fR structures (see \fB<sys/auxv.h>\fR). The values are those that
 were passed by the operating system as startup information to the dynamic
 linker.
+.SS "argv"
+.LP
+Contains the concatenation of each of the argument strings, including their
+\fBNUL\fR terminators, in the argument vector (\fBargv\fR) for the process. If
+the process has modified either its argument vector, or the contents of any of
+the strings referenced by that vector, those changes will be visible here.
 .SS "ldt"
-.sp
 .LP
 This file exists only on x86-based machines. It is non-empty only if the
 process has established a local descriptor table (\fBLDT\fR). If non-empty, the
@@ -1067,7 +1081,6 @@
 elements of type \fBstruct ssd\fR, defined in \fB<sys/sysi86.h>\fR, one element
 for each active \fBLDT\fR entry.
 .SS "map, xmap"
-.sp
 .LP
 Contain information about the virtual address map of the process. The map file
 contains an array of \fBprmap\fR structures while the xmap file contains an
@@ -1235,7 +1248,6 @@
 \fBpr_pagesize.\fR The possible values are hardware architecture specific, and
 may change over a mapping's lifetime.
 .SS "rmap"
-.sp
 .LP
 Contains information about the reserved address ranges of the process. The file
 contains an array of \fBprmap\fR structures, as defined above for the \fBmap\fR
@@ -1246,7 +1258,6 @@
 include the address ranges reserved for the process stack and the individual
 thread stacks of a multi-threaded process.
 .SS "cwd"
-.sp
 .LP
 A symbolic link to the process's current working directory. See \fBchdir\fR(2).
 A \fBreadlink\fR(2) of \fB/proc/\fIpid\fR/cwd\fR yields a null string. However,
@@ -1253,7 +1264,6 @@
 it can be opened, listed, and searched as a directory, and can be the target of
 \fBchdir\fR(2).
 .SS "root"
-.sp
 .LP
 A symbolic link to the process's root directory.
 \fB/proc/\fR\fIpid\fR\fB/root\fR can differ from the system root directory if
@@ -1260,7 +1270,6 @@
 the process or one of its ancestors executed \fBchroot\fR(2) as super user. It
 has the same semantics as \fB/proc/\fR\fIpid\fR\fB/cwd\fR.
 .SS "fd"
-.sp
 .LP
 A directory containing references to the open files of the process. Each entry
 is a decimal number corresponding to an open file descriptor in the process.
@@ -1274,7 +1283,6 @@
 \fB/proc/\fIpid\fR/cwd\fR. An attempt to open any other type of entry fails
 with \fBEACCES\fR.
 .SS "object"
-.sp
 .LP
 A directory containing read-only files with names corresponding to the
 \fBpr_mapname\fR entries in the \fBmap\fR and \fBpagedata\fR files. Opening
@@ -1287,7 +1295,6 @@
 access to the object file and any shared libraries (and consequently the symbol
 tables) without having to know the actual path names of the executable files.
 .SS "path"
-.sp
 .LP
 A directory containing symbolic links to files opened by the process. The
 directory includes one entry for \fBcwd\fR and \fBroot\fR. The directory also
@@ -1299,7 +1306,6 @@
 files. For the file descriptor entries, the path may be different from the one
 used by the process to open the file.
 .SS "pagedata"
-.sp
 .LP
 Opening the page data file enables tracking of address space references and
 modifications on a per-page basis.
@@ -1381,13 +1387,11 @@
 data being collected by the system for the others. An open of the page data
 file will fail with \fBENOMEM\fR if the system-imposed limit would be exceeded.
 .SS "watch"
-.sp
 .LP
 Contains an array of \fBprwatch\fR structures, one for each watched area
 established by the \fBPCWATCH\fR control operation. See \fBPCWATCH\fR for
 details.
 .SS "usage"
-.sp
 .LP
 Contains process usage information described by a \fBprusage\fR structure which
 contains at least the following fields:
@@ -1434,7 +1438,6 @@
 information is now never an estimate represents time the process has spent in
 various states.
 .SS "lstatus"
-.sp
 .LP
 Contains a \fBprheader\fR structure followed by an array of \fBlwpstatus\fR
 structures, one for each active lwp in the process (see also
@@ -1459,13 +1462,11 @@
 files that include a \fBprheader\fR structure (\fBlpsinfo\fR and \fBlusage\fR,
 below).
 .SS "lpsinfo"
-.sp
 .LP
 Contains a \fBprheader\fR structure followed by an array of \fBlwpsinfo\fR
 structures, one for eachactive and zombie lwp in the process. See also
 \fB/proc/\fR\fIpid\fR\fB/lwp/\fR\fIlwpid\fR/\fBlwpsinfo\fR, below.
 .SS "lusage"
-.sp
 .LP
 Contains a \fBprheader\fR structure followed by an array of \fBprusage\fR
 structures, one for each active lwp in the process, plus an additional element
@@ -1476,7 +1477,6 @@
 information obtained from the \fBusage\fR file. (See also
 \fB/proc/\fR\fIpid\fR\fB/lwp/\fR\fIlwpid\fR/\fBlwpusage\fR, below.)
 .SS "lwp"
-.sp
 .LP
 A directory containing entries each of which names an active or zombie lwp
 within the process. These entries are themselves directories containing
@@ -1483,24 +1483,20 @@
 additional files as described below. Only the \fBlwpsinfo\fR file exists in the
 directory of a zombie lwp.
 .SH STRUCTURE OF \fB/proc/\fR\fIpid\fR\fB/lwp/\fR\fIlwpid\fR
-.sp
 .LP
 A given directory \fB/proc/\fR\fIpid\fR\fB/lwp/\fR\fIlwpid\fR contains the
 following entries:
 .SS "lwpctl"
-.sp
 .LP
 Write-only control file. The messages written to this file affect the specific
 lwp rather than the representative lwp, as is the case for the process's
 \fBctl\fR file.
 .SS "lwpstatus"
-.sp
 .LP
 lwp-specific state information. This file contains the \fBlwpstatus\fR
 structure for the specific lwp as described above for the representative lwp in
 the process's \fBstatus\fR file.
 .SS "lwpsinfo"
-.sp
 .LP
 lwp-specific \fBps\fR(1) information. This file contains the \fBlwpsinfo\fR
 structure for the specific lwp as described above for the representative lwp in
@@ -1507,12 +1503,10 @@
 the process's \fBpsinfo\fR file. The \fBlwpsinfo\fR file remains accessible
 after an lwp becomes a zombie.
 .SS "lwpusage"
-.sp
 .LP
 This file contains the \fBprusage\fR structure for the specific lwp as
 described above for the process's \fBusage\fR file.
 .SS "gwindows"
-.sp
 .LP
 This file exists only on SPARC based machines. If it is non-empty, it contains
 a \fBgwindows_t\fR structure, defined in \fB<sys/regset.h>\fR, with the values
@@ -1523,7 +1517,6 @@
 register windows that could not be stored on the stack, the file is empty (the
 usual case).
 .SS "xregs"
-.sp
 .LP
 Extra state registers. The extra state register set is architecture dependent;
 this file is empty if the system does not support extra state registers. If the
@@ -1532,7 +1525,6 @@
 extra state registers. If the lwp is not stopped, all register values are
 undefined. See also the \fBPCSXREG\fR control operation, below.
 .SS "asrs"
-.sp
 .LP
 This file exists only for 64-bit SPARC V9 processes. It contains an
 \fBasrset_t\fR structure, defined in <\fBsys/regset.h\fR>, containing the
@@ -1540,7 +1532,6 @@
 not stopped, all register values are undefined. See also the \fBPCSASRS\fR
 control operation, below.
 .SS "spymaster"
-.sp
 .LP
 For an agent lwp (see \fBPCAGENT\fR), this file contains a \fBpsinfo_t\fR
 structure that corresponds to the process that created the agent lwp at the
@@ -1549,7 +1540,6 @@
 correspond to the CPU time for the process, but rather to the creation time of
 the agent lwp.
 .SS "templates"
-.sp
 .LP
 A directory which contains references to the active templates for the lwp,
 named by the contract type. Changes made to an active template descriptor do
@@ -1557,7 +1547,6 @@
 active template. It is not possible to activate an active template descriptor.
 See \fBcontract\fR(4).
 .SH CONTROL MESSAGES
-.sp
 .LP
 Process state changes are effected through messages written to a process's
 \fBctl\fR file or to an individual lwp's \fBlwpctl\fR file. All control
@@ -1577,7 +1566,6 @@
 message to a control file for a process or lwp that has terminated elicits the
 error \fBENOENT\fR.
 .SS "PCSTOP PCDSTOP PCWSTOP PCTWSTOP"
-.sp
 .LP
 When applied to the process control file, \fBPCSTOP\fR directs all lwps to stop
 and waits for them to stop, \fBPCDSTOP\fR directs all lwps to stop without
@@ -1626,7 +1614,6 @@
 be stopped. Applying one of these operations to a system process or any of its
 lwps elicits the error \fBEBUSY\fR.
 .SS "PCRUN"
-.sp
 .LP
 Make an lwp runnable again after a stop. This operation takes a \fBlong\fR
 operand containing zero or more of the following flags:
@@ -1707,7 +1694,6 @@
 a consequence, all lwps are in the \fBPR_REQUESTED\fR or \fBPR_SUSPENDED\fR
 stop state, all lwps showing \fBPR_REQUESTED\fR are made runnable.
 .SS "PCSTRACE"
-.sp
 .LP
 Define a set of signals to be traced in the process. The receipt of one of
 these signals by an lwp causes the lwp to stop. The set of signals is defined
@@ -1720,11 +1706,9 @@
 is removed from the held signal set, either by the lwp itself or by setting the
 held signal set with \fBPCSHOLD\fR.
 .SS "PCCSIG"
-.sp
 .LP
 The current signal, if any, is cleared from the specific or representative lwp.
 .SS "PCSSIG"
-.sp
 .LP
 The current signal and its associated signal information for the specific or
 representative lwp are set according to the contents of the operand
@@ -1736,7 +1720,6 @@
 is traced. Setting the current signal to \fBSIGKILL\fR terminates the process
 immediately.
 .SS "PCKILL"
-.sp
 .LP
 If applied to the process control file, a signal is sent to the process with
 semantics identical to those of \fBkill\fR(2). If applied to an lwp control
@@ -1744,7 +1727,6 @@
 \fBlong\fR operand contained in the message. Sending \fBSIGKILL\fR terminates
 the process immediately.
 .SS "PCUNKILL"
-.sp
 .LP
 A signal is deleted, that is, it is removed from the set of pending signals. If
 applied to the process control file, the signal is deleted from the process's
@@ -1753,7 +1735,6 @@
 signal is named in a \fBlong\fR operand in the control message. It is an error
 (\fBEINVAL\fR) to attempt to delete \fBSIGKILL\fR.
 .SS "PCSHOLD"
-.sp
 .LP
 Set the set of held signals for the specific or representative lwp (signals
 whose delivery will be blocked if sent to the lwp). The set of signals is
@@ -1760,7 +1741,6 @@
 specified with a \fBsigset_t\fR operand. \fBSIGKILL\fR and \fBSIGSTOP\fR cannot
 be held; if specified, they are silently ignored.
 .SS "PCSFAULT"
-.sp
 .LP
 Define a set of hardware faults to be traced in the process. On incurring one
 of these faults, an lwp stops. The set is defined via the operand
@@ -1885,12 +1865,10 @@
 identifies the signal to be sent and contains machine-specific information
 about the fault.
 .SS "PCCFAULT"
-.sp
 .LP
 The current fault, if any, is cleared; the associated signal will not be sent
 to the specific or representative lwp.
 .SS "PCSENTRY PCSEXIT"
-.sp
 .LP
 These control operations instruct the process's lwps to stop on entry to or
 exit from specified system calls. The set of system calls to be traced is
@@ -1911,7 +1889,6 @@
 flag in a \fBPCRUN\fR control message. Unless exit from the system call is
 being traced, the lwp returns to user level showing \fBEINTR\fR.
 .SS "PCWATCH"
-.sp
 .LP
 Set or clear a watched area in the controlled process from a \fBprwatch\fR
 structure operand:
@@ -2060,7 +2037,6 @@
 All watched areas are cancelled when the traced process performs a successful
 \fBexec\fR(2).
 .SS "PCSET PCUNSET"
-.sp
 .LP
 \fBPCSET\fR sets one or more modes of operation for the traced process.
 \fBPCUNSET\fR unsets these modes. The modes to be set or unset are specified by
@@ -2176,7 +2152,6 @@
 reported in the \fBpr_flags\fR field of \fB/proc/\fR\fIpid\fR\fB/status\fR and
 \fB/proc/\fR\fIpid\fR\fB/lwp/\fR\fIlwp\fR\fB/lwpstatus\fR.
 .SS "PCSREG"
-.sp
 .LP
 Set the general registers for the specific or representative lwp according to
 the operand \fBprgregset_t\fR structure.
@@ -2195,7 +2170,6 @@
 \fBPCSREG\fR fails with \fBEBUSY\fR if the lwp is not stopped on an event of
 interest.
 .SS "PCSVADDR"
-.sp
 .LP
 Set the address at which execution will resume for the specific or
 representative lwp from the operand \fBlong\fR. On SPARC based systems, both
@@ -2203,7 +2177,6 @@
 address. On x86-based systems, only %eip is set. \fBPCSVADDR\fR fails with
 \fBEBUSY\fR if the lwp is not stopped on an event of interest.
 .SS "PCSFPREG"
-.sp
 .LP
 Set the floating-point registers for the specific or representative lwp
 according to the operand \fBprfpregset_t\fR structure. An error (\fBEINVAL\fR)
@@ -2212,7 +2185,6 @@
 instructions). \fBPCSFPREG\fR fails with \fBEBUSY\fR if the lwp is not stopped
 on an event of interest.
 .SS "PCSXREG"
-.sp
 .LP
 Set the extra state registers for the specific or representative lwp according
 to the architecture-dependent operand \fBprxregset_t\fR structure. An error
@@ -2220,7 +2192,6 @@
 registers. \fBPCSXREG\fR fails with \fBEBUSY\fR if the lwp is not stopped on an
 event of interest.
 .SS "PCSASRS"
-.sp
 .LP
 Set the ancillary state registers for the specific or representative lwp
 according to the SPARC V9 platform-dependent operand \fBasrset_t\fR structure.
@@ -2230,7 +2201,6 @@
 that can be modified are set; all others are silently ignored. \fBPCSASRS\fR
 fails with \fBEBUSY\fR if the lwp is not stopped on an event of interest.
 .SS "PCAGENT"
-.sp
 .LP
 Create an agent lwp in the controlled process with register values from the
 operand \fBprgregset_t\fR structure (see \fBPCSREG\fR, above). The agent lwp is
@@ -2291,7 +2261,6 @@
 Symbolic constants for system call trap numbers like \fBSYS_lwp_exit\fR and
 \fBSYS_lwp_create\fR can be found in the header file <\fBsys/syscall.h\fR>.
 .SS "PCREAD PCWRITE"
-.sp
 .LP
 Read or write the target process's address space via a \fBpriovec\fR structure
 operand:
@@ -2318,7 +2287,6 @@
 and \fBpwrite\fR(2), no provision is made for partial reads or writes; if the
 operation cannot be performed completely, it fails with \fBEIO\fR.
 .SS "PCNICE"
-.sp
 .LP
 The traced process's \fBnice\fR(2) value is incremented by the amount in the
 operand \fBlong\fR. Only a process with the {\fBPRIV_PROC_PRIOCNTL\fR}
@@ -2326,7 +2294,6 @@
 way, but any user may lower the priority. This operation is not meaningful for
 all scheduling classes.
 .SS "PCSCRED"
-.sp
 .LP
 Set the target process credentials to the values contained in the
 \fBprcred_t\fR structure operand (see \fB/proc/\fR\fIpid\fR\fB/cred\fR). The
@@ -2336,13 +2303,11 @@
 ignored. Only the privileged processes can perform this operation; for all
 others it fails with \fBEPERM\fR.
 .SS "PCSCREDX"
-.sp
 .LP
 Operates like \fBPCSCRED\fR but also sets the supplementary groups; the length
 of the data written with this control operation should be "sizeof
 (\fBprcred_t\fR) + sizeof (\fBgid_t)\fR * (#groups - 1)".
 .SS "PCSPRIV"
-.sp
 .LP
 Set the target process privilege to the values contained in the \fBprpriv_t\fR
 operand (see \fB/proc/pid/priv\fR). The effective, permitted, inheritable, and
@@ -2360,7 +2325,6 @@
 If any of the above restrictions are not met, \fBEPERM\fR is returned. If the
 structure written is improperly formatted, \fBEINVAL\fR is returned.
 .SH PROGRAMMING NOTES
-.sp
 .LP
 For security reasons, except for the \fBpsinfo\fR, \fBusage\fR, \fBlpsinfo\fR,
 \fBlusage\fR, \fBlwpsinfo\fR, and \fBlwpusage\fR files, which are
@@ -2419,7 +2383,6 @@
 to a system process (see \fBPCSTOP\fR). The requested events may be empty to
 wait simply for termination.
 .SH FILES
-.sp
 .ne 2
 .na
 \fB\fB/proc\fR\fR
@@ -2582,6 +2545,16 @@
 .sp
 .ne 2
 .na
+\fB\fB/proc/\fIpid\fR/argv\fR\fR
+.ad
+.sp .6
+.RS 4n
+process argument vector
+.RE
+
+.sp
+.ne 2
+.na
 \fB\fB/proc/\fIpid\fR/ldt\fR\fR
 .ad
 .sp .6
@@ -2820,7 +2793,6 @@
 .RE
 
 .SH SEE ALSO
-.sp
 .LP
 \fBls\fR(1), \fBps\fR(1), \fBchroot\fR(1M), \fBalarm\fR(2), \fBbrk\fR(2),
 \fBchdir\fR(2), \fBchroot\fR(2), \fBclose\fR(2), \fBcreat\fR(2), \fBdup\fR(2),
@@ -2836,7 +2808,6 @@
 \fBwait\fR(3C), \fBcontract\fR(4), \fBcore\fR(4), \fBprocess\fR(4),
 \fBlfcompile\fR(5), \fBprivileges\fR(5)
 .SH DIAGNOSTICS
-.sp
 .LP
 Errors that can occur in addition to the errors normally associated with file
 system access:
@@ -2991,7 +2962,6 @@
 .RE
 
 .SH NOTES
-.sp
 .LP
 Descriptions of structures in this document include only interesting structure
 elements, not filler and padding fields, and may show elements out of order for
@@ -2998,7 +2968,6 @@
 descriptive clarity. The actual structure definitions are contained in
 \fB<procfs.h>\fR\&.
 .SH BUGS
-.sp
 .LP
 Because the old \fBioctl\fR(2)-based version of \fB/proc\fR is currently
 supported for binary compatibility with old applications, the top-level
--- old/usr/src/man/man4/process.4	Wed Oct  5 00:32:01 2016
+++ new/usr/src/man/man4/process.4	Wed Oct  5 00:32:01 2016
@@ -1,9 +1,10 @@
 '\" te
 .\" Copyright (c) 2008, Sun Microsystems, Inc. All Rights Reserved.
+.\" Copyright 2016, Joyent, Inc.
 .\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
 .\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
 .\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
-.TH PROCESS 4 "Mar 25, 2008"
+.TH PROCESS 4 "Sept 6, 2016"
 .SH NAME
 process \- process contract type
 .SH SYNOPSIS
@@ -202,6 +203,21 @@
 exits.
 .RE
 
+.sp
+.ne 2
+.na
+\fB\fBCT_PR_KEEP_EXEC\fR\fR
+.ad
+.sp .6
+.RS 4n
+If set, the process contract template remains active across \fBexec\fR(2).
+This can be used to setup a contract for children of an application which
+is not contract-aware. If this is not set then the system clears the active
+template when the process execs. Because this option is intended for an
+application which is not contract-aware, new child process contracts will be
+automatically abandoned by the parent.
+.RE
+
 .sp
 .ne 2
 .na
--- old/usr/src/man/man5/Makefile	Wed Oct  5 00:32:01 2016
+++ new/usr/src/man/man5/Makefile	Wed Oct  5 00:32:01 2016
@@ -14,7 +14,7 @@
 # Copyright (c) 2012 by Delphix. All rights reserved.
 # Copyright 2014 Nexenta Systems, Inc.
 # Copyright 2014 Garrett D'Amore <garrett@damore.org>
-# Copyright (c) 2015, Joyent, Inc.  All rights reserved.
+# Copyright 2016, Joyent, Inc.
 #
 
 include		$(SRC)/Makefile.master
@@ -59,6 +59,7 @@
 		iconv_unicode.5		\
 		ieee802.3.5		\
 		ieee802.11.5		\
+		inotify.5		\
 		ipfilter.5		\
 		isalist.5		\
 		kerberos.5		\
@@ -69,6 +70,7 @@
 		lfcompile.5		\
 		lfcompile64.5		\
 		locale.5		\
+		lx.5			\
 		man.5			\
 		mandoc_char.5		\
 		mandoc_roff.5		\
--- old/usr/src/man/man5/brands.5	Wed Oct  5 00:32:01 2016
+++ new/usr/src/man/man5/brands.5	Wed Oct  5 00:32:01 2016
@@ -83,5 +83,4 @@
 \fBin.rlogind\fR(1M), \fBsshd\fR(1M), \fBzoneadm\fR(1M), \fBzonecfg\fR(1M),
 \fBkill\fR(2), \fBpriocntl\fR(2), \fBgetzoneid\fR(3C), \fBucred_get\fR(3C),
 \fBgetzoneid\fR(3C), \fBproc\fR(4), \fBattributes\fR(5), \fBlx\fR(5),
-\fBnative\fR(5), \fBprivileges\fR(5), \fBzones\fR(5), \fBlx_systrace\fR(7D),
-\fBcrgetzoneid\fR(9F)
+\fBprivileges\fR(5), \fBzones\fR(5), \fBcrgetzoneid\fR(9F)
--- /dev/null	Wed Oct  5 00:32:02 2016
+++ new/usr/src/man/man5/inotify.5	Wed Oct  5 00:32:02 2016
@@ -0,0 +1,305 @@
+'\" te
+.\"  Copyright (c) 2014, Joyent, Inc. All Rights Reserved.
+.\"  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.
+.TH INOTIFY 5 "Sep 17, 2014"
+.SH NAME
+inotify \- Linux-compatible file event notification facility
+.SH SYNOPSIS
+
+.LP
+.nf
+#include <sys/inotify.h>
+.fi
+
+.SH DESCRIPTION
+.sp
+.LP
+
+\fBinotify\fR is a facility for receiving file system events on specified
+files or directories.  When monitoring a directory, \fBinotify\fR can be
+used to retrieve events not only on the directory, but also on any files
+that the directory contains.  \fBinotify\fR originated with Linux, and
+this facility is designed to be binary-compatible with the Linux facility,
+including the following interfaces:
+
+.RS +4
+.TP
+.ie t \(bu
+.el o
+\fBinotify_init\fR(3C) creates an \fBinotify\fR instance, returning a file
+descriptor associated with the in-kernel event queue.
+.RE
+.RS +4
+.TP
+.ie t \(bu
+.el o
+\fBinotify_init1\fR(3C) also creates an \fBinotify\fR instance, but allows
+for a flags argument that controls some attributes of the returned file
+descriptor.
+.RE
+.RS +4
+.TP
+.ie t \(bu
+.el o
+\fBinotify_add_watch\fR(3C) allows a watch of a particular file or directory
+to be added to a watch list associated with the specified \fBinotify\fR
+instance. \fBinotify_add_watch\fR(3C) returns a watch descriptor that will
+be reflected in the \fIwd\fR member of the \fIinotify_event\fR structure
+returned via a \fBread\fR(2) of the instance.
+.RE
+.RS +4
+.TP
+.ie t \(bu
+.el o
+\fBinotify_rm_watch\fR(3C) removes the watch that corresponds to the specified
+watch descriptor.
+.RE
+
+When all file descriptors referring to a particular \fBinotify\fR instance
+are closed, the instance and all watches associated with that instance are
+freed.
+
+To consume events on an \fBinotify\fR instance, an application should 
+issue a \fBread\fR(2) to the instance.  If no events are available
+(and the \fBinotify\fR instance has not been explicitly made non-blocking
+via \fBinotify_init1\fR(3C)) the \fBread\fR(2) will block until a
+watched event occurs. If and when events are available, \fBread\fR(2) will
+return an array of the following structures:
+
+.sp
+.in +2
+.nf
+struct inotify_event {
+        int      wd;       /* watch descriptor */
+        uint32_t mask;     /* mask of event */
+        uint32_t cookie;   /* cookie for associating renames */
+        uint32_t len;      /* size of name field */
+        char     name[];   /* optional name */
+};
+.fi
+.in -2
+
+\fIwd\fR contains the watch descriptor that corresponds to the event,
+as returned by \fBinotify_add_watch\fR(3C).
+
+\fImask\fR is a bitwise \fBOR\fR of event masks (see below) that
+describes the event.
+
+\fIcookie\fR is an opaque value that can be used to associate different
+events into a single logical event. In particular, it allows consumers to
+associate \fBIN_MOVED_FROM\fR events with subsequent \fBIN_MOVED_TO\fR
+events.
+
+\fIlen\fR denotes the length of the \fIname\fR field, including any padding
+required for trailing null bytes and alignment. The size of the entire
+event is therefore the size of the \fIinotify_event\fR structure plus the
+value of \fIlen\fR.
+
+\fIname\fR contains the name of the file associated with the event, if any.
+This field is only present when the watched entity is a directory and
+the event corresponds to a file that was contained by the watched directory
+(though see \fBNOTES\fR and \fBWARNINGS\fR for details and limitations).
+When present, \fIname\fR is null terminated, and may contain additional
+zero bytes
+to pad for alignment. (The length of this field -- including any bytes
+for alignment -- is denoted by the \fIlen\fR field.)
+
+.SS "Events"
+
+The events that can be generated on a watched entity are as follows:
+
+.sp
+.in +2
+.TS
+c c
+l l .
+\fIEvent\fR	\fIDescription\fR
+\fBIN_ACCESS\fR	File/directory was accessed
+\fBIN_ATTRIB\fR	File/directory attributes were changed
+\fBIN_CLOSE_WRITE\fR	File/directory opened for writing was closed 
+\fBIN_CLOSE_NOWRITE\fR	File/directory not opened for writing was closed
+\fBIN_CREATE\fR	File/directory created in watched directory
+\fBIN_DELETE\fR	File/directory deleted from watched directory
+\fBIN_DELETE_SELF\fR	Watched file/directory was deleted
+\fBIN_MODIFY\fR	File/directory was modified
+\fBIN_MODIFY_SELF\fR	Watched file/directory was modified
+\fBIN_MOVED_FROM\fR	File was renamed from entity in watched directory
+\fBIN_MOVED_TO\fR	File was renamed to entity in watched directory
+\fBIN_OPEN\fR	File/directory was opened
+.TE
+.in -2
+
+Of these, all events except \fBIN_MOVE_SELF\fR and \fBIN_DELETE_SELF\fR
+can refer to either the watched entity or (if the watched entity
+is a directory) a file or directory contained by the watched directory.
+(See \fBNOTES\fR and \fBWARNINGS\fR, below for details on this
+mechanism and its limitations.)
+If the event corresponds to a contained entity,
+\fIname\fR will be set to the name of the affected
+entity.
+
+In addition to speciyfing events of interest, watched events may
+be modified by potentially setting any of the following when adding a
+watch via \fBinotify_add_watch\fR(3C):
+
+.sp
+.ne 2
+.na
+\fBIN_DONT_FOLLOW\fR
+.ad
+.RS 12n
+Don't follow the specified pathname if it is a symbolic link.
+.RE
+
+.sp
+.ne 2
+.na
+\fBIN_EXCL_UNLINK\fR
+.ad
+.RS 12n
+If watching a directory and a contained entity becomes unlinked, cease
+generating events for that entity. (By default, contained entities will
+continue to generate events on their former parent directory.)
+.RE
+
+.sp
+.ne 2
+.na
+\fBIN_MASK_ADD\fR
+.ad
+.RS 12n
+If the specified pathname is already being watched, the specified events
+will be added to the watched events instead of the default behavior of
+replacing them. (If one
+may forgive the editorializing, this particular interface gewgaw
+seems entirely superfluous, and a canonical example of
+feasibility trumping wisdom.)
+.RE
+
+.sp
+.ne 2
+.na
+\fBIN_ONESHOT\fR
+.ad
+.RS 12n
+Once an event has been generated for the watched entity, remove the
+watch from the watch list as if \fBinotify_rm_watch\fR(3C) had been called
+on it (thereby inducing an \fBIN_IGNORED\fR event).
+.RE
+
+.sp
+.ne 2
+.na
+\fBIN_ONLYDIR\fR
+.ad
+.RS 12n
+Only watch the specified pathname if it is a directory.
+.RE
+
+In addition to the specified events, the following bits may be specified
+in the \fImask\fR field as returned from \fBread\fR(2):
+
+.sp
+.ne 2
+.na
+\fBIN_IGNORED\fR
+.ad
+.RS 12n
+A watch was removed explicitly (i.e, via \fBinotify_rm_watch\fR(3C)) or
+implicitly (e.g., because \fBIN_ONESHOT\fR was set or because the watched
+entity was deleted). 
+.RE
+
+.sp
+.ne 2
+.na
+\fBIN_ISDIR\fR
+.ad
+.RS 12n
+The entity inducing the event is a directory.
+.RE
+
+.sp
+.ne 2
+.na
+\fBIN_Q_OVERFLOW\fR
+.ad
+.RS 12n
+The event queue exceeded the maximum event queue length per instance.
+(By default, this is 16384, but it can be tuned by setting 
+\fBinotify_maxevents\fR via \fB/etc/system\fR.)
+.RE
+
+.sp
+.ne 2
+.na
+\fBIN_UNMOUNT\fR
+.ad
+.RS 12n
+The filesystem containing the watched entity was unmounted.
+.RE
+
+.sp
+.SH NOTES
+.sp
+.LP
+
+\fBinotify\fR instances can be monitored via \fBpoll\fR(2), 
+\fBport_get\fR(3C), \fBepoll\fR(5), etc.
+
+The event queue associated with an \fBinotify\fR instance is serialized
+and ordered: events will be placed on the tail of the queue in the order
+that they occur.
+
+If at the time an event occurs the tail of the event queue is identical
+to the newly received event, the newly received event will be dropped,
+effectively coalescing the two events.
+
+When watching a directory and receieving events on contained elements
+(i.e., a contained file or subdirectory), note that the information
+received in the \fIname\fR field may be stale:  the file may have been
+renamed between the event and its processing.  If a file has been unlinked
+(and if \fBIN_EXCL_UNLINK\fR has not been set),
+the \fIname\fR will reflect the last name that resolved to the file.
+If a new file is created in the same directory with the old name, events
+on the new file and the old (unlinked) file will become undistinguishable.
+
+The number of bytes that are available to be read on an \fBinotify\fR 
+instance can be determined via a \fBFIONREAD\fR \fBioctl\fR(2).
+
+.sp
+.SH WARNINGS
+.sp
+.LP
+
+While a best effort has been made to mimic the Linux semantics, there
+remains a fundamental difference with respect to hard links:  on Linux,
+if a file has multiple hard links to it, a notification on a watched
+directory or file will be received if and only if that event was received
+via the watched path.  For events that are induced by open files
+(such as \fBIN_MODIFY\fR), these semantics seem peculiar:  the watched
+file is in fact changing, but because it is not changing via the watched
+path, no notification is received.  By contrast, the implementation here
+will always yield an event in this case -- even if the event was induced
+by an \fBopen\fR(2) via an unwatched path.  If an event occurs within a
+watched directory on a file for which there exist multiple hard links within
+the same (watched) directory, the event's \fIname\fR will correspond to one
+of the links to the file.  If multiple hard links exist to the
+same file in the same watched directory and one of the links is removed,
+notifications may not necessarily continue to be received for the file,
+despite the (remaining) link in the watched directory; users of
+\fBinotify\fR should exercise extreme caution when watching directories
+that contain files with multiple hard links in the same directory.
+
+.SH SEE ALSO
+.sp
+.LP
+\fBinotify_init\fR(3C), \fBinotify_init1\fR(3C), \fBinotify_add_watch\fR(3C),
+\fBinotify_rm_watch\fR(3C), \fBport_get\fR(3C), \fBepoll\fR(5)
--- /dev/null	Wed Oct  5 00:32:02 2016
+++ new/usr/src/man/man5/lx.5	Wed Oct  5 00:32:02 2016
@@ -0,0 +1,108 @@
+.\"
+.\" 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, Joyent, Inc.
+.\"
+.Dd February 5, 2106
+.Dt LX 5
+.Os
+.Sh NAME
+.Nm lx
+.Nd zone brand for running a GNU/Linux user-level environment
+.Sh DESCRIPTION
+The
+.Em lx
+brand
+uses the
+.Xr brands 5
+framework to provide an environment for running binary applications built
+for GNU/Linux. User-level code, including an entire Linux distribution, can
+run inside the zone. Both 32-bit and 64-bit applications are supported. The
+majority of Linux system calls are provided, along with emulation for a
+variety of Linux file systems, such as
+.Em proc ,
+.Em cgroup
+and
+.Em sysfs .
+.Pp
+The
+.Em /proc
+file system within the zone is a subset of a full Linux
+.Em /proc .
+Most kernel-level tuning applied to
+.Em /proc
+is unavailable or ignored. Some tuning can be performed, but only to reduce
+the overall limits that have been specified on the zone's configuration.
+That is, within the zone there is no way to increase the resource limits set
+on the zone itself.
+.Pp
+The zone must be installed using a clone of a
+.Xr zfs 1m
+dataset which contains an image of the software to be run in the zone.
+.Pp
+Example:
+.Dl zoneadm -z myzone install -x nodataset -t debian7
+.Pp
+Applications provided by the base SunOS operating system are also available
+within the zone under the
+.Em /native
+mount point. This allows the use of various native tools such as
+.Xr dtrace 1m ,
+.Xr mdb 1 ,
+or the
+.Xr proc 1
+tools on GNU/Linux applications. However, not every native tool will work
+properly within an
+.Em lx
+zone.
+.Sh CONFIGURATION
+The
+.Em kernel-version
+attribute can be included in the zone's
+.Xr zonecfg 1m
+settings as a way to specify the Linux version that the zone is emulating. For
+example, the value could be
+.Em 3.13.0 .
+.Sh LIMITATIONS
+The brand only supports the exclusive IP stack zone configuration.
+.Pp
+Most modern GNU/Linux application software runs on
+.Em lx ,
+but because there are some system calls or file systems which are not currently
+implemented, it's possible that an application won't run. This does not
+preclude the application running in the future as the
+.Em lx
+brand adds new capabilities.
+.Pp
+Because there is only the single SunOS kernel running on the system, there
+is no support for any Linux kernel-level modules. That is, there is no support
+for add-on drivers or any other modules that are part of the Linux kernel
+itself. If that is required, a full virtual machine should be used instead of
+an
+.Em lx
+branded zone.
+.Pp
+Any core files produced within the zone are in the native SunOS format.
+.Pp
+As with any zone, the normal security mechanisms and privileges apply. Thus,
+certain operations (for example, changing the system time), will not be allowed
+unless the zone has been configured with the appropriate additional privileges.
+.Sh SEE ALSO
+.Xr mdb 1 ,
+.Xr proc 1 ,
+.Xr dtrace 1m ,
+.Xr zfs 1m ,
+.Xr zoneadm 1m ,
+.Xr zonecfg 1m ,
+.Xr brands 5 ,
+.Xr privileges 5 ,
+.Xr resource_controls 5 ,
+.Xr zones 5
--- old/usr/src/man/man7d/Makefile	Wed Oct  5 00:32:02 2016
+++ new/usr/src/man/man7d/Makefile	Wed Oct  5 00:32:02 2016
@@ -11,6 +11,7 @@
 
 #
 # Copyright 2011, Richard Lowe
+# Copyright 2014 Joyent, Inc. All rights reserved.
 # Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
 # Copyright 2016 Garrett D'Amore <garrett@damore.org>
 # Copyright 2016 Joyent, Inc.
@@ -147,7 +148,8 @@
 		xge.7d		\
 		yge.7d		\
 		zcons.7d	\
-		zero.7d
+		zero.7d		\
+		zfd.7d
 
 sparc_MANFILES=	audiocs.7d	\
 		bbc_beep.7d	\
--- old/usr/src/man/man7d/cpuid.7d	Wed Oct  5 00:32:02 2016
+++ new/usr/src/man/man7d/cpuid.7d	Wed Oct  5 00:32:02 2016
@@ -1,5 +1,6 @@
 '\" te
 .\" Copyright (c) 2004, Sun Microsystems, Inc.  All Rights Reserved
+.\" Copyright 2015, Joyent, Inc.
 .\" The contents of this file are subject to the terms of the Common Development and Distribution License (the "License").  You may not use this file except in compliance with the License.
 .\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing.  See the License for the specific language governing permissions and limitations under the License.
 .\" When distributing Covered Code, include this CDDL HEADER in each file and include the License file at usr/src/OPENSOLARIS.LICENSE.  If applicable, add the following below this CDDL HEADER, with the fields enclosed by brackets "[]" replaced with your own identifying information: Portions Copyright [yyyy] [name of copyright owner]
@@ -43,6 +44,10 @@
 See the processor manufacturers documentation for further information about the
 syntax and semantics of the wide variety of information available from this
 instruction.
+.LP
+Some systems can be configured to limit the cpuid opcodes which are accessible.
+While illumos handles this condition, other software may malfunction when such
+limits are enabled.  Those settings are typically manipulated in the BIOS.
 .SH EXAMPLE
 .LP
 This example allows you to determine if the current x86 processor supports
--- /dev/null	Wed Oct  5 00:32:02 2016
+++ new/usr/src/man/man7d/zfd.7d	Wed Oct  5 00:32:02 2016
@@ -0,0 +1,81 @@
+.\"
+.\" 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 2015, Joyent, Inc. All rights reserved.
+.\"
+.Dd "Oct 16, 2015"
+.Dt ZFD 7D
+.Os
+.Sh NAME
+.Nm zfd
+.Nd Zone file descriptor driver
+.Sh DESCRIPTION
+The
+.Nm zfd
+character driver exports devices into the zone which can be used by a
+a standalone process within the zone as
+.Vt stdin ,
+.Vt stdout ,
+and
+.Vt stderr .
+The
+.Nm zfd
+driver behaves in a similar manner as the
+.Nm zcons(7D)
+device.
+Inside a zone, the slave side devices appear as
+.Nm /dev/zfd/[0-4] .
+.sp
+The zone's zfd device configuration is driven by
+.Nm zoneadmd
+and a zone attribute
+.Nm zlog-mode
+which is somewhat of a misnomer since its purpose has evolved. The attribute
+can have a variety of values, but the lowest two positions in the value string
+are used to control how many zfd devices are created inside the zone and if the
+primary stream is a tty.
+.sp
+.Dl --
+.Dl -n
+.Dl t-
+.Dl tn
+.sp
+With the
+.Nm t
+flag set,
+.Vt stdin ,
+.Vt stdout ,
+and
+.Vt stderr ,
+are multiplexed onto a single full-duplex stream which is configured as a tty.
+That is,
+.Nm ptem ,
+.Nm ldterm
+and
+.Nm ttycompat
+are autopushed onto the stream when the slave side is opened. There is only a
+single zfd device (0) needed for the primary stream.
+.sp
+When the
+.Nm n
+flag is set, it is assumed that output logging will be done within the zone
+itself. In this configuration 1 or 2 additional zfd devices, depending on tty
+mode
+.Nm ( t
+flag), are created within the zone. An application can then configure the
+zfd streams driver into a multiplexer. Output from the stdout/stderr zfd(s)
+will be teed into the correspond logging zfd(s) within the zone.
+.sp
+.Sh SEE ALSO
+.Xr zlogin 1 ,
+.Xr zoneadmd 1M ,
+.Xr zonecfg 1M ,
+.Xr zcons 7D
--- old/usr/src/pkg/manifests/SUNWlx.mf	Wed Oct  5 00:32:02 2016
+++ new/usr/src/pkg/manifests/SUNWlx.mf	Wed Oct  5 00:32:02 2016
@@ -20,11 +20,12 @@
 #
 
 #
-# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
 #
 
-# Was renamed to system/zones/brand/lx, both now obsolete.
-
-set name=pkg.fmri value=pkg:/SUNWlx@0.5.11,5.11-0.143
-set name=pkg.obsolete value=true
+set name=pkg.fmri value=pkg:/SUNWlx@0.5.11,5.11-0.133
+set name=pkg.renamed value=true
 set name=variant.arch value=i386
+set name=variant.opensolaris.zone value=global value=nonglobal
+depend fmri=pkg:/system/zones/brand/lx@0.5.11,5.11-0.133 type=require
--- old/usr/src/pkg/manifests/system-header.mf	Wed Oct  5 00:32:02 2016
+++ new/usr/src/pkg/manifests/system-header.mf	Wed Oct  5 00:32:02 2016
@@ -1178,6 +1178,7 @@
 file path=usr/include/sys/ldterm.h
 file path=usr/include/sys/lgrp.h
 file path=usr/include/sys/lgrp_user.h
+file path=usr/include/sys/limits.h
 file path=usr/include/sys/link.h
 file path=usr/include/sys/list.h
 file path=usr/include/sys/list_impl.h
--- old/usr/src/pkg/manifests/system-kernel.mf	Wed Oct  5 00:32:02 2016
+++ new/usr/src/pkg/manifests/system-kernel.mf	Wed Oct  5 00:32:02 2016
@@ -236,7 +236,9 @@
 driver name=poll perms="* 0666 root sys"
 $(sparc_ONLY)driver name=power alias=ali1535d+-power
 $(i386_ONLY)driver name=power
-driver name=pseudo alias=zconsnex
+driver name=pseudo \
+    alias=zconsnex \
+    alias=zfdnex
 driver name=ptc perms="* 0666 root sys"
 driver name=ptsl perms="* 0666 root sys"
 $(sparc_ONLY)driver name=ramdisk alias=SUNW,ramdisk perms="* 0600 root sys" \
--- old/usr/src/pkg/manifests/system-test-ostest.mf	Wed Oct  5 00:32:02 2016
+++ new/usr/src/pkg/manifests/system-test-ostest.mf	Wed Oct  5 00:32:02 2016
@@ -24,6 +24,7 @@
 dir path=opt/os-tests/bin
 dir path=opt/os-tests/runfiles
 dir path=opt/os-tests/tests
+dir path=opt/os-tests/tests/file-locking
 dir path=opt/os-tests/tests/sigqueue
 file path=opt/os-tests/README mode=0444
 file path=opt/os-tests/bin/ostest mode=0555
@@ -30,6 +31,10 @@
 file path=opt/os-tests/runfiles/delphix.run mode=0444
 file path=opt/os-tests/runfiles/omnios.run mode=0444
 file path=opt/os-tests/runfiles/openindiana.run mode=0444
+file path=opt/os-tests/tests/file-locking/acquire-lock.32 mode=0555
+file path=opt/os-tests/tests/file-locking/acquire-lock.64 mode=0555
+file path=opt/os-tests/tests/file-locking/runtests.32 mode=0555
+file path=opt/os-tests/tests/file-locking/runtests.64 mode=0555
 file path=opt/os-tests/tests/poll_test mode=0555
 file path=opt/os-tests/tests/sigqueue/sigqueue_queue_size mode=0555
 file path=opt/os-tests/tests/spoof-ras mode=0555
--- old/usr/src/pkg/manifests/system-zones-brand-lx.mf	Wed Oct  5 00:32:02 2016
+++ new/usr/src/pkg/manifests/system-zones-brand-lx.mf	Wed Oct  5 00:32:03 2016
@@ -20,9 +20,133 @@
 #
 
 #
-# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+# Copyright 2015 Joyent, Inc.
 #
 
-set name=pkg.fmri value=pkg:/system/zones/brand/lx@0.5.11,5.11-0.143
-set name=pkg.obsolete value=true
+#
+# This package will install successfully into any zone, global or
+# non-global.  The files, directories, links, and hardlinks, however,
+# will only be installed into the global zone.
+#
+<include global_zone_only_component>
+set name=pkg.fmri value=pkg:/system/zones/brand/lx@$(PKGVERS)
+set name=pkg.description value="Support for the 'lx' Brand"
+set name=pkg.summary value="lx Brand"
+set name=info.classification \
+    value="org.opensolaris.category.2008:Applications/System Utilities"
 set name=variant.arch value=i386
+dir path=etc group=sys
+dir path=etc/zones group=sys
+dir path=kernel group=sys
+dir path=kernel/fs group=sys
+dir path=kernel/fs/$(ARCH64) group=sys
+dir path=kernel/socketmod group=sys
+dir path=kernel/socketmod/$(ARCH64) group=sys
+dir path=usr group=sys
+dir path=usr/include group=bin
+dir path=usr/include/sys group=bin
+dir path=usr/kernel group=sys
+dir path=usr/kernel/brand group=sys
+dir path=usr/kernel/brand/$(ARCH64) group=sys
+dir path=usr/kernel/drv group=sys
+dir path=usr/kernel/drv/$(ARCH64) group=sys
+dir path=usr/kernel/dtrace group=sys
+dir path=usr/kernel/dtrace/$(ARCH64) group=sys
+dir path=usr/kernel/fs group=sys
+dir path=usr/kernel/fs/$(ARCH64) group=sys
+dir path=usr/kernel/strmod group=sys
+dir path=usr/kernel/strmod/$(ARCH64) group=sys
+dir path=usr/lib
+dir path=usr/lib/brand
+dir path=usr/lib/brand/lx
+dir path=usr/lib/brand/lx/$(ARCH64)
+dir path=usr/lib/brand/lx/ld
+dir path=usr/lib/brand/lx/ld/64
+dir path=usr/lib/devfsadm group=sys
+dir path=usr/lib/devfsadm/linkmod group=sys
+dir path=usr/share
+dir path=usr/share/man
+dir path=usr/share/man/man3c
+dir path=usr/share/man/man5
+dir path=usr/share/man/man7fs
+driver name=inotify perms="* 0666 root sys"
+driver name=lx_ptm perms="lx_ptmajor 0666 root sys"
+driver name=lx_systrace perms="* 0644 root sys"
+driver name=lxautofs perms="lxautofs 0600 root sys"
+file path=etc/zones/SUNWlx.xml mode=0444
+file path=etc/zones/SUNWlx26.xml mode=0444
+file path=kernel/socketmod/$(ARCH64)/lx_netlink mode=0755
+file path=kernel/socketmod/lx_netlink mode=0755
+file path=usr/kernel/brand/$(ARCH64)/lx_brand group=sys mode=0755
+file path=usr/kernel/brand/lx_brand group=sys mode=0755
+file path=usr/kernel/drv/$(ARCH64)/inotify group=sys mode=0755
+file path=usr/kernel/drv/$(ARCH64)/lx_ptm group=sys mode=0755
+file path=usr/kernel/drv/$(ARCH64)/lx_systrace group=sys mode=0755
+file path=usr/kernel/drv/$(ARCH64)/lxautofs group=sys mode=0755
+file path=usr/kernel/drv/inotify group=sys mode=0755
+file path=usr/kernel/drv/inotify.conf group=sys
+file path=usr/kernel/drv/lx_ptm group=sys
+file path=usr/kernel/drv/lx_ptm.conf group=sys
+file path=usr/kernel/drv/lx_systrace group=sys
+file path=usr/kernel/drv/lx_systrace.conf group=sys
+file path=usr/kernel/drv/lxautofs group=sys mode=0755
+file path=usr/kernel/drv/lxautofs.conf group=sys
+file path=usr/kernel/fs/$(ARCH64)/lx_cgroup group=sys mode=0755
+file path=usr/kernel/fs/$(ARCH64)/lx_devfs group=sys mode=0755
+file path=usr/kernel/fs/$(ARCH64)/lx_proc group=sys mode=0755
+file path=usr/kernel/fs/$(ARCH64)/lx_sysfs group=sys mode=0755
+file path=usr/kernel/fs/lx_cgroup group=sys mode=0755
+file path=usr/kernel/fs/lx_devfs group=sys mode=0755
+file path=usr/kernel/fs/lx_proc group=sys mode=0755
+file path=usr/kernel/fs/lx_sysfs group=sys mode=0755
+#file path=usr/kernel/strmod/$(ARCH64)/ldlinux group=sys mode=0755
+#file path=usr/kernel/strmod/ldlinux group=sys mode=0755
+file path=usr/lib/$(ARCH64)/lx_brand.so.1
+file path=usr/lib/brand/lx/$(ARCH64)/lx_librtld_db.so.1
+file path=usr/lib/brand/lx/$(ARCH64)/lx_vdso.so.1 mode=0755
+file path=usr/lib/brand/lx/config.xml mode=0444
+file path=usr/lib/brand/lx/etc_default_nfs group=sys mode=0444
+file path=usr/lib/brand/lx/ld/64/ld.config group=sys
+file path=usr/lib/brand/lx/ld/ld.config group=sys
+file path=usr/lib/brand/lx/ltp_skiplist group=sys mode=0444
+file path=usr/lib/brand/lx/ltp_tests group=sys mode=0444
+file path=usr/lib/brand/lx/lx_boot mode=0755
+file path=usr/lib/brand/lx/lx_boot_zone_busybox mode=0755
+file path=usr/lib/brand/lx/lx_boot_zone_debian mode=0755
+file path=usr/lib/brand/lx/lx_boot_zone_redhat mode=0755
+file path=usr/lib/brand/lx/lx_boot_zone_ubuntu mode=0755
+file path=usr/lib/brand/lx/lx_install mode=0755
+file path=usr/lib/brand/lx/lx_librtld_db.so.1
+file path=usr/lib/brand/lx/lx_support mode=0755
+file path=usr/lib/brand/lx/lx_vdso.so.1 mode=0755
+file path=usr/lib/brand/lx/lxinit mode=0555
+file path=usr/lib/brand/lx/platform.xml mode=0444
+file path=usr/lib/devfsadm/linkmod/SUNW_lx_link_$(ARCH).so group=sys
+file path=usr/lib/lx_brand.so.1
+file path=usr/share/man/man3c/inotify_add_watch.3c
+file path=usr/share/man/man3c/inotify_init.3c
+file path=usr/share/man/man3c/inotify_rm_watch.3c
+file path=usr/share/man/man5/inotify.5
+file path=usr/share/man/man5/lx.5
+hardlink path=usr/kernel/dtrace/$(ARCH64)/lx_systrace \
+    target=../../../kernel/drv/$(ARCH64)/lx_systrace
+hardlink path=usr/kernel/dtrace/lx_systrace \
+    target=../../kernel/drv/lx_systrace
+hardlink path=usr/kernel/fs/$(ARCH64)/lxautofs \
+    target=../../../kernel/drv/$(ARCH64)/lxautofs
+hardlink path=usr/kernel/fs/lxautofs target=../../kernel/drv/lxautofs
+legacy pkg=SUNWlxr arch=$(ARCH) category=system \
+    desc="Support for the 'lx' Brand" \
+    hotline="Please contact your local service provider" \
+    name="lx Brand (Root)" vendor="Sun Microsystems, Inc." \
+    version=11.11,REV=2009.11.11
+legacy pkg=SUNWlxu arch=$(ARCH) category=system \
+    desc="Support for the 'lx' Brand" \
+    hotline="Please contact your local service provider" \
+    name="lx Brand (Usr)" vendor="Sun Microsystems, Inc." \
+    version=11.11,REV=2009.11.11
+license cr_Sun license=cr_Sun
+license lic_CDDL license=lic_CDDL
+link path=usr/lib/brand/lx/64 target=$(ARCH64)
--- old/usr/src/pkg/manifests/system-zones.mf	Wed Oct  5 00:32:03 2016
+++ new/usr/src/pkg/manifests/system-zones.mf	Wed Oct  5 00:32:03 2016
@@ -60,6 +60,7 @@
 dir path=usr/share/man/man5
 dir path=usr/share/man/man7d
 driver name=zcons
+driver name=zfd
 file path=etc/zones/SUNWblank.xml mode=0444 variant.opensolaris.zone=global
 file path=etc/zones/SUNWdefault.xml mode=0444 variant.opensolaris.zone=global
 file path=etc/zones/index group=sys original_name=SUNWzone:etc/zones/index \
@@ -77,7 +78,9 @@
     variant.opensolaris.zone=global
 file path=usr/bin/zonestat mode=0555
 file path=usr/kernel/drv/$(ARCH64)/zcons group=sys
+file path=usr/kernel/drv/$(ARCH64)/zfd group=sys
 $(i386_ONLY)file path=usr/kernel/drv/zcons group=sys
+$(i386_ONLY)file path=usr/kernel/drv/zfd group=sys mode=0755
 file path=usr/lib/$(ARCH64)/libbrand.so.1
 file path=usr/lib/$(ARCH64)/libzonecfg.so.1
 file path=usr/lib/$(ARCH64)/libzonestat.so.1
@@ -106,6 +109,7 @@
 file path=usr/share/man/man5/brands.5
 file path=usr/share/man/man5/zones.5
 file path=usr/share/man/man7d/zcons.7d
+file path=usr/share/man/man7d/zfd.7d
 legacy pkg=SUNWzoner desc="Solaris Zones Configuration Files" \
     name="Solaris Zones (Root)"
 legacy pkg=SUNWzoneu desc="Solaris Zones Configuration and Administration" \
--- old/usr/src/test/os-tests/runfiles/omnios.run	Wed Oct  5 00:32:03 2016
+++ new/usr/src/test/os-tests/runfiles/omnios.run	Wed Oct  5 00:32:03 2016
@@ -26,3 +26,10 @@
 
 [/opt/os-tests/tests/sigqueue]
 tests = ['sigqueue_queue_size']
+
+[/opt/os-tests/tests/tmpfs]
+user = root
+tests = ['tmpfs_badmount', 'tmpfs_enospc']
+
+[/opt/os-tests/tests/file-locking]
+tests = ['runtests.32', 'runtests.64']
--- old/usr/src/test/os-tests/tests/Makefile	Wed Oct  5 00:32:03 2016
+++ new/usr/src/test/os-tests/tests/Makefile	Wed Oct  5 00:32:03 2016
@@ -13,6 +13,6 @@
 # Copyright (c) 2012 by Delphix. All rights reserved.
 #
 
-SUBDIRS = poll sigqueue spoof-ras
+SUBDIRS = poll sigqueue spoof-ras file-locking
 
 include $(SRC)/test/Makefile.com
--- /dev/null	Wed Oct  5 00:32:03 2016
+++ new/usr/src/test/os-tests/tests/file-locking/Makefile	Wed Oct  5 00:32:03 2016
@@ -0,0 +1,75 @@
+#
+# 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 Joyent, Inc.
+#
+
+include $(SRC)/cmd/Makefile.cmd
+include $(SRC)/test/Makefile.com
+
+UTILS = util.c
+
+PROGS = \
+	runtests \
+	acquire-lock
+
+C99MODE = -xc99=%all
+
+SRCS = $(PROGS:%=%.c) $(UTILS)
+LINTS = $(SRCS:%.c=%.ln)
+PROGS32 = $(PROGS:%=%.32)
+PROGS64 = $(PROGS:%=%.64)
+
+ROOTOPTDIR = $(ROOT)/opt/os-tests/tests/file-locking
+ROOTOPTPROGS = $(PROGS32:%=$(ROOTOPTDIR)/%) \
+	$(PROGS64:%=$(ROOTOPTDIR)/%)
+
+all     :=      TARGET = all
+install :=      TARGET = install
+clean   :=      TARGET = clean
+clobber :=      TARGET = clobber
+lint    :=      TARGET = lint
+
+.KEEP_STATE:
+
+install: $(ROOTOPTPROGS)
+
+all: $(PROGS32) $(PROGS64)
+
+lint: $(LINTS)
+
+clean:
+	-rm $(PROGS32) $(PROGS64)
+
+$(ROOTOPTPROGS): $(PROGS32) $(PROGS64) $(ROOTOPTDIR)
+
+$(ROOTOPTDIR):
+	$(INS.dir)
+
+$(ROOTOPTDIR)/%: %
+	$(INS.file)
+
+$(ROOTOPTDIR)/%: %.ksh
+	$(INS.rename)
+
+%.64: %.c
+	$(LINK64.c) -o $@ $< $(UTILS) $(LDLIBS64)
+	$(POST_PROCESS)
+
+%.32: %.c
+	$(LINK.c) -o $@ $< $(UTILS) $(LDLIBS)
+	$(POST_PROCESS)
+
+clobber:
+	$(RM) $(PROGS32) $(PROGS64)
+
+FRC:
--- /dev/null	Wed Oct  5 00:32:03 2016
+++ new/usr/src/test/os-tests/tests/file-locking/acquire-lock.c	Wed Oct  5 00:32:03 2016
@@ -0,0 +1,161 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+/*
+ * Acquire the specified kind of lock with the specified parameters. After
+ * acquiring the lock, a byte will be written to stdout. The program will
+ * then wait for a byte to be written to stdin before exiting.
+ *
+ * Usage: <posix|ofd|flock> <shared|exclusive> <path>
+ */
+
+#include "util.h"
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <sys/file.h>
+#include <unistd.h>
+
+
+static	void	acq_fcntl(int, int, int);
+static	void	acq_flock(int fd, int mode);
+static	void	acq_run(int, lock_style_t, boolean_t);
+
+
+static void
+acq_fcntl(int fd, int cmd, int mode)
+{
+	struct flock fl;
+	int ret, i;
+
+	/*
+	 * Acquire the lock, and then try reacquiring it several times. Once we
+	 * have acquired the lock, trying to acquire it again should succeed,
+	 * and shouldn't upgrade, downgrade or free the lock.
+	 */
+	for (i = 0; i < 3; i++) {
+		flock_reinit(&fl, mode);
+		flock_log("Acquiring lock (fcntl)...\n");
+		ret = fcntl(fd, cmd, &fl);
+		if (ret == -1) {
+			err(EXIT_FAILURE, "fcntl failed");
+		}
+	}
+
+
+	/* Let the parent know we have the lock and wait */
+	flock_log("Waiting (fcntl)...\n");
+	flock_alert(1);
+	flock_block(0);
+
+	/* Now unlock */
+	flock_reinit(&fl, F_UNLCK);
+	flock_log("Releasing lock (fcntl)...\n");
+	ret = fcntl(fd, cmd, &fl);
+	if (ret == -1) {
+		err(EXIT_FAILURE, "fcntl failed");
+	}
+}
+
+
+static void
+acq_flock(int fd, int mode)
+{
+	int ret, i;
+
+	/*
+	 * Acquire the lock, and then try reacquiring it several times. Once we
+	 * have acquired the lock, trying to acquire it again should succeed,
+	 * and shouldn't upgrade, downgrade or free the lock.
+	 */
+	for (i = 0; i < 3; i++) {
+		flock_log("Acquiring lock (flock)...\n");
+		ret = flock(fd, mode);
+		if (ret == -1) {
+			err(EXIT_FAILURE, "flock failed");
+		}
+	}
+
+	/* Wait to be okayed to unlock */
+	flock_log("Waiting (flock)...\n");
+	flock_alert(1);
+	flock_block(0);
+
+	/* Release lock */
+	flock_log("Releasing lock (flock)...\n");
+	ret = flock(fd, LOCK_UN);
+	if (ret == -1) {
+		err(EXIT_FAILURE, "flock failed");
+	}
+}
+
+
+static void
+acq_run(int fd, lock_style_t style, boolean_t is_exclusive)
+{
+	switch (style) {
+	case LSTYLE_POSIX:
+		acq_fcntl(fd, F_SETLKW, is_exclusive ? F_WRLCK : F_RDLCK);
+		break;
+	case LSTYLE_OFD:
+		acq_fcntl(fd, F_OFD_SETLKW, is_exclusive ? F_WRLCK : F_RDLCK);
+		break;
+	case LSTYLE_FLOCK:
+		acq_flock(fd, is_exclusive ? LOCK_EX : LOCK_SH);
+		break;
+	default:
+		abort();
+	}
+}
+
+
+int
+main(int argc, char *argv[])
+{
+	char *modestr, *path;
+	lock_style_t style;
+	boolean_t is_exclusive;
+	int fd;
+
+	if (argc < 4) {
+		errx(EXIT_FAILURE, BAD_ARGS_MESSAGE, argc - 1);
+	}
+
+	modestr = argv[2];
+	path = argv[3];
+
+	style = flock_styleenum(argv[1]);
+
+	if (strcmp(modestr, "shared") == 0) {
+		is_exclusive = B_FALSE;
+	} else if (strcmp(modestr, "exclusive") == 0) {
+		is_exclusive = B_TRUE;
+	} else {
+		errx(EXIT_FAILURE, BAD_MODE_MESSAGE);
+	}
+
+	boolean_t rdonly = style == LSTYLE_FLOCK || !is_exclusive;
+	fd = open(path, rdonly ? O_RDONLY : O_WRONLY);
+	if (fd == -1) {
+		err(EXIT_FAILURE, "Failed to open %s", path);
+	}
+
+	acq_run(fd, style, is_exclusive);
+
+	return (0);
+}
--- /dev/null	Wed Oct  5 00:32:03 2016
+++ new/usr/src/test/os-tests/tests/file-locking/runtests.c	Wed Oct  5 00:32:03 2016
@@ -0,0 +1,677 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+/*
+ * Validate various fcntl(2) and flock(3C) operations.
+ */
+
+#include "util.h"
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <sys/debug.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+
+#define	LOCKFILE_FMT	"/tmp/.lockfile-%s-%ld"
+#define	LOCKDIR_FMT	"/tmp/.lockdir-%s-%ld"
+
+typedef struct lockinfo {
+	char *lf_name;
+	char *lf_path;
+	int lf_fd;
+} lockinfo_t;
+
+
+static	void	assert_write_locked_by(lockinfo_t *, pid_t);
+static	void	assert_read_locked_by(lockinfo_t *, pid_t);
+static	void	assert_unlocked(lockinfo_t *);
+static	void	assert_all_unlocked(void);
+
+static	int	flock_copyfil(lockinfo_t *, lockinfo_t *);
+static	int	flock_mkfil(lockinfo_t *);
+static	int	flock_mkdir(lockinfo_t *);
+static	void	flock_rminfo(lockinfo_t *);
+
+static	void	flock_fcntl(lockinfo_t *lf, int cmd, struct flock *fl);
+static	void	flock_run(lock_style_t, boolean_t, lockinfo_t *,
+		    pid_t *, int[]);
+static	int	flock_wait(pid_t pid);
+static	void	flock_cleanup_child(pid_t, int []);
+
+static	void	flock_test_invalid(lockinfo_t *, int, short, short,
+		    off_t, off_t);
+static	void	flock_test_exclusive(lock_style_t, lock_style_t,
+		    lockinfo_t *, lockinfo_t *, boolean_t);
+static	void	flock_test_shared(lock_style_t, lock_style_t, lockinfo_t *,
+		    lockinfo_t *, boolean_t);
+static	void	flock_test_upgrade_downgrade(void);
+
+static char *acqprog = NULL;
+
+static lockinfo_t flock_fileA = { "a", NULL, -1 };
+static lockinfo_t flock_fileB = { "b", NULL, -1 };
+static lockinfo_t flock_dirA = { "a", NULL, -1 };
+static lockinfo_t flock_dirB = { "b", NULL, -1 };
+
+
+static short cmds[8] = {
+	F_SETLK, F_SETLKW, F_GETLK,
+	F_OFD_SETLK, F_OFD_SETLKW, F_OFD_GETLK,
+	F_FLOCK, F_FLOCKW
+};
+
+
+static void
+flock_kill(pid_t pid)
+{
+	while (kill(pid, SIGKILL) == -1) {
+		if (errno == EINTR)
+			continue;
+
+		err(EXIT_FAILURE, "kill failed");
+	}
+}
+
+
+static void
+flock_fcntl(lockinfo_t *lf, int cmd, struct flock *fl)
+{
+	if (fcntl(lf->lf_fd, cmd, fl) == -1) {
+		err(EXIT_FAILURE, "fcntl failed");
+	}
+}
+
+
+static void
+assert_write_locked_by(lockinfo_t *lf, pid_t pid)
+{
+	struct flock fl;
+
+	flock_reinit(&fl, F_WRLCK);
+	flock_fcntl(lf, F_GETLK, &fl);
+	VERIFY3_IMPL(fl.l_type, ==, F_WRLCK, short);
+	VERIFY3_IMPL(fl.l_sysid, ==, 0, int);
+	VERIFY3_IMPL(fl.l_pid, ==, pid, pid_t);
+
+	flock_reinit(&fl, F_WRLCK);
+	flock_fcntl(lf, F_OFD_GETLK, &fl);
+	VERIFY3_IMPL(fl.l_type, ==, F_WRLCK, short);
+	VERIFY3_IMPL(fl.l_sysid, ==, 0, int);
+	VERIFY3_IMPL(fl.l_pid, ==, pid, pid_t);
+
+	flock_reinit(&fl, F_RDLCK);
+	flock_fcntl(lf, F_GETLK, &fl);
+	VERIFY3_IMPL(fl.l_type, ==, F_WRLCK, short);
+	VERIFY3_IMPL(fl.l_sysid, ==, 0, int);
+	VERIFY3_IMPL(fl.l_pid, ==, pid, pid_t);
+
+	flock_reinit(&fl, F_RDLCK);
+	flock_fcntl(lf, F_OFD_GETLK, &fl);
+	VERIFY3_IMPL(fl.l_type, ==, F_WRLCK, short);
+	VERIFY3_IMPL(fl.l_sysid, ==, 0, int);
+	VERIFY3_IMPL(fl.l_pid, ==, pid, pid_t);
+}
+
+
+static void
+assert_read_locked_by(lockinfo_t *lf, pid_t pid)
+{
+	struct flock fl;
+
+	flock_reinit(&fl, F_WRLCK);
+	flock_fcntl(lf, F_GETLK, &fl);
+	VERIFY3_IMPL(fl.l_type, ==, F_RDLCK, short);
+	VERIFY3_IMPL(fl.l_sysid, ==, 0, int);
+	VERIFY3_IMPL(fl.l_pid, ==, pid, pid_t);
+
+	flock_reinit(&fl, F_WRLCK);
+	flock_fcntl(lf, F_OFD_GETLK, &fl);
+	VERIFY3_IMPL(fl.l_type, ==, F_RDLCK, short);
+	VERIFY3_IMPL(fl.l_sysid, ==, 0, int);
+	VERIFY3_IMPL(fl.l_pid, ==, pid, pid_t);
+
+	flock_reinit(&fl, F_RDLCK);
+	flock_fcntl(lf, F_GETLK, &fl);
+	VERIFY3_IMPL(fl.l_type, ==, F_UNLCK, short);
+	VERIFY3_IMPL(fl.l_sysid, ==, 0, int);
+	VERIFY3_IMPL(fl.l_pid, ==, 0, pid_t);
+
+	flock_reinit(&fl, F_RDLCK);
+	flock_fcntl(lf, F_OFD_GETLK, &fl);
+	VERIFY3_IMPL(fl.l_type, ==, F_UNLCK, short);
+	VERIFY3_IMPL(fl.l_sysid, ==, 0, int);
+	VERIFY3_IMPL(fl.l_pid, ==, 0, pid_t);
+}
+
+static void
+assert_unlocked(lockinfo_t *lf)
+{
+	struct flock fl;
+
+	flock_reinit(&fl, F_WRLCK);
+	flock_fcntl(lf, F_GETLK, &fl);
+	VERIFY3_IMPL(fl.l_type, ==, F_UNLCK, short);
+	VERIFY3_IMPL(fl.l_sysid, ==, 0, int);
+	VERIFY3_IMPL(fl.l_pid, ==, 0, pid_t);
+
+	flock_reinit(&fl, F_WRLCK);
+	flock_fcntl(lf, F_OFD_GETLK, &fl);
+	VERIFY3_IMPL(fl.l_type, ==, F_UNLCK, short);
+	VERIFY3_IMPL(fl.l_sysid, ==, 0, int);
+	VERIFY3_IMPL(fl.l_pid, ==, 0, pid_t);
+
+	flock_reinit(&fl, F_RDLCK);
+	flock_fcntl(lf, F_GETLK, &fl);
+	VERIFY3_IMPL(fl.l_type, ==, F_UNLCK, short);
+	VERIFY3_IMPL(fl.l_sysid, ==, 0, int);
+	VERIFY3_IMPL(fl.l_pid, ==, 0, pid_t);
+
+	flock_reinit(&fl, F_RDLCK);
+	flock_fcntl(lf, F_OFD_GETLK, &fl);
+	VERIFY3_IMPL(fl.l_type, ==, F_UNLCK, short);
+	VERIFY3_IMPL(fl.l_sysid, ==, 0, int);
+	VERIFY3_IMPL(fl.l_pid, ==, 0, pid_t);
+}
+
+
+static void
+assert_all_unlocked(void)
+{
+	assert_unlocked(&flock_fileA);
+	assert_unlocked(&flock_fileB);
+	assert_unlocked(&flock_dirA);
+	assert_unlocked(&flock_dirB);
+}
+
+
+static int
+flock_copyfil(lockinfo_t *src, lockinfo_t *dst)
+{
+	dst->lf_name = NULL;
+	dst->lf_path = NULL;
+	if ((dst->lf_fd = open(src->lf_path, O_RDWR)) == -1) {
+		warn("Failed to open %s", src->lf_path);
+		return (-1);
+	}
+
+	return (0);
+}
+
+
+static int
+flock_mkfil(lockinfo_t *lf)
+{
+	if (asprintf(&lf->lf_path, LOCKFILE_FMT, lf->lf_name, getpid()) < 0) {
+		warnx("Failed to generate lockfile name");
+		return (-1);
+	}
+
+	if ((lf->lf_fd = open(lf->lf_path, O_RDWR|O_CREAT, 0600)) == -1)  {
+		warn("Failed to open %s", lf->lf_path);
+		return (-1);
+	}
+
+	return (0);
+}
+
+
+static int
+flock_mkdir(lockinfo_t *lf)
+{
+	if (asprintf(&lf->lf_path, LOCKDIR_FMT, lf->lf_name, getpid()) < 0) {
+		warnx("Failed to generate lockfile name");
+		return (-1);
+	}
+
+	if (mkdir(lf->lf_path, 0700) == -1)  {
+		warn("Failed to make %s", lf->lf_path);
+		return (-1);
+	}
+
+	if ((lf->lf_fd = open(lf->lf_path, O_RDONLY)) == -1)  {
+		warn("Failed to open %s", lf->lf_path);
+		return (-1);
+	}
+
+	return (0);
+}
+
+
+static void
+flock_rminfo(lockinfo_t *lf)
+{
+	if (lf->lf_fd != -1) {
+		(void) close(lf->lf_fd);
+	}
+	if (lf->lf_path != NULL) {
+		(void) unlink(lf->lf_path);
+		free(lf->lf_path);
+	}
+}
+
+
+static void
+flock_run(lock_style_t style, boolean_t is_exclusive, lockinfo_t *lf,
+    pid_t *pid, int fds[])
+{
+	char *stylestr = flock_stylestr(style);
+	char *modestr = is_exclusive ? "exclusive" : "shared";
+	char *argv[5] = { acqprog, stylestr, modestr, lf->lf_path, NULL };
+	int ret = pipe(fds);
+	if (ret == -1) {
+		err(EXIT_FAILURE, "pipe failed");
+	}
+
+	*pid = fork();
+	if (*pid == (pid_t)-1) {
+		err(EXIT_FAILURE, "fork failed");
+	} else if (*pid == (pid_t)0) {
+		/* Set up pipe for communicating with child */
+		ret = dup2(fds[1], 0);
+		if (ret == -1) {
+			err(EXIT_FAILURE, "dup2 failed");
+		}
+		ret = dup2(fds[1], 1);
+		if (ret == -1) {
+			err(EXIT_FAILURE, "dup2 failed");
+		}
+		closefrom(3);
+
+		(void) execv(acqprog, argv);
+		err(EXIT_FAILURE, "Failed to execute %s", acqprog);
+	}
+}
+
+
+static int
+flock_wait(pid_t pid)
+{
+	int childstat = 0;
+
+	while (waitpid(pid, &childstat, 0) == -1) {
+		if (errno == EINTR)
+			continue;
+
+		err(EXIT_FAILURE, "Failed to wait on child");
+	}
+
+	if (WIFEXITED(childstat)) {
+		return (WEXITSTATUS(childstat));
+	} else if (WIFSIGNALED(childstat)) {
+		return (1);
+	} else {
+		abort();
+		return (1);
+	}
+}
+
+
+static void
+flock_cleanup_child(pid_t pid, int fds[])
+{
+	(void) flock_wait(pid);
+	(void) close(fds[0]);
+	(void) close(fds[1]);
+}
+
+
+static void
+flock_test_upgrade_downgrade(void)
+{
+	lockinfo_t afd1, afd2, afd3;
+	pid_t pid;
+	int fds[2];
+
+	VERIFY3S(flock_copyfil(&flock_fileA, &afd1), ==, 0);
+	VERIFY3S(flock_copyfil(&flock_fileA, &afd2), ==, 0);
+	VERIFY3S(flock_copyfil(&flock_fileA, &afd3), ==, 0);
+
+	flock_log("Acquiring shared locks 1, 2 and 3...");
+	VERIFY3S(flock(afd1.lf_fd, LOCK_SH), ==, 0);
+	VERIFY3S(flock(afd2.lf_fd, LOCK_SH), ==, 0);
+	VERIFY3S(flock(afd3.lf_fd, LOCK_SH), ==, 0);
+	assert_read_locked_by(&flock_fileA, -1);
+	flock_log(" ok\n");
+
+	flock_log("Upgrading lock 3 should fail w/ EWOULDBLOCK...");
+	VERIFY3S(flock(afd3.lf_fd, LOCK_EX|LOCK_NB), ==, -1);
+	VERIFY3U(errno, ==, EWOULDBLOCK);
+	assert_read_locked_by(&flock_fileA, -1);
+	flock_log(" ok\n");
+
+	flock_log("Upgrading 3 should succeed after releasing locks 1 & 2...");
+	VERIFY3S(flock(afd1.lf_fd, LOCK_UN), ==, 0);
+	VERIFY3S(flock(afd2.lf_fd, LOCK_UN), ==, 0);
+	VERIFY3S(flock(afd3.lf_fd, LOCK_EX), ==, 0);
+	assert_write_locked_by(&flock_fileA, -1);
+	flock_log(" ok\n");
+
+
+	flock_log("Starting up child, then downgrading lock 3 to shared...");
+	flock_run(LSTYLE_FLOCK, B_FALSE, &flock_fileA, &pid, fds);
+	VERIFY3_IMPL(flock_nodata(fds[0]), ==, B_TRUE, boolean_t);
+	VERIFY3S(flock(afd3.lf_fd, LOCK_SH), ==, 0);
+	flock_block(fds[0]);
+	assert_read_locked_by(&flock_fileA, -1);
+	flock_log(" ok\n");
+
+	flock_log("Releasing child and upgrading...");
+	flock_alert(fds[0]);
+	flock_cleanup_child(pid, fds);
+	assert_read_locked_by(&flock_fileA, -1);
+	VERIFY3S(flock(afd3.lf_fd, LOCK_EX), ==, 0);
+	assert_write_locked_by(&flock_fileA, -1);
+	flock_log(" ok\n");
+
+	flock_log("Releasing lock 3...");
+	VERIFY3S(flock(afd3.lf_fd, LOCK_UN), ==, 0);
+	flock_rminfo(&afd1);
+	flock_rminfo(&afd2);
+	flock_rminfo(&afd3);
+	assert_all_unlocked();
+	flock_log(" ok\n");
+}
+
+
+static void
+flock_test_invalid(lockinfo_t *lf, int cmd, short l_type, short l_whence,
+    off_t l_start, off_t l_len)
+{
+	struct flock fl = {
+		.l_type = l_type,
+		.l_whence = l_whence,
+		.l_start = l_start,
+		.l_len = l_len
+	};
+
+	flock_log("fcntl(fd, %s, { %hd, %hd, %ld, %ld, ... })...",
+	    flock_cmdname(cmd), l_type, l_whence, l_start, l_len);
+	VERIFY3S(fcntl(lf->lf_fd, cmd, &fl), ==, -1);
+	VERIFY3U(errno, ==, EINVAL);
+	flock_log(" ok\n");
+}
+
+
+static void
+flock_test_exclusive(lock_style_t styleA, lock_style_t styleB,
+    lockinfo_t *lock1, lockinfo_t *lock2, boolean_t kill_firstborn)
+{
+	pid_t pidA, pidB;
+	int fdsA[2], fdsB[2];
+
+	flock_log("Running %s + %s tests (%s)...",
+	    flock_stylename(styleA), flock_stylename(styleB),
+	    kill_firstborn ? "kill child" : "child exits");
+
+	/* Create child, and wait for it to acquire the lock */
+	flock_run(styleA, B_TRUE, lock1, &pidA, fdsA);
+	flock_block(fdsA[0]);
+
+	/* Create second child, which shouldn't acquire & signal */
+	flock_run(styleB, B_TRUE, lock1, &pidB, fdsB);
+	VERIFY3_IMPL(flock_nodata(fdsB[0]), ==, B_TRUE, boolean_t);
+
+	/* lock1 is blocked for reading and writing */
+	assert_write_locked_by(lock1, styleA == LSTYLE_POSIX ? pidA : -1);
+	assert_unlocked(lock2);
+
+	/* Tell pidA to exit */
+	if (kill_firstborn) {
+		flock_kill(pidA);
+	} else {
+		flock_alert(fdsA[0]);
+	}
+	flock_cleanup_child(pidA, fdsA);
+
+	/* Wait for pidB to signal us */
+	flock_block(fdsB[0]);
+
+	/* lock1 is blocked for reading and writing */
+	assert_write_locked_by(lock1, styleB == LSTYLE_POSIX ? pidB : -1);
+	assert_unlocked(lock2);
+
+	/* Tell pidB to exit */
+	flock_alert(fdsB[0]);
+
+	flock_cleanup_child(pidB, fdsB);
+
+	/*
+	 * Tests after child has released lock
+	 */
+	assert_all_unlocked();
+
+	flock_log(" ok\n");
+}
+
+
+static void
+flock_test_shared(lock_style_t styleA, lock_style_t styleB,
+    lockinfo_t *lock1, lockinfo_t *lock2, boolean_t kill_firstborn)
+{
+	pid_t pidA, pidB;
+	int fdsA[2], fdsB[2];
+
+	flock_log("Running %s + %s tests (%s)...",
+	    flock_stylename(styleA), flock_stylename(styleB),
+	    kill_firstborn ? "kill child" : "child exits");
+
+	/* Create children, and wait for it to acquire the lock */
+	flock_run(styleB, B_FALSE, lock1, &pidB, fdsB);
+	flock_block(fdsB[0]);
+	flock_run(styleA, B_FALSE, lock1, &pidA, fdsA);
+	flock_block(fdsA[0]);
+
+	/* testfileA is only blocked for writing */
+	assert_read_locked_by(lock1, styleA == LSTYLE_POSIX ? pidA : -1);
+	assert_unlocked(lock2);
+
+	/* Tell pidA to exit */
+	if (kill_firstborn) {
+		flock_kill(pidA);
+	} else {
+		flock_alert(fdsA[0]);
+	}
+	flock_cleanup_child(pidA, fdsA);
+
+	/* testfileA is still blocked for writing by pidB */
+	assert_read_locked_by(lock1, styleB == LSTYLE_POSIX ? pidB : -1);
+	assert_unlocked(lock2);
+
+	/* Tell pidB to exit */
+	flock_alert(fdsB[0]);
+	flock_cleanup_child(pidB, fdsB);
+
+	assert_all_unlocked();
+
+	flock_log(" ok\n");
+}
+
+
+static void
+flock_test_ofd_sameproc(void)
+{
+	lockinfo_t afd1, afd2, afd3;
+
+	VERIFY3S(flock_copyfil(&flock_fileA, &afd1), ==, 0);
+	VERIFY3S(flock_copyfil(&flock_fileA, &afd2), ==, 0);
+	VERIFY3S(flock_copyfil(&flock_fileA, &afd3), ==, 0);
+
+	flock_log("Acquiring first two shared locks...");
+	VERIFY3S(flock(afd1.lf_fd, LOCK_SH), ==, 0);
+	VERIFY3S(flock(afd2.lf_fd, LOCK_SH), ==, 0);
+	assert_read_locked_by(&flock_fileA, -1);
+	flock_log(" ok\n");
+
+	flock_log("Acquiring an exclusive lock should fail w/ EWOULDBLOCK...");
+	VERIFY3S(flock(afd3.lf_fd, LOCK_EX|LOCK_NB), ==, -1);
+	VERIFY3U(errno, ==, EWOULDBLOCK);
+	flock_log(" ok\n");
+
+	flock_log("Releasing to acquire an exclusive lock...");
+	VERIFY3S(flock(afd1.lf_fd, LOCK_UN), ==, 0);
+	VERIFY3S(flock(afd2.lf_fd, LOCK_UN), ==, 0);
+	flock_log(" ok\n");
+
+	flock_log("Acquiring an exclusive lock...");
+	VERIFY3S(flock(afd3.lf_fd, LOCK_EX), ==, 0);
+	assert_write_locked_by(&flock_fileA, -1);
+	flock_log(" ok\n");
+
+	flock_log("Acquiring a shared lock should fail w/ EWOULDBLOCK...");
+	VERIFY3S(flock(afd1.lf_fd, LOCK_EX|LOCK_NB), ==, -1);
+	VERIFY3U(errno, ==, EWOULDBLOCK);
+	VERIFY3S(flock(afd2.lf_fd, LOCK_EX|LOCK_NB), ==, -1);
+	VERIFY3U(errno, ==, EWOULDBLOCK);
+	flock_log(" ok\n");
+
+	flock_log("Releasing exclusive lock...");
+	VERIFY3S(flock(afd3.lf_fd, LOCK_UN), ==, 0);
+	assert_all_unlocked();
+	flock_log(" ok\n");
+
+	flock_rminfo(&afd1);
+	flock_rminfo(&afd2);
+	flock_rminfo(&afd3);
+}
+
+
+static void
+flock_runtests(void)
+{
+	lock_style_t first, second;
+	int i;
+
+	flock_log("# Exclusive lock tests\n");
+	for (first = (lock_style_t)0; first < LSTYLE_LAST; first++) {
+		for (second = (lock_style_t)0; second < LSTYLE_LAST; second++) {
+			flock_test_exclusive(first, second,
+			    &flock_fileA, &flock_fileB, B_TRUE);
+			flock_test_exclusive(first, second,
+			    &flock_fileA, &flock_fileB, B_FALSE);
+		}
+	}
+
+	flock_log("# Shared lock tests\n");
+	for (first = (lock_style_t)0; first < LSTYLE_LAST; first++) {
+		for (second = (lock_style_t)0; second < LSTYLE_LAST; second++) {
+			flock_test_shared(first, second,
+			    &flock_fileA, &flock_fileB, B_TRUE);
+			flock_test_shared(first, second,
+			    &flock_fileA, &flock_fileB, B_FALSE);
+		}
+	}
+
+	flock_log("# flock(3C) directory lock tests\n");
+	flock_test_exclusive(LSTYLE_FLOCK, LSTYLE_FLOCK,
+	    &flock_dirA, &flock_dirB, B_TRUE);
+	flock_test_exclusive(LSTYLE_FLOCK, LSTYLE_FLOCK,
+	    &flock_dirA, &flock_dirB, B_FALSE);
+	flock_test_shared(LSTYLE_FLOCK, LSTYLE_FLOCK,
+	    &flock_dirA, &flock_dirB, B_TRUE);
+	flock_test_shared(LSTYLE_FLOCK, LSTYLE_FLOCK,
+	    &flock_dirA, &flock_dirB, B_FALSE);
+
+
+	flock_log("# Invalid fcntl(2) parameters tests\n");
+	for (i = 0; i < sizeof (cmds) / sizeof (short); i++) {
+		flock_test_invalid(&flock_fileA, cmds[i], 200, 0, 0, 0);
+		flock_test_invalid(&flock_fileA, cmds[i], -1, 0, 0, 0);
+	}
+	for (i = 3; i < sizeof (cmds) / sizeof (short); i++) {
+		flock_test_invalid(&flock_fileA, cmds[i], F_WRLCK, 1, 0, 0);
+		flock_test_invalid(&flock_fileA, cmds[i], F_WRLCK, 0, 1, 0);
+		flock_test_invalid(&flock_fileA, cmds[i], F_WRLCK, 0, 0, 1);
+	}
+
+	flock_log("# Testing that multiple OFD locks work in a process\n");
+	flock_test_ofd_sameproc();
+
+	flock_log("# Testing flock(3C) upgrade/downgrade tests\n");
+	flock_test_upgrade_downgrade();
+}
+
+
+int
+main(int argc, char *argv[])
+{
+	char *basestr, *suffix, *dirstr, *dirpath;
+	pid_t testrunner;
+	int exval;
+
+	LOG = B_TRUE;
+
+	if (argc < 1) {
+		errx(EXIT_FAILURE, "Can't find program name!");
+	}
+
+	dirstr = strdup(argv[0]);
+	dirpath = dirname(dirstr);
+	basestr = strdup(argv[0]);
+	suffix = basename(basestr);
+
+	while (*suffix != '.' && *suffix != '\0') {
+		suffix += 1;
+	}
+
+	if (asprintf(&acqprog, "%s/acquire-lock%s", dirpath, suffix) < 0) {
+		errx(EXIT_FAILURE,
+		    "Can't generate lock acquisition program name!");
+	}
+
+	if (access(acqprog, X_OK) != 0) {
+		err(EXIT_FAILURE,
+		    "Can't run lock acquisition program %s", acqprog);
+	}
+
+	/* Create several lockfiles for testing */
+	if (flock_mkfil(&flock_fileA) != 0 ||
+	    flock_mkfil(&flock_fileB) != 0 ||
+	    flock_mkdir(&flock_dirA) != 0 ||
+	    flock_mkdir(&flock_dirB) != 0) {
+		exval = 1;
+		goto cleanup;
+	}
+
+	/*
+	 * We run the tests in a child process so that when tests fail
+	 * we can still clean up our temporary files.
+	 */
+	testrunner = fork();
+	if (testrunner == (pid_t)-1) {
+		err(EXIT_FAILURE, "Unable to fork to run tests");
+	} else if (testrunner == (pid_t)0) {
+		flock_runtests();
+		return (0);
+	}
+
+	exval = flock_wait(testrunner);
+
+cleanup:
+	free(basestr);
+	free(dirstr);
+	flock_rminfo(&flock_fileA);
+	flock_rminfo(&flock_fileB);
+	flock_rminfo(&flock_dirA);
+	flock_rminfo(&flock_dirB);
+	return (exval);
+}
--- /dev/null	Wed Oct  5 00:32:03 2016
+++ new/usr/src/test/os-tests/tests/file-locking/util.c	Wed Oct  5 00:32:03 2016
@@ -0,0 +1,175 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+/*
+ * Utility functions for use in both acquire-lock and runtests.
+ */
+
+#include "util.h"
+#include <err.h>
+#include <errno.h>
+#include <poll.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+
+
+boolean_t LOG = B_FALSE;
+
+
+void
+flock_log(const char *format, ...)
+{
+	va_list ap;
+	if (!LOG) {
+		return;
+	}
+
+	va_start(ap, format);
+	(void) vfprintf(stderr, format, ap);
+	va_end(ap);
+}
+
+
+boolean_t
+flock_nodata(int fd)
+{
+	struct pollfd pfd = { fd, POLLIN, 0 };
+	int ret = poll(&pfd, 1, 1000);
+
+	if (ret == -1) {
+		err(EXIT_FAILURE, "poll failed");
+	}
+
+	return (ret == 0);
+}
+
+
+void
+flock_block(int fd)
+{
+	char buf[1];
+	int ret = 0;
+	while (ret < 1) {
+		ret = read(fd, buf, 1);
+		if (ret == -1) {
+			if (errno == EINTR)
+				continue;
+			err(EXIT_FAILURE, "read failed");
+		}
+	}
+}
+
+
+void
+flock_alert(int fd)
+{
+	int ret = 0;
+	while (ret < 1) {
+		ret = write(fd, "1", 1);
+		if (ret == -1) {
+			if (errno == EINTR)
+				continue;
+			err(EXIT_FAILURE, "write failed");
+		}
+	}
+}
+
+
+lock_style_t
+flock_styleenum(char *stylestr)
+{
+	if (strcmp(stylestr, "posix") == 0) {
+		return (LSTYLE_POSIX);
+	} else if (strcmp(stylestr, "ofd") == 0) {
+		return (LSTYLE_OFD);
+	} else if (strcmp(stylestr, "flock") == 0) {
+		return (LSTYLE_FLOCK);
+	} else {
+		errx(EXIT_FAILURE, BAD_LOCK_MESSAGE);
+		return (LSTYLE_LAST);
+	}
+}
+
+
+char *
+flock_stylestr(lock_style_t style)
+{
+	switch (style) {
+	case LSTYLE_POSIX:
+		return ("posix");
+	case LSTYLE_OFD:
+		return ("ofd");
+	case LSTYLE_FLOCK:
+		return ("flock");
+	default:
+		abort();
+		return ("<unreachable>");
+	}
+}
+
+
+char *
+flock_stylename(lock_style_t style)
+{
+	switch (style) {
+	case LSTYLE_POSIX:
+		return ("fcntl(2) POSIX");
+	case LSTYLE_OFD:
+		return ("fcntl(2) OFD");
+	case LSTYLE_FLOCK:
+		return ("flock(3C)");
+	default:
+		abort();
+		return ("<unreachable>");
+	}
+}
+
+
+void
+flock_reinit(struct flock *flp, int ltype)
+{
+	bzero(flp, sizeof (*flp));
+	flp->l_type = ltype;
+}
+
+
+char *
+flock_cmdname(int cmd)
+{
+	switch (cmd) {
+	case F_SETLK:
+		return ("F_SETLK");
+	case F_OFD_SETLK:
+		return ("F_OFD_SETLK");
+	case F_SETLKW:
+		return ("F_SETLKW");
+	case F_OFD_SETLKW:
+		return ("F_OFD_SETLKW");
+	case F_GETLK:
+		return ("F_GETLK");
+	case F_OFD_GETLK:
+		return ("F_OFD_GETLK");
+	case F_FLOCK:
+		return ("F_FLOCK");
+	case F_FLOCKW:
+		return ("F_FLOCKW");
+	default:
+		abort();
+		return ("<unreachable>");
+	}
+}
--- /dev/null	Wed Oct  5 00:32:03 2016
+++ new/usr/src/test/os-tests/tests/file-locking/util.h	Wed Oct  5 00:32:03 2016
@@ -0,0 +1,49 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#ifndef FLOCK_TEST_UTIL_H
+#define	FLOCK_TEST_UTIL_H
+
+#include <fcntl.h>
+#include <sys/types.h>
+
+#define	BAD_ARGS_MESSAGE	"Expected to receive 3 arguments, but found %d."
+#define	BAD_MODE_MESSAGE	"Lock mode must be one of " \
+	"\"shared\" or \"exclusive\""
+#define	BAD_LOCK_MESSAGE	"Lock style must be one of " \
+	"\"posix\", \"ofd\", or \"exclusive\""
+
+typedef enum lock_style {
+	LSTYLE_POSIX,
+	LSTYLE_OFD,
+	LSTYLE_FLOCK,
+	LSTYLE_LAST
+} lock_style_t;
+
+extern boolean_t	LOG;
+
+extern boolean_t	flock_nodata(int);
+
+extern void	flock_block(int);
+extern void	flock_alert(int);
+extern void	flock_log(const char *, ...);
+extern void	flock_reinit(struct flock *, int);
+
+extern char		*flock_cmdname(int);
+extern char		*flock_stylename(lock_style_t);
+extern char		*flock_stylestr(lock_style_t);
+extern lock_style_t	flock_styleenum(char *);
+
+#endif /* FLOCK_TEST_UTIL_H */
--- old/usr/src/uts/common/Makefile.files	Wed Oct  5 00:32:03 2016
+++ new/usr/src/uts/common/Makefile.files	Wed Oct  5 00:32:03 2016
@@ -24,6 +24,7 @@
 # Copyright (c) 2011, 2014 by Delphix. All rights reserved.
 # Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
 # Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
+# Copyright 2015, Joyent, Inc.
 # Copyright 2016 Garrett D'Amore <garrett@damore.org>
 # Copyright 2016 Joyent, Inc.
 # Copyright 2016 OmniTI Computer Consulting, Inc.  All rights reserved.
@@ -304,6 +305,7 @@
 		seg_map.o	\
 		seg_vn.o	\
 		seg_spt.o	\
+		seg_umap.o	\
 		semaphore.o	\
 		sendfile.o	\
 		session.o	\
@@ -429,6 +431,8 @@
 
 SYSTRACE_OBJS += systrace.o
 
+LX_SYSTRACE_OBJS += lx_systrace.o
+
 LOCKSTAT_OBJS += lockstat.o
 
 FASTTRAP_OBJS += fasttrap.o fasttrap_isa.o
@@ -493,6 +497,10 @@
 
 PTM_OBJS +=	ptm.o
 
+LX_PTM_OBJS +=	lx_ptm.o
+
+LX_NETLINK_OBJS +=	lx_netlink.o
+
 MII_OBJS +=	mii.o mii_cicada.o mii_natsemi.o mii_intel.o mii_qualsemi.o \
 		mii_marvell.o mii_realtek.o mii_other.o
 
@@ -560,6 +568,7 @@
 		ip_helper_stream.o ip_tunables.o \
 		ip_output.o ip_input.o ip6_input.o ip6_output.o ip_arp.o \
 		conn_opt.o ip_attr.o ip_dce.o \
+		bpf_filter.o \
 		$(IP_ICMP_OBJS) \
 		$(IP_RTS_OBJS) \
 		$(IP_TCP_OBJS) \
@@ -640,7 +649,7 @@
 
 DUMP_OBJS +=	dump.o
 
-BPF_OBJS +=	bpf.o bpf_filter.o bpf_mod.o bpf_dlt.o bpf_mac.o
+BPF_OBJS +=	bpf.o bpf_wrap.o bpf_mod.o bpf_dlt.o bpf_mac.o
 
 CLONE_OBJS +=	clone.o
 
@@ -1002,6 +1011,8 @@
 
 I8042_OBJS +=	i8042.o
 
+INOTIFY_OBJS +=	inotify.o
+
 KB8042_OBJS +=	\
 		at_keyprocess.o	\
 		kb8042.o	\
@@ -1076,6 +1087,8 @@
 
 ZCONS_OBJS += zcons.o
 
+ZFD_OBJS += zfd.o
+
 NV_SATA_OBJS += nv_sata.o
 
 SI3124_OBJS += si3124.o
@@ -1300,8 +1313,8 @@
 PCFS_OBJS +=	pc_alloc.o	pc_dir.o	pc_node.o	pc_subr.o \
 		pc_vfsops.o	pc_vnops.o
 
-PROC_OBJS +=	prcontrol.o	prioctl.o	prsubr.o	prusrio.o \
-		prvfsops.o	prvnops.o
+PROC_OBJS +=	prargv.o	prcontrol.o	prioctl.o	prsubr.o \
+		prusrio.o	prvfsops.o	prvnops.o
 
 MNTFS_OBJS +=	mntvfsops.o	mntvnops.o
 
--- old/usr/src/uts/common/Makefile.rules	Wed Oct  5 00:32:03 2016
+++ new/usr/src/uts/common/Makefile.rules	Wed Oct  5 00:32:03 2016
@@ -1803,6 +1803,9 @@
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/fs/lofs/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
+$(LINTS_DIR)/%.ln:		$(UTSBASE)/common/fs/lxproc/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
+
 $(LINTS_DIR)/%.ln:		$(UTSBASE)/common/fs/mntfs/%.c
 	@($(LHEAD) $(LINT.c) $< $(LTAIL))
 
--- /dev/null	Wed Oct  5 00:32:04 2016
+++ new/usr/src/uts/common/brand/lx/autofs/lx_autofs.c	Wed Oct  5 00:32:04 2016
@@ -0,0 +1,3159 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+/*
+ * See the big theory statement in ../sys/lx_autofs.h
+ */
+
+#include <fs/fs_subr.h>
+#include <sys/stat.h>
+#include <sys/atomic.h>
+#include <sys/cmn_err.h>
+#include <sys/dirent.h>
+#include <sys/fs/fifonode.h>
+#include <sys/modctl.h>
+#include <sys/mount.h>
+#include <sys/policy.h>
+#include <sys/sunddi.h>
+#include <sys/conf.h>
+#include <sys/sdt.h>
+
+#include <sys/sysmacros.h>
+#include <sys/vfs.h>
+#include <sys/vfs_opreg.h>
+
+#include <sys/dnlc.h>
+#include <nfs/rnode.h>
+#include <nfs/rnode4.h>
+#include <sys/lx_autofs_impl.h>
+#include <sys/lx_types.h>
+
+/*
+ * External functions
+ */
+extern uintptr_t		space_fetch(char *key);
+extern int			space_store(char *key, uintptr_t ptr);
+extern int			umount2_engine(vfs_t *, int, cred_t *, int);
+
+/*
+ * Globals
+ */
+static vfsops_t			*lx_autofs_vfsops;
+static vnodeops_t		*lx_autofs_vn_ops = NULL;
+static int			lx_autofs_fstype;
+static major_t			lx_autofs_major;
+static minor_t			lx_autofs_minor = 0;
+static dev_info_t		*lx_autofs_dip = NULL;
+
+#define	LX_AUTOFS_DEV_VERSION_MAJOR	1
+#define	LX_AUTOFS_DEV_VERSION_MINOR	0
+
+/* The Linux autofs superblock magic number */
+#define	LX_AUTOFS_SB_MAGIC	0x0187
+
+/* Linux autofs mount types */
+#define	LX_AUTOFS_TYPE_INDIRECT		1
+#define	LX_AUTOFS_TYPE_DIRECT		2
+#define	LX_AUTOFS_TYPE_OFFSET		4
+
+/* Structure passed for autofs dev ioctls */
+typedef struct lx_autofs_dv_ioctl {
+	uint32_t lad_ver_major;
+	uint32_t lad_ver_minor;
+	uint32_t lad_size;
+	uint32_t lad_ioctlfd;
+	uint32_t lad_arg1;
+	uint32_t lad_arg2;
+	/* LINTED - C99 construct. */
+	char	lad_path[0];
+} lx_autofs_dv_ioctl_t;
+
+/*
+ * Support functions
+ */
+static void
+lx_autofs_strfree(char *str)
+{
+	kmem_free(str, strlen(str) + 1);
+}
+
+static char *
+lx_autofs_strdup(char *str)
+{
+	int	n = strlen(str);
+	char	*ptr = kmem_alloc(n + 1, KM_SLEEP);
+	bcopy(str, ptr, n + 1);
+	return (ptr);
+}
+
+static int
+lx_autofs_str_to_int(char *str, int *val)
+{
+	long	res;
+
+	if (str == NULL)
+		return (-1);
+
+	if ((ddi_strtol(str, NULL, 10, &res) != 0) ||
+	    (res < INT_MIN) || (res > INT_MAX))
+		return (-1);
+
+	*val = res;
+	return (0);
+}
+
+static void
+ls_autofs_stack_init(list_t *lp)
+{
+	list_create(lp,
+	    sizeof (stack_elem_t), offsetof(stack_elem_t, se_list));
+}
+
+static void
+lx_autofs_stack_fini(list_t *lp)
+{
+	ASSERT(list_head(lp) == NULL);
+	list_destroy(lp);
+}
+
+static void
+lx_autofs_stack_push(list_t *lp, caddr_t ptr1, caddr_t ptr2, caddr_t ptr3)
+{
+	stack_elem_t	*se;
+
+	se = kmem_alloc(sizeof (*se), KM_SLEEP);
+	se->se_ptr1 = ptr1;
+	se->se_ptr2 = ptr2;
+	se->se_ptr3 = ptr3;
+	list_insert_head(lp, se);
+}
+
+static int
+lx_autofs_stack_pop(list_t *lp, caddr_t *ptr1, caddr_t *ptr2, caddr_t *ptr3)
+{
+	stack_elem_t	*se;
+
+	if ((se = list_head(lp)) == NULL)
+		return (-1);
+	list_remove(lp, se);
+	if (ptr1 != NULL)
+		*ptr1 = se->se_ptr1;
+	if (ptr2 != NULL)
+		*ptr2 = se->se_ptr2;
+	if (ptr3 != NULL)
+		*ptr3 = se->se_ptr3;
+	kmem_free(se, sizeof (*se));
+	return (0);
+}
+
+static vnode_t *
+lx_autofs_fifo_peer_vp(vnode_t *vp)
+{
+	fifonode_t *fnp = VTOF(vp);
+	fifonode_t *fn_dest = fnp->fn_dest;
+	return (FTOV(fn_dest));
+}
+
+static vnode_t *
+lx_autofs_vn_alloc(vfs_t *vfsp, vnode_t *uvp)
+{
+	lx_autofs_vfs_t	*data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+	vnode_t		*vp, *vp_old;
+
+	/* Allocate a new vnode structure in case we need it. */
+	vp = vn_alloc(KM_SLEEP);
+	vn_setops(vp, lx_autofs_vn_ops);
+	VN_SET_VFS_TYPE_DEV(vp, vfsp, uvp->v_type, uvp->v_rdev);
+	vp->v_data = uvp;
+	ASSERT(vp->v_count == 1);
+
+	/*
+	 * Take a hold on the vfs structure.  This is how unmount will
+	 * determine if there are any active vnodes in the file system.
+	 */
+	VFS_HOLD(vfsp);
+
+	/*
+	 * Check if we already have a vnode allocated for this underlying
+	 * vnode_t.
+	 */
+	mutex_enter(&data->lav_lock);
+	if (mod_hash_find(data->lav_vn_hash,
+	    (mod_hash_key_t)uvp, (mod_hash_val_t *)&vp_old) != 0) {
+
+		/*
+		 * Didn't find an existing node.
+		 * Add this node to the hash and return.
+		 */
+		VERIFY(mod_hash_insert(data->lav_vn_hash,
+		    (mod_hash_key_t)uvp,
+		    (mod_hash_val_t)vp) == 0);
+		mutex_exit(&data->lav_lock);
+		return (vp);
+	}
+
+	/* Get a hold on the existing vnode and free up the one we allocated. */
+	VN_HOLD(vp_old);
+	mutex_exit(&data->lav_lock);
+
+	/* Free up the new vnode we allocated. */
+	VN_RELE(uvp);
+	VFS_RELE(vfsp);
+	vn_invalid(vp);
+	vn_free(vp);
+
+	return (vp_old);
+}
+
+static void
+lx_autofs_vn_free(vnode_t *vp)
+{
+	vfs_t		*vfsp = vp->v_vfsp;
+	lx_autofs_vfs_t	*data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+	vnode_t		*uvp = vp->v_data;
+	vnode_t	*vp_tmp;
+
+	ASSERT(MUTEX_HELD((&data->lav_lock)));
+	ASSERT(MUTEX_HELD((&vp->v_lock)));
+
+	ASSERT(vp->v_count == 0);
+
+	/* We're about to free this vnode so take it out of the hash. */
+	(void) mod_hash_remove(data->lav_vn_hash,
+	    (mod_hash_key_t)uvp, (mod_hash_val_t)&vp_tmp);
+
+	/*
+	 * No one else can lookup this vnode any more so there's no need
+	 * to hold locks.
+	 */
+	mutex_exit(&data->lav_lock);
+	mutex_exit(&vp->v_lock);
+
+	/* Release the underlying vnode. */
+	VN_RELE(uvp);
+	VFS_RELE(vfsp);
+	vn_invalid(vp);
+	vn_free(vp);
+}
+
+static lx_autofs_automnt_req_t *
+lx_autofs_la_alloc(lx_autofs_vfs_t *data, boolean_t *is_dup, boolean_t expire,
+    char *nm)
+{
+	lx_autofs_automnt_req_t	*laar, *laar_dup;
+
+	/* Pre-allocate a new automounter request before grabbing locks. */
+	laar = kmem_zalloc(sizeof (*laar), KM_SLEEP);
+	mutex_init(&laar->laar_lock, NULL, MUTEX_DEFAULT, NULL);
+	cv_init(&laar->laar_cv, NULL, CV_DEFAULT, NULL);
+	laar->laar_ref = 1;
+
+	if (data->lav_min_proto == 5) {
+		laar->laar_pkt.lap_protover = LX_AUTOFS_PROTO_VERS5;
+
+		if (data->lav_mnttype == LXAMT_INDIR) {
+			if (expire) {
+				laar->laar_pkt.lap_type =
+				    LX_AUTOFS_PTYPE_EXPIRE_INDIR;
+			} else {
+				laar->laar_pkt.lap_type =
+				    LX_AUTOFS_PTYPE_MISSING_INDIR;
+			}
+		} else {
+			if (expire) {
+				laar->laar_pkt.lap_type =
+				    LX_AUTOFS_PTYPE_EXPIRE_DIRECT;
+			} else {
+				laar->laar_pkt.lap_type =
+				    LX_AUTOFS_PTYPE_MISSING_DIRECT;
+			}
+		}
+		laar->laar_pkt_size = sizeof (lx_autofs_v5_pkt_t);
+
+		laar->laar_pkt.lap_v5.lap_dev = data->lav_dev;
+		laar->laar_pkt.lap_v5.lap_ino = data->lav_ino;
+		/*
+		 * Note that we're currently not filling in the other v5 pkt
+		 * fields (pid, uid, etc.) since they don't appear to be used
+		 * by the automounter. We can fill those in later if it proves
+		 * necessary.
+		 */
+
+		/*
+		 * For indirect mounts the token expected by the automounter is
+		 * the name of the directory entry to look up (not the entire
+		 * path that is being accessed.) For direct mounts the Linux
+		 * kernel passes a dummy name, so this is just as good.
+		 */
+		laar->laar_pkt.lap_v5.lap_name_len = strlen(nm);
+		if (laar->laar_pkt.lap_v5.lap_name_len >
+		    (sizeof (laar->laar_pkt.lap_v5.lap_name) - 1)) {
+			zcmn_err(getzoneid(), CE_NOTE,
+			    "invalid autofs automnt req: \"%s\"", nm);
+			kmem_free(laar, sizeof (*laar));
+			return (NULL);
+		}
+		(void) strlcpy(laar->laar_pkt.lap_v5.lap_name, nm,
+		    sizeof (laar->laar_pkt.lap_v5.lap_name));
+
+	} else if (expire) {
+		zcmn_err(getzoneid(), CE_WARN,
+		    "unsupported expire protocol request: \"%s\"", nm);
+		kmem_free(laar, sizeof (*laar));
+		return (NULL);
+
+	} else {
+		ASSERT(expire == B_FALSE);
+
+		/* Older protocol pkt (really v2) */
+		laar->laar_pkt.lap_protover = LX_AUTOFS_PROTO_VERS2;
+		laar->laar_pkt.lap_type = LX_AUTOFS_PTYPE_MISSING;
+		laar->laar_pkt_size = sizeof (lx_autofs_v2_pkt_t);
+
+		/*
+		 * The token expected by the linux automount is the name of
+		 * the directory entry to look up.  (And not the entire
+		 * path that is being accessed.)
+		 */
+		laar->laar_pkt.lap_v2.lap_name_len = strlen(nm);
+		if (laar->laar_pkt.lap_v2.lap_name_len >
+		    (sizeof (laar->laar_pkt.lap_v2.lap_name) - 1)) {
+			zcmn_err(getzoneid(), CE_NOTE,
+			    "invalid autofs lookup: \"%s\"", nm);
+			kmem_free(laar, sizeof (*laar));
+			return (NULL);
+		}
+		(void) strlcpy(laar->laar_pkt.lap_v2.lap_name, nm,
+		    sizeof (laar->laar_pkt.lap_v2.lap_name));
+	}
+
+	/* Assign a unique id for this request. */
+	laar->laar_pkt.lap_id = id_alloc(data->lav_ids);
+
+	/* Check for an outstanding request for this path. */
+	mutex_enter(&data->lav_lock);
+	if (mod_hash_find(data->lav_path_hash,
+	    (mod_hash_key_t)nm, (mod_hash_val_t *)&laar_dup) == 0) {
+		/*
+		 * There's already an outstanding request for this
+		 * path so we don't need a new one.
+		 */
+		id_free(data->lav_ids, laar->laar_pkt.lap_id);
+		kmem_free(laar, sizeof (*laar));
+		laar = laar_dup;
+
+		/* Bump the ref count on the old request. */
+		atomic_add_int(&laar->laar_ref, 1);
+
+		*is_dup = 1;
+	} else {
+		/* Add it to the hashes. */
+		VERIFY(mod_hash_insert(data->lav_id_hash,
+		    (mod_hash_key_t)(uintptr_t)laar->laar_pkt.lap_id,
+		    (mod_hash_val_t)laar) == 0);
+		VERIFY(mod_hash_insert(data->lav_path_hash,
+		    (mod_hash_key_t)lx_autofs_strdup(nm),
+		    (mod_hash_val_t)laar) == 0);
+
+		*is_dup = 0;
+	}
+	mutex_exit(&data->lav_lock);
+
+	return (laar);
+}
+
+static lx_autofs_automnt_req_t *
+lx_autofs_la_find(lx_autofs_vfs_t *data, int id)
+{
+	lx_autofs_automnt_req_t	*laar;
+
+	/* Check for an outstanding request for this id. */
+	mutex_enter(&data->lav_lock);
+	if (mod_hash_find(data->lav_id_hash, (mod_hash_key_t)(uintptr_t)id,
+	    (mod_hash_val_t *)&laar) != 0) {
+		mutex_exit(&data->lav_lock);
+		return (NULL);
+	}
+	atomic_add_int(&laar->laar_ref, 1);
+	mutex_exit(&data->lav_lock);
+	return (laar);
+}
+
+static void
+lx_autofs_la_complete(lx_autofs_vfs_t *data, lx_autofs_automnt_req_t *laar)
+{
+	lx_autofs_automnt_req_t	*laar_tmp;
+
+	/* Remove this request from the hashes so no one can look it up. */
+	mutex_enter(&data->lav_lock);
+	(void) mod_hash_remove(data->lav_id_hash,
+	    (mod_hash_key_t)(uintptr_t)laar->laar_pkt.lap_id,
+	    (mod_hash_val_t)&laar_tmp);
+	if (data->lav_min_proto == 5) {
+		(void) mod_hash_remove(data->lav_path_hash,
+		    (mod_hash_key_t)laar->laar_pkt.lap_v5.lap_name,
+		    (mod_hash_val_t)&laar_tmp);
+	} else {
+		(void) mod_hash_remove(data->lav_path_hash,
+		    (mod_hash_key_t)laar->laar_pkt.lap_v2.lap_name,
+		    (mod_hash_val_t)&laar_tmp);
+	}
+	mutex_exit(&data->lav_lock);
+
+	/* Mark this requst as complete and wakeup anyone waiting on it. */
+	mutex_enter(&laar->laar_lock);
+	laar->laar_complete = 1;
+	cv_broadcast(&laar->laar_cv);
+	mutex_exit(&laar->laar_lock);
+}
+
+static void
+lx_autofs_la_release(lx_autofs_vfs_t *data, lx_autofs_automnt_req_t *laar)
+{
+	ASSERT(!MUTEX_HELD(&laar->laar_lock));
+	if (atomic_add_int_nv(&laar->laar_ref, -1) > 0)
+		return;
+	ASSERT(laar->laar_ref == 0);
+	id_free(data->lav_ids, laar->laar_pkt.lap_id);
+	kmem_free(laar, sizeof (*laar));
+}
+
+static void
+lx_autofs_la_abort(lx_autofs_vfs_t *data, lx_autofs_automnt_req_t *laar)
+{
+	lx_autofs_automnt_req_t	*laar_tmp;
+
+	/*
+	 * This is a little tricky.  We're aborting the wait for this
+	 * request.  So if anyone else is waiting for this request we
+	 * can't free it, but if no one else is waiting for the request
+	 * we should free it.
+	 */
+	mutex_enter(&data->lav_lock);
+	if (atomic_add_int_nv(&laar->laar_ref, -1) > 0) {
+		mutex_exit(&data->lav_lock);
+		return;
+	}
+	ASSERT(laar->laar_ref == 0);
+
+	/* Remove this request from the hashes so no one can look it up. */
+	(void) mod_hash_remove(data->lav_id_hash,
+	    (mod_hash_key_t)(uintptr_t)laar->laar_pkt.lap_id,
+	    (mod_hash_val_t)&laar_tmp);
+	if (data->lav_min_proto == 5) {
+		(void) mod_hash_remove(data->lav_path_hash,
+		    (mod_hash_key_t)laar->laar_pkt.lap_v5.lap_name,
+		    (mod_hash_val_t)&laar_tmp);
+	} else {
+		(void) mod_hash_remove(data->lav_path_hash,
+		    (mod_hash_key_t)laar->laar_pkt.lap_v2.lap_name,
+		    (mod_hash_val_t)&laar_tmp);
+	}
+	mutex_exit(&data->lav_lock);
+
+	/* It's ok to free this now because the ref count was zero. */
+	id_free(data->lav_ids, laar->laar_pkt.lap_id);
+	kmem_free(laar, sizeof (*laar));
+}
+
+static int
+lx_autofs_fifo_lookup(pid_t pgrp, int fd, file_t **fpp_wr, file_t **fpp_rd)
+{
+	proc_t		*prp;
+	uf_info_t	*fip;
+	uf_entry_t	*ufp_wr, *ufp_rd = NULL;
+	file_t		*fp_wr, *fp_rd = NULL;
+	vnode_t		*vp_wr, *vp_rd;
+	int		i;
+
+	/*
+	 * sprlock() is zone aware, so assuming this mount call was
+	 * initiated by a process in a zone, if it tries to specify
+	 * a pgrp outside of it's zone this call will fail.
+	 *
+	 * Also, we want to grab hold of the main automounter process
+	 * and its going to be the group leader for pgrp, so its
+	 * pid will be equal to pgrp.
+	 */
+	prp = sprlock(pgrp);
+	if (prp == NULL)
+		return (-1);
+	mutex_exit(&prp->p_lock);
+
+	/* Now we want to access the processes open file descriptors. */
+	fip = P_FINFO(prp);
+	mutex_enter(&fip->fi_lock);
+
+	/* Sanity check fifo write fd. */
+	if (fd >= fip->fi_nfiles) {
+		mutex_exit(&fip->fi_lock);
+		mutex_enter(&prp->p_lock);
+		sprunlock(prp);
+		return (-1);
+	}
+
+	/* Get a pointer to the write fifo. */
+	UF_ENTER(ufp_wr, fip, fd);
+	if (((fp_wr = ufp_wr->uf_file) == NULL) ||
+	    ((vp_wr = fp_wr->f_vnode) == NULL) || (vp_wr->v_type != VFIFO)) {
+		/* Invalid fifo fd. */
+		UF_EXIT(ufp_wr);
+		mutex_exit(&fip->fi_lock);
+		mutex_enter(&prp->p_lock);
+		sprunlock(prp);
+		return (-1);
+	}
+
+	/*
+	 * Now we need to find the read end of the fifo (for reasons
+	 * explained below.)  We assume that the read end of the fifo
+	 * is in the same process as the write end.
+	 */
+	vp_rd = lx_autofs_fifo_peer_vp(fp_wr->f_vnode);
+	for (i = 0; i < fip->fi_nfiles; i++) {
+		if (i == fd)
+			continue;
+		UF_ENTER(ufp_rd, fip, i);
+		if (((fp_rd = ufp_rd->uf_file) != NULL) &&
+		    (fp_rd->f_vnode == vp_rd))
+			break;
+		UF_EXIT(ufp_rd);
+	}
+	if (i == fip->fi_nfiles) {
+		/* Didn't find it. */
+		UF_EXIT(ufp_wr);
+		mutex_exit(&fip->fi_lock);
+		mutex_enter(&prp->p_lock);
+		sprunlock(prp);
+		return (-1);
+	}
+
+	/*
+	 * We need to drop fi_lock before we can try to acquire f_tlock
+	 * the good news is that the file pointers are protected because
+	 * we're still holding uf_lock.
+	 */
+	mutex_exit(&fip->fi_lock);
+
+	/*
+	 * Here we bump the open counts on the fifos.  The reason
+	 * that we do this is because when we go to write to the
+	 * fifo we want to ensure that they are actually open (and
+	 * not in the process of being closed) without having to
+	 * stop the automounter.  (If the write end of the fifo
+	 * were closed and we tried to write to it we would panic.
+	 * If the read end of the fifo was closed and we tried to
+	 * write to the other end, the process that invoked the
+	 * lookup operation would get an unexpected SIGPIPE.)
+	 */
+	mutex_enter(&fp_wr->f_tlock);
+	fp_wr->f_count++;
+	ASSERT(fp_wr->f_count >= 2);
+	mutex_exit(&fp_wr->f_tlock);
+
+	mutex_enter(&fp_rd->f_tlock);
+	fp_rd->f_count++;
+	ASSERT(fp_rd->f_count >= 2);
+	mutex_exit(&fp_rd->f_tlock);
+
+	/* Release all our locks. */
+	UF_EXIT(ufp_wr);
+	UF_EXIT(ufp_rd);
+	mutex_enter(&prp->p_lock);
+	sprunlock(prp);
+
+	/* Return the file pointers. */
+	*fpp_rd = fp_rd;
+	*fpp_wr = fp_wr;
+	return (0);
+}
+
+static uint_t
+/*ARGSUSED*/
+lx_autofs_fifo_close_cb(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
+{
+	int	*id = (int *)arg;
+	/* Return the key and terminate the walk. */
+	*id = (uintptr_t)key;
+	return (MH_WALK_TERMINATE);
+}
+
+static void
+lx_autofs_fifo_close(lx_autofs_vfs_t *data)
+{
+	/*
+	 * Close the fifo to prevent any future requests from
+	 * getting sent to the automounter.
+	 */
+	mutex_enter(&data->lav_lock);
+	if (data->lav_fifo_wr != NULL) {
+		(void) closef(data->lav_fifo_wr);
+		data->lav_fifo_wr = NULL;
+	}
+	if (data->lav_fifo_rd != NULL) {
+		(void) closef(data->lav_fifo_rd);
+		data->lav_fifo_rd = NULL;
+	}
+	mutex_exit(&data->lav_lock);
+
+	/*
+	 * Wakeup any threads currently waiting for the automounter
+	 * note that it's possible for multiple threads to have entered
+	 * this function and to be doing the work below simultaneously.
+	 */
+	for (;;) {
+		lx_autofs_automnt_req_t	*laar;
+		int			id;
+
+		/* Lookup the first entry in the hash. */
+		id = -1;
+		mod_hash_walk(data->lav_id_hash,
+		    lx_autofs_fifo_close_cb, &id);
+		if (id == -1) {
+			/* No more id's in the hash. */
+			break;
+		}
+		if ((laar = lx_autofs_la_find(data, id)) == NULL) {
+			/* Someone else beat us to it. */
+			continue;
+		}
+
+		/* Mark the request as complete and release it. */
+		lx_autofs_la_complete(data, laar);
+		lx_autofs_la_release(data, laar);
+	}
+}
+
+static int
+lx_autofs_fifo_verify_rd(lx_autofs_vfs_t *data)
+{
+	proc_t		*prp;
+	uf_info_t	*fip;
+	uf_entry_t	*ufp_rd = NULL;
+	file_t		*fp_rd =  NULL;
+	vnode_t		*vp_rd;
+	int		i;
+
+	ASSERT(MUTEX_HELD((&data->lav_lock)));
+
+	/* Check if we've already been shut down. */
+	if (data->lav_fifo_wr == NULL) {
+		ASSERT(data->lav_fifo_rd == NULL);
+		return (-1);
+	}
+	vp_rd = lx_autofs_fifo_peer_vp(data->lav_fifo_wr->f_vnode);
+
+	/*
+	 * sprlock() is zone aware, so assuming this mount call was
+	 * initiated by a process in a zone, if it tries to specify
+	 * a pgrp outside of it's zone this call will fail.
+	 *
+	 * Also, we want to grab hold of the main automounter process
+	 * and its going to be the group leader for pgrp, so its
+	 * pid will be equal to pgrp.
+	 */
+	prp = sprlock(data->lav_pgrp);
+	if (prp == NULL)
+		return (-1);
+	mutex_exit(&prp->p_lock);
+
+	/* Now we want to access the processes open file descriptors. */
+	fip = P_FINFO(prp);
+	mutex_enter(&fip->fi_lock);
+
+	/*
+	 * Now we need to find the read end of the fifo (for reasons
+	 * explained below.)  We assume that the read end of the fifo
+	 * is in the same process as the write end.
+	 */
+	for (i = 0; i < fip->fi_nfiles; i++) {
+		UF_ENTER(ufp_rd, fip, i);
+		if (((fp_rd = ufp_rd->uf_file) != NULL) &&
+		    (fp_rd->f_vnode == vp_rd))
+			break;
+		UF_EXIT(ufp_rd);
+	}
+	if (i == fip->fi_nfiles) {
+		/* Didn't find it. */
+		mutex_exit(&fip->fi_lock);
+		mutex_enter(&prp->p_lock);
+		sprunlock(prp);
+		return (-1);
+	}
+
+	/*
+	 * Seems the automounter still has the read end of the fifo
+	 * open, we're done here.  Release all our locks and exit.
+	 */
+	mutex_exit(&fip->fi_lock);
+	UF_EXIT(ufp_rd);
+	mutex_enter(&prp->p_lock);
+	sprunlock(prp);
+
+	return (0);
+}
+
+static int
+lx_autofs_fifo_write(lx_autofs_vfs_t *data, lx_autofs_automnt_req_t *laarp)
+{
+	struct uio	uio;
+	struct iovec	iov;
+	file_t		*fp_wr, *fp_rd;
+	int		error;
+
+	/*
+	 * The catch here is we need to make sure _we_ don't close
+	 * the the fifo while writing to it.  (Another thread could come
+	 * along and realize the automounter process is gone and close
+	 * the fifo.  To do this we bump the open count before we
+	 * write to the fifo.
+	 */
+	mutex_enter(&data->lav_lock);
+	if (data->lav_fifo_wr == NULL) {
+		ASSERT(data->lav_fifo_rd == NULL);
+		mutex_exit(&data->lav_lock);
+		return (ENOENT);
+	}
+	fp_wr = data->lav_fifo_wr;
+	fp_rd = data->lav_fifo_rd;
+
+	/* Bump the open count on the write fifo. */
+	mutex_enter(&fp_wr->f_tlock);
+	fp_wr->f_count++;
+	mutex_exit(&fp_wr->f_tlock);
+
+	/* Bump the open count on the read fifo. */
+	mutex_enter(&fp_rd->f_tlock);
+	fp_rd->f_count++;
+	mutex_exit(&fp_rd->f_tlock);
+
+	mutex_exit(&data->lav_lock);
+
+	iov.iov_base = (caddr_t)&laarp->laar_pkt;
+	iov.iov_len = laarp->laar_pkt_size;
+	uio.uio_iov = &iov;
+	uio.uio_iovcnt = 1;
+	uio.uio_loffset = 0;
+	uio.uio_segflg = (short)UIO_SYSSPACE;
+	uio.uio_resid = laarp->laar_pkt_size;
+	uio.uio_llimit = 0;
+	uio.uio_fmode = FWRITE | FNDELAY | FNONBLOCK;
+
+	error = VOP_WRITE(fp_wr->f_vnode, &uio, 0, kcred, NULL);
+	(void) closef(fp_wr);
+	(void) closef(fp_rd);
+
+	/*
+	 * After every write we verify that the automounter still has
+	 * these files open.
+	 */
+	mutex_enter(&data->lav_lock);
+	if (lx_autofs_fifo_verify_rd(data) != 0) {
+		/*
+		 * Something happened to the automounter.
+		 * Close down the communication pipe we setup.
+		 */
+		mutex_exit(&data->lav_lock);
+		lx_autofs_fifo_close(data);
+		if (error != 0)
+			return (error);
+		return (ENOENT);
+	}
+	mutex_exit(&data->lav_lock);
+
+	return (error);
+}
+
+static int
+lx_autofs_bs_readdir(vnode_t *dvp, list_t *dir_stack, list_t *file_stack)
+{
+	struct iovec	iov;
+	struct uio	uio;
+	dirent64_t	*dp, *dbuf;
+	vnode_t		*vp;
+	size_t		dlen, dbuflen;
+	int		eof, error, ndirents = 64;
+	char		*nm;
+
+	dlen = ndirents * (sizeof (*dbuf));
+	dbuf = kmem_alloc(dlen, KM_SLEEP);
+
+	uio.uio_iov = &iov;
+	uio.uio_iovcnt = 1;
+	uio.uio_segflg = UIO_SYSSPACE;
+	uio.uio_fmode = 0;
+	uio.uio_extflg = UIO_COPY_CACHED;
+	uio.uio_loffset = 0;
+	uio.uio_llimit = MAXOFFSET_T;
+
+	eof = 0;
+	error = 0;
+	while (!error && !eof) {
+		uio.uio_resid = dlen;
+		iov.iov_base = (char *)dbuf;
+		iov.iov_len = dlen;
+
+		(void) VOP_RWLOCK(dvp, V_WRITELOCK_FALSE, NULL);
+		if (VOP_READDIR(dvp, &uio, kcred, &eof, NULL, 0) != 0) {
+			VOP_RWUNLOCK(dvp, V_WRITELOCK_FALSE, NULL);
+			kmem_free(dbuf, dlen);
+			return (-1);
+		}
+		VOP_RWUNLOCK(dvp, V_WRITELOCK_FALSE, NULL);
+
+		if ((dbuflen = dlen - uio.uio_resid) == 0) {
+			/* We're done. */
+			break;
+		}
+
+		for (dp = dbuf; ((intptr_t)dp < (intptr_t)dbuf + dbuflen);
+		    dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen)) {
+
+			nm = dp->d_name;
+
+			if (strcmp(nm, ".") == 0 || strcmp(nm, "..") == 0)
+				continue;
+
+			if (VOP_LOOKUP(dvp, nm, &vp, NULL, 0, NULL, kcred,
+			    NULL, NULL, NULL) != 0) {
+				kmem_free(dbuf, dlen);
+				return (-1);
+			}
+			if (vp->v_type == VDIR) {
+				if (dir_stack != NULL) {
+					lx_autofs_stack_push(dir_stack,
+					    (caddr_t)dvp,
+					    (caddr_t)vp, lx_autofs_strdup(nm));
+				} else {
+					VN_RELE(vp);
+				}
+			} else {
+				if (file_stack != NULL) {
+					lx_autofs_stack_push(file_stack,
+					    (caddr_t)dvp,
+					    (caddr_t)vp, lx_autofs_strdup(nm));
+				} else {
+					VN_RELE(vp);
+				}
+			}
+		}
+	}
+	kmem_free(dbuf, dlen);
+	return (0);
+}
+
+static void
+lx_autofs_bs_destroy(vnode_t *dvp, char *path)
+{
+	list_t	search_stack;
+	list_t	dir_stack;
+	list_t	file_stack;
+	vnode_t	*pdvp, *vp;
+	char	*dpath, *fpath;
+	int	ret;
+
+	if (VOP_LOOKUP(dvp, path, &vp, NULL, 0, NULL, kcred,
+	    NULL, NULL, NULL) != 0) {
+		/* A directory entry with this name doesn't actually exist. */
+		return;
+	}
+
+	if ((vp->v_type & VDIR) == 0) {
+		/* Easy, the directory entry is a file so delete it. */
+		VN_RELE(vp);
+		(void) VOP_REMOVE(dvp, path, kcred, NULL, 0);
+		return;
+	}
+
+	/*
+	 * The directory entry is a subdirectory, now we have a bit more
+	 * work to do.  (We'll have to recurse into the sub directory.)
+	 * It would have been much easier to do this recursively but kernel
+	 * stacks are notoriously small.
+	 */
+	ls_autofs_stack_init(&search_stack);
+	ls_autofs_stack_init(&dir_stack);
+	ls_autofs_stack_init(&file_stack);
+
+	/* Save our newfound subdirectory into a list. */
+	lx_autofs_stack_push(&search_stack, (caddr_t)dvp, (caddr_t)vp,
+	    lx_autofs_strdup(path));
+
+	/* Do a recursive depth first search into the subdirectories. */
+	while (lx_autofs_stack_pop(&search_stack,
+	    (caddr_t *)&pdvp, (caddr_t *)&dvp, &dpath) == 0) {
+
+		/* Get a list of the subdirectories in this directory. */
+		if (lx_autofs_bs_readdir(dvp, &search_stack, NULL) != 0)
+			goto exit;
+
+		/* Save the current directory a separate stack. */
+		lx_autofs_stack_push(&dir_stack, (caddr_t)pdvp, (caddr_t)dvp,
+		    dpath);
+	}
+
+	/*
+	 * Now dir_stack contains a list of directories, the deepest paths
+	 * are at the top of the list.  So let's go through and process them.
+	 */
+	while (lx_autofs_stack_pop(&dir_stack,
+	    (caddr_t *)&pdvp, (caddr_t *)&dvp, &dpath) == 0) {
+
+		/* Get a list of the files in this directory. */
+		if (lx_autofs_bs_readdir(dvp, NULL, &file_stack) != 0) {
+			VN_RELE(dvp);
+			lx_autofs_strfree(dpath);
+			goto exit;
+		}
+
+		/* Delete all the files in this directory. */
+		while (lx_autofs_stack_pop(&file_stack,
+		    NULL, (caddr_t *)&vp, &fpath) == 0) {
+			VN_RELE(vp)
+			ret = VOP_REMOVE(dvp, fpath, kcred, NULL, 0);
+			lx_autofs_strfree(fpath);
+			if (ret != 0) {
+				lx_autofs_strfree(dpath);
+				goto exit;
+			}
+		}
+
+		/* Delete this directory. */
+		VN_RELE(dvp);
+		ret = VOP_RMDIR(pdvp, dpath, pdvp, kcred, NULL, 0);
+		lx_autofs_strfree(dpath);
+		if (ret != 0)
+			goto exit;
+	}
+
+exit:
+	while (
+	    (lx_autofs_stack_pop(&search_stack, NULL, (caddr_t *)&vp,
+	    &path) == 0) ||
+	    (lx_autofs_stack_pop(&dir_stack, NULL, (caddr_t *)&vp,
+	    &path) == 0) ||
+	    (lx_autofs_stack_pop(&file_stack, NULL, (caddr_t *)&vp,
+	    &path) == 0)) {
+		VN_RELE(vp);
+		lx_autofs_strfree(path);
+	}
+	lx_autofs_stack_fini(&search_stack);
+	lx_autofs_stack_fini(&dir_stack);
+	lx_autofs_stack_fini(&file_stack);
+}
+
+static vnode_t *
+lx_autofs_bs_create(vnode_t *dvp, char *bs_name)
+{
+	vnode_t	*vp;
+	vattr_t	vattr;
+
+	/*
+	 * After looking at the mkdir syscall path it seems we don't need
+	 * to initialize all of the vattr_t structure.
+	 */
+	bzero(&vattr, sizeof (vattr));
+	vattr.va_type = VDIR;
+	vattr.va_mode = 0755; /* u+rwx,og=rx */
+	vattr.va_mask = AT_TYPE|AT_MODE;
+
+	if (VOP_MKDIR(dvp, bs_name, &vattr, &vp, kcred, NULL, 0, NULL) != 0)
+		return (NULL);
+	return (vp);
+}
+
+static int
+lx_autofs_automounter_call(vnode_t *dvp, char *nm)
+{
+	lx_autofs_automnt_req_t	*laar;
+	lx_autofs_vfs_t		*data;
+	int			error;
+	boolean_t		is_dup;
+
+	/* Get a pointer to the vfs mount data. */
+	data = (lx_autofs_vfs_t *)dvp->v_vfsp->vfs_data;
+
+	/* The automounter only supports queries in the root directory. */
+	if (dvp != data->lav_root)
+		return (ENOENT);
+
+	/*
+	 * Check if the current process is in the automounters process
+	 * group.  (If it is, the current process is either the autmounter
+	 * itself or one of it's forked child processes.)  If so, don't
+	 * redirect this call back into the automounter because we'll
+	 * hang.
+	 */
+	mutex_enter(&pidlock);
+	if (data->lav_pgrp == curproc->p_pgrp) {
+		mutex_exit(&pidlock);
+		return (ENOENT);
+	}
+	mutex_exit(&pidlock);
+
+	/* Verify that the automount process pipe still exists. */
+	mutex_enter(&data->lav_lock);
+	if (data->lav_fifo_wr == NULL) {
+		ASSERT(data->lav_fifo_rd == NULL);
+		mutex_exit(&data->lav_lock);
+		return (ENOENT);
+	}
+	mutex_exit(&data->lav_lock);
+
+	/* Allocate an automounter request structure. */
+	if ((laar = lx_autofs_la_alloc(data, &is_dup, B_FALSE,
+	    nm)) == NULL)
+		return (ENOENT);
+
+	/*
+	 * If we were the first one to allocate this request then we
+	 * need to send it to the automounter.
+	 */
+	if ((!is_dup) &&
+	    ((error = lx_autofs_fifo_write(data, laar)) != 0)) {
+		/*
+		 * Unable to send the request to the automounter.
+		 * Unblock any other threads waiting on the request
+		 * and release the request.
+		 */
+		lx_autofs_la_complete(data, laar);
+		lx_autofs_la_release(data, laar);
+		return (error);
+	}
+
+	/* Wait for someone to signal us that this request has completed. */
+	mutex_enter(&laar->laar_lock);
+	while (!laar->laar_complete) {
+		if (cv_wait_sig(&laar->laar_cv, &laar->laar_lock) == 0) {
+			/* We got a signal, abort this call. */
+			mutex_exit(&laar->laar_lock);
+			lx_autofs_la_abort(data, laar);
+			return (EINTR);
+		}
+	}
+	mutex_exit(&laar->laar_lock);
+
+	if (laar->laar_result == LXACR_READY) {
+		/*
+		 * Mount succeeded, keep track for future expire calls.
+		 *
+		 * See vfs lav_vn_hash. Is this something we could use for
+		 * iterating mounts under this autofs? Used by
+		 * lx_autofs_vn_alloc
+		 */
+		lx_autofs_mntent_t *mp;
+
+		mp = kmem_zalloc(sizeof (lx_autofs_mntent_t), KM_SLEEP);
+		mp->lxafme_len = strlen(nm) + 1;
+		mp->lxafme_path = kmem_zalloc(mp->lxafme_len, KM_SLEEP);
+		mp->lxafme_ts = TICK_TO_SEC(ddi_get_lbolt64());
+		(void) strlcpy(mp->lxafme_path, nm, mp->lxafme_len);
+
+		mutex_enter(&data->lav_lock);
+		list_insert_tail(&data->lav_mnt_list, mp);
+		mutex_exit(&data->lav_lock);
+	}
+
+	lx_autofs_la_release(data, laar);
+
+	return (0);
+}
+
+/*
+ * Same preliminary checks as in lx_autofs_unmount.
+ */
+static boolean_t
+lx_autofs_may_unmount(vfs_t *vfsp, struct cred *cr)
+{
+	lx_autofs_vfs_t *data;
+
+	if (secpolicy_fs_unmount(cr, vfsp) != 0)
+		return (B_FALSE);
+
+	/*
+	 * We should never have a reference count of less than 2: one for the
+	 * caller, one for the root vnode.
+	 */
+	ASSERT(vfsp->vfs_count >= 2);
+
+	/* If there are any outstanding vnodes, we can't unmount. */
+	if (vfsp->vfs_count > 2)
+		return (B_FALSE);
+
+	data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+	ASSERT(data->lav_root->v_vfsp == vfsp);
+
+	/* Check for any remaining holds on the root vnode. */
+	if (data->lav_root->v_count > 1)
+		return (B_FALSE);
+
+	return (B_TRUE);
+}
+
+static vfs_t *
+lx_autofs_get_mountvfs(char *fs_mntpt, int *cnt)
+{
+	struct vfs *vfsp;
+	struct vfs *vfslist;
+	vfs_t *fnd_vfs = NULL;
+	int fsmplen;
+	int acnt = 0;
+
+	fsmplen = strlen(fs_mntpt);
+
+	vfs_list_read_lock();
+
+	vfsp = vfslist = curzone->zone_vfslist;
+	if (vfslist == NULL) {
+		vfs_list_unlock();
+		*cnt = 0;
+		return (NULL);
+	}
+
+	do {
+		/* Skip mounts we shouldn't show. */
+		if (!(vfsp->vfs_flag & VFS_NOMNTTAB)) {
+			char *mntpt;
+
+			mntpt = (char *)refstr_value(vfsp->vfs_mntpt);
+			if (strncmp(fs_mntpt, mntpt, fsmplen) == 0 &&
+			    (mntpt[fsmplen] == '\0' || mntpt[fsmplen] == '/')) {
+				/*
+				 * We'll return the first one we find but don't
+				 * return a mount that is actually autofs (i.e.
+				 * autofs direct or offset mount).
+				 */
+				if (vfsp->vfs_op == lx_autofs_vfsops) {
+					acnt++;
+				} else if (fnd_vfs == NULL) {
+					fnd_vfs = vfsp;
+					VFS_HOLD(fnd_vfs)
+				}
+			}
+		}
+		vfsp = vfsp->vfs_zone_next;
+	} while (vfsp != vfslist);
+
+	vfs_list_unlock();
+
+	*cnt = acnt;
+	return (fnd_vfs);
+}
+
+/*
+ * Unmount all autofs offset mounts below the given path.
+ */
+static boolean_t
+lx_autofs_umount_offset(char *fs_mntpt, struct cred *cr)
+{
+	struct vfs *vfsp;
+	struct vfs *vfslist;
+	boolean_t busy = B_FALSE;
+	int fsmplen = strlen(fs_mntpt);
+
+restart:
+	vfs_list_read_lock();
+
+	vfsp = vfslist = curzone->zone_vfslist;
+	if (vfslist == NULL) {
+		vfs_list_unlock();
+		return (B_FALSE);
+	}
+
+	do {
+		char *mntpt;
+		lx_autofs_vfs_t *data;
+
+		/* Skip mounts we should ignore. */
+		if ((vfsp->vfs_flag & VFS_NOMNTTAB)) {
+			vfsp = vfsp->vfs_zone_next;
+			continue;
+		}
+
+		mntpt = (char *)refstr_value(vfsp->vfs_mntpt);
+		if (strncmp(fs_mntpt, mntpt, fsmplen) != 0 ||
+		    (mntpt[fsmplen] != '\0' && mntpt[fsmplen] != '/')) {
+			vfsp = vfsp->vfs_zone_next;
+			continue;
+		}
+
+		if (vfsp->vfs_op != lx_autofs_vfsops) {
+			/*
+			 * Something got mounted over the autofs mountpoint
+			 * after we checked that this inidrect hierarchy was
+			 * not busy.
+			 */
+			busy = B_TRUE;
+			break;
+		}
+
+		data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+		if (data->lav_mnttype != LXAMT_OFFSET) {
+			/*
+			 * Something mounted a non-offset autofs fs under this
+			 * indirect mnt!
+			 */
+			busy = B_TRUE;
+			break;
+		}
+
+		/*
+		 * Attempt to umount - set busy if fails.
+		 *
+		 * umount2_engine will call VFS_RELE, so we need to take an
+		 * extra hold to match the behavior during the normal umount
+		 * path.
+		 *
+		 * We also need to drop the list lock to prevent deadlock
+		 * during umount.
+		 */
+		VFS_HOLD(vfsp);
+		vfs_list_unlock();
+		if (umount2_engine(vfsp, 0, cr, 0) != 0) {
+			busy = B_TRUE;
+			goto errexit;
+		}
+
+		/* Retake list lock and look for more. */
+		goto restart;
+	} while (vfsp != vfslist);
+
+	vfs_list_unlock();
+
+errexit:
+	return (busy);
+}
+
+
+/*
+ * Note that lx_autofs_automounter_call() only supports queries in the root
+ * directory, so all mntent names are relative to that.
+ */
+static int
+lx_autofs_expire(vfs_t *vfsp, struct cred *cr)
+{
+	lx_autofs_vfs_t *data;
+	lx_autofs_mntent_t *mp;
+	lx_autofs_automnt_req_t	*laar;
+	boolean_t is_dup;
+	vfs_t *fnd_vfs;
+	int autofs_cnt;
+	boolean_t busy = B_FALSE;
+	char exp_path[MAXPATHLEN];
+
+	data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+
+	/*
+	 * We process only the first element (i.e. do not do multi). This
+	 * works fine for the automounter.
+	 */
+	mutex_enter(&data->lav_lock);
+	mp = (lx_autofs_mntent_t *)list_remove_head(&data->lav_mnt_list);
+	mutex_exit(&data->lav_lock);
+	if (mp == NULL) {
+		if (data->lav_mnttype == LXAMT_OFFSET) {
+			/*
+			 * During restart the automounter will openmount each
+			 * offset mount for management. It won't closemount the
+			 * offset mount until we expire it, even though nothing
+			 * is mounted over that offset. We handle this as a
+			 * special expiration case.
+			 */
+			int cnt;
+
+			mutex_enter(&data->lav_lock);
+			cnt = data->lav_openmnt_cnt;
+			mutex_exit(&data->lav_lock);
+
+			if (cnt == 1 && vn_ismntpt(data->lav_root) == 0) {
+				char *mntpt = (char *)
+				    refstr_value(vfsp->vfs_mntpt);
+				char *nm = ZONE_PATH_TRANSLATE(mntpt, curzone);
+
+				mp = kmem_zalloc(sizeof (lx_autofs_mntent_t),
+				    KM_SLEEP);
+				mp->lxafme_len = strlen(nm) + 1;
+				mp->lxafme_path = kmem_zalloc(mp->lxafme_len,
+				    KM_SLEEP);
+				mp->lxafme_ts = TICK_TO_SEC(ddi_get_lbolt64());
+				(void) strlcpy(mp->lxafme_path, nm,
+				    mp->lxafme_len);
+
+				goto exp_offset;
+			}
+		}
+
+		return (EAGAIN);
+	}
+
+	/*
+	 * We only return an expired mount if it is inactive for the full
+	 * timeout. This reduces overly aggressive umount/mount activity.
+	 */
+	if (data->lav_timeout > 0) {
+		uint64_t now = TICK_TO_SEC(ddi_get_lbolt64());
+
+		if ((now - mp->lxafme_ts) < data->lav_timeout) {
+			/* put it back at the end of the line */
+			mutex_enter(&data->lav_lock);
+			list_insert_tail(&data->lav_mnt_list, mp);
+			mutex_exit(&data->lav_lock);
+			return (EAGAIN);
+		}
+	}
+
+	if (data->lav_mnttype == LXAMT_INDIR) {
+		(void) snprintf(exp_path, sizeof (exp_path), "%s/%s",
+		    (char *)refstr_value(vfsp->vfs_mntpt), mp->lxafme_path);
+	} else {
+		(void) strlcpy(exp_path, (char *)refstr_value(vfsp->vfs_mntpt),
+		    sizeof (exp_path));
+	}
+
+	fnd_vfs = lx_autofs_get_mountvfs(exp_path, &autofs_cnt);
+	if (fnd_vfs != NULL) {
+		boolean_t skip = B_FALSE;
+		vfssw_t *vfssw;
+
+		/*
+		 * If it's an NFS file system (typical) then we check in
+		 * advance to see if it can be unmounted, otherwise, proceed.
+		 * The fs-specific umount attempted by the automounter will
+		 * either succeed or fail. Both are valid outcomes but checking
+		 * now for nfs will save a bunch of work by the automounter
+		 * if the fs is busy.
+		 *
+		 * Unfortunately, for NFS the vfs_fstype is the same for all
+		 * versions of NFS, so we need to check the vfs_op member to
+		 * determine which version of NFS we're dealing with.
+		 */
+		if (!skip && (vfssw = vfs_getvfssw("nfs4")) != NULL) {
+			if (vfs_matchops(fnd_vfs, &vfssw->vsw_vfsops)) {
+				(void) dnlc_purge_vfsp(fnd_vfs, 0);
+				if (check_rtable4(fnd_vfs))
+					busy = B_TRUE;
+				skip = B_TRUE;
+			}
+			vfs_unrefvfssw(vfssw);
+		}
+
+		if (!skip && (vfssw = vfs_getvfssw("nfs3")) != NULL) {
+			if (vfs_matchops(fnd_vfs, &vfssw->vsw_vfsops)) {
+				(void) dnlc_purge_vfsp(fnd_vfs, 0);
+				if (check_rtable(fnd_vfs))
+					busy = B_TRUE;
+			}
+			vfs_unrefvfssw(vfssw);
+		}
+
+		VFS_RELE(fnd_vfs);
+
+	} else if (autofs_cnt > 0) {
+		/*
+		 * The automounter is asking us to expire and we pulled this
+		 * name from our vfs mountpoint list, but if
+		 * lx_autofs_get_mountvfs returns null then that means we
+		 * didn't find a non-autofs mount under this name. Thus, the
+		 * name could be a subdirectory under an autofs toplevel
+		 * indirect mount with one or more offset mounts below.
+		 * autofs_cnt will indicate how many autofs mounts exist below
+		 * this subdirectory name.
+		 *
+		 * The automounter will take care of unmounting any fs mounted
+		 * over one of these offset mounts (i.e. offset is like a
+		 * direct mount which the automounter will manage) but the
+		 * automounter will not unmount the actual autofs offset mount
+		 * itself, so we have to do that before we can expire the
+		 * top-level subrectory name.
+		 */
+		busy = lx_autofs_umount_offset(exp_path, cr);
+	}
+
+	if (busy) {
+		/*
+		 * Can't unmount this one right now, put it at the end of the
+		 * list and return. The caller will return EAGAIN for the
+		 * expire ioctl and the automounter will check again later.
+		 */
+		mp->lxafme_ts = TICK_TO_SEC(ddi_get_lbolt64());
+		mutex_enter(&data->lav_lock);
+		list_insert_tail(&data->lav_mnt_list, mp);
+		mutex_exit(&data->lav_lock);
+		return (EAGAIN);
+	}
+
+	/*
+	 * See lx_autofs_automounter_call. We want to send a msg up the pipe
+	 * to the automounter in a similar way.
+	 */
+
+exp_offset:
+	/* Verify that the automount process pipe still exists. */
+	mutex_enter(&data->lav_lock);
+	if (data->lav_fifo_wr == NULL) {
+		ASSERT(data->lav_fifo_rd == NULL);
+		mutex_exit(&data->lav_lock);
+		goto err_free;
+	}
+	mutex_exit(&data->lav_lock);
+
+	/* Allocate an automounter expire structure. */
+	if ((laar = lx_autofs_la_alloc(data, &is_dup, B_TRUE,
+	    mp->lxafme_path)) == NULL)
+		goto err_free;
+
+	/*
+	 * If we were the first one to allocate this request then we
+	 * need to send it to the automounter.
+	 */
+	if (!is_dup && lx_autofs_fifo_write(data, laar) != 0) {
+		/*
+		 * Unable to send the request to the automounter.
+		 * Unblock any other threads waiting on the request
+		 * and release the request.
+		 */
+		lx_autofs_la_complete(data, laar);
+		lx_autofs_la_release(data, laar);
+		goto err_free;
+	}
+
+	/* Wait for someone to signal us that this request has completed. */
+	mutex_enter(&laar->laar_lock);
+	while (!laar->laar_complete) {
+		if (cv_wait_sig(&laar->laar_cv, &laar->laar_lock) == 0) {
+			/* We got a signal, abort this request. */
+			mutex_exit(&laar->laar_lock);
+			lx_autofs_la_abort(data, laar);
+			goto err_free;
+		}
+	}
+	mutex_exit(&laar->laar_lock);
+
+	/*
+	 * If it failed or if the file system is still mounted after we get the
+	 * response from our expire msg, then that means the automounter tried
+	 * to unmount it but failed because the file system is busy, so we put
+	 * this entry back on our list to try to expire it again later.
+	 */
+	fnd_vfs = NULL;
+	if (laar->laar_result == LXACR_FAIL ||
+	    (fnd_vfs = lx_autofs_get_mountvfs(exp_path, &autofs_cnt)) != NULL ||
+	    autofs_cnt > 0) {
+		if (fnd_vfs != NULL)
+			VFS_RELE(fnd_vfs);
+		mp->lxafme_ts = TICK_TO_SEC(ddi_get_lbolt64());
+		mutex_enter(&data->lav_lock);
+		list_insert_tail(&data->lav_mnt_list, mp);
+		mutex_exit(&data->lav_lock);
+	} else {
+		kmem_free(mp->lxafme_path, mp->lxafme_len);
+		kmem_free(mp, sizeof (lx_autofs_mntent_t));
+	}
+
+	lx_autofs_la_release(data, laar);
+	return (0);
+
+err_free:
+	kmem_free(mp->lxafme_path, mp->lxafme_len);
+	kmem_free(mp, sizeof (lx_autofs_mntent_t));
+	return (EAGAIN);
+}
+
+static int
+lx_autofs_ack(int reqid, vfs_t *vfsp, enum lx_autofs_callres result)
+{
+	lx_autofs_vfs_t	*data;
+	lx_autofs_automnt_req_t	*laar;
+
+	data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+	if ((laar = lx_autofs_la_find(data, reqid)) == NULL)
+		return (ENXIO);
+
+	/* Mark the request as complete and release it. */
+	laar->laar_result = result;
+	lx_autofs_la_complete(data, laar);
+	lx_autofs_la_release(data, laar);
+	return (0);
+}
+
+static int
+lx_autofs_automounter_ioctl(vnode_t *vp, int cmd, intptr_t arg, cred_t *cr)
+{
+	lx_autofs_vfs_t *data = (lx_autofs_vfs_t *)vp->v_vfsp->vfs_data;
+	int			id = arg;
+	int			v;
+	int			err;
+
+	/*
+	 * Be strict.
+	 * We only accept ioctls from the automounter process group.
+	 */
+	mutex_enter(&pidlock);
+	if (data->lav_pgrp != curproc->p_pgrp) {
+		mutex_exit(&pidlock);
+		return (ENOENT);
+	}
+	mutex_exit(&pidlock);
+
+	switch ((unsigned int)cmd) {
+	case LX_AUTOFS_IOC_READY:
+		if ((err = lx_autofs_ack(id, vp->v_vfsp, LXACR_READY)) != 0)
+			return (err);
+		return (0);
+
+	case LX_AUTOFS_IOC_FAIL:
+		if ((err = lx_autofs_ack(id, vp->v_vfsp, LXACR_FAIL)) != 0)
+			return (err);
+		return (0);
+
+	case LX_AUTOFS_IOC_CATATONIC:
+		/* The automounter is shutting down. */
+		lx_autofs_fifo_close(data);
+		return (0);
+
+	case LX_AUTOFS_IOC_PROTOVER:
+		v = LX_AUTOFS_PROTO_VERS5;
+		if (copyout(&v, (caddr_t)arg, sizeof (int)) != 0)
+			return (EFAULT);
+		return (0);
+
+	case LX_AUTOFS_IOC_PROTOSUBVER:
+		v = LX_AUTOFS_PROTO_SUBVERSION;
+		if (copyout(&v, (caddr_t)arg, sizeof (int)) != 0)
+			return (EFAULT);
+		return (0);
+
+	case LX_AUTOFS_IOC_ASKUMOUNT:
+		/*
+		 * This is asking if autofs can be unmounted, not asking to
+		 * actually unmount it. We return 1 if it is busy or 0 if it
+		 * can be unmounted.
+		 */
+		v = 1;
+		if (lx_autofs_may_unmount(vp->v_vfsp, cr))
+			v = 0;
+
+		if (copyout(&v, (caddr_t)arg, sizeof (int)) != 0)
+			return (EFAULT);
+		return (0);
+
+	case LX_AUTOFS_IOC_SETTIMEOUT:
+		if (copyin((caddr_t)arg, &data->lav_timeout, sizeof (ulong_t))
+		    != 0)
+			return (EFAULT);
+		return (0);
+
+	case LX_AUTOFS_IOC_EXPIRE:
+		return (ENOTSUP);
+
+	case LX_AUTOFS_IOC_EXPIRE_MULTI:
+		/* Should we return lx_autofs_expire()'s value instead? */
+		(void) lx_autofs_expire(vp->v_vfsp, cr);
+		return (EAGAIN);
+
+	default:
+		ASSERT(0);
+		return (ENOTSUP);
+	}
+}
+
+static int
+lx_autofs_parse_mntopt(vfs_t *vfsp, lx_autofs_vfs_t *data)
+{
+	char		*fd_str, *pgrp_str, *minproto_str, *maxproto_str;
+	int		fd, pgrp, minproto, maxproto;
+	file_t		*fp_wr, *fp_rd;
+
+	/* Require these options to be present. */
+	if ((vfs_optionisset(vfsp, LX_MNTOPT_FD, &fd_str) != 1) ||
+	    (vfs_optionisset(vfsp, LX_MNTOPT_PGRP, &pgrp_str) != 1) ||
+	    (vfs_optionisset(vfsp, LX_MNTOPT_MINPROTO, &minproto_str) != 1) ||
+	    (vfs_optionisset(vfsp, LX_MNTOPT_MAXPROTO, &maxproto_str) != 1))
+		return (EINVAL);
+
+	/* Get the values for each parameter. */
+	if ((lx_autofs_str_to_int(fd_str, &fd) != 0) ||
+	    (lx_autofs_str_to_int(pgrp_str, &pgrp) != 0) ||
+	    (lx_autofs_str_to_int(minproto_str, &minproto) != 0) ||
+	    (lx_autofs_str_to_int(maxproto_str, &maxproto) != 0))
+		return (EINVAL);
+
+	/*
+	 * We primarily support v2 & v5 of the linux kernel automounter
+	 * protocol. The userland daemon typically needs v5. We'll reject
+	 * unsupported ioctls later if we get one.
+	 */
+	if ((minproto > 5) || (maxproto < 2))
+		return (EINVAL);
+
+	/*
+	 * Now we need to lookup the fifos we'll be using
+	 * to talk to the userland automounter process.
+	 */
+	if (lx_autofs_fifo_lookup(pgrp, fd, &fp_wr, &fp_rd) != 0) {
+		/*
+		 * The automounter doesn't always have the same id as the pgrp.
+		 * This happens when it is started via one of the various
+		 * service managers. In this case the fifo lookup will fail
+		 * so we retry with our own pid.
+		 */
+		int pid = (int)curproc->p_pid;
+
+		if (lx_autofs_fifo_lookup(pid, fd, &fp_wr, &fp_rd) != 0)
+			return (EINVAL);
+	}
+
+	if (vfs_optionisset(vfsp, LX_MNTOPT_INDIRECT, NULL)) {
+		data->lav_mnttype = LXAMT_INDIR;
+	}
+	if (vfs_optionisset(vfsp, LX_MNTOPT_DIRECT, NULL)) {
+		if (data->lav_mnttype != LXAMT_NONE)
+			return (EINVAL);
+		data->lav_mnttype = LXAMT_DIRECT;
+	}
+	if (vfs_optionisset(vfsp, LX_MNTOPT_OFFSET, NULL)) {
+		if (data->lav_mnttype != LXAMT_NONE)
+			return (EINVAL);
+		data->lav_mnttype = LXAMT_OFFSET;
+	}
+	/* The automounter does test mounts with none of the options */
+	if (data->lav_mnttype == LXAMT_NONE)
+		data->lav_mnttype = LXAMT_DIRECT;
+
+	/* Save the mount options and fifo pointers. */
+	data->lav_fd = fd;
+	data->lav_min_proto = minproto;
+	data->lav_pgrp = pgrp;
+	data->lav_fifo_rd = fp_rd;
+	data->lav_fifo_wr = fp_wr;
+	return (0);
+}
+
+static uint64_t
+s2l_dev(dev_t dev)
+{
+	major_t	maj = getmajor(dev);
+	minor_t	min = getminor(dev);
+
+	return (LX_MAKEDEVICE(maj, min));
+}
+
+/*
+ * VFS entry points
+ */
+static int
+lx_autofs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
+{
+	lx_autofs_vfs_t	*data;
+	dev_t		dev;
+	char		name[40];
+	int		error;
+	vattr_t		va;
+
+	if (secpolicy_fs_mount(cr, mvp, vfsp) != 0)
+		return (EPERM);
+
+	if (mvp->v_type != VDIR)
+		return (ENOTDIR);
+
+	if ((uap->flags & MS_OVERLAY) == 0 &&
+	    (mvp->v_count > 1 || (mvp->v_flag & VROOT)))
+		return (EBUSY);
+
+	/* We don't support mounts in the global zone. */
+	if (getzoneid() == GLOBAL_ZONEID)
+		return (EPERM);
+
+	/*
+	 * Offset mounts will occur below the top-level mountpoint so we
+	 * need to allow for autofs mounts even though mvp is an autofs.
+	 */
+
+	/* Allocate a vfs struct. */
+	data = kmem_zalloc(sizeof (lx_autofs_vfs_t), KM_SLEEP);
+
+	/* Parse mount options. */
+	if ((error = lx_autofs_parse_mntopt(vfsp, data)) != 0) {
+		kmem_free(data, sizeof (lx_autofs_vfs_t));
+		return (error);
+	}
+
+	/* Initialize the backing store. */
+	lx_autofs_bs_destroy(mvp, LX_AUTOFS_BS_DIR);
+	data->lav_bs_vp = lx_autofs_bs_create(mvp, LX_AUTOFS_BS_DIR);
+	if (data->lav_bs_vp == NULL) {
+		kmem_free(data, sizeof (lx_autofs_vfs_t));
+		return (EBUSY);
+	}
+	data->lav_bs_name = LX_AUTOFS_BS_DIR;
+
+	/* Get the backing store inode for use in v5 protocol msgs */
+	va.va_mask = AT_STAT;
+	if ((error = VOP_GETATTR(data->lav_bs_vp, &va, 0, cr, NULL)) != 0) {
+		kmem_free(data, sizeof (lx_autofs_vfs_t));
+		return (error);
+	}
+	data->lav_ino = va.va_nodeid;
+
+	/* We have to hold the underlying vnode we're mounted on. */
+	data->lav_mvp = mvp;
+	VN_HOLD(mvp);
+
+	/* Initialize vfs fields */
+	vfsp->vfs_bsize = DEV_BSIZE;
+	vfsp->vfs_fstype = lx_autofs_fstype;
+	vfsp->vfs_data = data;
+
+	/* Invent a dev_t (sigh) */
+	do {
+		dev = makedevice(lx_autofs_major,
+		    atomic_add_32_nv(&lx_autofs_minor, 1) & L_MAXMIN32);
+	} while (vfs_devismounted(dev));
+	vfsp->vfs_dev = dev;
+	vfs_make_fsid(&vfsp->vfs_fsid, dev, lx_autofs_fstype);
+
+	data->lav_dev = s2l_dev(vfsp->vfs_dev);
+
+	/* Create an id space arena for automounter requests. */
+	(void) snprintf(name, sizeof (name), "lx_autofs_id_%d",
+	    getminor(vfsp->vfs_dev));
+	data->lav_ids = id_space_create(name, 1, INT_MAX);
+
+	/* Create hashes to keep track of automounter requests. */
+	mutex_init(&data->lav_lock, NULL, MUTEX_DEFAULT, NULL);
+	(void) snprintf(name, sizeof (name), "lx_autofs_path_hash_%d",
+	    getminor(vfsp->vfs_dev));
+	data->lav_path_hash = mod_hash_create_strhash(name,
+	    LX_AUTOFS_VFS_PATH_HASH_SIZE, mod_hash_null_valdtor);
+	(void) snprintf(name, sizeof (name), "lx_autofs_id_hash_%d",
+	    getminor(vfsp->vfs_dev));
+	data->lav_id_hash = mod_hash_create_idhash(name,
+	    LX_AUTOFS_VFS_ID_HASH_SIZE, mod_hash_null_valdtor);
+
+	/* Create a hash to keep track of vnodes. */
+	(void) snprintf(name, sizeof (name), "lx_autofs_vn_hash_%d",
+	    getminor(vfsp->vfs_dev));
+	data->lav_vn_hash = mod_hash_create_ptrhash(name,
+	    LX_AUTOFS_VFS_VN_HASH_SIZE, mod_hash_null_valdtor,
+	    sizeof (vnode_t));
+
+	list_create(&data->lav_mnt_list, sizeof (lx_autofs_mntent_t),
+	    offsetof(lx_autofs_mntent_t, lxafme_lst));
+
+	/* Create root vnode */
+	data->lav_root = lx_autofs_vn_alloc(vfsp, data->lav_bs_vp);
+
+	data->lav_root->v_flag |= VROOT | VNOCACHE | VNOMAP | VNOSWAP;
+
+	/*
+	 * For a direct mountpoint we need to allow a filesystem to be
+	 * mounted overtop of this autofs mount. Otherwise, disallow that.
+	 */
+	if (data->lav_mnttype == LXAMT_INDIR)
+		data->lav_root->v_flag |= VNOMOUNT;
+
+	return (0);
+}
+
+static int
+lx_autofs_unmount(vfs_t *vfsp, int flag, struct cred *cr)
+{
+	lx_autofs_vfs_t *data;
+
+	if (secpolicy_fs_unmount(cr, vfsp) != 0)
+		return (EPERM);
+
+	/* We do not currently support forced unmounts. */
+	if (flag & MS_FORCE)
+		return (ENOTSUP);
+
+	/*
+	 * We should never have a reference count of less than 2: one for the
+	 * caller, one for the root vnode.
+	 */
+	ASSERT(vfsp->vfs_count >= 2);
+
+	/* If there are any outstanding vnodes, we can't unmount. */
+	if (vfsp->vfs_count > 2)
+		return (EBUSY);
+
+	/* Check for any remaining holds on the root vnode. */
+	data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+	ASSERT(data->lav_root->v_vfsp == vfsp);
+	if (data->lav_root->v_count > 1)
+		return (EBUSY);
+
+	/* Close the fifo to the automount process. */
+	if (data->lav_fifo_wr != NULL)
+		(void) closef(data->lav_fifo_wr);
+	if (data->lav_fifo_rd != NULL)
+		(void) closef(data->lav_fifo_rd);
+
+	/*
+	 * We have to release our hold on our root vnode before we can
+	 * delete the backing store.  (Since the root vnode is linked
+	 * to the backing store.)
+	 */
+	VN_RELE(data->lav_root);
+
+	/* Cleanup the backing store. */
+	lx_autofs_bs_destroy(data->lav_mvp, data->lav_bs_name);
+	VN_RELE(data->lav_mvp);
+
+	/*
+	 * Delete all listed mounts.
+	 */
+	for (;;) {
+		lx_autofs_mntent_t *mp;
+
+		mp = list_remove_head(&data->lav_mnt_list);
+		if (mp == NULL)
+			break;
+		kmem_free(mp->lxafme_path, mp->lxafme_len);
+		kmem_free(mp, sizeof (lx_autofs_mntent_t));
+	}
+
+	/* Cleanup out remaining data structures. */
+	mod_hash_destroy_strhash(data->lav_path_hash);
+	mod_hash_destroy_idhash(data->lav_id_hash);
+	mod_hash_destroy_ptrhash(data->lav_vn_hash);
+	id_space_destroy(data->lav_ids);
+	list_destroy(&data->lav_mnt_list);
+	kmem_free(data, sizeof (lx_autofs_vfs_t));
+
+	return (0);
+}
+
+static int
+lx_autofs_root(vfs_t *vfsp, vnode_t **vpp)
+{
+	lx_autofs_vfs_t	*data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+
+	*vpp = data->lav_root;
+	VN_HOLD(*vpp);
+
+	return (0);
+}
+
+static int
+lx_autofs_statvfs(vfs_t *vfsp, statvfs64_t *sp)
+{
+	lx_autofs_vfs_t	*data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+	vnode_t		*urvp = data->lav_root->v_data;
+	dev32_t		d32;
+	int		error;
+
+	if ((error = VFS_STATVFS(urvp->v_vfsp, sp)) != 0)
+		return (error);
+
+	/* Update some of values before returning. */
+	(void) cmpldev(&d32, vfsp->vfs_dev);
+	sp->f_fsid = d32;
+	(void) strlcpy(sp->f_basetype, vfssw[vfsp->vfs_fstype].vsw_name,
+	    sizeof (sp->f_basetype));
+	sp->f_flag = vf_to_stf(vfsp->vfs_flag);
+	bzero(sp->f_fstr, sizeof (sp->f_fstr));
+	return (0);
+}
+
+static const fs_operation_def_t lx_autofs_vfstops[] = {
+	{ VFSNAME_MOUNT,	{ .vfs_mount = lx_autofs_mount } },
+	{ VFSNAME_UNMOUNT,	{ .vfs_unmount = lx_autofs_unmount } },
+	{ VFSNAME_ROOT,		{ .vfs_root = lx_autofs_root } },
+	{ VFSNAME_STATVFS,	{ .vfs_statvfs = lx_autofs_statvfs } },
+	{ NULL, NULL }
+};
+
+/*
+ * VOP entry points - simple passthrough
+ *
+ * For most VOP entry points we can simply pass the request on to
+ * the underlying filesystem we're mounted on.
+ */
+static int
+lx_autofs_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr,
+    caller_context_t *ctp)
+{
+	vnode_t *uvp = vp->v_data;
+	return (VOP_CLOSE(uvp, flag, count, offset, cr, ctp));
+}
+
+static int
+lx_autofs_readdir(vnode_t *vp, uio_t *uiop, cred_t *cr, int *eofp,
+    caller_context_t *ctp, int flags)
+{
+	vnode_t *uvp = vp->v_data;
+	return (VOP_READDIR(uvp, uiop, cr, eofp, ctp, flags));
+}
+
+static int
+lx_autofs_access(vnode_t *vp, int mode, int flags, cred_t *cr,
+    caller_context_t *ctp)
+{
+	vnode_t *uvp = vp->v_data;
+	return (VOP_ACCESS(uvp, mode, flags, cr, ctp));
+}
+
+static int
+lx_autofs_rwlock(struct vnode *vp, int write_lock, caller_context_t *ctp)
+{
+	vnode_t *uvp = vp->v_data;
+	return (VOP_RWLOCK(uvp, write_lock, ctp));
+}
+
+static void
+lx_autofs_rwunlock(struct vnode *vp, int write_lock, caller_context_t *ctp)
+{
+	vnode_t *uvp = vp->v_data;
+	VOP_RWUNLOCK(uvp, write_lock, ctp);
+}
+
+/*
+ * Check if attempting to access a 'direct' mount and if so, call the
+ * automounter to perform the mount. Once the mount occurs, the new filesystem
+ * will be mounted overtop of this autofs mountpoint and we will no longer
+ * come through this path.
+ */
+static vnode_t *
+lx_autofs_do_direct(vnode_t *vp)
+{
+	vfs_t	*vfsp = vp->v_vfsp;
+	lx_autofs_vfs_t *data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+	vnode_t *nvp;
+	boolean_t skip_am_call = B_FALSE;
+
+	if (data->lav_mnttype == LXAMT_INDIR)
+		return (NULL);
+
+	/*
+	 * Check if the current process is in the automounter's process group.
+	 * If it is, the current process is either the automounter itself or
+	 * one of it's children. If so, don't call back into the automounter.
+	 */
+	mutex_enter(&pidlock);
+	if (data->lav_pgrp == curproc->p_pgrp) {
+		skip_am_call = B_TRUE;
+	}
+	mutex_exit(&pidlock);
+
+	/*
+	 * It is possible there is already a new fs mounted on top of our vnode.
+	 * This can happen if the caller first did a lookup of a file name
+	 * using our vnode as the directory vp. The lookup would trigger the
+	 * autofs mount on top of ourself, but if the caller then uses our
+	 * vnode to do a getattr on the directory, it will use the autofs
+	 * vnode and not the newly mounted vnode. We need to skip re-calling
+	 * the automounter for this case.
+	 */
+	if (!skip_am_call && vn_mountedvfs(vp) == NULL) {
+		char tbuf[MAXPATHLEN];
+		char *nm;
+
+		(void) strlcpy(tbuf, (char *)refstr_value(vfsp->vfs_mntpt),
+		    sizeof (tbuf));
+		nm = tbuf + strlen(tbuf);
+		while (*nm != '/' && nm != tbuf)
+			nm--;
+		if (*nm == '/')
+			nm++;
+		(void) lx_autofs_automounter_call(vp, nm);
+	}
+
+	/*
+	 * We need to take an extra hold on our vp (which is the autofs
+	 * root vp) to account for the rele done in traverse. traverse will
+	 * take a hold on the new vp so the caller is responsible for calling
+	 * VN_RELE on the returned vp.
+	 */
+	VN_HOLD(vp);
+	nvp = vp;
+	if (traverse(&nvp) != 0) {
+		VN_RELE(nvp);
+		return (NULL);
+	}
+
+	/* Confirm that we have a non-autofs fs mounted now */
+	if (nvp->v_op == lx_autofs_vn_ops) {
+		VN_RELE(nvp);
+		return (NULL);
+	}
+
+	return (nvp);
+}
+
+/*ARGSUSED*/
+static int
+lx_autofs_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr,
+    caller_context_t *ctp, int flags)
+{
+	vnode_t *udvp = dvp->v_data;
+	vnode_t		*nvp;
+
+	/* handle direct mount here */
+	if ((nvp = lx_autofs_do_direct(dvp)) != NULL) {
+		int error;
+
+		error = VOP_RMDIR(nvp, nm, cdir, cr, ctp, flags);
+		VN_RELE(nvp);
+		return (error);
+	}
+
+	/*
+	 * cdir is the calling processes current directory.
+	 * If cdir is lx_autofs vnode then get its real underlying
+	 * vnode ptr.  (It seems like the only thing cdir is
+	 * ever used for is to make sure the user doesn't delete
+	 * their current directory.)
+	 */
+	if (vn_matchops(cdir, lx_autofs_vn_ops)) {
+		vnode_t *ucdir = cdir->v_data;
+		return (VOP_RMDIR(udvp, nm, ucdir, cr, ctp, flags));
+	}
+
+	return (VOP_RMDIR(udvp, nm, cdir, cr, ctp, flags));
+}
+
+/*
+ * VOP entry points - special passthrough
+ *
+ * For some VOP entry points we will first pass the request on to
+ * the underlying filesystem we're mounted on.  If there's an error
+ * then we immediately return the error, but if the request succeeds
+ * we have to do some extra work before returning.
+ */
+static int
+lx_autofs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ctp)
+{
+	vnode_t		*ovp = *vpp;
+	vnode_t		*uvp = ovp->v_data;
+	int		error;
+
+	/* direct mounts were handled by the lookup to get *vpp */
+
+	if ((error = VOP_OPEN(&uvp, flag, cr, ctp)) != 0)
+		return (error);
+
+	/* Check for clone opens. */
+	if (uvp == ovp->v_data)
+		return (0);
+
+	/* Deal with clone opens by returning a new vnode. */
+	*vpp = lx_autofs_vn_alloc(ovp->v_vfsp, uvp);
+	VN_RELE(ovp);
+	return (0);
+}
+
+static int
+lx_autofs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
+    caller_context_t *ctp)
+{
+	vnode_t		*uvp = vp->v_data;
+	vnode_t		*dvp;
+	int		error;
+	lx_autofs_vfs_t *data = (lx_autofs_vfs_t *)vp->v_vfsp->vfs_data;
+
+	if ((dvp = lx_autofs_do_direct(vp)) != NULL) {
+		uvp = dvp;
+	}
+
+	error = VOP_GETATTR(uvp, vap, flags, cr, ctp);
+
+	if (dvp != NULL) {
+		/* we operated on the direct mounted fs */
+		VN_RELE(dvp);
+		if (error == 0) {
+			/*
+			 * During automounter restart recovery the automounter
+			 * will fstat the fd provided in the setpipe ioctl. It
+			 * uses the resulting inode & dev to correlate future
+			 * autofs fifo requests to the correct entry. Thus, we
+			 * have to update the attributes with our own id's.
+			 */
+			vap->va_fsid = data->lav_dev;
+			vap->va_nodeid = data->lav_ino;
+		}
+	} else if (error == 0) {
+		/* Update the attributes with our filesystem id. */
+		vap->va_fsid = data->lav_dev;
+	}
+
+	return (error);
+}
+
+static int
+lx_autofs_mkdir(vnode_t *dvp, char *nm, struct vattr *vap, vnode_t **vpp,
+    cred_t *cr, caller_context_t *ctp, int flags, vsecattr_t *vsecp)
+{
+	vnode_t		*udvp = dvp->v_data;
+	vnode_t		*nvp;
+	int		error;
+
+	if ((nvp = lx_autofs_do_direct(dvp)) != NULL) {
+		udvp = nvp;
+	}
+
+	error = VOP_MKDIR(udvp, nm, vap, vpp, cr, ctp, flags, vsecp);
+
+	if (nvp != NULL) {
+		/* we operated on the direct mounted fs */
+		VN_RELE(nvp);
+	} else if (error == 0) {
+		vnode_t		*uvp = NULL;
+
+		/* Update the attributes with our filesystem id. */
+		vap->va_fsid = dvp->v_vfsp->vfs_dev;
+
+		/* Allocate our new vnode. */
+		uvp = *vpp;
+		*vpp = lx_autofs_vn_alloc(dvp->v_vfsp, uvp);
+	}
+
+	return (error);
+}
+
+/*
+ * VOP entry points - custom
+ */
+/*ARGSUSED*/
+static void
+lx_autofs_inactive(struct vnode *vp, struct cred *cr, caller_context_t *ctp)
+{
+	lx_autofs_vfs_t	*data = (lx_autofs_vfs_t *)vp->v_vfsp->vfs_data;
+
+	/*
+	 * We need to hold the vfs lock because if we're going to free
+	 * this vnode we have to prevent anyone from looking it up
+	 * in the vnode hash.
+	 */
+	mutex_enter(&data->lav_lock);
+	mutex_enter(&vp->v_lock);
+
+	if (vp->v_count < 1) {
+		panic("lx_autofs_inactive: bad v_count");
+		/*NOTREACHED*/
+	}
+
+	/* Drop the temporary hold by vn_rele now. */
+	if (--vp->v_count > 0) {
+		mutex_exit(&vp->v_lock);
+		mutex_exit(&data->lav_lock);
+		return;
+	}
+
+	/*
+	 * No one should have been blocked on this lock because we're
+	 * about to free this vnode.
+	 */
+	lx_autofs_vn_free(vp);
+}
+
+static int
+lx_autofs_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct pathname *pnp,
+    int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ctp,
+    int *direntflags, pathname_t *realpnp)
+{
+	vnode_t			*udvp = dvp->v_data;
+	vnode_t			*uvp = NULL;
+	lx_autofs_vfs_t		*data;
+	int			error = ENOENT;
+
+	data = (lx_autofs_vfs_t *)dvp->v_vfsp->vfs_data;
+
+	/*
+	 * For an indirect mount first try to lookup if this path component
+	 * already exists.
+	 */
+	if (data->lav_mnttype == LXAMT_INDIR) {
+		if ((error = VOP_LOOKUP(udvp, nm, &uvp, pnp, flags, rdir, cr,
+		    ctp, direntflags, realpnp)) == 0) {
+			*vpp = lx_autofs_vn_alloc(dvp->v_vfsp, uvp);
+			return (0);
+		}
+	}
+
+	/* Only query the automounter if the path does not exist. */
+	if (error != ENOENT)
+		return (error);
+
+	if (data->lav_catatonic)
+		return (ENOENT);
+
+	/* Save the uid/gid for the requestor ioctl. */
+	data->lav_uid = crgetuid(cr);
+	data->lav_gid = crgetgid(cr);
+
+	/* Refer the lookup to the automounter. */
+	if ((error = lx_autofs_automounter_call(dvp, nm)) != 0)
+		return (error);
+
+	if (data->lav_mnttype == LXAMT_INDIR) {
+		/*
+		 * Indirect mount. The automounter call should have mounted
+		 * something on nm. Retry the lookup operation.
+		 */
+		if ((error = VOP_LOOKUP(udvp, nm, &uvp, pnp, flags, rdir, cr,
+		    ctp, direntflags, realpnp)) == 0) {
+			*vpp = lx_autofs_vn_alloc(dvp->v_vfsp, uvp);
+			return (0);
+		}
+	} else {
+		/*
+		 * Direct or offset mount. The automounter call should have
+		 * covered our 'dvp' with a new filesystem. Traverse into the
+		 * new mount and retry the lookup.
+		 *
+		 * We need to take an extra hold on our vp (which is the autofs
+		 * root vp) to acount for the rele done in traverse. Our caller
+		 * will also do a rele on the original dvp and that would leave
+		 * us one ref short on our autofs root vnode.
+		 */
+		VN_HOLD(dvp);
+		if ((error = traverse(&dvp)) != 0) {
+			VN_RELE(dvp);
+			return (error);
+		}
+
+		error = VOP_LOOKUP(dvp, nm, vpp, pnp, flags, rdir, cr, ctp,
+		    direntflags, realpnp);
+
+		/* release the traverse hold */
+		VN_RELE(dvp);
+	}
+	return (error);
+}
+
+static int
+lx_autofs_ioctl(vnode_t *vp, int cmd, intptr_t arg, int mode, cred_t *cr,
+    int *rvalp, caller_context_t *ctp)
+{
+	vnode_t			*uvp = vp->v_data;
+
+	/* Intercept our ioctls. */
+	switch ((uint_t)cmd) {
+	case LX_AUTOFS_IOC_READY:
+	case LX_AUTOFS_IOC_FAIL:
+	case LX_AUTOFS_IOC_CATATONIC:
+	case LX_AUTOFS_IOC_PROTOVER:
+	case LX_AUTOFS_IOC_SETTIMEOUT:
+	case LX_AUTOFS_IOC_EXPIRE:
+	case LX_AUTOFS_IOC_EXPIRE_MULTI:
+	case LX_AUTOFS_IOC_PROTOSUBVER:
+	case LX_AUTOFS_IOC_ASKUMOUNT:
+		return (lx_autofs_automounter_ioctl(vp, cmd, arg, cr));
+	}
+
+	/* Pass any remaining ioctl on. */
+	return (VOP_IOCTL(uvp, cmd, arg, mode, cr, rvalp, ctp));
+}
+
+/*
+ * VOP entry points definitions
+ */
+static const fs_operation_def_t lx_autofs_tops_root[] = {
+	{ VOPNAME_OPEN,		{ .vop_open = lx_autofs_open } },
+	{ VOPNAME_CLOSE,	{ .vop_close = lx_autofs_close } },
+	{ VOPNAME_IOCTL,	{ .vop_ioctl = lx_autofs_ioctl } },
+	{ VOPNAME_RWLOCK,	{ .vop_rwlock = lx_autofs_rwlock } },
+	{ VOPNAME_RWUNLOCK,	{ .vop_rwunlock = lx_autofs_rwunlock } },
+	{ VOPNAME_GETATTR,	{ .vop_getattr = lx_autofs_getattr } },
+	{ VOPNAME_ACCESS,	{ .vop_access = lx_autofs_access } },
+	{ VOPNAME_READDIR,	{ .vop_readdir = lx_autofs_readdir } },
+	{ VOPNAME_LOOKUP,	{ .vop_lookup = lx_autofs_lookup } },
+	{ VOPNAME_INACTIVE,	{ .vop_inactive = lx_autofs_inactive } },
+	{ VOPNAME_MKDIR,	{ .vop_mkdir = lx_autofs_mkdir } },
+	{ VOPNAME_RMDIR,	{ .vop_rmdir = lx_autofs_rmdir } },
+	{ NULL }
+};
+
+/*
+ * DEV-specific entry points
+ */
+
+/*ARGSUSED*/
+static int
+lx_autofs_dev_open(dev_t *devp, int flags, int otyp, cred_t *credp)
+{
+	return (0);
+}
+
+/*ARGSUSED*/
+static int
+lx_autofs_dev_close(dev_t dev, int flags, int otyp, cred_t *credp)
+{
+	return (0);
+}
+
+static int
+lx_autofs_dev_validate_cmd(intptr_t arg, lx_autofs_dv_ioctl_t *dcmd)
+{
+	if (copyin((caddr_t)arg, dcmd, sizeof (lx_autofs_dv_ioctl_t)) != 0)
+		return (EFAULT);
+
+	if (dcmd->lad_ver_major != LX_AUTOFS_DEV_VERSION_MAJOR ||
+	    dcmd->lad_ver_minor > LX_AUTOFS_DEV_VERSION_MINOR)
+		return (EINVAL);
+
+	DTRACE_PROBE1(lx__dev__cmd, void *, dcmd);
+
+	/* Fill in the version for return */
+	dcmd->lad_ver_major = LX_AUTOFS_DEV_VERSION_MAJOR;
+	dcmd->lad_ver_minor = LX_AUTOFS_DEV_VERSION_MINOR;
+	return (0);
+}
+
+static vfs_t *
+lx_autofs_dev_getvfs_bypath(char *fs_mntpt)
+{
+	struct vfs *vfsp;
+	struct vfs *vfslist;
+	vfs_t *fnd_vfs = NULL;
+	zone_t *zone = curzone;
+
+	vfs_list_read_lock();
+
+	vfsp = vfslist = curzone->zone_vfslist;
+	if (vfslist == NULL) {
+		vfs_list_unlock();
+		return (NULL);
+	}
+
+	do {
+		if (vfsp->vfs_op == lx_autofs_vfsops) {
+			char *mntpt = (char *)refstr_value(vfsp->vfs_mntpt);
+
+			if (strcmp(fs_mntpt, ZONE_PATH_TRANSLATE(mntpt, zone))
+			    == 0) {
+				fnd_vfs = vfsp;
+				VFS_HOLD(fnd_vfs)
+				break;
+			}
+		}
+		vfsp = vfsp->vfs_zone_next;
+	} while (vfsp != vfslist);
+
+	vfs_list_unlock();
+
+	return (fnd_vfs);
+}
+
+static int
+lx_autofs_dev_fd_preamble(intptr_t arg, lx_autofs_dv_ioctl_t *dc, vfs_t **vfspp)
+{
+	int err;
+	lx_autofs_vfs_t	*data;
+	file_t *fp;
+	vfs_t *vfsp;
+
+	if ((err = lx_autofs_dev_validate_cmd(arg, dc)) != 0)
+		return (err);
+
+	if ((fp = getf(dc->lad_ioctlfd)) == NULL)
+		return (EBADF);
+
+	vfsp = fp->f_vnode->v_vfsp;
+	if (vfsp->vfs_op != lx_autofs_vfsops) {
+		releasef(dc->lad_ioctlfd);
+		return (EBADF);
+	}
+
+	data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+	if (data->lav_root->v_count <= 1) {
+		releasef(dc->lad_ioctlfd);
+		return (EBADF);
+	}
+
+	VFS_HOLD(vfsp);
+	*vfspp = vfsp;
+
+	releasef(dc->lad_ioctlfd);
+	return (0);
+}
+
+static int
+lx_autofs_dev_vers(intptr_t arg)
+{
+	int err;
+	lx_autofs_dv_ioctl_t dcmd;
+
+	if ((err = lx_autofs_dev_validate_cmd(arg, &dcmd)) != 0)
+		return (err);
+
+	if (copyout(&dcmd, (caddr_t)arg, sizeof (dcmd)) != 0)
+		return (EFAULT);
+
+	return (0);
+}
+
+static int
+lx_autofs_dev_protver(intptr_t arg)
+{
+	int err;
+	lx_autofs_dv_ioctl_t dcmd;
+
+	if ((err = lx_autofs_dev_validate_cmd(arg, &dcmd)) != 0)
+		return (err);
+
+	dcmd.lad_arg1 = LX_AUTOFS_PROTO_VERS5;
+
+	if (copyout(&dcmd, (caddr_t)arg, sizeof (dcmd)) != 0)
+		return (EFAULT);
+
+	return (0);
+}
+
+static int
+lx_autofs_dev_protosubver(intptr_t arg)
+{
+	int err;
+	lx_autofs_dv_ioctl_t dcmd;
+
+	if ((err = lx_autofs_dev_validate_cmd(arg, &dcmd)) != 0)
+		return (err);
+
+	dcmd.lad_arg1 = LX_AUTOFS_PROTO_SUBVERSION;
+
+	if (copyout(&dcmd, (caddr_t)arg, sizeof (dcmd)) != 0)
+		return (EFAULT);
+
+	return (0);
+}
+
+static int
+lx_autofs_dev_get_path_cmd(intptr_t arg, lx_autofs_dv_ioctl_t **dcp)
+{
+	int err;
+	lx_autofs_dv_ioctl_t dcmd, *dc;
+
+	if ((err = lx_autofs_dev_validate_cmd(arg, &dcmd)) != 0)
+		return (err);
+
+	if (dcmd.lad_size <= sizeof (dcmd) ||
+	    dcmd.lad_size > (sizeof (dcmd) + MAXPATHLEN))
+		return (EINVAL);
+
+	dc = kmem_alloc(dcmd.lad_size, KM_SLEEP);
+
+	/* re-copyin the full struct with the path */
+	if (copyin((caddr_t)arg, dc, dcmd.lad_size) != 0) {
+		kmem_free(dc, dcmd.lad_size);
+		return (EFAULT);
+	}
+	dc->lad_size = dcmd.lad_size;
+
+	if (dc->lad_path[0] != '/' ||
+	    dc->lad_path[dcmd.lad_size - sizeof (dcmd) - 1] != '\0') {
+		kmem_free(dc, dcmd.lad_size);
+		return (EINVAL);
+	}
+
+	*dcp = dc;
+	return (0);
+}
+
+static int
+lx_autofs_dev_openmount(intptr_t arg)
+{
+	int err;
+	int fd;
+	lx_autofs_dv_ioctl_t *dc;
+	vfs_t *vfsp;
+	lx_autofs_vfs_t	*data;
+
+	if ((err = lx_autofs_dev_get_path_cmd(arg, &dc)) != 0)
+		return (err);
+
+	if ((vfsp = lx_autofs_dev_getvfs_bypath(dc->lad_path)) == NULL) {
+		kmem_free(dc, dc->lad_size);
+		return (EINVAL);
+	}
+
+	/* lad_arg1 is the dev number of the mnt but we don't check that */
+
+	/*
+	 * Do an "open" on the root vnode. To fully simulate "open" we also add
+	 * a hold on the root vnode itself since lx_autofs_open will only open
+	 * (and hold) the underlying vnode.
+	 */
+	data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+	VN_HOLD(data->lav_root);
+	if ((err = fassign(&data->lav_root, FWRITE|FREAD, &fd)) != 0) {
+		VN_RELE(data->lav_root);
+		VFS_RELE(vfsp);
+		kmem_free(dc, dc->lad_size);
+		return (err);
+	}
+
+	mutex_enter(&data->lav_lock);
+	data->lav_openmnt_cnt++;
+	mutex_exit(&data->lav_lock);
+
+	dc->lad_ioctlfd = fd;
+
+	if (copyout(dc, (caddr_t)arg, sizeof (lx_autofs_dv_ioctl_t)) != 0) {
+		mutex_enter(&data->lav_lock);
+		data->lav_openmnt_cnt--;
+		mutex_exit(&data->lav_lock);
+		(void) closeandsetf(fd, NULL);
+		VFS_RELE(vfsp);
+		kmem_free(dc, dc->lad_size);
+		return (EFAULT);
+	}
+	VFS_RELE(vfsp);
+
+	kmem_free(dc, dc->lad_size);
+	return (0);
+}
+
+static int
+lx_autofs_dev_closemount(intptr_t arg)
+{
+	int err;
+	lx_autofs_dv_ioctl_t dcmd;
+	vfs_t *vfsp;
+	lx_autofs_vfs_t	*data;
+
+	if ((err = lx_autofs_dev_fd_preamble(arg, &dcmd, &vfsp)) != 0)
+		return (err);
+
+	data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+
+	/* "close" the vnode */
+	if ((err = closeandsetf(dcmd.lad_ioctlfd, NULL)) != 0) {
+		VFS_RELE(vfsp);
+		return (err);
+	}
+
+	mutex_enter(&data->lav_lock);
+	ASSERT(data->lav_openmnt_cnt > 0);
+	data->lav_openmnt_cnt--;
+	mutex_exit(&data->lav_lock);
+
+	VFS_RELE(vfsp);
+	return (0);
+}
+
+static int
+lx_autofs_dev_ready(intptr_t arg)
+{
+	int err;
+	lx_autofs_dv_ioctl_t dcmd;
+	vfs_t *vfsp;
+
+	if ((err = lx_autofs_dev_fd_preamble(arg, &dcmd, &vfsp)) != 0)
+		return (err);
+
+	if ((err = lx_autofs_ack(dcmd.lad_arg1, vfsp, LXACR_READY)) != 0) {
+		VFS_RELE(vfsp);
+		return (err);
+	}
+
+	VFS_RELE(vfsp);
+	return (0);
+}
+
+static int
+lx_autofs_dev_fail(intptr_t arg)
+{
+	int err;
+	lx_autofs_dv_ioctl_t dcmd;
+	vfs_t *vfsp;
+
+	if ((err = lx_autofs_dev_fd_preamble(arg, &dcmd, &vfsp)) != 0)
+		return (err);
+
+	if ((err = lx_autofs_ack(dcmd.lad_arg1, vfsp, LXACR_FAIL)) != 0) {
+		VFS_RELE(vfsp);
+		return (err);
+	}
+
+	VFS_RELE(vfsp);
+	return (0);
+}
+
+/*
+ * Update the fifo pipe information we use to talk to the automounter. The
+ * ioctl is used when the automounter restarts. This logic is similar to the
+ * handling done in lx_autofs_parse_mntopt() when the filesytem is first
+ * mounted.
+ */
+static int
+lx_autofs_dev_setpipefd(intptr_t arg)
+{
+	int err;
+	lx_autofs_dv_ioctl_t dcmd;
+	vfs_t *vfsp;
+	lx_autofs_vfs_t	*data;
+	int fd, pgrp;
+	file_t *fp_wr, *fp_rd;
+
+	if ((err = lx_autofs_dev_fd_preamble(arg, &dcmd, &vfsp)) != 0)
+		return (err);
+
+	mutex_enter(&pidlock);
+	pgrp = curproc->p_pgrp;
+	mutex_exit(&pidlock);
+	fd = dcmd.lad_arg1;
+
+	/* Lookup the new fifos. See comment in lx_autofs_parse_mntopt. */
+	if (lx_autofs_fifo_lookup(pgrp, fd, &fp_wr, &fp_rd) != 0) {
+		int pid = (int)curproc->p_pid;
+
+		if (lx_autofs_fifo_lookup(pid, fd, &fp_wr, &fp_rd) != 0) {
+			VFS_RELE(vfsp);
+			return (EINVAL);
+		}
+	}
+
+	data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+
+	/* Close the old fifos. */
+	if (data->lav_fifo_wr != NULL)
+		(void) closef(data->lav_fifo_wr);
+	if (data->lav_fifo_rd != NULL)
+		(void) closef(data->lav_fifo_rd);
+
+	data->lav_fd = fd;
+	data->lav_pgrp = pgrp;
+	data->lav_fifo_rd = fp_rd;
+	data->lav_fifo_wr = fp_wr;
+	/*
+	 * Not explicitly in the ioctl spec. but necessary for correct recovery
+	 */
+	data->lav_catatonic = B_FALSE;
+
+	VFS_RELE(vfsp);
+
+	return (0);
+}
+
+static int
+lx_autofs_dev_catatonic(intptr_t arg)
+{
+	int err;
+	lx_autofs_dv_ioctl_t dcmd;
+	vfs_t *vfsp;
+	lx_autofs_vfs_t	*data;
+
+	if ((err = lx_autofs_dev_fd_preamble(arg, &dcmd, &vfsp)) != 0)
+		return (err);
+
+	data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+	data->lav_catatonic = B_TRUE;
+	VFS_RELE(vfsp);
+
+	return (0);
+}
+
+static int
+lx_autofs_dev_expire(intptr_t arg)
+{
+	int err;
+	lx_autofs_dv_ioctl_t dcmd;
+	vfs_t *vfsp;
+
+	if ((err = lx_autofs_dev_fd_preamble(arg, &dcmd, &vfsp)) != 0)
+		return (err);
+
+	/* If it succeeds in expiring then we don't want to return EAGAIN */
+	if ((err = lx_autofs_expire(vfsp, kcred)) == 0) {
+		VFS_RELE(vfsp);
+		return (0);
+	}
+
+	VFS_RELE(vfsp);
+	return (EAGAIN);
+}
+
+static int
+lx_autofs_dev_timeout(intptr_t arg)
+{
+	int err;
+	lx_autofs_dv_ioctl_t dcmd;
+	vfs_t *vfsp;
+	lx_autofs_vfs_t	*data;
+
+	if ((err = lx_autofs_dev_fd_preamble(arg, &dcmd, &vfsp)) != 0)
+		return (err);
+
+	data = (lx_autofs_vfs_t *)vfsp->vfs_data;
+	data->lav_timeout = dcmd.lad_arg1;
+	VFS_RELE(vfsp);
+
+	return (0);
+}
+
+static int
+lx_autofs_dev_requestor(intptr_t arg)
+{
+	int err;
+	lx_autofs_dv_ioctl_t *dc;
+	vfs_t *vfsp;
+	vfs_t *fnd_vfs = NULL;
+	struct vfs *vfslist;
+	zone_t *zone = curzone;
+	lx_autofs_vfs_t	*data;
+	uid_t uid;
+	gid_t gid;
+
+	if ((err = lx_autofs_dev_get_path_cmd(arg, &dc)) != 0)
+		return (err);
+
+	vfs_list_read_lock();
+	vfsp = vfslist = curzone->zone_vfslist;
+	if (vfslist == NULL) {
+		vfs_list_unlock();
+		kmem_free(dc, dc->lad_size);
+		return (EINVAL);
+	}
+
+	do {
+		/* Skip mounts we shouldn't show. */
+		if (!(vfsp->vfs_flag & VFS_NOMNTTAB)) {
+			char *mntpt = (char *)refstr_value(vfsp->vfs_mntpt);
+
+			if (strcmp(dc->lad_path,
+			    ZONE_PATH_TRANSLATE(mntpt, zone)) == 0) {
+
+				if (vfsp->vfs_op != lx_autofs_vfsops) {
+					/*
+					 * Found an indirect mount (probably
+					 * NFS) so we need to get the vfs it's
+					 * mounted onto.
+					 */
+					vnode_t *vn = vfsp->vfs_vnodecovered;
+					vfsp = vn->v_vfsp;
+
+					if (vfsp->vfs_op != lx_autofs_vfsops) {
+						/*
+						 * autofs doesn't manage this
+						 * path.
+						 */
+						break;
+					}
+				}
+
+				fnd_vfs = vfsp;
+				VFS_HOLD(fnd_vfs)
+				break;
+			}
+		}
+		vfsp = vfsp->vfs_zone_next;
+	} while (vfsp != vfslist);
+	vfs_list_unlock();
+
+	if (fnd_vfs == NULL) {
+		kmem_free(dc, dc->lad_size);
+		return (EINVAL);
+	}
+
+	data = (lx_autofs_vfs_t *)fnd_vfs->vfs_data;
+	uid = data->lav_uid;
+	gid = data->lav_gid;
+	VFS_RELE(fnd_vfs);
+
+	dc->lad_arg1 = uid;
+	dc->lad_arg2 = gid;
+
+	if (copyout(dc, (caddr_t)arg, sizeof (lx_autofs_dv_ioctl_t)) != 0) {
+		kmem_free(dc, dc->lad_size);
+		return (EFAULT);
+	}
+
+	kmem_free(dc, dc->lad_size);
+	return (0);
+}
+
+static int
+lx_autofs_dev_ismntpt(intptr_t arg)
+{
+	int err = 0;
+	lx_autofs_dv_ioctl_t *dc;
+	struct vfs *vfslist;
+	vfs_t *vfsp;
+	vfs_t *fnd_vfs = NULL;
+	zone_t *zone = curzone;
+
+	if ((err = lx_autofs_dev_get_path_cmd(arg, &dc)) != 0)
+		return (err);
+
+	/*
+	 * The automounter will always pass a path. It can also either pass an
+	 * ioctlfd or, if it's -1, arg1 can be an LX_AUTOFS_TYPE_* value. We
+	 * currently don't need those for our algorithm.
+	 */
+
+	vfs_list_read_lock();
+	vfsp = vfslist = curzone->zone_vfslist;
+	if (vfslist == NULL) {
+		vfs_list_unlock();
+		kmem_free(dc, dc->lad_size);
+		return (0);	/* return 0 if not a mount point */
+	}
+
+	do {
+		if (!(vfsp->vfs_flag & VFS_NOMNTTAB)) {
+			char *mntpt = (char *)refstr_value(vfsp->vfs_mntpt);
+
+			if (strcmp(dc->lad_path,
+			    ZONE_PATH_TRANSLATE(mntpt, zone)) == 0) {
+
+				/*
+				 * To handle direct mounts (on top of an autofs
+				 * mount), we must prefer non-autofs vfs for
+				 * this request.
+				 */
+				if (fnd_vfs != NULL)
+					VFS_RELE(fnd_vfs);
+
+				fnd_vfs = vfsp;
+				VFS_HOLD(fnd_vfs)
+
+				if (fnd_vfs->vfs_op != lx_autofs_vfsops)
+					break;
+			}
+		}
+		vfsp = vfsp->vfs_zone_next;
+	} while (vfsp != vfslist);
+	vfs_list_unlock();
+
+	if (fnd_vfs == NULL) {
+		kmem_free(dc, dc->lad_size);
+		return (0);	/* return 0 if not a mount point */
+	}
+
+	/*
+	 * arg1 is device number, arg2 is superblock magic number
+	 * The superblock value only matters if autofs or not.
+	 */
+	dc->lad_arg1 = fnd_vfs->vfs_dev;
+	if (fnd_vfs->vfs_op == lx_autofs_vfsops) {
+		dc->lad_arg2 = LX_AUTOFS_SB_MAGIC;
+	} else {
+		dc->lad_arg2 = ~((uint32_t)LX_AUTOFS_SB_MAGIC);
+	}
+
+	VFS_RELE(fnd_vfs);
+
+	if (copyout(dc, (caddr_t)arg, sizeof (lx_autofs_dv_ioctl_t)) != 0) {
+		kmem_free(dc, dc->lad_size);
+		return (EFAULT);
+	}
+
+	kmem_free(dc, dc->lad_size);
+
+	/*
+	 * We have to return 1 if it is a mount point. The lx ioctl autofs
+	 * translator will convert a negative value back to a positive,
+	 * non-error return value.
+	 */
+	return (-1);
+}
+
+static int
+lx_autofs_dev_askumount(intptr_t arg)
+{
+	int err;
+	int v;
+	lx_autofs_dv_ioctl_t dcmd;
+	vfs_t *vfsp;
+
+	if ((err = lx_autofs_dev_fd_preamble(arg, &dcmd, &vfsp)) != 0)
+		return (err);
+
+	if (lx_autofs_may_unmount(vfsp, kcred)) {
+		v = 0;
+	} else {
+		v = 1;
+	}
+	VFS_RELE(vfsp);
+
+	dcmd.lad_arg1 = v;
+	if (copyout(&dcmd, (caddr_t)arg, sizeof (dcmd)) != 0)
+		return (EFAULT);
+
+	return (0);
+}
+
+/*ARGSUSED*/
+static int
+lx_autofs_dev_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
+    int *rvalp)
+{
+	/*
+	 * Many of these constants are negative for a signed int.
+	 * Cast "cmd" to unsigned to prevent lint errors and not have any
+	 * other problems.
+	 */
+	switch ((unsigned int)cmd) {
+	case LX_AUTOFS_DEV_IOC_VERSION_CMD:
+		return (lx_autofs_dev_vers(arg));
+
+	case LX_AUTOFS_DEV_IOC_PROTOVER_CMD:
+		return (lx_autofs_dev_protver(arg));
+
+	case LX_AUTOFS_DEV_IOC_PROTOSUBVER_CMD:
+		return (lx_autofs_dev_protosubver(arg));
+
+	case LX_AUTOFS_DEV_IOC_OPENMOUNT_CMD:
+		return (lx_autofs_dev_openmount(arg));
+
+	case LX_AUTOFS_DEV_IOC_CLOSEMOUNT_CMD:
+		return (lx_autofs_dev_closemount(arg));
+
+	case LX_AUTOFS_DEV_IOC_READY_CMD:
+		return (lx_autofs_dev_ready(arg));
+
+	case LX_AUTOFS_DEV_IOC_FAIL_CMD:
+		return (lx_autofs_dev_fail(arg));
+
+	case LX_AUTOFS_DEV_IOC_SETPIPEFD_CMD:
+		return (lx_autofs_dev_setpipefd(arg));
+
+	case LX_AUTOFS_DEV_IOC_CATATONIC_CMD:
+		return (lx_autofs_dev_catatonic(arg));
+
+	case LX_AUTOFS_DEV_IOC_TIMEOUT_CMD:
+		return (lx_autofs_dev_timeout(arg));
+
+	case LX_AUTOFS_DEV_IOC_REQUESTER_CMD:
+		return (lx_autofs_dev_requestor(arg));
+
+	case LX_AUTOFS_DEV_IOC_EXPIRE_CMD:
+		return (lx_autofs_dev_expire(arg));
+
+	case LX_AUTOFS_DEV_IOC_ASKUMOUNT_CMD:
+		return (lx_autofs_dev_askumount(arg));
+
+	case LX_AUTOFS_DEV_IOC_ISMOUNTPOINT_CMD:
+		return (lx_autofs_dev_ismntpt(arg));
+	}
+
+	return (EINVAL);
+}
+
+/*
+ * lx_autofs_init() gets invoked via the mod_install() call in
+ * this module's _init() routine.  Therefore, the code that cleans
+ * up the structures we allocate below is actually found in
+ * our _fini() routine.
+ */
+/* ARGSUSED */
+static int
+lx_autofs_init(int fstype, char *name)
+{
+	int		error;
+
+	lx_autofs_major = ddi_name_to_major(LX_AUTOFS_NAME);
+
+	lx_autofs_fstype = fstype;
+	if ((error = vfs_setfsops(fstype, lx_autofs_vfstops,
+	    &lx_autofs_vfsops)) != 0) {
+		cmn_err(CE_WARN, "lx_autofs_init: bad vfs ops template");
+		return (error);
+	}
+
+	if ((error = vn_make_ops(name, lx_autofs_tops_root,
+	    &lx_autofs_vn_ops)) != 0) {
+		VERIFY(vfs_freevfsops_by_type(fstype) == 0);
+		lx_autofs_vn_ops = NULL;
+		return (error);
+	}
+
+	return (0);
+}
+
+/*ARGSUSED*/
+static int
+lx_autofs_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+	int	instance = ddi_get_instance(dip);
+
+	if (cmd != DDI_ATTACH)
+		return (DDI_FAILURE);
+
+	ASSERT(instance == 0);
+	if (instance != 0)
+		return (DDI_FAILURE);
+
+	/* create our minor node */
+	if (ddi_create_minor_node(dip, LX_AUTOFS_MINORNAME, S_IFCHR, 0,
+	    DDI_PSEUDO, 0) != DDI_SUCCESS)
+		return (DDI_FAILURE);
+
+	lx_autofs_dip = dip;
+
+	return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+lx_autofs_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+	if (cmd != DDI_DETACH)
+		return (DDI_FAILURE);
+
+	lx_autofs_dip = NULL;
+
+	return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+lx_autofs_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
+    void **resultp)
+{
+	switch (infocmd) {
+	case DDI_INFO_DEVT2DEVINFO:
+		*resultp = lx_autofs_dip;
+		return (DDI_SUCCESS);
+
+	case DDI_INFO_DEVT2INSTANCE:
+		*resultp = (void *)0;
+		return (DDI_SUCCESS);
+	}
+	return (DDI_FAILURE);
+}
+
+/*
+ * Driver flags
+ */
+static struct cb_ops lx_autofs_cb_ops = {
+	lx_autofs_dev_open,	/* open */
+	lx_autofs_dev_close,	/* close */
+	nodev,			/* strategy */
+	nodev,			/* print */
+	nodev,			/* dump */
+	nodev,			/* read */
+	nodev,			/* write */
+	lx_autofs_dev_ioctl,	/* ioctl */
+	nodev,			/* devmap */
+	nodev,			/* mmap */
+	nodev,			/* segmap */
+	nochpoll,		/* poll */
+	ddi_prop_op,		/* vb_prop_op */
+	NULL,			/* streamtab */
+	D_NEW | D_MP		/* Driver compatibility flag */
+};
+
+/*
+ * Module linkage
+ */
+static mntopt_t lx_autofs_mntopt[] = {
+	{ LX_MNTOPT_FD,		NULL,	0,	MO_HASVALUE },
+	{ LX_MNTOPT_PGRP,	NULL,	0,	MO_HASVALUE },
+	{ LX_MNTOPT_MINPROTO,	NULL,	0,	MO_HASVALUE },
+	{ LX_MNTOPT_MAXPROTO,	NULL,	0,	MO_HASVALUE },
+	{ LX_MNTOPT_INDIRECT,	NULL,	0,	0 },
+	{ LX_MNTOPT_DIRECT,	NULL,	0,	0 },
+	{ LX_MNTOPT_OFFSET,	NULL,	0,	0 }
+};
+
+static mntopts_t lx_autofs_mntopts = {
+	sizeof (lx_autofs_mntopt) / sizeof (mntopt_t),
+	lx_autofs_mntopt
+};
+
+static vfsdef_t vfw = {
+	VFSDEF_VERSION,
+	LX_AUTOFS_NAME,
+	lx_autofs_init,
+	VSW_HASPROTO | VSW_VOLATILEDEV | VSW_ZMOUNT,
+	&lx_autofs_mntopts
+};
+
+static struct dev_ops lx_autofs_dev_ops = {
+	DEVO_REV,		/* version */
+	0,			/* refcnt */
+	lx_autofs_info,		/* info */
+	nulldev,		/* identify */
+	nulldev,		/* probe */
+	lx_autofs_attach,	/* attach */
+	lx_autofs_detach,	/* detach */
+	nodev,			/* reset */
+	&lx_autofs_cb_ops,	/* driver operations */
+	NULL,			/* no bus operations */
+	NULL,			/* power */
+	ddi_quiesce_not_needed	/* quiesce */
+};
+
+extern struct mod_ops mod_fsops;
+
+static struct modlfs modlfs = {
+	&mod_fsops, "lx autofs filesystem", &vfw
+};
+
+static struct modldrv modldrv = {
+	&mod_driverops, "lx autofs driver", &lx_autofs_dev_ops
+};
+
+static struct modlinkage modlinkage = {
+	MODREV_1,
+	(void *)&modlfs,
+	(void *)&modldrv,
+	NULL
+};
+
+int
+_init(void)
+{
+	int error;
+
+	if ((error = mod_install(&modlinkage)) != 0) {
+		return (error);
+	}
+
+	return (0);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+	int		error;
+
+	if ((error = mod_remove(&modlinkage)) != 0)
+		return (error);
+
+	if (lx_autofs_vn_ops != NULL) {
+		vn_freevnodeops(lx_autofs_vn_ops);
+		lx_autofs_vn_ops = NULL;
+	}
+
+	/*
+	 * In our init routine, if we get an error after calling
+	 * vfs_setfsops() we cleanup by calling vfs_freevfsops_by_type().
+	 * But we don't need to call vfs_freevfsops_by_type() here
+	 * because the fs framework did this for us as part of the
+	 * mod_remove() call above.
+	 */
+	return (0);
+}
--- /dev/null	Wed Oct  5 00:32:04 2016
+++ new/usr/src/uts/common/brand/lx/autofs/lxautofs.conf	Wed Oct  5 00:32:04 2016
@@ -0,0 +1,14 @@
+#
+# 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 Joyent, Inc.
+#
+
+name="lxautofs" parent="pseudo" instance=0;
--- /dev/null	Wed Oct  5 00:32:04 2016
+++ new/usr/src/uts/common/brand/lx/cgroups/cgrps.h	Wed Oct  5 00:32:04 2016
@@ -0,0 +1,222 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#ifndef	_LXCGRPS_H
+#define	_LXCGRPS_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/*
+ * cgrps.h: declarations, data structures and macros for lx_cgroup
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/policy.h>
+#include <sys/dirent.h>
+#include <sys/errno.h>
+#include <sys/kmem.h>
+#include <sys/pathname.h>
+#include <sys/systm.h>
+#include <sys/var.h>
+#include <sys/sysmacros.h>
+#include <sys/cred.h>
+#include <sys/priv.h>
+#include <sys/vnode.h>
+#include <sys/vfs.h>
+#include <sys/statvfs.h>
+#include <sys/cmn_err.h>
+#include <sys/zone.h>
+#include <sys/uio.h>
+#include <sys/utsname.h>
+#include <sys/atomic.h>
+#include <vm/anon.h>
+
+/*
+ * cgrpmgr ioctl interface.
+ */
+#define	CGRPFS_IOC	('C' << 16 | 'G' << 8)
+#define	CGRPFS_GETEVNT	(CGRPFS_IOC | 1)
+
+typedef struct cgrpmgr_info {
+	pid_t	cgmi_pid;
+	char	*cgmi_rel_agent_path;
+	char	*cgmi_cgroup_path;
+} cgrpmgr_info_t;
+
+#if defined(_KERNEL)
+
+#include <sys/lx_brand.h>
+
+typedef struct cgrpmgr_info32 {
+	pid_t		cgmi_pid;
+	caddr32_t	cgmi_rel_agent_path;
+	caddr32_t	cgmi_cgroup_path;
+} cgrpmgr_info32_t;
+
+#define	CG_PSNSIZE	256	/* max size of pseudo file name entries */
+#define	CG_PSDSIZE	16	/* pretend that a dir entry takes 16 bytes */
+
+/*
+ * The order of these entries must be in sync with the cg_ssde_dir array.
+ */
+typedef enum cgrp_ssid {
+	CG_SSID_GENERIC = 1,
+	CG_SSID_NUM		/* last ssid for range checking */
+} cgrp_ssid_t;
+
+typedef enum cgrp_nodetype {
+	CG_CGROUP_DIR = 1,	/* cgroup directory entry */
+	CG_NOTIFY,		/* notify_on_release file */
+	CG_PROCS,		/* cgroup.procs file */
+	CG_REL_AGENT,		/* release_agent file */
+	CG_TASKS,		/* tasks file */
+} cgrp_nodetype_t;
+
+typedef struct cgrp_subsys_dirent {
+	cgrp_nodetype_t cgrp_ssd_type;
+	char		*cgrp_ssd_name;
+} cgrp_subsys_dirent_t;
+
+#define	N_DIRENTS(m)	(cgrp_num_pseudo_ents((m)->cg_ssid) + 2)
+
+/*
+ * A modern systemd-based Linux system typically has 50-60 cgroups so
+ * we size the hash for 2x that number.
+ */
+#define	CGRP_HASH_SZ	128
+#define	CGRP_AGENT_LEN	(MAXPATHLEN + 1)
+
+/*
+ * cgroups per-mount data structure.
+ *
+ * All but the event related fields are protected by cg_contents.
+ * The evnt_list and counter is protected by cg_events.
+ */
+typedef struct cgrp_mnt {
+	struct vfs	*cg_vfsp;	/* filesystem's vfs struct */
+	struct cgrp_node *cg_rootnode;	/* root cgrp_node */
+	char 		*cg_mntpath;	/* name of cgroup mount point */
+	cgrp_ssid_t	cg_ssid;	/* subsystem type */
+	dev_t		cg_dev;		/* unique dev # of mounted `device' */
+	uint_t		cg_gen;		/* node ID source for files */
+	uint_t		cg_grp_gen;	/* ID source for cgroups */
+	kmutex_t	cg_contents;	/* global lock for most fs activity */
+	char		cg_agent[CGRP_AGENT_LEN]; /* release_agent path */
+	/* ptr to zone data for containing zone */
+	lx_zone_data_t	*cg_lxzdata;
+	struct cgrp_node **cg_grp_hash;	/* hash list of cgroups in the fs */
+} cgrp_mnt_t;
+
+/*
+ * cgrp_node is the file system dependent node for cgroups.
+ *
+ * The node is used to represent both directories (a cgroup) and pseudo files
+ * within the directory.
+ *
+ * Members are tagged in the comment to note which type of node they apply to:
+ * A - all
+ * D - dir (i.e. a cgroup)
+ * F - pseudo file
+ */
+
+typedef struct cgrp_node {
+	struct cgrp_node	*cgn_back;	/* A lnked lst of cgrp_nodes */
+	struct cgrp_node	*cgn_forw;	/* A lnked lst of cgrp_nodes */
+	struct cgrp_dirent	*cgn_dir;	/* D dirent list */
+	struct cgrp_node	*cgn_parent;	/* A dir containing this node */
+	struct cgrp_node	*cgn_next;	/* D link in per-mount cgroup */
+						/*   hash table */
+	uint_t			cgn_dirents;	/* D number of dirents */
+	cgrp_nodetype_t		cgn_type;	/* A type for this node */
+	uint_t			cgn_notify;	/* D notify_on_release value */
+	uint_t			cgn_task_cnt;	/* D number of threads in grp */
+	struct vnode 		*cgn_vnode;	/* A vnode for this cgrp_node */
+	uint_t 			cgn_id;		/* D ID number for the cgroup */
+	struct vattr		cgn_attr;	/* A attributes */
+} cgrp_node_t;
+
+/*
+ * File system independent to cgroups conversion macros
+ */
+#define	VFSTOCGM(vfsp)		((cgrp_mnt_t *)(vfsp)->vfs_data)
+#define	VTOCGM(vp)		((cgrp_mnt_t *)(vp)->v_vfsp->vfs_data)
+#define	VTOCGN(vp)		((struct cgrp_node *)(vp)->v_data)
+#define	CGNTOV(cn)		((cn)->cgn_vnode)
+#define	cgnode_hold(cn)		VN_HOLD(CGNTOV(cn))
+#define	cgnode_rele(cn)		VN_RELE(CGNTOV(cn))
+
+/*
+ * Attributes
+ */
+#define	cgn_mask	cgn_attr.va_mask
+#define	cgn_mode	cgn_attr.va_mode
+#define	cgn_uid		cgn_attr.va_uid
+#define	cgn_gid		cgn_attr.va_gid
+#define	cgn_fsid	cgn_attr.va_fsid
+#define	cgn_nodeid	cgn_attr.va_nodeid
+#define	cgn_nlink	cgn_attr.va_nlink
+#define	cgn_size	cgn_attr.va_size
+#define	cgn_atime	cgn_attr.va_atime
+#define	cgn_mtime	cgn_attr.va_mtime
+#define	cgn_ctime	cgn_attr.va_ctime
+#define	cgn_rdev	cgn_attr.va_rdev
+#define	cgn_blksize	cgn_attr.va_blksize
+#define	cgn_nblocks	cgn_attr.va_nblocks
+#define	cgn_seq		cgn_attr.va_seq
+
+/*
+ * cgroup directories are made up of a linked list of cg_dirent structures
+ * hanging off directory cgrp_nodes.  File names are not fixed length,
+ * but are null terminated.
+ */
+typedef struct cgrp_dirent {
+	struct cgrp_node	*cgd_cgrp_node;	/* cg node for this file */
+	struct cgrp_dirent	*cgd_next;	/* next directory entry */
+	struct cgrp_dirent	*cgd_prev;	/* prev directory entry */
+	uint_t			cgd_offset;	/* "offset" of dir entry */
+	uint_t			cgd_hash;	/* a hash of cgd_name */
+	struct cgrp_dirent	*cgd_link;	/* linked via hash table */
+	struct cgrp_node	*cgd_parent;	/* parent, dir we are in */
+	char			*cgd_name;	/* null terminated */
+} cgrp_dirent_t;
+
+enum de_op	{ DE_CREATE, DE_MKDIR, DE_RENAME };	/* direnter ops */
+enum dr_op	{ DR_REMOVE, DR_RMDIR, DR_RENAME };	/* dirremove ops */
+
+extern struct vnodeops *cgrp_vnodeops;
+
+int cgrp_dirdelete(cgrp_node_t *, cgrp_node_t *, char *, enum dr_op, cred_t *);
+int cgrp_direnter(cgrp_mnt_t *, cgrp_node_t *, char *, enum de_op,
+    cgrp_node_t *, struct vattr *, cgrp_node_t **, cred_t *);
+void cgrp_dirinit(cgrp_node_t *, cgrp_node_t *, cred_t *);
+int cgrp_dirlookup(cgrp_node_t *, char *, cgrp_node_t **, cred_t *);
+void cgrp_dirtrunc(cgrp_node_t *);
+void cgrp_node_init(cgrp_mnt_t *, cgrp_node_t *, vattr_t *, cred_t *);
+int cgrp_taccess(void *, int, cred_t *);
+ino_t cgrp_inode(cgrp_nodetype_t, unsigned int);
+int cgrp_num_pseudo_ents(cgrp_ssid_t);
+cgrp_node_t *cgrp_cg_hash_lookup(cgrp_mnt_t *, uint_t);
+void cgrp_rel_agent_event(cgrp_mnt_t *, cgrp_node_t *, boolean_t);
+
+#endif /* KERNEL */
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif /* _LXCGRPS_H */
--- /dev/null	Wed Oct  5 00:32:04 2016
+++ new/usr/src/uts/common/brand/lx/cgroups/cgrps_node.c	Wed Oct  5 00:32:04 2016
@@ -0,0 +1,1014 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/vfs.h>
+#include <sys/vnode.h>
+#include <sys/errno.h>
+#include <sys/cmn_err.h>
+#include <sys/cred.h>
+#include <sys/stat.h>
+#include <sys/mode.h>
+#include <sys/policy.h>
+#include <sys/sdt.h>
+
+#include "cgrps.h"
+
+static int cgrp_dirmakecgnode(cgrp_node_t *, cgrp_mnt_t *, struct vattr *,
+	enum de_op, cgrp_node_t **, struct cred *);
+static int cgrp_diraddentry(cgrp_node_t *, cgrp_node_t *, char *);
+
+static cgrp_subsys_dirent_t cgrp_generic_dir[] = {
+	{ CG_PROCS,		"cgroup.procs" },
+	{ CG_NOTIFY,		"notify_on_release" },
+	{ CG_TASKS,		"tasks" }
+};
+
+typedef struct cgrp_ssde {
+	cgrp_subsys_dirent_t	*cg_ssde_files;
+	int			cg_ssde_nfiles;
+} cgrp_ssde_t;
+
+#define	CGDIRLISTSZ(l)		(sizeof (l) / sizeof ((l)[0]))
+
+/*
+ * Note, these entries must be in the same order as the cgrp_ssid_t entries.
+ */
+static cgrp_ssde_t cg_ssde_dir[] = {
+	/* subsystems start at 1 */
+	{NULL, 0},
+
+	/* CG_SSID_GENERIC */
+	{cgrp_generic_dir, CGDIRLISTSZ(cgrp_generic_dir)},
+};
+
+
+#define	CG_HASH_SIZE	8192		/* must be power of 2 */
+#define	CG_MUTEX_SIZE	64
+
+static cgrp_dirent_t	*cg_hashtable[CG_HASH_SIZE];
+static kmutex_t		 cg_hashmutex[CG_MUTEX_SIZE];
+
+#define	CG_HASH_INDEX(a)	((a) & (CG_HASH_SIZE-1))
+#define	CG_MUTEX_INDEX(a)	((a) & (CG_MUTEX_SIZE-1))
+
+#define	CG_HASH(cp, name, hash)				\
+	{							\
+		char Xc, *Xcp;					\
+		hash = (uint_t)(uintptr_t)(cp) >> 8;		\
+		for (Xcp = (name); (Xc = *Xcp) != 0; Xcp++)	\
+			hash = (hash << 4) + hash + (uint_t)Xc;	\
+	}
+
+#define	MODESHIFT	3
+
+typedef enum cgrp_nodehold {
+	NOHOLD,
+	HOLD
+} cgrp_nodehold_t;
+
+void
+cgrp_hash_init(void)
+{
+	int i;
+
+	for (i = 0; i < CG_MUTEX_SIZE; i++)
+		mutex_init(&cg_hashmutex[i], NULL, MUTEX_DEFAULT, NULL);
+}
+
+static void
+cgrp_hash_in(cgrp_dirent_t *c)
+{
+	uint_t		hash;
+	cgrp_dirent_t	**prevpp;
+	kmutex_t	*cg_hmtx;
+
+	CG_HASH(c->cgd_parent, c->cgd_name, hash);
+	c->cgd_hash = hash;
+	prevpp = &cg_hashtable[CG_HASH_INDEX(hash)];
+	cg_hmtx = &cg_hashmutex[CG_MUTEX_INDEX(hash)];
+	mutex_enter(cg_hmtx);
+	c->cgd_link = *prevpp;
+	*prevpp = c;
+	mutex_exit(cg_hmtx);
+}
+
+static void
+cgrp_hash_out(cgrp_dirent_t *c)
+{
+	uint_t		hash;
+	cgrp_dirent_t	**prevpp;
+	kmutex_t	*cg_hmtx;
+
+	hash = c->cgd_hash;
+	prevpp = &cg_hashtable[CG_HASH_INDEX(hash)];
+	cg_hmtx = &cg_hashmutex[CG_MUTEX_INDEX(hash)];
+	mutex_enter(cg_hmtx);
+	while (*prevpp != c)
+		prevpp = &(*prevpp)->cgd_link;
+	*prevpp = c->cgd_link;
+	mutex_exit(cg_hmtx);
+}
+
+static cgrp_dirent_t *
+cgrp_hash_lookup(char *name, cgrp_node_t *parent, cgrp_nodehold_t hold,
+    cgrp_node_t **found)
+{
+	cgrp_dirent_t	*l;
+	uint_t		hash;
+	kmutex_t	*cg_hmtx;
+	cgrp_node_t	*cnp;
+
+	CG_HASH(parent, name, hash);
+	cg_hmtx = &cg_hashmutex[CG_MUTEX_INDEX(hash)];
+	mutex_enter(cg_hmtx);
+	l = cg_hashtable[CG_HASH_INDEX(hash)];
+	while (l) {
+		if ((l->cgd_hash == hash) &&
+		    (l->cgd_parent == parent) &&
+		    (strcmp(l->cgd_name, name) == 0)) {
+			/*
+			 * We need to make sure that the cgrp_node that
+			 * we put a hold on is the same one that we pass back.
+			 * Hence, temporary variable cnp is necessary.
+			 */
+			cnp = l->cgd_cgrp_node;
+			if (hold == HOLD) {
+				ASSERT(cnp);
+				cgnode_hold(cnp);
+			}
+			if (found)
+				*found = cnp;
+			mutex_exit(cg_hmtx);
+			return (l);
+		} else {
+			l = l->cgd_link;
+		}
+	}
+	mutex_exit(cg_hmtx);
+	return (NULL);
+}
+
+/*
+ * The following functions maintain the per-mount cgroup hash table.
+ */
+static void
+cgrp_cg_hash_insert(cgrp_mnt_t *cgm, cgrp_node_t *cn)
+{
+	uint_t cgid;
+	int hsh;
+
+	ASSERT(MUTEX_HELD(&cgm->cg_contents));
+
+	cgid = cn->cgn_id;
+	hsh = cgid % CGRP_HASH_SZ;
+
+	cn->cgn_next = cgm->cg_grp_hash[hsh];
+	cgm->cg_grp_hash[hsh] = cn;
+}
+
+static void
+cgrp_cg_hash_remove(cgrp_mnt_t *cgm, cgrp_node_t *cn)
+{
+	uint_t cgid;
+	int hsh;
+	cgrp_node_t *np = NULL, *curp, *prevp = NULL;
+
+	ASSERT(MUTEX_HELD(&cgm->cg_contents));
+
+	cgid = cn->cgn_id;
+	hsh = cgid % CGRP_HASH_SZ;
+
+	for (curp = cgm->cg_grp_hash[hsh]; curp != NULL;
+	    curp = curp->cgn_next) {
+		if (curp->cgn_id == cgid) {
+			if (prevp == NULL) {
+				cgm->cg_grp_hash[hsh] = curp->cgn_next;
+			} else {
+				prevp->cgn_next = curp->cgn_next;
+			}
+			np = curp;
+			np->cgn_next = NULL;
+			break;
+		}
+
+		prevp = curp;
+	}
+
+	ASSERT(np != NULL);
+	ASSERT(np->cgn_task_cnt == 0);
+}
+
+/*
+ * Count up the number of threads already running in the zone and initialize the
+ * first cgroup's task counter.
+ *
+ * We have to look at all of the processes to find applicable ones.
+ */
+static void
+cgrp_cg_hash_init(cgrp_node_t *cn)
+{
+	int i;
+	int cnt = 0;
+	zoneid_t zoneid = curproc->p_zone->zone_id;
+	pid_t schedpid = curproc->p_zone->zone_zsched->p_pid;
+
+	/* Scan all of the process entries */
+	mutex_enter(&pidlock);
+	for (i = 1; i < v.v_proc; i++) {
+		proc_t *p;
+
+		/*
+		 * Skip indices for which there is no pid_entry, PIDs for
+		 * which there is no corresponding process, system processes,
+		 * a PID of 0, the pid for our zsched process,  anything the
+		 * security policy doesn't allow us to look at, its not an
+		 * lx-branded process and processes that are not in the zone.
+		 */
+		if ((p = pid_entry(i)) == NULL ||
+		    p->p_stat == SIDL ||
+		    (p->p_flag & SSYS) != 0 ||
+		    p->p_pid == 0 ||
+		    p->p_pid == schedpid ||
+		    secpolicy_basic_procinfo(CRED(), p, curproc) != 0 ||
+		    p->p_zone->zone_id != zoneid) {
+			continue;
+		}
+
+		mutex_enter(&p->p_lock);
+		if (p->p_brand != &lx_brand) {
+			mutex_exit(&p->p_lock);
+			continue;
+		}
+		cnt += p->p_lwpcnt;
+		mutex_exit(&p->p_lock);
+	}
+
+	/*
+	 * There should be at least the init process with 1 thread in the zone
+	 */
+	ASSERT(cnt > 0);
+	cn->cgn_task_cnt = cnt;
+
+	DTRACE_PROBE2(cgrp__grp__init, void *, cn, int, cnt);
+
+	mutex_exit(&pidlock);
+}
+
+cgrp_node_t *
+cgrp_cg_hash_lookup(cgrp_mnt_t *cgm, uint_t cgid)
+{
+	int hsh = cgid % CGRP_HASH_SZ;
+	cgrp_node_t *curp;
+
+	ASSERT(MUTEX_HELD(&cgm->cg_contents));
+
+	for (curp = cgm->cg_grp_hash[hsh]; curp != NULL;
+	    curp = curp->cgn_next) {
+		if (curp->cgn_id == cgid) {
+			return (curp);
+		}
+	}
+
+	return (NULL);
+}
+
+/*
+ * Calculate an inode number
+ *
+ * This takes various bits of info and munges them to give the inode number for
+ * a cgrp pseudo file node.
+ */
+ino_t
+cgrp_inode(cgrp_nodetype_t type, unsigned int cgrpid)
+{
+	/*
+	 * cgroup inode format:
+	 * 00000000AABBBBBB
+	 *
+	 * AA		- node type (from subsystem list)
+	 * BBBBBB	- id of the cgroup
+	 */
+
+	return ((ino_t)(type << 24) | (cgrpid & 0xffffff));
+}
+
+/*
+ * Return the number of pseudo file entries in a cgroup directory for the
+ * given subsystem.
+ */
+int
+cgrp_num_pseudo_ents(cgrp_ssid_t ssid)
+{
+	cgrp_ssde_t *ssdp = &cg_ssde_dir[ssid];
+
+	return (ssdp->cg_ssde_nfiles);
+}
+
+int
+cgrp_taccess(void *vcp, int mode, cred_t *cred)
+{
+	cgrp_node_t *cn = vcp;
+	int shift = 0;
+	/*
+	 * Check access based on owner, group and public perms in cgrp_node.
+	 */
+	if (crgetuid(cred) != cn->cgn_uid) {
+		shift += MODESHIFT;
+		if (groupmember(cn->cgn_gid, cred) == 0)
+			shift += MODESHIFT;
+	}
+
+	return (secpolicy_vnode_access2(cred, CGNTOV(cn), cn->cgn_uid,
+	    cn->cgn_mode << shift, mode));
+}
+
+/*
+ * Search directory 'parent' for entry 'name'.
+ *
+ * 0 is returned on success and *foundcp points
+ * to the found cgrp_node with its vnode held.
+ */
+int
+cgrp_dirlookup(cgrp_node_t *parent, char *name, cgrp_node_t **foundcp,
+    cred_t *cred)
+{
+	int error;
+
+	ASSERT(MUTEX_HELD(&VTOCGM(parent->cgn_vnode)->cg_contents));
+	*foundcp = NULL;
+	if (parent->cgn_type != CG_CGROUP_DIR)
+		return (ENOTDIR);
+
+	if ((error = cgrp_taccess(parent, VEXEC, cred)))
+		return (error);
+
+	if (*name == '\0') {
+		cgnode_hold(parent);
+		*foundcp = parent;
+		return (0);
+	}
+
+	/*
+	 * Search the directory for the matching name
+	 * We need the lock protecting the cgn_dir list
+	 * so that it doesn't change out from underneath us.
+	 * cgrp_hash_lookup() will pass back the cgrp_node
+	 * with a hold on it.
+	 */
+
+	if (cgrp_hash_lookup(name, parent, HOLD, foundcp) != NULL) {
+		ASSERT(*foundcp);
+		return (0);
+	}
+
+	return (ENOENT);
+}
+
+/*
+ * Enter a directory entry for 'name' and 'cp' into directory 'dir'
+ *
+ * Returns 0 on success.
+ */
+int
+cgrp_direnter(
+	cgrp_mnt_t	*cgm,
+	cgrp_node_t	*dir,		/* target directory to make entry in */
+	char		*name,		/* name of entry */
+	enum de_op	op,		/* entry operation */
+	cgrp_node_t	*cn,		/* existing cgrp_node, if rename */
+	struct vattr	*va,
+	cgrp_node_t	**cnp,		/* return cgrp_node, if create/mkdir */
+	cred_t		*cred)
+{
+	cgrp_dirent_t *cdp;
+	cgrp_node_t *found = NULL;
+	int error = 0;
+	char *s;
+
+	ASSERT(MUTEX_HELD(&cgm->cg_contents));
+	ASSERT(dir->cgn_type == CG_CGROUP_DIR);
+
+	/*
+	 * Don't allow '/' characters in pathname component,
+	 */
+	for (s = name; *s; s++)
+		if (*s == '/')
+			return (EACCES);
+
+	if (name[0] == '\0')
+		panic("cgrp_direnter: NULL name");
+
+	/*
+	 * For rename lock the source entry and check the link count
+	 * to see if it has been removed while it was unlocked.
+	 * Remember that we can only rename within the same directory.
+	 */
+	if (op == DE_RENAME) {
+		if (cn->cgn_nlink == 0) {
+			return (ENOENT);
+		}
+
+		if (cn->cgn_nlink == MAXLINK) {
+			return (EMLINK);
+		}
+		cn->cgn_nlink++;
+		gethrestime(&cn->cgn_ctime);
+	}
+
+	/*
+	 * This might be a "dangling detached directory".
+	 * it could have been removed, but a reference
+	 * to it kept in u_cwd.  don't bother searching
+	 * it, and with any luck the user will get tired
+	 * of dealing with us and cd to some absolute
+	 * pathway.  *sigh*, thus in ufs, too.
+	 */
+	if (dir->cgn_nlink == 0) {
+		error = ENOENT;
+		goto out;
+	}
+
+	/*
+	 * Search for the entry. In all cases it is an error if it exists.
+	 */
+	cdp = cgrp_hash_lookup(name, dir, HOLD, &found);
+
+	if (cdp) {
+		ASSERT(found != NULL);
+		error = EEXIST;
+		mutex_exit(&cgm->cg_contents);
+		cgnode_rele(found);
+		mutex_enter(&cgm->cg_contents);
+	} else {
+
+		/*
+		 * The entry does not exist. Check write permission in
+		 * directory to see if entry can be created.
+		 */
+		if ((error = cgrp_taccess(dir, VWRITE, cred)) != 0)
+			goto out;
+		if (op == DE_CREATE || op == DE_MKDIR) {
+			/*
+			 * Make new cgrp_node and directory entry as required.
+			 */
+			error = cgrp_dirmakecgnode(dir, cgm, va, op, &cn, cred);
+			if (error)
+				goto out;
+
+			if (op == DE_MKDIR) {
+				/*
+				 * inherit notify_on_release value from parent
+				 */
+				cn->cgn_notify = dir->cgn_notify;
+			}
+		}
+
+		error = cgrp_diraddentry(dir, cn, name);
+		if (error != 0) {
+			if (op == DE_CREATE || op == DE_MKDIR) {
+				/*
+				 * Unmake the inode we just made.
+				 */
+				if ((cn->cgn_type) == CG_CGROUP_DIR) {
+					ASSERT(cdp == NULL);
+					/*
+					 * cleanup allocs made by cgrp_dirinit
+					 */
+					cgrp_dirtrunc(cn);
+				}
+				cn->cgn_nlink = 0;
+				gethrestime(&cn->cgn_ctime);
+				mutex_exit(&cgm->cg_contents);
+				cgnode_rele(cn);
+				mutex_enter(&cgm->cg_contents);
+				cn = NULL;
+			}
+		} else if (cnp) {
+			*cnp = cn;
+		} else if (op == DE_CREATE || op == DE_MKDIR) {
+			mutex_exit(&cgm->cg_contents);
+			cgnode_rele(cn);
+			mutex_enter(&cgm->cg_contents);
+		}
+	}
+
+out:
+	if (error && op == DE_RENAME) {
+		/* Undo bumped link count. */
+		cn->cgn_nlink--;
+		gethrestime(&cn->cgn_ctime);
+	}
+	return (error);
+}
+
+/*
+ * Delete entry cn of name "nm" from parent dir. This is used to both remove
+ * a cgroup directory and to remove the pseudo file nodes within the cgroup
+ * directory (by recursively calling itself). It frees the dir entry space
+ * and decrements link count on cgrp_node(s).
+ *
+ * Return 0 on success.
+ */
+int
+cgrp_dirdelete(cgrp_node_t *dir, cgrp_node_t *cn, char *nm, enum dr_op op,
+    cred_t *cred)
+{
+	cgrp_mnt_t *cgm = VTOCGM(cn->cgn_vnode);
+	cgrp_dirent_t *cndp;
+	int error;
+	size_t namelen;
+	cgrp_node_t *cnnp;
+	timestruc_t now;
+
+	ASSERT(MUTEX_HELD(&cgm->cg_contents));
+
+	if (nm[0] == '\0')
+		panic("cgrp_dirdelete: empty name for 0x%p", (void *)cn);
+
+	/*
+	 * return error when removing . and ..
+	 */
+	if (nm[0] == '.') {
+		if (nm[1] == '\0')
+			return (EINVAL);
+		if (nm[1] == '.' && nm[2] == '\0')
+			return (EEXIST); /* thus in ufs */
+	}
+
+	if ((error = cgrp_taccess(dir, VEXEC|VWRITE, cred)) != 0)
+		return (error);
+
+	if (dir->cgn_dir == NULL)
+		return (ENOENT);
+
+	if (op == DR_RMDIR) {
+		/*
+		 * This is the top-level removal of a cgroup dir. Start by
+		 * removing the fixed pseudo file entries from the dir. We do
+		 * this by recursively calling back into this function with
+		 * a different op code. The caller of this function has
+		 * already verified that it is safe to remove this directory.
+		 */
+		cgrp_dirent_t *cdp;
+
+		ASSERT(cn->cgn_type == CG_CGROUP_DIR);
+
+		cdp = cn->cgn_dir;
+		while (cdp) {
+			cgrp_node_t *pseudo_node;
+			cgrp_dirent_t *nextp;
+
+			if (strcmp(cdp->cgd_name, ".") == 0 ||
+			    strcmp(cdp->cgd_name, "..") == 0) {
+				cdp = cdp->cgd_next;
+				continue;
+			}
+
+			pseudo_node = cdp->cgd_cgrp_node;
+			nextp = cdp->cgd_next;
+
+			cgnode_hold(pseudo_node);
+			error = cgrp_dirdelete(cn, pseudo_node,
+			    cdp->cgd_name, DR_REMOVE, cred);
+			mutex_exit(&cgm->cg_contents);
+			cgnode_rele(pseudo_node);
+			mutex_enter(&cgm->cg_contents);
+
+			cdp = nextp;
+		}
+
+		cgrp_cg_hash_remove(cgm, cn);
+	}
+
+	cndp = cgrp_hash_lookup(nm, dir, NOHOLD, &cnnp);
+	VERIFY(cndp != NULL);
+	VERIFY(cn == cnnp);
+
+	cgrp_hash_out(cndp);
+
+	/* Take cndp out of the directory list. */
+	ASSERT(cndp->cgd_next != cndp);
+	ASSERT(cndp->cgd_prev != cndp);
+	if (cndp->cgd_prev) {
+		cndp->cgd_prev->cgd_next = cndp->cgd_next;
+	}
+	if (cndp->cgd_next) {
+		cndp->cgd_next->cgd_prev = cndp->cgd_prev;
+	}
+
+	/*
+	 * If the roving slot pointer happens to match cndp,
+	 * point it at the previous dirent.
+	 */
+	if (dir->cgn_dir->cgd_prev == cndp) {
+		dir->cgn_dir->cgd_prev = cndp->cgd_prev;
+	}
+	ASSERT(cndp->cgd_next != cndp);
+	ASSERT(cndp->cgd_prev != cndp);
+
+	/* cndp points to the correct directory entry */
+	namelen = strlen(cndp->cgd_name) + 1;
+
+	kmem_free(cndp, sizeof (cgrp_dirent_t) + namelen);
+	dir->cgn_size -= (sizeof (cgrp_dirent_t) + namelen);
+	dir->cgn_dirents--;
+
+	gethrestime(&now);
+	dir->cgn_mtime = now;
+	dir->cgn_ctime = now;
+	cn->cgn_ctime = now;
+
+	ASSERT(cn->cgn_nlink > 0);
+	cn->cgn_nlink--;
+	if (op == DR_RMDIR && cn->cgn_type == CG_CGROUP_DIR) {
+		cgrp_dirtrunc(cn);
+		ASSERT(cn->cgn_nlink == 0);
+	}
+	return (0);
+}
+
+/*
+ * Initialize a cgrp_node and add it to file list under mount point.
+ */
+void
+cgrp_node_init(cgrp_mnt_t *cgm, cgrp_node_t *cn, vattr_t *vap, cred_t *cred)
+{
+	struct vnode *vp;
+	timestruc_t now;
+
+	ASSERT(MUTEX_HELD(&cgm->cg_contents));
+	ASSERT(vap != NULL);
+
+	cn->cgn_mode = MAKEIMODE(vap->va_type, vap->va_mode);
+	cn->cgn_mask = 0;
+	cn->cgn_attr.va_type = vap->va_type;
+	cn->cgn_nlink = 1;
+	cn->cgn_size = 0;
+
+	if (cred == NULL) {
+		cn->cgn_uid = vap->va_uid;
+		cn->cgn_gid = vap->va_gid;
+	} else {
+		cn->cgn_uid = crgetuid(cred);
+		cn->cgn_gid = crgetgid(cred);
+	}
+
+	cn->cgn_fsid = cgm->cg_dev;
+	cn->cgn_rdev = vap->va_rdev;
+	cn->cgn_blksize = PAGESIZE;
+	cn->cgn_nblocks = 0;
+	gethrestime(&now);
+	cn->cgn_atime = now;
+	cn->cgn_mtime = now;
+	cn->cgn_ctime = now;
+	cn->cgn_seq = 0;
+	cn->cgn_dir = NULL;
+
+	cn->cgn_vnode = vn_alloc(KM_SLEEP);
+	vp = CGNTOV(cn);
+	vn_setops(vp, cgrp_vnodeops);
+	vp->v_vfsp = cgm->cg_vfsp;
+	vp->v_type = vap->va_type;
+	vp->v_rdev = vap->va_rdev;
+	vp->v_data = (caddr_t)cn;
+
+	cn->cgn_nodeid = cgm->cg_gen++;
+
+	/*
+	 * Add new cgrp_node to end of linked list of cgrp_nodes for this
+	 * cgroup fs. Root directory is handled specially in cgrp_mount.
+	 */
+	if (cgm->cg_rootnode != (cgrp_node_t *)NULL) {
+		cn->cgn_forw = NULL;
+		cn->cgn_back = cgm->cg_rootnode->cgn_back;
+		cn->cgn_back->cgn_forw = cgm->cg_rootnode->cgn_back = cn;
+	}
+	vn_exists(vp);
+}
+
+void
+cgrp_addnode(cgrp_mnt_t *cgm, cgrp_node_t *dir, char *name,
+    cgrp_nodetype_t type, struct vattr *nattr, cred_t *cr)
+{
+	cgrp_node_t *ncn;
+
+	ASSERT(MUTEX_HELD(&cgm->cg_contents));
+
+	VERIFY0(cgrp_direnter(cgm, dir, name, DE_CREATE, (cgrp_node_t *)NULL,
+	    nattr, &ncn, cr));
+
+	/*
+	 * Fix the inode and assign the pseudo file type to be correct.
+	 */
+	ncn->cgn_nodeid = cgrp_inode(type, dir->cgn_nodeid);
+	ncn->cgn_type = type;
+
+	/*
+	 * Since we're creating these entries here and not via the
+	 * normal VOP_CREATE code path, we need to do the rele to drop
+	 * our hold. This will leave the vnode v_count at 0 when we
+	 * come out of cgrp_inactive but we won't reclaim the vnode
+	 * there since the cgn_nlink value will still be 1.
+	 */
+	mutex_exit(&cgm->cg_contents);
+	cgnode_rele(ncn);
+	mutex_enter(&cgm->cg_contents);
+}
+
+/*
+ * cgrp_dirinit is used internally to initialize a directory (dir)
+ * with '.' and '..' entries without checking permissions and locking
+ * It also creates the entries for the pseudo file nodes that reside in the
+ * directory.
+ */
+void
+cgrp_dirinit(cgrp_node_t *parent, cgrp_node_t *dir, cred_t *cr)
+{
+	cgrp_dirent_t *dot, *dotdot;
+	timestruc_t now;
+	cgrp_mnt_t *cgm = VTOCGM(dir->cgn_vnode);
+	cgrp_ssde_t *ssdp;
+	cgrp_subsys_dirent_t *pseudo_files;
+	struct vattr nattr;
+	int i;
+
+	ASSERT(MUTEX_HELD(&cgm->cg_contents));
+	ASSERT(dir->cgn_type == CG_CGROUP_DIR);
+
+	ASSERT(cgm->cg_ssid > 0 && cgm->cg_ssid < CG_SSID_NUM);
+	ssdp = &cg_ssde_dir[cgm->cg_ssid];
+
+	/*
+	 * If this is the top-level cgroup created by the mount then we need to
+	 * count up the number of procs and tasks already running in the zone.
+	 */
+
+	/*
+	 * Set the cgroup ID for this cgrp_node by using a counter on each
+	 * mount.
+	 */
+	dir->cgn_id = cgm->cg_grp_gen++;
+	cgrp_cg_hash_insert(cgm, dir);
+	/* Initialise the first cgroup if this is top-level group */
+	if (parent == dir)
+		cgrp_cg_hash_init(dir);
+
+	/*
+	 * Initialize the entries
+	 */
+	dot = kmem_zalloc(sizeof (cgrp_dirent_t) + 2, KM_SLEEP);
+	dot->cgd_cgrp_node = dir;
+	dot->cgd_offset = 0;
+	dot->cgd_name = (char *)dot + sizeof (cgrp_dirent_t);
+	dot->cgd_name[0] = '.';
+	dot->cgd_parent = dir;
+	cgrp_hash_in(dot);
+
+	dotdot = kmem_zalloc(sizeof (cgrp_dirent_t) + 3, KM_SLEEP);
+	dotdot->cgd_cgrp_node = parent;
+	dotdot->cgd_offset = 1;
+	dotdot->cgd_name = (char *)dotdot + sizeof (cgrp_dirent_t);
+	dotdot->cgd_name[0] = '.';
+	dotdot->cgd_name[1] = '.';
+	dotdot->cgd_parent = dir;
+	cgrp_hash_in(dotdot);
+
+	/*
+	 * Initialize directory entry list.
+	 */
+	dot->cgd_next = dotdot;
+	dot->cgd_prev = dotdot;	/* dot's cgd_prev holds roving slot pointer */
+	dotdot->cgd_next = NULL;
+	dotdot->cgd_prev = dot;
+
+	gethrestime(&now);
+	dir->cgn_mtime = now;
+	dir->cgn_ctime = now;
+
+	parent->cgn_nlink++;
+	parent->cgn_ctime = now;
+
+	dir->cgn_dir = dot;
+	dir->cgn_size = 2 * sizeof (cgrp_dirent_t) + 5;	/* dot and dotdot */
+	dir->cgn_dirents = 2;
+	dir->cgn_nlink = 2;
+
+	bzero(&nattr, sizeof (struct vattr));
+	nattr.va_mode = (mode_t)(0644);
+	nattr.va_type = VREG;
+	nattr.va_rdev = 0;
+
+	/*
+	 * If this is the top-level dir in the file system then it always
+	 * has a release_agent pseudo file. Only the top-level dir has this
+	 * file.
+	 */
+	if (parent == dir) {
+		cgrp_addnode(cgm, dir, "release_agent", CG_REL_AGENT, &nattr,
+		    cr);
+	}
+
+	pseudo_files = ssdp->cg_ssde_files;
+	for (i = 0; i < ssdp->cg_ssde_nfiles; i++) {
+		cgrp_addnode(cgm, dir, pseudo_files[i].cgrp_ssd_name,
+		    pseudo_files[i].cgrp_ssd_type, &nattr, cr);
+	}
+}
+
+/*
+ * cgrp_dirtrunc is called to remove all directory entries under this directory.
+ */
+void
+cgrp_dirtrunc(cgrp_node_t *dir)
+{
+	cgrp_dirent_t *cgdp;
+	timestruc_t now;
+
+	ASSERT(MUTEX_HELD(&VTOCGM(dir->cgn_vnode)->cg_contents));
+	ASSERT(dir->cgn_type == CG_CGROUP_DIR);
+
+	for (cgdp = dir->cgn_dir; cgdp; cgdp = dir->cgn_dir) {
+		size_t namelen;
+		cgrp_node_t *cn;
+
+		ASSERT(cgdp->cgd_next != cgdp);
+		ASSERT(cgdp->cgd_prev != cgdp);
+		ASSERT(cgdp->cgd_cgrp_node);
+
+		dir->cgn_dir = cgdp->cgd_next;
+		namelen = strlen(cgdp->cgd_name) + 1;
+
+		/*
+		 * Adjust the link counts to account for this directory entry
+		 * removal. We do hold/rele operations to free up these nodes.
+		 */
+		cn = cgdp->cgd_cgrp_node;
+		ASSERT(cn->cgn_nlink > 0);
+		cn->cgn_nlink--;
+
+		cgrp_hash_out(cgdp);
+		kmem_free(cgdp, sizeof (cgrp_dirent_t) + namelen);
+		dir->cgn_size -= (sizeof (cgrp_dirent_t) + namelen);
+		dir->cgn_dirents--;
+	}
+
+	gethrestime(&now);
+	dir->cgn_mtime = now;
+	dir->cgn_ctime = now;
+
+	ASSERT(dir->cgn_dir == NULL);
+	ASSERT(dir->cgn_size == 0);
+	ASSERT(dir->cgn_dirents == 0);
+}
+
+static int
+cgrp_diraddentry(cgrp_node_t *dir, cgrp_node_t *cn, char *name)
+{
+	cgrp_dirent_t *cdp, *cpdp;
+	size_t		namelen, alloc_size;
+	timestruc_t	now;
+
+	/*
+	 * Make sure the parent directory wasn't removed from
+	 * underneath the caller.
+	 */
+	if (dir->cgn_dir == NULL)
+		return (ENOENT);
+
+	/* Check that everything is on the same filesystem. */
+	if (cn->cgn_vnode->v_vfsp != dir->cgn_vnode->v_vfsp)
+		return (EXDEV);
+
+	/* Allocate and initialize directory entry */
+	namelen = strlen(name) + 1;
+	alloc_size = namelen + sizeof (cgrp_dirent_t);
+	cdp = kmem_zalloc(alloc_size, KM_NOSLEEP | KM_NORMALPRI);
+	if (cdp == NULL)
+		return (ENOSPC);
+
+	cn->cgn_parent = dir;
+
+	dir->cgn_size += alloc_size;
+	dir->cgn_dirents++;
+	cdp->cgd_cgrp_node = cn;
+	cdp->cgd_parent = dir;
+
+	/* The directory entry and its name were allocated sequentially. */
+	cdp->cgd_name = (char *)cdp + sizeof (cgrp_dirent_t);
+	(void) strcpy(cdp->cgd_name, name);
+
+	cgrp_hash_in(cdp);
+
+	/*
+	 * Some utilities expect the size of a directory to remain
+	 * somewhat static.  For example, a routine which removes
+	 * subdirectories between calls to readdir(); the size of the
+	 * directory changes from underneath it and so the real
+	 * directory offset in bytes is invalid.  To circumvent
+	 * this problem, we initialize a directory entry with an
+	 * phony offset, and use this offset to determine end of
+	 * file in cgrp_readdir.
+	 */
+	cpdp = dir->cgn_dir->cgd_prev;
+	/*
+	 * Install at first empty "slot" in directory list.
+	 */
+	while (cpdp->cgd_next != NULL && (cpdp->cgd_next->cgd_offset -
+	    cpdp->cgd_offset) <= 1) {
+		ASSERT(cpdp->cgd_next != cpdp);
+		ASSERT(cpdp->cgd_prev != cpdp);
+		ASSERT(cpdp->cgd_next->cgd_offset > cpdp->cgd_offset);
+		cpdp = cpdp->cgd_next;
+	}
+	cdp->cgd_offset = cpdp->cgd_offset + 1;
+
+	/*
+	 * If we're at the end of the dirent list and the offset (which
+	 * is necessarily the largest offset in this directory) is more
+	 * than twice the number of dirents, that means the directory is
+	 * 50% holes.  At this point we reset the slot pointer back to
+	 * the beginning of the directory so we start using the holes.
+	 * The idea is that if there are N dirents, there must also be
+	 * N holes, so we can satisfy the next N creates by walking at
+	 * most 2N entries; thus the average cost of a create is constant.
+	 * Note that we use the first dirent's cgd_prev as the roving
+	 * slot pointer; it's ugly, but it saves a word in every dirent.
+	 */
+	if (cpdp->cgd_next == NULL && cpdp->cgd_offset > 2 * dir->cgn_dirents)
+		dir->cgn_dir->cgd_prev = dir->cgn_dir->cgd_next;
+	else
+		dir->cgn_dir->cgd_prev = cdp;
+
+	ASSERT(cpdp->cgd_next != cpdp);
+	ASSERT(cpdp->cgd_prev != cpdp);
+
+	cdp->cgd_next = cpdp->cgd_next;
+	if (cdp->cgd_next) {
+		cdp->cgd_next->cgd_prev = cdp;
+	}
+	cdp->cgd_prev = cpdp;
+	cpdp->cgd_next = cdp;
+
+	ASSERT(cdp->cgd_next != cdp);
+	ASSERT(cdp->cgd_prev != cdp);
+	ASSERT(cpdp->cgd_next != cpdp);
+	ASSERT(cpdp->cgd_prev != cpdp);
+
+	gethrestime(&now);
+	dir->cgn_mtime = now;
+	dir->cgn_ctime = now;
+
+	return (0);
+}
+
+static int
+cgrp_dirmakecgnode(cgrp_node_t *dir, cgrp_mnt_t *cgm, struct vattr *va,
+    enum de_op op, cgrp_node_t **newnode, struct cred *cred)
+{
+	cgrp_node_t *cn;
+
+	ASSERT(MUTEX_HELD(&cgm->cg_contents));
+	ASSERT(va != NULL);
+
+	if (((va->va_mask & AT_ATIME) && TIMESPEC_OVERFLOW(&va->va_atime)) ||
+	    ((va->va_mask & AT_MTIME) && TIMESPEC_OVERFLOW(&va->va_mtime)))
+		return (EOVERFLOW);
+
+	cn = kmem_zalloc(sizeof (cgrp_node_t), KM_SLEEP);
+	cgrp_node_init(cgm, cn, va, cred);
+
+	cn->cgn_vnode->v_rdev = cn->cgn_rdev = NODEV;
+	cn->cgn_vnode->v_type = va->va_type;
+	cn->cgn_uid = crgetuid(cred);
+	cn->cgn_gid = crgetgid(cred);
+
+	if (va->va_mask & AT_ATIME)
+		cn->cgn_atime = va->va_atime;
+	if (va->va_mask & AT_MTIME)
+		cn->cgn_mtime = va->va_mtime;
+
+	if (op == DE_MKDIR) {
+		cn->cgn_type = CG_CGROUP_DIR;
+		cgrp_dirinit(dir, cn, cred);
+	}
+
+	*newnode = cn;
+	return (0);
+}
--- /dev/null	Wed Oct  5 00:32:04 2016
+++ new/usr/src/uts/common/brand/lx/cgroups/cgrps_vfsops.c	Wed Oct  5 00:32:04 2016
@@ -0,0 +1,1071 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+/*
+ * The cgroup file system implements a subset of the Linux cgroup functionality
+ * for use by lx-branded zones. On Linux, cgroups are a generic process grouping
+ * mechanism which is used to apply various behaviors to the processes within
+ * the group, although it's primary purpose is for resource management.
+ *
+ * In Linux, the cgroup file system provides two pieces of functionality:
+ * 1) A per-mount set of cgroups arranged in a tree, such that every task in
+ *    the system is in one, and only one, of the cgroups in the tree.
+ * 2) A set of subsystems; each subsystem has subsystem-specific state and
+ *    behavior and is associated with a cgroup mount. This provides a way to
+ *    apply arbitrary functionality (but generally resource management related)
+ *    to the processes associated with the nodes in the tree at that mount
+ *    point.
+ *
+ * For example, it is common to see cgroup trees (each is its own mount with a
+ * different subsystem controller) for blkio, cpuset, memory, systemd (has no
+ * controller), etc. Within each tree there is a top-level directory with at
+ * least a cgroup.procs, notify_on_release, release_agent, and tasks file.
+ * The cgroup.procs file lists the processes within that group and the tasks
+ * file lists the threads in the group. There could be subdirectories, which
+ * define new cgroups, that then contain a subset of the processes. Each
+ * subdirectory also has, at a minimum, a cgroup.procs, notify_on_release, and
+ * tasks file.
+ *
+ * Since we're using lx to run user-level code within zones, the majority (all?)
+ * of the cgroup resource management functionality simply doesn't apply to us.
+ * The primary need for cgroups is to support the init program 'systemd' as the
+ * consumer. systemd only requires the process grouping hierarchy of cgroups,
+ * although it can also use the resource management features if they are
+ * available. Given this, our cgroup file system only implements the process
+ * hierarchy and does not report that any resource management controllers are
+ * available for separate mounts.
+ *
+ * In addition to the hierarchy, the other important component of cgroups that
+ * is used by systemd is the 'release_agent'. This provides a mechanism to
+ * run a command when a cgroup becomes empty (the last task in the group
+ * leaves, either by exit or move, and there are no more sub-cgroups). The
+ * 'release_agent' file only exists in the top-level cgroup of the mounted
+ * file system and holds the path to a command to run. The 'notify_on_release'
+ * file exists in each cgroup dir. If that file contains a '1' then the agent
+ * is run when that group becomes empty. The agent is passed a path string of
+ * the cgroup, relative to the file system mount point (e.g. a mount on
+ * /sys/fs/cgroups/systemd with a sub-cgroup of /sys/fs/cgroups/systemd/foo/bar
+ * gets the arg /foo/bar).
+ *
+ * Cgroup membership is implemented via hooks into the lx brand code. When
+ * the cgroup file system loads it installs callbacks for:
+ *    lx_cgrp_initlwp
+ *    lx_cgrp_freelwp
+ * and when it unloads it clears those hooks. The lx brand code calls those
+ * hooks when a lwp starts and when it exits. Internally we use a
+ * simple reference counter (cgn_task_cnt) on the cgroup node to track how many
+ * threads are in the group, so we can tell when a group becomes empty.
+ * To make this quick, a hash table (cg_grp_hash) is maintained on the
+ * cgrp_mnt_t struct to allow quick lookups by cgroup ID. The hash table is
+ * sized so that there should typically only be 0 or 1 cgroups per bucket.
+ * We also keep a reference to the file system in the zone-specific brand data
+ * (lxzd_cgroup) so that the lx brand code can pass in the correct vfs_t
+ * when it runs the hook.
+ *
+ * Once a cgroup is about to become empty, the final process exiting the cgroup
+ * will launch a new user-level process which execs the release agent. The new
+ * process is created as a child of zsched (indicated by the -1 pid argument
+ * to newproc) and is not associated with the exiting process in any way.
+ *
+ * This file system is similar to tmpfs in that directories only exist in
+ * memory. Each subdirectory represents a different cgroup. Within the cgroup
+ * there are pseudo files (see cg_ssde_dir) with well-defined names which
+ * control the configuration and behavior of the cgroup (see cgrp_nodetype_t).
+ * The primary files within every cgroup are named 'cgroup.procs',
+ * 'notify_on_release', and 'tasks' (as well as 'release_agent' in the
+ * top-level cgroup). The cgroup.procs and tasks files are used to control and
+ * list which processes/threads belong to the cgroup. In the general case there
+ * could be additional files in the cgroup, which defined additional behavior
+ * (i.e. subsystem specific pseudo files), although none exist at this time.
+ *
+ * Each cgroup node has a unique ID (cgn_nodeid) within the mount. This ID is
+ * used to correlate with the threads to determine cgroup membership. When
+ * assigning a PID to a cgroup (via write) the code updates the br_cgroupid
+ * member in the brand-specific lx_lwp_data structure to control which cgroup
+ * the thread belongs to. Note that because the br_cgroupid lives in
+ * lx_lwp_data, native processes will not appear in the cgroup hierarchy.
+ *
+ * An overview of the behavior for the various vnode operations is:
+ * - no hardlinks or symlinks
+ * - no file create (the subsystem-specific files are a fixed list of
+ *   pseudo-files accessible within the directory)
+ * - no file remove
+ * - no file rename, but a directory (i.e. a cgroup) can be renamed within the
+ *   containing directory, but not into a different directory
+ * - can mkdir and rmdir to create/destroy cgroups
+ * - cannot rmdir while it contains tasks or a subdir (i.e. a sub-cgroup)
+ * - open, read/write, close on the subsytem-specific pseudo files is
+ *   allowed, as this is the interface to configure and report on the cgroup.
+ *   The pseudo file's mode controls write access and cannot be changed.
+ *
+ * The locking in this file system is simple since the file system is not
+ * subjected to heavy I/O activity and all data is in-memory. There is a single
+ * global mutex for each mount (cg_contents). This mutex is held for the life
+ * of most vnode operations. The most active path is probably the LWP start and
+ * exit hooks which increment/decrement the reference counter on the cgroup
+ * node. The lock is important for this case since we don't want concurrent
+ * activity (such as moving the process into another cgroup) while we're trying
+ * to lookup the cgroup from the mount's hash table. We must be careful to
+ * avoid a deadlock while reading or writing since that code can take pidlock
+ * and p_lock, but the cgrp_lwp_fork_helper can also be called while one of
+ * those is held. To prevent deadlock we always take cg_contents after pidlock
+ * and p_lock.
+ *
+ * EXTENDING THE FILE SYSTEM
+ *
+ * When adding support for a new subsystem, be sure to also update the
+ * lxpr_read_cgroups function in lx_procfs so that the subsystem is reported
+ * by proc.
+ *
+ * Although we don't currently support any subsystem controllers, the design
+ * allows for the file system to be extended to add controller emulation
+ * if needed. New controller IDs (i.e. different subsystems) for a mount can
+ * be defined in the cgrp_ssid_t enum (e.g. CG_SSID_CPUSET or CG_SSID_MEMORY)
+ * and new node types for additional pseudo files in the tree can be defined in
+ * the cgrp_nodetype_t enum (e.g. CG_CPUSET_CPUS or CG_MEMORY_USAGE_IN_BYTES).
+ * The cg_ssde_dir array would need a new entry for the new subsystem to
+ * control which nodes are visible in a directory for the new subsystem.
+ *
+ * New emulation would then need to be written to manage the behavior on the
+ * new pseudo file(s) associated with new cgrp_nodetype_t types.
+ *
+ * Within lx procfs the lxpr_read_pid_cgroup() function would need to be
+ * updated so that it reported the various subsystems used by the different
+ * mounts.
+ *
+ * In addition, in order to support more than one cgroup mount we would need a
+ * list of cgroup IDs associated with every thread, instead of just one ID
+ * (br_cgroupid). The thread data would need to become a struct which held
+ * both an ID and an indication as to which mounted cgroup file system instance
+ * the ID was associated with. We would also need a list of cgroup mounts per
+ * zone, instead the current single zone reference.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <sys/kmem.h>
+#include <sys/time.h>
+#include <sys/pathname.h>
+#include <sys/vfs.h>
+#include <sys/vfs_opreg.h>
+#include <sys/vnode.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/cmn_err.h>
+#include <sys/cred.h>
+#include <sys/statvfs.h>
+#include <sys/mount.h>
+#include <sys/systm.h>
+#include <sys/mntent.h>
+#include <sys/policy.h>
+#include <sys/sdt.h>
+#include <sys/ddi.h>
+#include <sys/vmparam.h>
+#include <sys/corectl.h>
+#include <sys/contract_impl.h>
+#include <sys/pool.h>
+#include <sys/stack.h>
+#include <sys/rt.h>
+#include <sys/fx.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+
+#include "cgrps.h"
+
+/* Module level parameters */
+static int	cgrp_fstype;
+static dev_t	cgrp_dev;
+
+#define	MAX_AGENT_EVENTS	32		/* max num queued events */
+
+#define	UMNT_DELAY_TIME	drv_usectohz(50000)	/* 500th of a second */
+#define	UMNT_RETRY_MAX	100			/* 100 times - 2 secs */
+
+/*
+ * cgrp_mountcount is used to prevent module unloads while there is still
+ * state from a former mount hanging around. The filesystem module must not be
+ * allowed to go away before the last VFS_FREEVFS() call has been made. Since
+ * this is just an atomic counter, there's no need for locking.
+ */
+static uint32_t cgrp_mountcount;
+
+/*
+ * cgrp_minfree is the minimum amount of swap space that cgroups leaves for
+ * the rest of the zone. In other words, if the amount of free swap space
+ * in the zone drops below cgrp_minfree, cgroup anon allocations will fail.
+ * This number is only likely to become factor when DRAM and swap have both
+ * been capped low to allow for maximum tenancy.
+ */
+size_t cgrp_minfree = 0;
+
+/*
+ * CGMINFREE -- the value from which cgrp_minfree is derived -- should be
+ * configured to a value that is roughly the smallest practical value for
+ * memory + swap minus the largest reasonable size for cgroups in such
+ * a configuration. As of this writing, the smallest practical memory + swap
+ * configuration is 128MB, and it seems reasonable to allow cgroups to consume
+ * no more than half of this, yielding a CGMINFREE of 64MB.
+ */
+#define	CGMINFREE	64 * 1024 * 1024	/* 64 Megabytes */
+
+extern pgcnt_t swapfs_minfree;
+
+/*
+ * cgroup vfs operations.
+ */
+static int cgrp_init(int, char *);
+static int cgrp_mount(struct vfs *, struct vnode *,
+	struct mounta *, struct cred *);
+static int cgrp_unmount(struct vfs *, int, struct cred *);
+static int cgrp_root(struct vfs *, struct vnode **);
+static int cgrp_statvfs(struct vfs *, struct statvfs64 *);
+static void cgrp_freevfs(vfs_t *vfsp);
+
+/* Forward declarations for hooks */
+static void cgrp_lwp_fork_helper(vfs_t *, uint_t, id_t, pid_t);
+static void cgrp_lwp_exit_helper(vfs_t *, uint_t, id_t, pid_t);
+
+/*
+ * Loadable module wrapper
+ */
+#include <sys/modctl.h>
+
+static vfsdef_t vfw = {
+	VFSDEF_VERSION,
+	"lx_cgroup",
+	cgrp_init,
+	VSW_ZMOUNT,
+	NULL
+};
+
+/*
+ * Module linkage information
+ */
+static struct modlfs modlfs = {
+	&mod_fsops, "lx brand cgroups", &vfw
+};
+
+static struct modlinkage modlinkage = {
+	MODREV_1, &modlfs, NULL
+};
+
+int
+_init()
+{
+	return (mod_install(&modlinkage));
+}
+
+int
+_fini()
+{
+	int error;
+
+	if (cgrp_mountcount)
+		return (EBUSY);
+
+	if ((error = mod_remove(&modlinkage)) != 0)
+		return (error);
+
+	/* Disable hooks used by the lx brand module. */
+	lx_cgrp_initlwp = NULL;
+	lx_cgrp_freelwp = NULL;
+
+	/*
+	 * Tear down the operations vectors
+	 */
+	(void) vfs_freevfsops_by_type(cgrp_fstype);
+	vn_freevnodeops(cgrp_vnodeops);
+	return (0);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&modlinkage, modinfop));
+}
+
+/*
+ * Initialize global locks, etc. Called when loading cgroup module.
+ */
+static int
+cgrp_init(int fstype, char *name)
+{
+	static const fs_operation_def_t cgrp_vfsops_template[] = {
+		VFSNAME_MOUNT,		{ .vfs_mount = cgrp_mount },
+		VFSNAME_UNMOUNT,	{ .vfs_unmount = cgrp_unmount },
+		VFSNAME_ROOT,		{ .vfs_root = cgrp_root },
+		VFSNAME_STATVFS,	{ .vfs_statvfs = cgrp_statvfs },
+		VFSNAME_FREEVFS,	{ .vfs_freevfs = cgrp_freevfs },
+		NULL,			NULL
+	};
+	extern const struct fs_operation_def cgrp_vnodeops_template[];
+	int error;
+	extern  void    cgrp_hash_init();
+	major_t dev;
+
+	cgrp_hash_init();
+	cgrp_fstype = fstype;
+	ASSERT(cgrp_fstype != 0);
+
+	error = vfs_setfsops(fstype, cgrp_vfsops_template, NULL);
+	if (error != 0) {
+		cmn_err(CE_WARN, "cgrp_init: bad vfs ops template");
+		return (error);
+	}
+
+	error = vn_make_ops(name, cgrp_vnodeops_template, &cgrp_vnodeops);
+	if (error != 0) {
+		(void) vfs_freevfsops_by_type(fstype);
+		cmn_err(CE_WARN, "cgrp_init: bad vnode ops template");
+		return (error);
+	}
+
+	/*
+	 * cgrp_minfree doesn't need to be some function of configured
+	 * swap space since it really is an absolute limit of swap space
+	 * which still allows other processes to execute.
+	 */
+	if (cgrp_minfree == 0) {
+		/* Set if not patched */
+		cgrp_minfree = btopr(CGMINFREE);
+	}
+
+	if ((dev = getudev()) == (major_t)-1) {
+		cmn_err(CE_WARN, "cgrp_init: Can't get unique device number.");
+		dev = 0;
+	}
+
+	/*
+	 * Make the pseudo device
+	 */
+	cgrp_dev = makedevice(dev, 0);
+
+	/* Install the hooks used by the lx brand module. */
+	lx_cgrp_initlwp = cgrp_lwp_fork_helper;
+	lx_cgrp_freelwp = cgrp_lwp_exit_helper;
+
+	return (0);
+}
+
+static int
+cgrp_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
+{
+	cgrp_mnt_t *cgm = NULL;
+	struct cgrp_node *cp;
+	struct pathname dpn;
+	int error;
+	struct vattr rattr;
+	cgrp_ssid_t ssid = CG_SSID_GENERIC;
+	lx_zone_data_t *lxzdata;
+
+	if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0)
+		return (error);
+
+	if (mvp->v_type != VDIR)
+		return (ENOTDIR);
+
+	/*
+	 * Since we depend on per-thread lx brand data, only allow mounting
+	 * within lx zones.
+	 */
+	if (curproc->p_zone->zone_brand != &lx_brand)
+		return (EINVAL);
+
+	/*
+	 * Ensure we don't allow overlaying mounts
+	 */
+	mutex_enter(&mvp->v_lock);
+	if ((uap->flags & MS_OVERLAY) == 0 &&
+	    (mvp->v_count > 1 || (mvp->v_flag & VROOT))) {
+		mutex_exit(&mvp->v_lock);
+		return (EBUSY);
+	}
+	mutex_exit(&mvp->v_lock);
+
+	/*
+	 * Having the resource be anything but "swap" doesn't make sense.
+	 */
+	vfs_setresource(vfsp, "swap", 0);
+
+	/* cgroups don't support read-only mounts */
+	if (vfs_optionisset(vfsp, MNTOPT_RO, NULL)) {
+		error = EINVAL;
+		goto out;
+	}
+
+	/*
+	 * Here is where we could support subsystem-specific controller
+	 * mounting. For example, if mounting a cgroup fs with the 'cpuset'
+	 * option to specify that particular controller.
+	 *
+	 * char *argstr;
+	 * if (vfs_optionisset(vfsp, "cpuset", &argstr)) {
+	 *	if (ssid != CG_SSID_GENERIC) {
+	 *		error = EINVAL;
+	 *		goto out;
+	 *	}
+	 *	ssid = CG_SSID_CPUSET;
+	 * }
+	 */
+
+	error = pn_get(uap->dir,
+	    (uap->flags & MS_SYSSPACE) ? UIO_SYSSPACE : UIO_USERSPACE, &dpn);
+	if (error != 0)
+		goto out;
+
+	/*
+	 * We currently only support one mount per zone.
+	 */
+	lxzdata = ztolxzd(curproc->p_zone);
+	mutex_enter(&lxzdata->lxzd_lock);
+	if (lxzdata->lxzd_cgroup != NULL) {
+		mutex_exit(&lxzdata->lxzd_lock);
+		return (EINVAL);
+	}
+
+	cgm = kmem_zalloc(sizeof (*cgm), KM_SLEEP);
+
+	/* Set but don't bother entering the mutex (not on mount list yet) */
+	mutex_init(&cgm->cg_contents, NULL, MUTEX_DEFAULT, NULL);
+
+	cgm->cg_vfsp = lxzdata->lxzd_cgroup = vfsp;
+	mutex_exit(&lxzdata->lxzd_lock);
+
+	cgm->cg_lxzdata = lxzdata;
+	cgm->cg_ssid = ssid;
+
+	vfsp->vfs_data = (caddr_t)cgm;
+	vfsp->vfs_fstype = cgrp_fstype;
+	vfsp->vfs_dev = cgrp_dev;
+	vfsp->vfs_bsize = PAGESIZE;
+	vfsp->vfs_flag |= VFS_NOTRUNC;
+	vfs_make_fsid(&vfsp->vfs_fsid, cgrp_dev, cgrp_fstype);
+	cgm->cg_mntpath = kmem_zalloc(dpn.pn_pathlen + 1, KM_SLEEP);
+	(void) strcpy(cgm->cg_mntpath, dpn.pn_path);
+
+	cgm->cg_grp_hash = kmem_zalloc(sizeof (cgrp_node_t *) * CGRP_HASH_SZ,
+	    KM_SLEEP);
+
+	/* allocate and initialize root cgrp_node structure */
+	bzero(&rattr, sizeof (struct vattr));
+	rattr.va_mode = (mode_t)(S_IFDIR | 0755);
+	rattr.va_type = VDIR;
+	rattr.va_rdev = 0;
+	cp = kmem_zalloc(sizeof (struct cgrp_node), KM_SLEEP);
+
+	mutex_enter(&cgm->cg_contents);
+	cgrp_node_init(cgm, cp, &rattr, cr);
+
+	CGNTOV(cp)->v_flag |= VROOT;
+
+	/*
+	 * initialize linked list of cgrp_nodes so that the back pointer of
+	 * the root cgrp_node always points to the last one on the list
+	 * and the forward pointer of the last node is null
+	 */
+	cp->cgn_back = cp;
+	cp->cgn_forw = NULL;
+	cp->cgn_nlink = 0;
+	cgm->cg_rootnode = cp;
+
+	cp->cgn_type = CG_CGROUP_DIR;
+	cp->cgn_nodeid = cgrp_inode(CG_CGROUP_DIR, cgm->cg_gen);
+
+	/*
+	 * This initial cgrp_node will have an ID of 0. All existing processes
+	 * inside the zone will have been started with, or inherited, a
+	 * br_cgroupid of 0. The cgrp_cg_hash_init function will initialize the
+	 * cgn_task_cnt for cgroup 0 to reflect the number of tasks already in
+	 * the group.
+	 *
+	 * Because we must hold cg_contents in cgrp_lwp_fork_helper and
+	 * cgrp_lwp_exit_helper, no process can be creating or exiting another
+	 * thread (although that is unlikely anyway since the cgroup filesystem
+	 * is normally mounted at the start of zone bootup, before anything
+	 * else is started).
+	 */
+	cgrp_dirinit(cp, cp, cr);
+
+	mutex_exit(&cgm->cg_contents);
+
+	pn_free(&dpn);
+	error = 0;
+	atomic_inc_32(&cgrp_mountcount);
+
+out:
+	if (error == 0)
+		vfs_set_feature(vfsp, VFSFT_SYSATTR_VIEWS);
+
+	return (error);
+}
+
+static int
+cgrp_unmount(struct vfs *vfsp, int flag, struct cred *cr)
+{
+	cgrp_mnt_t *cgm = (cgrp_mnt_t *)VFSTOCGM(vfsp);
+	cgrp_node_t *cgnp, *cancel;
+	struct vnode	*vp;
+	int error;
+	uint_t cnt;
+	int retry_cnt = 0;
+
+	if ((error = secpolicy_fs_unmount(cr, vfsp)) != 0)
+		return (error);
+
+retry:
+	mutex_enter(&cgm->cg_contents);
+
+	/*
+	 * In the normal unmount case, if there were no open files, only the
+	 * root node would have a reference count. However, the user-level
+	 * agent manager should have the root vnode open and be waiting in
+	 * ioctl. We need to wake the manager and it may take some retries
+	 * before it closes its file descriptor.
+	 *
+	 * With cg_contents held, nothing can be added or removed.
+	 * There may be some dirty pages.  To prevent fsflush from
+	 * disrupting the unmount, put a hold on each node while scanning.
+	 * If we find a previously referenced node, undo the holds we have
+	 * placed and fail EBUSY.
+	 */
+	cgnp = cgm->cg_rootnode;
+
+	ASSERT(cgm->cg_lxzdata->lxzd_cgroup != NULL);
+
+	vp = CGNTOV(cgnp);
+	mutex_enter(&vp->v_lock);
+
+	if (flag & MS_FORCE) {
+		mutex_exit(&vp->v_lock);
+		mutex_exit(&cgm->cg_contents);
+		return (EINVAL);
+	}
+
+
+	cnt = vp->v_count;
+	if (cnt > 1) {
+		mutex_exit(&vp->v_lock);
+		mutex_exit(&cgm->cg_contents);
+		/* Likely because the user-level manager hasn't exited yet */
+		if (retry_cnt++ < UMNT_RETRY_MAX) {
+			delay(UMNT_DELAY_TIME);
+			goto retry;
+		}
+		return (EBUSY);
+	}
+
+	mutex_exit(&vp->v_lock);
+
+	/*
+	 * Check for open files. An open file causes everything to unwind.
+	 */
+	for (cgnp = cgnp->cgn_forw; cgnp; cgnp = cgnp->cgn_forw) {
+		vp = CGNTOV(cgnp);
+		mutex_enter(&vp->v_lock);
+		cnt = vp->v_count;
+		if (cnt > 0) {
+			/* An open file; unwind the holds we've been adding. */
+			mutex_exit(&vp->v_lock);
+			cancel = cgm->cg_rootnode->cgn_forw;
+			while (cancel != cgnp) {
+				vp = CGNTOV(cancel);
+				ASSERT(vp->v_count > 0);
+				VN_RELE(vp);
+				cancel = cancel->cgn_forw;
+			}
+			mutex_exit(&cgm->cg_contents);
+			return (EBUSY);
+		} else {
+			/* directly add a VN_HOLD since we have the lock */
+			vp->v_count++;
+			mutex_exit(&vp->v_lock);
+		}
+	}
+
+	mutex_enter(&cgm->cg_lxzdata->lxzd_lock);
+	cgm->cg_lxzdata->lxzd_cgroup = NULL;
+	mutex_exit(&cgm->cg_lxzdata->lxzd_lock);
+	kmem_free(cgm->cg_grp_hash, sizeof (cgrp_node_t *) * CGRP_HASH_SZ);
+
+	/*
+	 * We can drop the mutex now because
+	 * no one can find this mount anymore
+	 */
+	vfsp->vfs_flag |= VFS_UNMOUNTED;
+	mutex_exit(&cgm->cg_contents);
+
+	return (0);
+}
+
+/*
+ * Implementation of VFS_FREEVFS(). This is called by the vfs framework after
+ * umount and the last VFS_RELE, to trigger the release of any resources still
+ * associated with the given vfs_t. This is normally called immediately after
+ * cgrp_umount.
+ */
+void
+cgrp_freevfs(vfs_t *vfsp)
+{
+	cgrp_mnt_t *cgm = (cgrp_mnt_t *)VFSTOCGM(vfsp);
+	cgrp_node_t *cn;
+	struct vnode	*vp;
+
+	/*
+	 * Free all kmemalloc'd and anonalloc'd memory associated with
+	 * this filesystem.  To do this, we go through the file list twice,
+	 * once to remove all the directory entries, and then to remove
+	 * all the pseudo files.
+	 */
+
+	/*
+	 * Now that we are tearing ourselves down we need to remove the
+	 * UNMOUNTED flag. If we don't, we'll later hit a VN_RELE when we remove
+	 * files from the system causing us to have a negative value. Doing this
+	 * seems a bit better than trying to set a flag on the tmount that says
+	 * we're tearing down.
+	 */
+	vfsp->vfs_flag &= ~VFS_UNMOUNTED;
+
+	/*
+	 * Remove all directory entries
+	 */
+	for (cn = cgm->cg_rootnode; cn; cn = cn->cgn_forw) {
+		mutex_enter(&cgm->cg_contents);
+		if (cn->cgn_type == CG_CGROUP_DIR)
+			cgrp_dirtrunc(cn);
+		mutex_exit(&cgm->cg_contents);
+	}
+
+	ASSERT(cgm->cg_rootnode);
+
+	/*
+	 * All links are gone, v_count is keeping nodes in place.
+	 * VN_RELE should make the node disappear, unless somebody
+	 * is holding pages against it.  Nap and retry until it disappears.
+	 *
+	 * We re-acquire the lock to prevent others who have a HOLD on
+	 * a cgrp_node via its pages or anon slots from blowing it away
+	 * (in cgrp_inactive) while we're trying to get to it here. Once
+	 * we have a HOLD on it we know it'll stick around.
+	 *
+	 */
+	mutex_enter(&cgm->cg_contents);
+
+	/* Remove all the files (except the rootnode) backwards. */
+	while ((cn = cgm->cg_rootnode->cgn_back) != cgm->cg_rootnode) {
+		mutex_exit(&cgm->cg_contents);
+		/*
+		 * All nodes will be released here. Note we handled the link
+		 * count above.
+		 */
+		vp = CGNTOV(cn);
+		VN_RELE(vp);
+		mutex_enter(&cgm->cg_contents);
+		/*
+		 * It's still there after the RELE. Someone else like pageout
+		 * has a hold on it so wait a bit and then try again - we know
+		 * they'll give it up soon.
+		 */
+		if (cn == cgm->cg_rootnode->cgn_back) {
+			VN_HOLD(vp);
+			mutex_exit(&cgm->cg_contents);
+			delay(hz / 4);
+			mutex_enter(&cgm->cg_contents);
+		}
+	}
+	mutex_exit(&cgm->cg_contents);
+
+	VN_RELE(CGNTOV(cgm->cg_rootnode));
+
+	ASSERT(cgm->cg_mntpath);
+
+	kmem_free(cgm->cg_mntpath, strlen(cgm->cg_mntpath) + 1);
+
+	mutex_destroy(&cgm->cg_contents);
+	kmem_free(cgm, sizeof (cgrp_mnt_t));
+
+	/* Allow _fini() to succeed now */
+	atomic_dec_32(&cgrp_mountcount);
+}
+
+/*
+ * return root cgnode for given vnode
+ */
+static int
+cgrp_root(struct vfs *vfsp, struct vnode **vpp)
+{
+	cgrp_mnt_t *cgm = (cgrp_mnt_t *)VFSTOCGM(vfsp);
+	cgrp_node_t *cp = cgm->cg_rootnode;
+	struct vnode *vp;
+
+	ASSERT(cp);
+
+	vp = CGNTOV(cp);
+	VN_HOLD(vp);
+	*vpp = vp;
+	return (0);
+}
+
+static int
+cgrp_statvfs(struct vfs *vfsp, struct statvfs64 *sbp)
+{
+	cgrp_mnt_t *cgm = (cgrp_mnt_t *)VFSTOCGM(vfsp);
+	ulong_t	blocks;
+	dev32_t d32;
+	zoneid_t eff_zid;
+	struct zone *zp;
+
+	zp = cgm->cg_vfsp->vfs_zone;
+
+	if (zp == NULL)
+		eff_zid = GLOBAL_ZONEUNIQID;
+	else
+		eff_zid = zp->zone_id;
+
+	sbp->f_bsize = PAGESIZE;
+	sbp->f_frsize = PAGESIZE;
+
+	/*
+	 * Find the amount of available physical and memory swap
+	 */
+	mutex_enter(&anoninfo_lock);
+	ASSERT(k_anoninfo.ani_max >= k_anoninfo.ani_phys_resv);
+	blocks = (ulong_t)CURRENT_TOTAL_AVAILABLE_SWAP;
+	mutex_exit(&anoninfo_lock);
+
+	if (blocks > cgrp_minfree)
+		sbp->f_bfree = blocks - cgrp_minfree;
+	else
+		sbp->f_bfree = 0;
+
+	sbp->f_bavail = sbp->f_bfree;
+
+	/*
+	 * Total number of blocks is just what's available
+	 */
+	sbp->f_blocks = (fsblkcnt64_t)(sbp->f_bfree);
+
+	if (eff_zid != GLOBAL_ZONEUNIQID &&
+	    zp->zone_max_swap_ctl != UINT64_MAX) {
+		/*
+		 * If the fs is used by a zone with a swap cap,
+		 * then report the capped size.
+		 */
+		rctl_qty_t cap, used;
+		pgcnt_t pgcap, pgused;
+
+		mutex_enter(&zp->zone_mem_lock);
+		cap = zp->zone_max_swap_ctl;
+		used = zp->zone_max_swap;
+		mutex_exit(&zp->zone_mem_lock);
+
+		pgcap = btop(cap);
+		pgused = btop(used);
+
+		sbp->f_bfree = MIN(pgcap - pgused, sbp->f_bfree);
+		sbp->f_bavail = sbp->f_bfree;
+		sbp->f_blocks = MIN(pgcap, sbp->f_blocks);
+	}
+
+	/*
+	 * The maximum number of files available is approximately the number
+	 * of cgrp_nodes we can allocate from the remaining kernel memory
+	 * available to cgroups.  This is fairly inaccurate since it doesn't
+	 * take into account the names stored in the directory entries.
+	 */
+	sbp->f_ffree = sbp->f_files = ptob(availrmem) /
+	    (sizeof (cgrp_node_t) + sizeof (cgrp_dirent_t));
+	sbp->f_favail = (fsfilcnt64_t)(sbp->f_ffree);
+	(void) cmpldev(&d32, vfsp->vfs_dev);
+	sbp->f_fsid = d32;
+	(void) strcpy(sbp->f_basetype, vfssw[cgrp_fstype].vsw_name);
+	(void) strncpy(sbp->f_fstr, cgm->cg_mntpath, sizeof (sbp->f_fstr));
+	/* ensure null termination */
+	sbp->f_fstr[sizeof (sbp->f_fstr) - 1] = '\0';
+	sbp->f_flag = vf_to_stf(vfsp->vfs_flag);
+	sbp->f_namemax = MAXNAMELEN - 1;
+	return (0);
+}
+
+static int
+cgrp_get_dirname(cgrp_node_t *cn, char *buf, int blen)
+{
+	cgrp_node_t *parent;
+	cgrp_dirent_t *dp;
+
+	buf[0] = '\0';
+
+	parent = cn->cgn_parent;
+	if (parent == NULL || parent == cn) {
+		(void) strlcpy(buf, ".", blen);
+		return (0);
+	}
+
+	/*
+	 * Search the parent dir list to find this cn's name.
+	 */
+	for (dp = parent->cgn_dir; dp != NULL; dp = dp->cgd_next) {
+		if (dp->cgd_cgrp_node->cgn_id == cn->cgn_id) {
+			(void) strlcpy(buf, dp->cgd_name, blen);
+			return (0);
+		}
+	}
+
+	return (-1);
+}
+
+typedef struct cgrp_rra_arg {
+	char *crraa_agent_path;
+	char *crraa_event_path;
+} cgrp_rra_arg_t;
+
+static void
+cgrp_run_rel_agent(void *a)
+{
+	cgrp_rra_arg_t *rarg = a;
+	proc_t *p = ttoproc(curthread);
+	zone_t *z = p->p_zone;
+	struct core_globals *cg;
+	int res;
+
+	ASSERT(!INGLOBALZONE(curproc));
+
+	/* The following block is derived from start_init_common */
+	ASSERT_STACK_ALIGNED();
+
+	p->p_cstime = p->p_stime = p->p_cutime = p->p_utime = 0;
+	p->p_usrstack = (caddr_t)USRSTACK32;
+	p->p_model = DATAMODEL_ILP32;
+	p->p_stkprot = PROT_ZFOD & ~PROT_EXEC;
+	p->p_datprot = PROT_ZFOD & ~PROT_EXEC;
+	p->p_stk_ctl = INT32_MAX;
+
+	p->p_as = as_alloc();
+	p->p_as->a_proc = p;
+	p->p_as->a_userlimit = (caddr_t)USERLIMIT32;
+	(void) hat_setup(p->p_as->a_hat, HAT_INIT);
+
+	VERIFY((cg = zone_getspecific(core_zone_key, z)) != NULL);
+
+	corectl_path_hold(cg->core_default_path);
+	corectl_content_hold(cg->core_default_content);
+
+	curproc->p_corefile = cg->core_default_path;
+	curproc->p_content = cg->core_default_content;
+
+	init_mstate(curthread, LMS_SYSTEM);
+	res = exec_init(rarg->crraa_agent_path, rarg->crraa_event_path);
+
+	/* End of code derived from start_init_common */
+
+	kmem_free(rarg->crraa_event_path, MAXPATHLEN);
+	kmem_free(rarg->crraa_agent_path, CGRP_AGENT_LEN);
+	kmem_free(rarg, sizeof (cgrp_rra_arg_t));
+
+	/* The following is derived from zone_start_init - see comments there */
+	if (res != 0 || zone_status_get(global_zone) >= ZONE_IS_SHUTTING_DOWN) {
+		if (proc_exit(CLD_EXITED, res) != 0) {
+			mutex_enter(&p->p_lock);
+			ASSERT(p->p_flag & SEXITLWPS);
+			lwp_exit();
+		}
+	} else {
+		id_t cid = curthread->t_cid;
+
+		mutex_enter(&class_lock);
+		ASSERT(cid < loaded_classes);
+		if (strcmp(sclass[cid].cl_name, "FX") == 0 &&
+		    z->zone_fixed_hipri) {
+			pcparms_t pcparms;
+
+			pcparms.pc_cid = cid;
+			((fxkparms_t *)pcparms.pc_clparms)->fx_upri = FXMAXUPRI;
+			((fxkparms_t *)pcparms.pc_clparms)->fx_uprilim =
+			    FXMAXUPRI;
+			((fxkparms_t *)pcparms.pc_clparms)->fx_cflags =
+			    FX_DOUPRILIM | FX_DOUPRI;
+
+			mutex_enter(&pidlock);
+			mutex_enter(&curproc->p_lock);
+			(void) parmsset(&pcparms, curthread);
+			mutex_exit(&curproc->p_lock);
+			mutex_exit(&pidlock);
+		} else if (strcmp(sclass[cid].cl_name, "RT") == 0) {
+			curthread->t_pri = RTGPPRIO0;
+		}
+		mutex_exit(&class_lock);
+
+		/* cause the process to return to userland. */
+		lwp_rtt();
+	}
+}
+
+/*
+ * Launch the user-level release_agent manager. The event data is the
+ * pathname (relative to the mount point of the file system) of the newly empty
+ * cgroup.
+ *
+ * The cg_contents mutex is held on entry and dropped before returning.
+ */
+void
+cgrp_rel_agent_event(cgrp_mnt_t *cgm, cgrp_node_t *cn, boolean_t is_exit)
+{
+	cgrp_node_t *parent;
+	char nm[MAXNAMELEN];
+	char *argstr, *oldstr, *tmp;
+	id_t cid;
+	proc_t *p = ttoproc(curthread);
+	zone_t *z = p->p_zone;
+	lx_lwp_data_t *plwpd = ttolxlwp(curthread);
+	cgrp_rra_arg_t *rarg;
+
+	ASSERT(MUTEX_HELD(&cgm->cg_contents));
+
+	/* Nothing to do if the agent is not set */
+	if (cgm->cg_agent[0] == '\0') {
+		mutex_exit(&cgm->cg_contents);
+		return;
+	}
+
+	parent = cn->cgn_parent;
+	/* Cannot remove the top-level cgroup (only via unmount) */
+	if (parent == cn) {
+		mutex_exit(&cgm->cg_contents);
+		return;
+	}
+
+	argstr = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+	oldstr = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+	*argstr = '\0';
+
+	/*
+	 * Iterate up the directory tree to construct the agent argument string.
+	 */
+	do {
+		VERIFY0(cgrp_get_dirname(cn, nm, sizeof (nm)));
+		DTRACE_PROBE1(cgrp__dir__name, char *, nm);
+		if (*argstr == '\0') {
+			(void) snprintf(argstr, MAXPATHLEN, "/%s", nm);
+		} else {
+			tmp = oldstr;
+			oldstr = argstr;
+			argstr = tmp;
+			(void) snprintf(argstr, MAXPATHLEN, "/%s%s", nm,
+			    oldstr);
+		}
+
+		if (cn->cgn_parent == NULL)
+			break;
+		cn = cn->cgn_parent;
+		parent = cn->cgn_parent;
+
+		/*
+		 * The arg path is relative to the mountpoint so we stop when
+		 * we get to the top level.
+		 */
+		if (parent == NULL || parent == cn)
+			break;
+	} while (parent != cn);
+
+	kmem_free(oldstr, MAXPATHLEN);
+
+	rarg = kmem_alloc(sizeof (cgrp_rra_arg_t), KM_SLEEP);
+	rarg->crraa_agent_path = kmem_alloc(sizeof (cgm->cg_agent), KM_SLEEP);
+	(void) strlcpy(rarg->crraa_agent_path, cgm->cg_agent,
+	    sizeof (cgm->cg_agent));
+	rarg->crraa_event_path = argstr;
+
+	DTRACE_PROBE2(cgrp__agent__event, cgrp_rra_arg_t *, rarg,
+	    int, plwpd->br_cgroupid);
+
+	/*
+	 * When we're exiting, the release agent process cannot belong to our
+	 * cgroup. When the release agent is called for a move or rmdir, then
+	 * we do not change our cgroupid.
+	 */
+	if (is_exit) {
+		plwpd->br_cgroupid = 0;
+	}
+
+	/*
+	 * The cg_contents mutex cannot be held while taking the pool lock
+	 * or calling newproc.
+	 */
+	mutex_exit(&cgm->cg_contents);
+
+	if (z->zone_defaultcid > 0) {
+		cid = z->zone_defaultcid;
+	} else {
+		pool_lock();
+		cid = pool_get_class(z->zone_pool);
+		pool_unlock();
+	}
+	if (cid == -1)
+		cid = defaultcid;
+
+	if (newproc(cgrp_run_rel_agent, (void *)rarg, cid, minclsyspri - 1,
+	    NULL, -1) != 0) {
+		/* There's nothing we can do if creating the proc fails. */
+		kmem_free(rarg->crraa_event_path, MAXPATHLEN);
+		kmem_free(rarg->crraa_agent_path, sizeof (cgm->cg_agent));
+		kmem_free(rarg, sizeof (cgrp_rra_arg_t));
+	}
+}
+
+/*ARGSUSED*/
+static void
+cgrp_lwp_fork_helper(vfs_t *vfsp, uint_t cg_id, id_t tid, pid_t tpid)
+{
+	cgrp_mnt_t *cgm = (cgrp_mnt_t *)VFSTOCGM(vfsp);
+	cgrp_node_t *cn;
+
+	mutex_enter(&cgm->cg_contents);
+	cn = cgrp_cg_hash_lookup(cgm, cg_id);
+	ASSERT(cn != NULL);
+	cn->cgn_task_cnt++;
+	mutex_exit(&cgm->cg_contents);
+
+	DTRACE_PROBE1(cgrp__lwp__fork, void *, cn);
+}
+
+/*ARGSUSED*/
+static void
+cgrp_lwp_exit_helper(vfs_t *vfsp, uint_t cg_id, id_t tid, pid_t tpid)
+{
+	cgrp_mnt_t *cgm = (cgrp_mnt_t *)VFSTOCGM(vfsp);
+	cgrp_node_t *cn;
+
+	mutex_enter(&cgm->cg_contents);
+	cn = cgrp_cg_hash_lookup(cgm, cg_id);
+	ASSERT(cn != NULL);
+	if (cn->cgn_task_cnt == 0) {
+		/* top-level cgroup cnt can be 0 during reboot */
+		mutex_exit(&cgm->cg_contents);
+		return;
+	}
+	cn->cgn_task_cnt--;
+	DTRACE_PROBE1(cgrp__lwp__exit, void *, cn);
+
+	if (cn->cgn_task_cnt == 0 && cn->cgn_dirents == N_DIRENTS(cgm) &&
+	    cn->cgn_notify == 1) {
+		cgrp_rel_agent_event(cgm, cn, B_TRUE);
+		ASSERT(MUTEX_NOT_HELD(&cgm->cg_contents));
+	} else {
+		mutex_exit(&cgm->cg_contents);
+	}
+}
--- /dev/null	Wed Oct  5 00:32:04 2016
+++ new/usr/src/uts/common/brand/lx/cgroups/cgrps_vnops.c	Wed Oct  5 00:32:04 2016
@@ -0,0 +1,1600 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sysmacros.h>
+#include <sys/user.h>
+#include <sys/time.h>
+#include <sys/vfs.h>
+#include <sys/vfs_opreg.h>
+#include <sys/vnode.h>
+#include <sys/file.h>
+#include <sys/fcntl.h>
+#include <sys/flock.h>
+#include <sys/kmem.h>
+#include <sys/uio.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
+#include <sys/cred.h>
+#include <sys/dirent.h>
+#include <sys/pathname.h>
+#include <vm/seg_vn.h>
+#include <sys/cmn_err.h>
+#include <sys/buf.h>
+#include <sys/vm.h>
+#include <sys/prsystm.h>
+#include <sys/policy.h>
+#include <fs/fs_subr.h>
+#include <sys/sdt.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+
+#include "cgrps.h"
+
+typedef enum cgrp_wr_type {
+	CG_WR_PROCS = 1,
+	CG_WR_TASKS
+} cgrp_wr_type_t;
+
+/* ARGSUSED1 */
+static int
+cgrp_open(struct vnode **vpp, int flag, struct cred *cred, caller_context_t *ct)
+{
+	/*
+	 * swapon to a cgrp file is not supported so access is denied on open
+	 * if VISSWAP is set.
+	 */
+	if ((*vpp)->v_flag & VISSWAP)
+		return (EINVAL);
+
+	return (0);
+}
+
+/* ARGSUSED1 */
+static int
+cgrp_close(struct vnode *vp, int flag, int count, offset_t offset,
+    struct cred *cred, caller_context_t *ct)
+{
+	cleanlocks(vp, ttoproc(curthread)->p_pid, 0);
+	cleanshares(vp, ttoproc(curthread)->p_pid);
+	return (0);
+}
+
+/*
+ * Lookup proc or task based on pid and typ.
+ */
+static proc_t *
+cgrp_p_for_wr(pid_t pid, cgrp_wr_type_t typ)
+{
+	int i;
+	zoneid_t zoneid = curproc->p_zone->zone_id;
+	pid_t schedpid = curproc->p_zone->zone_zsched->p_pid;
+
+	ASSERT(MUTEX_HELD(&pidlock));
+
+	/* getting a proc from a pid is easy */
+	if (typ == CG_WR_PROCS)
+		return (prfind(pid));
+
+	ASSERT(typ == CG_WR_TASKS);
+
+	/*
+	 * We have to scan all of the process entries to find the proc
+	 * containing this task.
+	 */
+	mutex_exit(&pidlock);
+	for (i = 1; i < v.v_proc; i++) {
+		proc_t *p;
+		kthread_t *t;
+
+		mutex_enter(&pidlock);
+		/*
+		 * Skip indices for which there is no pid_entry, PIDs for
+		 * which there is no corresponding process, system processes,
+		 * a PID of 0, the pid for our zsched process, anything the
+		 * security policy doesn't allow us to look at, its not an
+		 * lx-branded process and processes that are not in the zone.
+		 */
+		if ((p = pid_entry(i)) == NULL ||
+		    p->p_stat == SIDL ||
+		    (p->p_flag & SSYS) != 0 ||
+		    p->p_pid == 0 ||
+		    p->p_pid == schedpid ||
+		    secpolicy_basic_procinfo(CRED(), p, curproc) != 0 ||
+		    p->p_brand != &lx_brand ||
+		    p->p_zone->zone_id != zoneid) {
+			mutex_exit(&pidlock);
+			continue;
+		}
+
+		mutex_enter(&p->p_lock);
+		if ((t = p->p_tlist) == NULL) {
+			/* no threads, skip it */
+			mutex_exit(&p->p_lock);
+			mutex_exit(&pidlock);
+			continue;
+		}
+
+		/*
+		 * Check all threads in this proc.
+		 */
+		do {
+			lx_lwp_data_t *plwpd = ttolxlwp(t);
+			if (plwpd != NULL && plwpd->br_pid == pid) {
+				mutex_exit(&p->p_lock);
+				return (p);
+			}
+
+			t = t->t_forw;
+		} while (t != p->p_tlist);
+
+		mutex_exit(&p->p_lock);
+		mutex_exit(&pidlock);
+	}
+
+	mutex_enter(&pidlock);
+	return (NULL);
+}
+
+/*
+ * Move a thread from one cgroup to another. If the old cgroup is empty
+ * we queue up an agent event. We return true in that case since we've
+ * dropped the locks and the caller needs to reacquire them.
+ */
+static boolean_t
+cgrp_thr_move(cgrp_mnt_t *cgm, lx_lwp_data_t *plwpd, cgrp_node_t *ncn,
+    uint_t cg_id, proc_t *p)
+{
+	cgrp_node_t *ocn;
+
+	ASSERT(MUTEX_HELD(&cgm->cg_contents));
+	ASSERT(MUTEX_HELD(&p->p_lock));
+
+	ocn = cgrp_cg_hash_lookup(cgm, plwpd->br_cgroupid);
+	VERIFY(ocn != NULL);
+
+	ASSERT(ocn->cgn_task_cnt > 0);
+	atomic_dec_32(&ocn->cgn_task_cnt);
+	atomic_inc_32(&ncn->cgn_task_cnt);
+	plwpd->br_cgroupid = cg_id;
+
+	if (ocn->cgn_task_cnt == 0 && ocn->cgn_dirents == N_DIRENTS(cgm) &&
+	    ocn->cgn_notify == 1) {
+		/*
+		 * We want to drop p_lock before queuing the event since
+		 * that might sleep. Dropping p_lock might cause the caller to
+		 * have to restart the move process from the beginning.
+		 */
+		mutex_exit(&p->p_lock);
+		cgrp_rel_agent_event(cgm, ocn, B_FALSE);
+		ASSERT(MUTEX_NOT_HELD(&cgm->cg_contents));
+		return (B_TRUE);
+	}
+
+	return (B_FALSE);
+}
+
+/*
+ * Assign either all of the threads, or a single thread, for the specified pid
+ * to the new cgroup. Controlled by the typ argument.
+ */
+static int
+cgrp_proc_set_id(cgrp_mnt_t *cgm, uint_t cg_id, pid_t pid, cgrp_wr_type_t typ)
+{
+	proc_t *p;
+	kthread_t *t;
+	int error;
+	cgrp_node_t *ncn;
+
+	if (pid == 1)
+		pid = curproc->p_zone->zone_proc_initpid;
+
+	/*
+	 * Move one or all threads to this cgroup.
+	 */
+	if (typ == CG_WR_TASKS) {
+		error = ESRCH;
+	} else {
+		error = 0;
+	}
+
+restart:
+	mutex_enter(&pidlock);
+
+	p = cgrp_p_for_wr(pid, typ);
+	if (p == NULL) {
+		mutex_exit(&pidlock);
+		return (ESRCH);
+	}
+
+	/*
+	 * Fail writes for pids for which there is no corresponding process,
+	 * system processes, a pid of 0, the pid for our zsched process,
+	 * anything the security policy doesn't allow us to look at, and
+	 * processes that are not in the zone.
+	 */
+	if (p->p_stat == SIDL ||
+	    (p->p_flag & SSYS) != 0 ||
+	    p->p_pid == 0 ||
+	    p->p_pid == curproc->p_zone->zone_zsched->p_pid ||
+	    secpolicy_basic_procinfo(CRED(), p, curproc) != 0 ||
+	    p->p_zone->zone_id != curproc->p_zone->zone_id) {
+		mutex_exit(&pidlock);
+		return (ESRCH);
+	}
+
+	/*
+	 * Ignore writes for PID which is not an lx-branded process or with
+	 * no threads.
+	 */
+
+	mutex_enter(&p->p_lock);
+	mutex_exit(&pidlock);
+	if (p->p_brand != &lx_brand || (t = p->p_tlist) == NULL ||
+	    p->p_flag & SEXITING) {
+		mutex_exit(&p->p_lock);
+		return (0);
+	}
+
+	mutex_enter(&cgm->cg_contents);
+
+	ncn = cgrp_cg_hash_lookup(cgm, cg_id);
+	VERIFY(ncn != NULL);
+
+	do {
+		lx_lwp_data_t *plwpd = ttolxlwp(t);
+		if (plwpd != NULL && plwpd->br_cgroupid != cg_id) {
+			if (typ == CG_WR_PROCS) {
+				if (cgrp_thr_move(cgm, plwpd, ncn, cg_id, p)) {
+					/*
+					 * We dropped all of the locks so we
+					 * need to start over.
+					 */
+					goto restart;
+				}
+
+			} else if (plwpd->br_pid == pid) {
+				/* type is CG_WR_TASKS and we found the task */
+				error = 0;
+				if (cgrp_thr_move(cgm, plwpd, ncn, cg_id, p)) {
+					goto done;
+				} else {
+					break;
+				}
+			}
+		}
+		t = t->t_forw;
+	} while (t != p->p_tlist);
+
+	mutex_exit(&cgm->cg_contents);
+	mutex_exit(&p->p_lock);
+done:
+
+	return (error);
+}
+
+/*
+ * User-level is writing a pid string. We need to get that string and convert
+ * it to a pid. The user-level code has to completely write an entire pid
+ * string at once. The user-level code could write multiple strings (delimited
+ * by newline) although that is frowned upon. However, we must handle this
+ * case too. Thus we consume the input one byte at a time until we get a whole
+ * pid string. We can't consume more than a byte at a time since otherwise we
+ * might be left with a partial pid string.
+ */
+static int
+cgrp_get_pid_str(struct uio *uio, pid_t *pid)
+{
+	char buf[16];	/* big enough for a pid string */
+	int i;
+	int error;
+	char *p = &buf[0];
+	char *ep;
+	long pidnum;
+
+	bzero(buf, sizeof (buf));
+	for (i = 0; uio->uio_resid > 0 && i < sizeof (buf); i++, p++) {
+		error = uiomove(p, 1, UIO_WRITE, uio);
+		if (error != 0)
+			return (error);
+		if (buf[i] == '\n') {
+			buf[i] = '\0';
+			break;
+		}
+	}
+
+	if (buf[0] == '\0' || i >= sizeof (buf)) /* no input or too long */
+		return (EINVAL);
+
+	error = ddi_strtol(buf, &ep, 10, &pidnum);
+	if (error != 0 || *ep != '\0' || pidnum > maxpid || pidnum < 0)
+		return (EINVAL);
+
+	*pid = (pid_t)pidnum;
+	return (0);
+}
+
+static int
+cgrp_wr_notify(cgrp_node_t *cn, struct uio *uio)
+{
+	int error;
+	uint_t value;
+
+	/*
+	 * This is cheesy but since we only take a 0 or 1 value we can
+	 * let the pid_str function do the uio string conversion.
+	 */
+	error = cgrp_get_pid_str(uio, (pid_t *)&value);
+	if (error != 0)
+		return (error);
+
+	if (value != 0 && value != 1)
+		return (EINVAL);
+
+	/*
+	 * The flag is on the containing dir. We don't bother taking the
+	 * cg_contents lock since this is a simple assignment.
+	 */
+	cn->cgn_parent->cgn_notify = value;
+	return (0);
+}
+
+static int
+cgrp_wr_rel_agent(cgrp_mnt_t *cgm, struct uio *uio)
+{
+	int error;
+	int len;
+	char *wrp;
+
+	len = uio->uio_offset + uio->uio_resid;
+	if (len > MAXPATHLEN)
+		return (EFBIG);
+
+	mutex_enter(&cgm->cg_contents);
+
+	wrp = &cgm->cg_agent[uio->uio_offset];
+	error = uiomove(wrp, uio->uio_resid, UIO_WRITE, uio);
+	cgm->cg_agent[len] = '\0';
+	if (len > 1 && cgm->cg_agent[len - 1] == '\n')
+		cgm->cg_agent[len - 1] = '\0';
+
+	mutex_exit(&cgm->cg_contents);
+	return (error);
+}
+
+static int
+cgrp_wr_proc_or_task(cgrp_mnt_t *cgm, cgrp_node_t *cn, struct uio *uio,
+    cgrp_wr_type_t typ)
+{
+	/* the cgroup ID is on the containing dir */
+	uint_t cg_id = cn->cgn_parent->cgn_id;
+	int error;
+	pid_t pidnum;
+
+	while (uio->uio_resid > 0) {
+		error = cgrp_get_pid_str(uio, &pidnum);
+		if (error != 0)
+			return (error);
+
+		error = cgrp_proc_set_id(cgm, cg_id, pidnum, typ);
+		if (error != 0)
+			return (error);
+	}
+
+	return (0);
+}
+
+static int
+cgrp_wr(cgrp_mnt_t *cgm, cgrp_node_t *cn, struct uio *uio)
+{
+	int error = 0;
+	rlim64_t limit = uio->uio_llimit;
+
+	ASSERT(CGNTOV(cn)->v_type == VREG);
+
+	if (uio->uio_loffset < 0)
+		return (EINVAL);
+
+	if (limit == RLIM64_INFINITY || limit > MAXOFFSET_T)
+		limit = MAXOFFSET_T;
+
+	if (uio->uio_loffset >= MAXOFF_T)
+		return (EFBIG);
+
+	if (uio->uio_resid == 0)
+		return (0);
+
+	if (limit > MAXOFF_T)
+		limit = MAXOFF_T;
+
+	switch (cn->cgn_type) {
+	case CG_NOTIFY:
+		error = cgrp_wr_notify(cn, uio);
+		break;
+	case CG_PROCS:
+		error = cgrp_wr_proc_or_task(cgm, cn, uio, CG_WR_PROCS);
+		break;
+	case CG_REL_AGENT:
+		error = cgrp_wr_rel_agent(cgm, uio);
+		break;
+	case CG_TASKS:
+		error = cgrp_wr_proc_or_task(cgm, cn, uio, CG_WR_TASKS);
+		break;
+	default:
+		VERIFY(0);
+	}
+
+	return (error);
+}
+
+/*
+ * pidlock is held on entry but dropped on exit. Because we might have to drop
+ * locks and loop if the process is already P_PR_LOCKed, it is possible that
+ * the process might be gone when we return from this function.
+ */
+static proc_t *
+cgrp_p_lock(proc_t *p)
+{
+	kmutex_t *mp;
+	pid_t pid;
+
+	ASSERT(MUTEX_HELD(&pidlock));
+
+	/* first try the fast path */
+	mutex_enter(&p->p_lock);
+	if (p->p_flag & SEXITING) {
+		mutex_exit(&p->p_lock);
+		mutex_exit(&pidlock);
+		return (NULL);
+	}
+
+	if (!(p->p_proc_flag & P_PR_LOCK)) {
+		p->p_proc_flag |= P_PR_LOCK;
+		mutex_exit(&p->p_lock);
+		mutex_exit(&pidlock);
+		THREAD_KPRI_REQUEST();
+		return (p);
+	}
+	mutex_exit(&p->p_lock);
+
+	pid = p->p_pid;
+	for (;;) {
+		/*
+		 * p_lock is persistent, but p itself is not -- it could
+		 * vanish during cv_wait().  Load p->p_lock now so we can
+		 * drop it after cv_wait() without referencing p.
+		 */
+		mp = &p->p_lock;
+		mutex_enter(mp);
+		mutex_exit(&pidlock);
+
+		if (p->p_flag & SEXITING) {
+			mutex_exit(mp);
+			return (NULL);
+		}
+
+		if (!(p->p_proc_flag & P_PR_LOCK))
+			break;
+
+		cv_wait(&pr_pid_cv[p->p_slot], mp);
+		mutex_exit(mp);
+
+		mutex_enter(&pidlock);
+		p = prfind(pid);
+		if (p == NULL || p->p_stat == SIDL) {
+			mutex_exit(&pidlock);
+			return (NULL);
+		}
+	}
+
+	p->p_proc_flag |= P_PR_LOCK;
+	mutex_exit(mp);
+	ASSERT(!MUTEX_HELD(&pidlock));
+	THREAD_KPRI_REQUEST();
+	return (p);
+}
+
+static void
+cgrp_p_unlock(proc_t *p)
+{
+	ASSERT(p->p_proc_flag & P_PR_LOCK);
+	ASSERT(MUTEX_HELD(&p->p_lock));
+	ASSERT(!MUTEX_HELD(&pidlock));
+
+	p->p_proc_flag &= ~P_PR_LOCK;
+	cv_signal(&pr_pid_cv[p->p_slot]);
+	mutex_exit(&p->p_lock);
+	THREAD_KPRI_RELEASE();
+}
+
+/*
+ * Read value from the notify_on_release pseudo file on the parent node
+ * (which is the actual cgroup node). We don't bother taking the cg_contents
+ * lock since it's a single instruction so an empty group action/read will
+ * only see one value or the other.
+ */
+/* ARGSUSED */
+static int
+cgrp_rd_notify(cgrp_mnt_t *cgm, cgrp_node_t *cn, struct uio *uio)
+{
+	int len;
+	int error = 0;
+	char buf[16];
+	char *rdp;
+	/* the flag is on the containing dir */
+	uint_t value = cn->cgn_parent->cgn_notify;
+
+	len = snprintf(buf, sizeof (buf), "%u\n", value);
+	if (uio->uio_offset > len)
+		return (0);
+
+	len -= uio->uio_offset;
+	rdp = &buf[uio->uio_offset];
+	len = (uio->uio_resid < len) ? uio->uio_resid : len;
+
+	error = uiomove(rdp, len, UIO_READ, uio);
+	return (error);
+}
+
+/*
+ * Read value from the release_agent pseudo file.
+ */
+static int
+cgrp_rd_rel_agent(cgrp_mnt_t *cgm, struct uio *uio)
+{
+	int len;
+	int error = 0;
+	char *rdp;
+
+	mutex_enter(&cgm->cg_contents);
+
+	if (cgm->cg_agent[0] == '\0') {
+		mutex_exit(&cgm->cg_contents);
+		return (0);
+	}
+
+	len = strlen(cgm->cg_agent);
+	if (uio->uio_offset > len) {
+		mutex_exit(&cgm->cg_contents);
+		return (0);
+	}
+
+	len -= uio->uio_offset;
+	rdp = &cgm->cg_agent[uio->uio_offset];
+	len = (uio->uio_resid < len) ? uio->uio_resid : len;
+
+	error = uiomove(rdp, len, UIO_READ, uio);
+
+	mutex_exit(&cgm->cg_contents);
+
+	return (error);
+}
+
+/*
+ * Read pids from the cgroup.procs pseudo file. We have to look at all of the
+ * processes to find applicable ones, then report pids for any process which
+ * has all of its threads in the same cgroup.
+ */
+static int
+cgrp_rd_procs(cgrp_mnt_t *cgm, cgrp_node_t *cn, struct uio *uio)
+{
+	int i;
+	ssize_t offset = 0;
+	ssize_t uresid;
+	zoneid_t zoneid = curproc->p_zone->zone_id;
+	int error = 0;
+	pid_t initpid = curproc->p_zone->zone_proc_initpid;
+	pid_t schedpid = curproc->p_zone->zone_zsched->p_pid;
+	/* the cgroup ID is on the containing dir */
+	uint_t cg_id = cn->cgn_parent->cgn_id;
+
+	/* Scan all of the process entries */
+	for (i = 1; i < v.v_proc && (uresid = uio->uio_resid) > 0; i++) {
+		proc_t *p;
+		ssize_t len;
+		pid_t pid;
+		char buf[16];
+		char *rdp;
+		kthread_t *t;
+		boolean_t in_cg;
+
+		mutex_enter(&pidlock);
+		/*
+		 * Skip indices for which there is no pid_entry, PIDs for
+		 * which there is no corresponding process, system processes,
+		 * a PID of 0, the pid for our zsched process,  anything the
+		 * security policy doesn't allow us to look at, its not an
+		 * lx-branded process and processes that are not in the zone.
+		 */
+		if ((p = pid_entry(i)) == NULL ||
+		    p->p_stat == SIDL ||
+		    (p->p_flag & SSYS) != 0 ||
+		    p->p_pid == 0 ||
+		    p->p_pid == schedpid ||
+		    secpolicy_basic_procinfo(CRED(), p, curproc) != 0 ||
+		    p->p_brand != &lx_brand ||
+		    p->p_zone->zone_id != zoneid) {
+			mutex_exit(&pidlock);
+			continue;
+		}
+
+		mutex_enter(&p->p_lock);
+		if ((t = p->p_tlist) == NULL) {
+			/* no threads, skip it */
+			mutex_exit(&p->p_lock);
+			mutex_exit(&pidlock);
+			continue;
+		}
+
+		/*
+		 * Check if all threads are in this cgroup.
+		 */
+		in_cg = B_TRUE;
+		mutex_enter(&cgm->cg_contents);
+		do {
+			lx_lwp_data_t *plwpd = ttolxlwp(t);
+			if (plwpd == NULL || plwpd->br_cgroupid != cg_id) {
+				in_cg = B_FALSE;
+				break;
+			}
+
+			t = t->t_forw;
+		} while (t != p->p_tlist);
+		mutex_exit(&cgm->cg_contents);
+
+		mutex_exit(&p->p_lock);
+		if (!in_cg) {
+			/*
+			 * This proc, or at least one of its threads, is not
+			 * in this cgroup.
+			 */
+			mutex_exit(&pidlock);
+			continue;
+		}
+
+		/*
+		 * Convert pid to the Linux default of 1 if we're the zone's
+		 * init process, otherwise use the value from the proc struct
+		 */
+		if (p->p_pid == initpid) {
+			pid = 1;
+		} else {
+			pid = p->p_pid;
+		}
+
+		mutex_exit(&pidlock);
+
+		/*
+		 * Generate pid line and write all or part of it if we're
+		 * in the right spot within the pseudo file.
+		 */
+		len = snprintf(buf, sizeof (buf), "%u\n", pid);
+		if ((offset + len) > uio->uio_offset) {
+			int diff = (int)(uio->uio_offset - offset);
+
+			ASSERT(diff < len);
+			offset += diff;
+			rdp = &buf[diff];
+			len -= diff;
+			if (len > uresid)
+				len = uresid;
+
+			error = uiomove(rdp, len, UIO_READ, uio);
+			if (error != 0)
+				return (error);
+		}
+		offset += len;
+	}
+
+	return (0);
+}
+
+/*
+ * We are given a locked process we know is valid, report on any of its thresds
+ * that are in the cgroup.
+ */
+static int
+cgrp_rd_proc_tasks(uint_t cg_id, proc_t *p, pid_t initpid, ssize_t *offset,
+    struct uio *uio)
+{
+	int error = 0;
+	uint_t tid;
+	char buf[16];
+	char *rdp;
+	kthread_t *t;
+
+	ASSERT(p->p_proc_flag & P_PR_LOCK);
+
+	/*
+	 * Report all threads in this cgroup.
+	 */
+	t = p->p_tlist;
+	do {
+		lx_lwp_data_t *plwpd = ttolxlwp(t);
+		if (plwpd == NULL) {
+			t = t->t_forw;
+			continue;
+		}
+
+		if (plwpd->br_cgroupid == cg_id) {
+			int len;
+
+			/*
+			 * Convert taskid to the Linux default of 1 if
+			 * we're the zone's init process.
+			 */
+			tid = plwpd->br_pid;
+			if (tid == initpid)
+				tid = 1;
+
+			len = snprintf(buf, sizeof (buf), "%u\n", tid);
+			if ((*offset + len) > uio->uio_offset) {
+				int diff;
+
+				diff = (int)(uio->uio_offset - *offset);
+				ASSERT(diff < len);
+				*offset = *offset + diff;
+				rdp = &buf[diff];
+				len -= diff;
+				if (len > uio->uio_resid)
+					len = uio->uio_resid;
+
+				error = uiomove(rdp, len, UIO_READ, uio);
+				if (error != 0)
+					return (error);
+			}
+			*offset = *offset + len;
+		}
+
+		t = t->t_forw;
+	} while (t != p->p_tlist && uio->uio_resid > 0);
+
+	return (0);
+}
+
+/*
+ * Read pids from the tasks pseudo file. We have to look at all of the
+ * processes to find applicable ones, then report pids for any thread in the
+ * cgroup. We return the emulated lx thread pid here, not the internal thread
+ * ID. Because we're possibly doing IO for each taskid we lock the process
+ * so that the threads don't change while we're working on it (although threads
+ * can change if we fill up the read buffer and come back later for a
+ * subsequent read).
+ */
+static int
+cgrp_rd_tasks(cgrp_mnt_t *cgm, cgrp_node_t *cn, struct uio *uio)
+{
+	int i;
+	ssize_t offset = 0;
+	zoneid_t zoneid = curproc->p_zone->zone_id;
+	int error = 0;
+	pid_t initpid = curproc->p_zone->zone_proc_initpid;
+	pid_t schedpid = curproc->p_zone->zone_zsched->p_pid;
+	/* the cgroup ID is on the containing dir */
+	uint_t cg_id = cn->cgn_parent->cgn_id;
+
+	/* Scan all of the process entries */
+	for (i = 1; i < v.v_proc && uio->uio_resid > 0; i++) {
+		proc_t *p;
+
+		mutex_enter(&pidlock);
+		/*
+		 * Skip indices for which there is no pid_entry, PIDs for
+		 * which there is no corresponding process, system processes,
+		 * a PID of 0, the pid for our zsched process,  anything the
+		 * security policy doesn't allow us to look at, its not an
+		 * lx-branded process and processes that are not in the zone.
+		 */
+		if ((p = pid_entry(i)) == NULL ||
+		    p->p_stat == SIDL ||
+		    (p->p_flag & SSYS) != 0 ||
+		    p->p_pid == 0 ||
+		    p->p_pid == schedpid ||
+		    secpolicy_basic_procinfo(CRED(), p, curproc) != 0 ||
+		    p->p_brand != &lx_brand ||
+		    p->p_zone->zone_id != zoneid) {
+			mutex_exit(&pidlock);
+			continue;
+		}
+
+		if (p->p_tlist == NULL) {
+			/* no threads, skip it */
+			mutex_exit(&pidlock);
+			continue;
+		}
+
+		p = cgrp_p_lock(p);
+		ASSERT(!MUTEX_HELD(&pidlock));
+		if (p == NULL)
+			continue;
+
+		mutex_enter(&cgm->cg_contents);
+		error = cgrp_rd_proc_tasks(cg_id, p, initpid, &offset, uio);
+		mutex_exit(&cgm->cg_contents);
+
+		mutex_enter(&p->p_lock);
+		cgrp_p_unlock(p);
+
+		if (error != 0)
+			return (error);
+	}
+
+	return (0);
+}
+
+static int
+cgrp_rd(cgrp_mnt_t *cgm, cgrp_node_t *cn, struct uio *uio)
+{
+	int error = 0;
+
+	if (uio->uio_loffset >= MAXOFF_T)
+		return (0);
+	if (uio->uio_loffset < 0)
+		return (EINVAL);
+	if (uio->uio_resid == 0)
+		return (0);
+
+	switch (cn->cgn_type) {
+	case CG_NOTIFY:
+		error = cgrp_rd_notify(cgm, cn, uio);
+		break;
+	case CG_PROCS:
+		error = cgrp_rd_procs(cgm, cn, uio);
+		break;
+	case CG_REL_AGENT:
+		error = cgrp_rd_rel_agent(cgm, uio);
+		break;
+	case CG_TASKS:
+		error = cgrp_rd_tasks(cgm, cn, uio);
+		break;
+	default:
+		VERIFY(0);
+	}
+
+	return (error);
+}
+
+/* ARGSUSED2 */
+static int
+cgrp_read(struct vnode *vp, struct uio *uiop, int ioflag, cred_t *cred,
+    struct caller_context *ct)
+{
+	cgrp_node_t *cn = VTOCGN(vp);
+	cgrp_mnt_t *cgm = VTOCGM(vp);
+	int error;
+
+	/*
+	 * We don't support reading non-regular files
+	 */
+	if (vp->v_type == VDIR)
+		return (EISDIR);
+	if (vp->v_type != VREG)
+		return (EINVAL);
+	error = cgrp_rd(cgm, cn, uiop);
+
+	return (error);
+}
+
+/* ARGSUSED */
+static int
+cgrp_write(struct vnode *vp, struct uio *uiop, int ioflag, struct cred *cred,
+    struct caller_context *ct)
+{
+	cgrp_node_t *cn = VTOCGN(vp);
+	cgrp_mnt_t *cgm = VTOCGM(vp);
+	int error;
+
+	/*
+	 * We don't support writing to non-regular files
+	 */
+	if (vp->v_type != VREG)
+		return (EINVAL);
+
+	if (ioflag & FAPPEND) {
+		/* In append mode start at end of file. */
+		uiop->uio_loffset = cn->cgn_size;
+	}
+
+	error = cgrp_wr(cgm, cn, uiop);
+
+	return (error);
+}
+
+/* ARGSUSED2 */
+static int
+cgrp_getattr(struct vnode *vp, struct vattr *vap, int flags, struct cred *cred,
+    caller_context_t *ct)
+{
+	cgrp_node_t *cn = VTOCGN(vp);
+	cgrp_mnt_t *cgm;
+
+	cgm = VTOCGM(cn->cgn_vnode);
+	mutex_enter(&cgm->cg_contents);
+	vap->va_type = vp->v_type;
+	vap->va_mode = cn->cgn_mode & MODEMASK;
+	vap->va_uid = cn->cgn_uid;
+	vap->va_gid = cn->cgn_gid;
+	vap->va_fsid = cn->cgn_fsid;
+	vap->va_nodeid = (ino64_t)cn->cgn_nodeid;
+	vap->va_nlink = cn->cgn_nlink;
+	vap->va_size = (u_offset_t)cn->cgn_size;
+	vap->va_atime = cn->cgn_atime;
+	vap->va_mtime = cn->cgn_mtime;
+	vap->va_ctime = cn->cgn_ctime;
+	vap->va_blksize = PAGESIZE;
+	vap->va_rdev = cn->cgn_rdev;
+	vap->va_seq = cn->cgn_seq;
+
+	vap->va_nblocks = (fsblkcnt64_t)btodb(ptob(btopr(vap->va_size)));
+	mutex_exit(&cgm->cg_contents);
+	return (0);
+}
+
+/*ARGSUSED4*/
+static int
+cgrp_setattr(struct vnode *vp, struct vattr *vap, int flags, struct cred *cred,
+    caller_context_t *ct)
+{
+	cgrp_node_t *cn = VTOCGN(vp);
+	cgrp_mnt_t *cgm;
+	int error = 0;
+	struct vattr *get;
+	long mask;
+
+	/*
+	 * Cannot set these attributes
+	 */
+	if ((vap->va_mask & AT_NOSET) || (vap->va_mask & AT_XVATTR) ||
+	    (vap->va_mode & (S_ISUID | S_ISGID)) || (vap->va_mask & AT_SIZE))
+		return (EINVAL);
+
+	cgm = VTOCGM(cn->cgn_vnode);
+	mutex_enter(&cgm->cg_contents);
+
+	get = &cn->cgn_attr;
+	/*
+	 * Change file access modes. Must be owner or have sufficient
+	 * privileges.
+	 */
+	error = secpolicy_vnode_setattr(cred, vp, vap, get, flags, cgrp_taccess,
+	    cn);
+
+	if (error)
+		goto out;
+
+	mask = vap->va_mask;
+
+	if (mask & AT_MODE) {
+		get->va_mode &= S_IFMT;
+		get->va_mode |= vap->va_mode & ~S_IFMT;
+	}
+
+	if (mask & AT_UID)
+		get->va_uid = vap->va_uid;
+	if (mask & AT_GID)
+		get->va_gid = vap->va_gid;
+	if (mask & AT_ATIME)
+		get->va_atime = vap->va_atime;
+	if (mask & AT_MTIME)
+		get->va_mtime = vap->va_mtime;
+
+	if (mask & (AT_UID | AT_GID | AT_MODE | AT_MTIME))
+		gethrestime(&cn->cgn_ctime);
+
+out:
+	mutex_exit(&cgm->cg_contents);
+	return (error);
+}
+
+/* ARGSUSED2 */
+static int
+cgrp_access(struct vnode *vp, int mode, int flags, struct cred *cred,
+    caller_context_t *ct)
+{
+	cgrp_node_t *cn = VTOCGN(vp);
+	cgrp_mnt_t *cgm;
+	int error;
+
+	cgm = VTOCGM(cn->cgn_vnode);
+	mutex_enter(&cgm->cg_contents);
+	error = cgrp_taccess(cn, mode, cred);
+	mutex_exit(&cgm->cg_contents);
+	return (error);
+}
+
+/* ARGSUSED3 */
+static int
+cgrp_lookup(struct vnode *dvp, char *nm, struct vnode **vpp,
+    struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred,
+    caller_context_t *ct, int *direntflags, pathname_t *realpnp)
+{
+	cgrp_node_t *cn = VTOCGN(dvp);
+	cgrp_mnt_t *cgm;
+	cgrp_node_t *ncn = NULL;
+	int error;
+
+	/* disallow extended attrs */
+	if (flags & LOOKUP_XATTR)
+		return (EINVAL);
+
+	/*
+	 * Null component name is a synonym for directory being searched.
+	 */
+	if (*nm == '\0') {
+		VN_HOLD(dvp);
+		*vpp = dvp;
+		return (0);
+	}
+	ASSERT(cn);
+
+	cgm = VTOCGM(cn->cgn_vnode);
+	mutex_enter(&cgm->cg_contents);
+	error = cgrp_dirlookup(cn, nm, &ncn, cred);
+	mutex_exit(&cgm->cg_contents);
+
+	if (error == 0) {
+		ASSERT(ncn);
+		*vpp = CGNTOV(ncn);
+	}
+
+	return (error);
+}
+
+/* ARGSUSED */
+static int
+cgrp_create(struct vnode *dvp, char *nm, struct vattr *vap,
+    enum vcexcl exclusive, int mode, struct vnode **vpp, struct cred *cred,
+    int flag, caller_context_t *ct, vsecattr_t *vsecp)
+{
+	cgrp_node_t *parent = VTOCGN(dvp);
+	cgrp_node_t *cn = NULL;
+	cgrp_mnt_t *cgm;
+	int error;
+
+	if (*nm == '\0')
+		return (EPERM);
+
+	cgm = VTOCGM(parent->cgn_vnode);
+	mutex_enter(&cgm->cg_contents);
+	error = cgrp_dirlookup(parent, nm, &cn, cred);
+	if (error == 0) {		/* name found */
+		ASSERT(cn);
+
+		mutex_exit(&cgm->cg_contents);
+		/*
+		 * Creating an existing file, allow it except for the following
+		 * errors.
+		 */
+		if (exclusive == EXCL) {
+			error = EEXIST;
+		} else if ((CGNTOV(cn)->v_type == VDIR) && (mode & VWRITE)) {
+			error = EISDIR;
+		} else {
+			error = cgrp_taccess(cn, mode, cred);
+		}
+		if (error != 0) {
+			cgnode_rele(cn);
+			return (error);
+		}
+		*vpp = CGNTOV(cn);
+		return (0);
+	}
+	mutex_exit(&cgm->cg_contents);
+
+	/*
+	 * cgroups doesn't allow creation of additional, non-subsystem specific
+	 * files in a dir
+	 */
+	return (EPERM);
+}
+
+/* ARGSUSED3 */
+static int
+cgrp_remove(struct vnode *dvp, char *nm, struct cred *cred,
+    caller_context_t *ct, int flags)
+{
+	cgrp_node_t *parent = VTOCGN(dvp);
+	int error;
+	cgrp_node_t *cn = NULL;
+	cgrp_mnt_t *cgm;
+
+	/*
+	 * Removal of subsystem-specific files is not allowed but we need
+	 * to return the correct error if they try to remove a non-existent
+	 * file.
+	 */
+
+	cgm = VTOCGM(parent->cgn_vnode);
+	mutex_enter(&cgm->cg_contents);
+	error = cgrp_dirlookup(parent, nm, &cn, cred);
+	mutex_exit(&cgm->cg_contents);
+	if (error)
+		return (error);
+
+	ASSERT(cn);
+	cgnode_rele(cn);
+	return (EPERM);
+}
+
+/* ARGSUSED */
+static int
+cgrp_link(struct vnode *dvp, struct vnode *srcvp, char *cnm, struct cred *cred,
+    caller_context_t *ct, int flags)
+{
+	/* cgroups doesn't support hard links */
+	return (EPERM);
+}
+
+/*
+ * Rename of subsystem-specific files is not allowed but we can rename
+ * directories (i.e. sub-groups). We cannot mv subdirs from one group to
+ * another so the src and dest vnode must be the same.
+ */
+/* ARGSUSED5 */
+static int
+cgrp_rename(
+	struct vnode *odvp,	/* source parent vnode */
+	char *onm,		/* source name */
+	struct vnode *ndvp,	/* destination parent vnode */
+	char *nnm,		/* destination name */
+	struct cred *cred,
+	caller_context_t *ct,
+	int flags)
+{
+	cgrp_node_t *fromparent;
+	cgrp_node_t *toparent;
+	cgrp_node_t *fromcn = NULL;	/* source cgrp_node */
+	cgrp_mnt_t *cgm = VTOCGM(odvp);
+	int error, err;
+
+	fromparent = VTOCGN(odvp);
+	toparent = VTOCGN(ndvp);
+
+	if (fromparent != toparent)
+		return (EIO);
+
+	/* discourage additional use of toparent */
+	toparent = NULL;
+
+	mutex_enter(&cgm->cg_contents);
+
+	/*
+	 * Look up cgrp_node of file we're supposed to rename.
+	 */
+	error = cgrp_dirlookup(fromparent, onm, &fromcn, cred);
+	if (error) {
+		mutex_exit(&cgm->cg_contents);
+		return (error);
+	}
+
+	if (fromcn->cgn_type != CG_CGROUP_DIR) {
+		error = EPERM;
+		goto done;
+	}
+
+	/*
+	 * Make sure we can delete the old (source) entry.  This
+	 * requires write permission on the containing directory.
+	 */
+	if (((error = cgrp_taccess(fromparent, VWRITE, cred)) != 0))
+		goto done;
+
+	/*
+	 * Check for renaming to or from '.' or '..' or that
+	 * fromcn == fromparent
+	 */
+	if ((onm[0] == '.' &&
+	    (onm[1] == '\0' || (onm[1] == '.' && onm[2] == '\0'))) ||
+	    (nnm[0] == '.' &&
+	    (nnm[1] == '\0' || (nnm[1] == '.' && nnm[2] == '\0'))) ||
+	    (fromparent == fromcn)) {
+		error = EINVAL;
+		goto done;
+	}
+
+	/*
+	 * Link source to new target
+	 */
+	error = cgrp_direnter(cgm, fromparent, nnm, DE_RENAME,
+	    fromcn, (struct vattr *)NULL,
+	    (cgrp_node_t **)NULL, cred);
+
+	if (error)
+		goto done;
+
+	/*
+	 * Unlink from source.
+	 */
+	error = err = cgrp_dirdelete(fromparent, fromcn, onm, DR_RENAME, cred);
+
+	/*
+	 * The following handles the case where our source cgrp_node was
+	 * removed before we got to it.
+	 */
+	if (error == ENOENT)
+		error = 0;
+
+	if (err == 0) {
+		vnevent_rename_src(CGNTOV(fromcn), odvp, onm, ct);
+		vnevent_rename_dest_dir(ndvp, CGNTOV(fromcn), nnm, ct);
+	}
+
+done:
+	mutex_exit(&cgm->cg_contents);
+	cgnode_rele(fromcn);
+
+	return (error);
+}
+
+/* ARGSUSED5 */
+static int
+cgrp_mkdir(struct vnode *dvp, char *nm, struct vattr *va, struct vnode **vpp,
+    struct cred *cred, caller_context_t *ct, int flags, vsecattr_t *vsecp)
+{
+	cgrp_node_t *parent = VTOCGN(dvp);
+	cgrp_node_t *self = NULL;
+	cgrp_mnt_t *cgm = VTOCGM(dvp);
+	int error;
+
+	/*
+	 * Might be dangling directory.  Catch it here, because a ENOENT
+	 * return from cgrp_dirlookup() is an "ok return".
+	 */
+	if (parent->cgn_nlink == 0)
+		return (ENOENT);
+
+	mutex_enter(&cgm->cg_contents);
+	error = cgrp_dirlookup(parent, nm, &self, cred);
+	if (error == 0) {
+		ASSERT(self != NULL);
+		mutex_exit(&cgm->cg_contents);
+		cgnode_rele(self);
+		return (EEXIST);
+	}
+	if (error != ENOENT) {
+		mutex_exit(&cgm->cg_contents);
+		return (error);
+	}
+
+	error = cgrp_direnter(cgm, parent, nm, DE_MKDIR, (cgrp_node_t *)NULL,
+	    va, &self, cred);
+	if (error) {
+		mutex_exit(&cgm->cg_contents);
+		if (self != NULL)
+			cgnode_rele(self);
+		return (error);
+	}
+	mutex_exit(&cgm->cg_contents);
+	*vpp = CGNTOV(self);
+	return (0);
+}
+
+/* ARGSUSED4 */
+static int
+cgrp_rmdir(struct vnode *dvp, char *nm, struct vnode *cdir, struct cred *cred,
+    caller_context_t *ct, int flags)
+{
+	cgrp_node_t *parent = VTOCGN(dvp);
+	cgrp_mnt_t *cgm;
+	cgrp_node_t *self = NULL;
+	struct vnode *vp;
+	int error = 0;
+
+	/*
+	 * Return error when removing . and ..
+	 */
+	if (strcmp(nm, ".") == 0)
+		return (EINVAL);
+	if (strcmp(nm, "..") == 0)
+		return (EEXIST); /* Should be ENOTEMPTY */
+
+	cgm = VTOCGM(parent->cgn_vnode);
+	mutex_enter(&cgm->cg_contents);
+
+	error = cgrp_dirlookup(parent, nm, &self, cred);
+	if (error) {
+		mutex_exit(&cgm->cg_contents);
+		return (error);
+	}
+
+	vp = CGNTOV(self);
+	if (vp == dvp || vp == cdir) {
+		error = EINVAL;
+		goto done;
+	}
+	if (self->cgn_type != CG_CGROUP_DIR) {
+		error = ENOTDIR;
+		goto done;
+	}
+
+	cgm = (cgrp_mnt_t *)VFSTOCGM(self->cgn_vnode->v_vfsp);
+
+	/*
+	 * Check for the existence of any sub-cgroup directories or tasks in
+	 * the cgroup.
+	 */
+	if (self->cgn_task_cnt > 0 || self->cgn_dirents > N_DIRENTS(cgm)) {
+		error = EEXIST;
+		/*
+		 * Update atime because checking cn_dirents is logically
+		 * equivalent to reading the directory
+		 */
+		gethrestime(&self->cgn_atime);
+		goto done;
+	}
+
+	if (vn_vfswlock(vp)) {
+		error = EBUSY;
+		goto done;
+	}
+	if (vn_mountedvfs(vp) != NULL) {
+		error = EBUSY;
+	} else {
+		error = cgrp_dirdelete(parent, self, nm, DR_RMDIR, cred);
+	}
+
+	vn_vfsunlock(vp);
+
+	if (parent->cgn_task_cnt == 0 &&
+	    parent->cgn_dirents == N_DIRENTS(cgm) && parent->cgn_notify == 1) {
+		cgrp_rel_agent_event(cgm, parent, B_FALSE);
+		ASSERT(MUTEX_NOT_HELD(&cgm->cg_contents));
+		goto dropped;
+	}
+
+done:
+	mutex_exit(&cgm->cg_contents);
+dropped:
+	vnevent_rmdir(CGNTOV(self), dvp, nm, ct);
+	cgnode_rele(self);
+
+	return (error);
+}
+
+/* ARGSUSED2 */
+static int
+cgrp_readdir(struct vnode *vp, struct uio *uiop, struct cred *cred, int *eofp,
+    caller_context_t *ct, int flags)
+{
+	cgrp_node_t *cn = VTOCGN(vp);
+	cgrp_mnt_t *cgm;
+	cgrp_dirent_t *cdp;
+	int error = 0;
+	size_t namelen;
+	struct dirent64 *dp;
+	ulong_t offset;
+	ulong_t total_bytes_wanted;
+	long outcount = 0;
+	long bufsize;
+	int reclen;
+	caddr_t outbuf;
+
+	if (uiop->uio_loffset >= MAXOFF_T) {
+		if (eofp)
+			*eofp = 1;
+		return (0);
+	}
+
+	if (uiop->uio_iovcnt != 1)
+		return (EINVAL);
+
+	if (vp->v_type != VDIR)
+		return (ENOTDIR);
+
+	cgm = VTOCGM(cn->cgn_vnode);
+	mutex_enter(&cgm->cg_contents);
+
+	if (cn->cgn_dir == NULL) {
+		VERIFY(cn->cgn_nlink == 0);
+		mutex_exit(&cgm->cg_contents);
+		return (0);
+	}
+
+	/*
+	 * Get space for multiple directory entries
+	 */
+	total_bytes_wanted = uiop->uio_iov->iov_len;
+	bufsize = total_bytes_wanted + sizeof (struct dirent64);
+	outbuf = kmem_alloc(bufsize, KM_SLEEP);
+
+	/* LINTED: alignment */
+	dp = (struct dirent64 *)outbuf;
+
+	offset = 0;
+	cdp = cn->cgn_dir;
+	while (cdp) {
+		namelen = strlen(cdp->cgd_name);	/* no +1 needed */
+		offset = cdp->cgd_offset;
+		if (offset >= uiop->uio_offset) {
+			reclen = (int)DIRENT64_RECLEN(namelen);
+			if (outcount + reclen > total_bytes_wanted) {
+				if (!outcount) {
+					/* Buffer too small for any entries. */
+					error = EINVAL;
+				}
+				break;
+			}
+			ASSERT(cdp->cgd_cgrp_node != NULL);
+
+			/* use strncpy(9f) to zero out uninitialized bytes */
+
+			(void) strncpy(dp->d_name, cdp->cgd_name,
+			    DIRENT64_NAMELEN(reclen));
+			dp->d_reclen = (ushort_t)reclen;
+			dp->d_ino = (ino64_t)cdp->cgd_cgrp_node->cgn_nodeid;
+			dp->d_off = (offset_t)cdp->cgd_offset + 1;
+			dp = (struct dirent64 *)((uintptr_t)dp + dp->d_reclen);
+			outcount += reclen;
+			ASSERT(outcount <= bufsize);
+		}
+		cdp = cdp->cgd_next;
+	}
+
+	if (!error)
+		error = uiomove(outbuf, outcount, UIO_READ, uiop);
+
+	if (!error) {
+		/*
+		 * If we reached the end of the list our offset should now be
+		 * just past the end.
+		 */
+		if (!cdp) {
+			offset += 1;
+			if (eofp)
+				*eofp = 1;
+		} else if (eofp)
+			*eofp = 0;
+		uiop->uio_offset = offset;
+	}
+	gethrestime(&cn->cgn_atime);
+
+	mutex_exit(&cgm->cg_contents);
+
+	kmem_free(outbuf, bufsize);
+	return (error);
+}
+
+/* ARGSUSED */
+static int
+cgrp_symlink(struct vnode *dvp, char *lnm, struct vattr *cva, char *cnm,
+    struct cred *cred, caller_context_t *ct, int flags)
+{
+	/* cgroups doesn't support symlinks */
+	return (EPERM);
+}
+
+/* ARGSUSED */
+static void
+cgrp_inactive(struct vnode *vp, struct cred *cred, caller_context_t *ct)
+{
+	cgrp_node_t *cn = VTOCGN(vp);
+	cgrp_mnt_t *cgm = VFSTOCGM(vp->v_vfsp);
+
+	mutex_enter(&cgm->cg_contents);
+	mutex_enter(&vp->v_lock);
+	ASSERT(vp->v_count >= 1);
+
+	/*
+	 * If we don't have the last hold or the link count is non-zero,
+	 * there's little to do -- just drop our hold.
+	 */
+	if (vp->v_count > 1 || cn->cgn_nlink != 0) {
+		vp->v_count--;
+		mutex_exit(&vp->v_lock);
+		mutex_exit(&cgm->cg_contents);
+		return;
+	}
+
+	if (cn->cgn_forw == NULL)
+		cgm->cg_rootnode->cgn_back = cn->cgn_back;
+	else
+		cn->cgn_forw->cgn_back = cn->cgn_back;
+	cn->cgn_back->cgn_forw = cn->cgn_forw;
+
+	mutex_exit(&vp->v_lock);
+	mutex_exit(&cgm->cg_contents);
+
+	/* Here's our chance to send invalid event */
+	vn_invalid(CGNTOV(cn));
+
+	vn_free(CGNTOV(cn));
+	kmem_free(cn, sizeof (cgrp_node_t));
+}
+
+/* ARGSUSED */
+static int
+cgrp_seek(struct vnode *vp, offset_t ooff, offset_t *noffp,
+    caller_context_t *ct)
+{
+	return ((*noffp < 0 || *noffp > MAXOFFSET_T) ? EINVAL : 0);
+}
+
+/* ARGSUSED */
+static int
+cgrp_rwlock(struct vnode *vp, int write_lock, caller_context_t *ctp)
+{
+	return (write_lock);
+}
+
+/* ARGSUSED */
+static void
+cgrp_rwunlock(struct vnode *vp, int write_lock, caller_context_t *ctp)
+{
+}
+
+static int
+cgrp_pathconf(struct vnode *vp, int cmd, ulong_t *valp, cred_t *cr,
+    caller_context_t *ct)
+{
+	int error;
+
+	switch (cmd) {
+	case _PC_XATTR_EXISTS:
+		if (vp->v_vfsp->vfs_flag & VFS_XATTR) {
+			*valp = 0;	/* assume no attributes */
+			error = 0;	/* okay to ask */
+		} else {
+			error = EINVAL;
+		}
+		break;
+	case _PC_SATTR_ENABLED:
+	case _PC_SATTR_EXISTS:
+		*valp = vfs_has_feature(vp->v_vfsp, VFSFT_SYSATTR_VIEWS) &&
+		    (vp->v_type == VREG || vp->v_type == VDIR);
+		error = 0;
+		break;
+	case _PC_TIMESTAMP_RESOLUTION:
+		/* nanosecond timestamp resolution */
+		*valp = 1L;
+		error = 0;
+		break;
+	default:
+		error = fs_pathconf(vp, cmd, valp, cr, ct);
+	}
+	return (error);
+}
+
+
+struct vnodeops *cgrp_vnodeops;
+
+const fs_operation_def_t cgrp_vnodeops_template[] = {
+	VOPNAME_OPEN,		{ .vop_open = cgrp_open },
+	VOPNAME_CLOSE,		{ .vop_close = cgrp_close },
+	VOPNAME_READ,		{ .vop_read = cgrp_read },
+	VOPNAME_WRITE,		{ .vop_write = cgrp_write },
+	VOPNAME_GETATTR,	{ .vop_getattr = cgrp_getattr },
+	VOPNAME_SETATTR,	{ .vop_setattr = cgrp_setattr },
+	VOPNAME_ACCESS,		{ .vop_access = cgrp_access },
+	VOPNAME_LOOKUP,		{ .vop_lookup = cgrp_lookup },
+	VOPNAME_CREATE,		{ .vop_create = cgrp_create },
+	VOPNAME_REMOVE,		{ .vop_remove = cgrp_remove },
+	VOPNAME_LINK,		{ .vop_link = cgrp_link },
+	VOPNAME_RENAME,		{ .vop_rename = cgrp_rename },
+	VOPNAME_MKDIR,		{ .vop_mkdir = cgrp_mkdir },
+	VOPNAME_RMDIR,		{ .vop_rmdir = cgrp_rmdir },
+	VOPNAME_READDIR,	{ .vop_readdir = cgrp_readdir },
+	VOPNAME_SYMLINK,	{ .vop_symlink = cgrp_symlink },
+	VOPNAME_INACTIVE,	{ .vop_inactive = cgrp_inactive },
+	VOPNAME_RWLOCK,		{ .vop_rwlock = cgrp_rwlock },
+	VOPNAME_RWUNLOCK,	{ .vop_rwunlock = cgrp_rwunlock },
+	VOPNAME_SEEK,		{ .vop_seek = cgrp_seek },
+	VOPNAME_PATHCONF,	{ .vop_pathconf = cgrp_pathconf },
+	VOPNAME_VNEVENT,	{ .vop_vnevent = fs_vnevent_support },
+	NULL,			NULL
+};
--- /dev/null	Wed Oct  5 00:32:04 2016
+++ new/usr/src/uts/common/brand/lx/devfs/lxd.h	Wed Oct  5 00:32:04 2016
@@ -0,0 +1,244 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#ifndef	_LXD_H
+#define	_LXD_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/*
+ * lxd.h: declarations, data structures and macros for lxd (lxd devfs).
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/policy.h>
+#include <sys/dirent.h>
+#include <sys/errno.h>
+#include <sys/kmem.h>
+#include <sys/pathname.h>
+#include <sys/systm.h>
+#include <sys/var.h>
+#include <sys/sysmacros.h>
+#include <sys/cred.h>
+#include <sys/priv.h>
+#include <sys/vnode.h>
+#include <sys/vfs.h>
+#include <sys/statvfs.h>
+#include <sys/cmn_err.h>
+#include <sys/zone.h>
+#include <sys/uio.h>
+#include <sys/utsname.h>
+#include <sys/atomic.h>
+#include <vm/anon.h>
+#include <sys/lx_types.h>
+
+#if defined(_KERNEL)
+
+#include <sys/lx_brand.h>
+
+/*
+ * It's unlikely that we need to create more than 50-60 subdirs/symlinks
+ * as front files so we size the file system hash for 2x that number.
+ * The back devfs typically has ~80 nodes so this is also a comfortable size
+ * for the back hash table.
+ */
+#define	LXD_HASH_SZ	128
+
+#define	LXD_BACK_HASH(v)	((((intptr_t)(v)) >> 10) & ((LXD_HASH_SZ) - 1))
+
+#define	LXD_NM_HASH(ldn, name, hash)				\
+	{							\
+		char Xc, *Xcp;					\
+		hash = (uint_t)(uintptr_t)(ldn) >> 8;		\
+		for (Xcp = (name); (Xc = *Xcp) != 0; Xcp++)	\
+			hash = (hash << 4) + hash + (uint_t)Xc;	\
+		hash &= (LXD_HASH_SZ - 1);			\
+	}
+
+
+enum lxd_node_type	{ LXDNT_NONE, LXDNT_BACK, LXDNT_FRONT };
+
+typedef struct lxd_dev_attr {
+	list_node_t	lxda_link;
+	char 		lxda_name[MAXPATHLEN];
+	uid_t 		lxda_uid;
+	gid_t 		lxda_gid;
+	mode_t 		lxda_mode;
+} lxd_dev_attr_t;
+
+/*
+ * lxd per-mount data structure.
+ *
+ * All fields are protected by lxd_contents.
+ * File renames on a specific file system are protected lxdm_renamelck.
+ */
+typedef struct lxd_mnt {
+	struct vfs	*lxdm_vfsp;	/* filesystem's vfs struct */
+	struct lxd_node *lxdm_rootnode;	/* root lxd_node */
+	char 		*lxdm_mntpath;	/* name of lxd mount point */
+	dev_t		lxdm_dev;	/* unique dev # of mounted `device' */
+	kmutex_t	lxdm_contents;	/* per-mount lock */
+	kmutex_t	lxdm_renamelck;	/* rename lock for this mount */
+	kmutex_t	lxdm_attrlck;	/* per-mount attr. file lock */
+	list_t		lxdm_devattrs;	/* list of device attr. settings */
+	uint_t		lxdm_gen;	/* node ID source for files */
+
+	/* protects buckets in both "dir ent" and "back" hash tables */
+	kmutex_t	lxdm_hash_mutex[LXD_HASH_SZ];
+
+	/* per-mount data for "back" vnodes in the fs */
+	uint_t		lxdm_back_refcnt; /* # outstanding "back" vnodes */
+	struct lxd_node *lxdm_back_htable[LXD_HASH_SZ];
+
+	/*
+	 * Per-mount directory data for "front" nodes in the fs.
+	 * Each front node has a directory entry but directory entries can live
+	 * on either front or back nodes.
+	 */
+	uint_t		lxdm_dent_refcnt; /* # outstanding dir ents */
+	struct lxd_dirent *lxdm_dent_htable[LXD_HASH_SZ];
+} lxd_mnt_t;
+
+/*
+ * lxd_node is the file system dependent node for lxd.
+ *
+ * The node is used to represent both front and back files. For front files
+ * the node can represent either a directory or symlink.
+ */
+typedef struct lxd_node {
+	enum lxd_node_type	lxdn_type;
+
+	/* Data for "front" nodes */
+	struct lxd_node		*lxdn_prev;	/* lnked lst of lxd nodes */
+	struct lxd_node		*lxdn_next;	/* lnked lst of lxd nodes */
+	struct lxd_node		*lxdn_parent;	/* dir containing this node */
+	krwlock_t		lxdn_rwlock;	/* serialize mods/dir updates */
+	kmutex_t		lxdn_tlock;	/* time, flag, and nlink lock */
+
+	/* these could be in a union ala tmpfs but not really necessary */
+	uint_t			lxdn_dirents;	/* number of dirents */
+	struct lxd_dirent	*lxdn_dir;	/* dirent list */
+	char			*lxdn_symlink;	/* pointer to symlink */
+	struct vattr		lxdn_attr;	/* attributes */
+
+	/* Hash table link */
+	struct lxd_node		*lxdn_hnxt;	/* link in per-mount entry */
+						/* hash table */
+	vnode_t 		*lxdn_vnode;	/* vnode for this lxd_node */
+
+	vnode_t			*lxdn_real_vp;	/* back file - real vnode */
+} lxd_node_t;
+
+/*
+ * Attributes
+ */
+#define	lxdn_mask	lxdn_attr.va_mask
+#define	lxdn_mode	lxdn_attr.va_mode
+#define	lxdn_uid	lxdn_attr.va_uid
+#define	lxdn_gid	lxdn_attr.va_gid
+#define	lxdn_fsid	lxdn_attr.va_fsid
+#define	lxdn_nodeid	lxdn_attr.va_nodeid
+#define	lxdn_nlink	lxdn_attr.va_nlink
+#define	lxdn_size	lxdn_attr.va_size
+#define	lxdn_atime	lxdn_attr.va_atime
+#define	lxdn_mtime	lxdn_attr.va_mtime
+#define	lxdn_ctime	lxdn_attr.va_ctime
+#define	lxdn_rdev	lxdn_attr.va_rdev
+#define	lxdn_blksize	lxdn_attr.va_blksize
+#define	lxdn_nblocks	lxdn_attr.va_nblocks
+#define	lxdn_seq	lxdn_attr.va_seq
+
+/*
+ * lx devfs conversion macros
+ */
+#define	VFSTOLXDM(vfsp)		((lxd_mnt_t *)(vfsp)->vfs_data)
+#define	VTOLXDM(vp)		((lxd_mnt_t *)(vp)->v_vfsp->vfs_data)
+#define	VTOLDN(vp)		((lxd_node_t *)(vp)->v_data)
+#define	LDNTOV(ln)		((ln)->lxdn_vnode)
+#define	ldnode_hold(ln)		VN_HOLD(LDNTOV(ln))
+#define	ldnode_rele(ln)		VN_RELE(LDNTOV(ln))
+
+#define	REALVP(vp)		(VTOLDN(vp)->lxdn_real_vp)
+
+/*
+ * front directories are made up of a linked list of lxd_dirent structures
+ * hanging off directory lxdn_nodes.  File names are not fixed length, but are
+ * null terminated.
+ */
+typedef struct lxd_dirent {
+	lxd_node_t		*lddir_node;	/* lxd node for this file */
+	struct lxd_dirent	*lddir_next;	/* next directory entry */
+	struct lxd_dirent	*lddir_prev;	/* prev directory entry */
+	uint_t			lddir_offset;	/* "offset" of dir entry */
+	uint_t			lddir_hash;	/* a hash of lddir_name */
+	struct lxd_dirent	*lddir_link;	/* linked via hash table */
+	lxd_node_t		*lddir_parent;	/* parent, dir we are in */
+	char			*lddir_name;	/* null terminated */
+} lxd_dirent_t;
+
+enum de_op	{ DE_CREATE, DE_MKDIR, DE_RENAME };	/* direnter ops */
+enum dr_op	{ DR_REMOVE, DR_RMDIR, DR_RENAME };	/* dirremove ops */
+
+typedef struct lxd_minor_translator {
+	char	*lxd_mt_path;		/* illumos minor node path */
+	minor_t	lxd_mt_minor;		/* illumos minor node number */
+	int	lxd_mt_lx_major;	/* linux major node number */
+	int	lxd_mt_lx_minor;	/* linux minor node number */
+} lxd_minor_translator_t;
+
+enum lxd_xl_tp	{ DTT_INVALID, DTT_LIST, DTT_CUSTOM };
+
+#define	xl_list		lxd_xl_minor.lxd_xl_list
+#define	xl_custom	lxd_xl_minor.lxd_xl_custom
+
+typedef struct lxd_devt_translator {
+	char		*lxd_xl_driver;	/* driver name */
+	major_t		lxd_xl_major;	/* driver number */
+
+	enum lxd_xl_tp	lxd_xl_type;	/* dictates how we intrep. xl_minor */
+	union {
+		uintptr_t		lxd_xl_foo; /* required to compile */
+		lxd_minor_translator_t	*lxd_xl_list;
+		void			(*lxd_xl_custom)(dev_t, dev_t *);
+	} lxd_xl_minor;
+} lxd_devt_translator_t;
+
+extern struct vnodeops *lxd_vnodeops;
+extern lxd_devt_translator_t lxd_devt_translators[];
+
+vnode_t *lxd_make_back_node(vnode_t *, lxd_mnt_t *);
+void lxd_free_back_node(lxd_node_t *);
+int lxd_dirdelete(lxd_node_t *, lxd_node_t *, char *, enum dr_op, cred_t *);
+int lxd_direnter(lxd_mnt_t *, lxd_node_t *, char *, enum de_op, lxd_node_t *,
+	lxd_node_t *, struct vattr *, lxd_node_t **, cred_t *);
+void lxd_dirinit(lxd_node_t *, lxd_node_t *);
+int lxd_dirlookup(lxd_node_t *, char *, lxd_node_t **, cred_t *);
+void lxd_dirtrunc(lxd_node_t *);
+void lxd_node_init(lxd_mnt_t *, lxd_node_t *, vnode_t *, vattr_t *, cred_t *);
+int lxd_naccess(void *, int, cred_t *);
+
+void lxd_save_attrs(lxd_mnt_t *, vnode_t *);
+void lxd_apply_db(lxd_mnt_t *);
+
+#endif /* KERNEL */
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif /* _LXD_H */
--- /dev/null	Wed Oct  5 00:32:04 2016
+++ new/usr/src/uts/common/brand/lx/devfs/lxd_attrdb.c	Wed Oct  5 00:32:04 2016
@@ -0,0 +1,368 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/vnode.h>
+#include <sys/vfs.h>
+#include <sys/vfs_opreg.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/cred.h>
+#include <sys/pathname.h>
+#include <sys/debug.h>
+#include <sys/sdt.h>
+#include <fs/fs_subr.h>
+
+#include "lxd.h"
+
+#define	LX_ATTR_FILE		"/etc/.lxd_dev_attr"
+
+#define	RD_BUFSIZE	MAXPATHLEN
+#define	ENTRY_BUFSIZE	(MAXPATHLEN + 32)
+
+static int
+lxd_db_open(int fmode, vnode_t **vpp)
+{
+	return (vn_open(LX_ATTR_FILE, UIO_SYSSPACE, fmode,
+	    (int)(0644 & MODEMASK), vpp, CRCREAT, PTOU(curproc)->u_cmask));
+}
+
+static int
+lxd_wr_entry(vnode_t *wvn, off_t offset, char *entry)
+{
+	int len, err;
+	struct uio auio;
+	struct iovec aiov;
+
+	len = strlen(entry);
+	aiov.iov_base = entry;
+	aiov.iov_len = len;
+
+	auio.uio_iov = &aiov;
+	auio.uio_iovcnt = 1;
+	auio.uio_loffset = offset;
+	auio.uio_segflg = UIO_SYSSPACE;
+	auio.uio_resid = len;
+	auio.uio_llimit = curproc->p_fsz_ctl;
+	auio.uio_fmode = FWRITE;
+	auio.uio_extflg = UIO_COPY_DEFAULT;
+
+	(void) VOP_RWLOCK(wvn, V_WRITELOCK_TRUE, NULL);
+	err = VOP_WRITE(wvn, &auio, FAPPEND, CRED(), NULL);
+	VOP_RWUNLOCK(wvn, V_WRITELOCK_TRUE, NULL);
+
+	if (err != 0)
+		return (0);
+	return (len);
+}
+
+/*
+ * Given an entry, apply a uid, gid and mode change to the given device. There
+ * is no strtok in the kernel but it's easy to tokenize the entry ourselves.
+ *
+ * entries have the form (newline removed by caller):
+ * path uid gid mode\0
+ */
+static int
+lxd_apply_entry(char *entry, char **dpath, uid_t *uidp, gid_t *gidp,
+    mode_t *modep)
+{
+	char *dp, *up, *gp, *mp, *ep;
+	long uid, gid, mode;
+	int error, res = 0;
+	vnode_t *vp;
+	vattr_t va;
+
+	dp = entry;
+
+	/* find and delimit the first field (device name) */
+	for (up = dp; *up != ' ' && *up != '\0'; up++)
+		;
+	if (*up != ' ')
+		return (-1);
+	*up++ = '\0';
+
+	/* find and delimit the second field (uid) */
+	for (gp = up; *gp != ' ' && *gp != '\0'; gp++)
+		;
+	if (*gp != ' ')
+		return (-1);
+	*gp++ = '\0';
+
+	/* find and delimit the third field (gid) */
+	for (mp = gp; *mp != ' ' && *mp != '\0'; mp++)
+		;
+	if (*mp != ' ')
+		return (-1);
+	*mp++ = '\0';
+
+	/* validate the fourth field (mode) */
+	ep = mp + strlen(mp);
+	if (*ep != '\0')
+		return (-1);
+
+	if (*dp != '/')
+		return (-1);
+
+	error = ddi_strtol(up, &ep, 10, &uid);
+	if (error != 0 || *ep != '\0' || uid > MAXUID || uid < 0)
+		return (-1);
+
+	error = ddi_strtol(gp, &ep, 10, &gid);
+	if (error != 0 || *ep != '\0' || gid > MAXUID || gid < 0)
+		return (-1);
+
+	/* note that the mode is octal */
+	error = ddi_strtol(mp, &ep, 8, &mode);
+	if (error != 0 || *ep != '\0' || mode > 0777 || mode < 0)
+		return (-1);
+
+	if (lookupname(dp, UIO_SYSSPACE, FOLLOW, NULLVPP, &vp) != 0) {
+		/*
+		 * It's likely the device is no longer visible to the zone.
+		 * No matter the reason, we indicate failure.
+		 */
+		return (-1);
+	}
+
+	va.va_mask =  AT_UID | AT_GID | AT_MODE;
+	va.va_uid = (uid_t)uid;
+	va.va_gid = (gid_t)gid;
+	va.va_mode = (mode_t)mode;
+
+	if (VOP_SETATTR(vp, &va, 0, CRED(), NULL) != 0)
+		res = -1;
+
+	VN_RELE(vp);
+
+	*dpath = dp;
+	*uidp = (uid_t)uid;
+	*gidp = (gid_t)gid;
+	*modep = (mode_t)mode;
+	return (res);
+}
+
+/*
+ * Return true if this is a pre-existing record.
+ */
+static boolean_t
+lxd_save_devattr(lxd_mnt_t *lxdm, char *dpath, uid_t uid, gid_t gid,
+    mode_t mode)
+{
+	lxd_dev_attr_t *da;
+
+	da = list_head(&lxdm->lxdm_devattrs);
+	while (da != NULL) {
+		if (strcmp(dpath, da->lxda_name) == 0) {
+			da->lxda_uid = uid;
+			da->lxda_gid = gid;
+			da->lxda_mode = mode;
+			return (B_TRUE);
+		}
+		da = list_next(&lxdm->lxdm_devattrs, da);
+	}
+
+	da = kmem_zalloc(sizeof (lxd_dev_attr_t), KM_SLEEP);
+	(void) strlcpy(da->lxda_name, dpath, sizeof (da->lxda_name));
+	da->lxda_uid = uid;
+	da->lxda_gid = gid;
+	da->lxda_mode = mode;
+
+	list_insert_tail(&lxdm->lxdm_devattrs, da);
+	return (B_FALSE);
+}
+
+static void
+lxd_save_db(lxd_mnt_t *lxdm)
+{
+	lxd_dev_attr_t *da;
+	char *entry;
+	vnode_t *wvn;
+	off_t woff = 0;
+
+	if (list_is_empty(&lxdm->lxdm_devattrs)) {
+		/* The attribute file is no longer needed. */
+		(void) vn_remove(LX_ATTR_FILE, UIO_SYSSPACE, RMFILE);
+		return;
+	}
+
+	if (lxd_db_open(FWRITE | FCREAT | FTRUNC, &wvn) != 0)
+		return;
+
+	entry = kmem_alloc(ENTRY_BUFSIZE, KM_SLEEP);
+
+	woff = lxd_wr_entry(wvn, woff, "# DO NOT EDIT: this file is "
+	    "automatically maintained for lx container devices\n");
+
+	da = list_head(&lxdm->lxdm_devattrs);
+	while (da != NULL) {
+		(void) snprintf(entry, ENTRY_BUFSIZE, "%s %d %d %o\n",
+		    da->lxda_name, da->lxda_uid, da->lxda_gid,
+		    da->lxda_mode & 0777);
+		woff += lxd_wr_entry(wvn, woff, entry);
+		da = list_next(&lxdm->lxdm_devattrs, da);
+	}
+
+	(void) VOP_CLOSE(wvn, FWRITE, 1, woff, CRED(), NULL);
+
+	kmem_free(entry, ENTRY_BUFSIZE);
+}
+
+/*
+ * This function records the uid, gid and mode information for an lx devfs
+ * block device node after a chown/chmod setattr operation so that these
+ * changes can be persistent across reboots. Since the actual setattr has
+ * already suceeded, the tracking of these changes is done on a "best effort"
+ * basis. That is, if we fail to record the change for some reason, the setattr
+ * will still return success. The vp passed in is the "real vp" for the back
+ * device node.
+ */
+void
+lxd_save_attrs(lxd_mnt_t *lxdm, vnode_t *vp)
+{
+	vattr_t va;
+	char devpath[MAXPATHLEN];
+
+	/* the path returned is relative to the zone's root */
+	if (vnodetopath(curproc->p_zone->zone_rootvp, vp, devpath,
+	    sizeof (devpath), CRED()) != 0)
+		return;
+
+	va.va_mask = AT_MODE | AT_UID | AT_GID;
+
+	/*
+	 * We just set attrs, so the getattr shouldn't fail. If the device
+	 * is not a block device we don't persist the change.
+	 */
+	if (VOP_GETATTR(vp, &va, 0, CRED(), NULL) != 0 ||
+	    ((va.va_mode & S_IFBLK) != S_IFBLK))
+		return;
+
+	/*
+	 * We serialize all updates to the attribute DB file. In practice this
+	 * should not be a problem since there is rarely concurrent device
+	 * file mode changes.
+	 */
+	mutex_enter(&lxdm->lxdm_attrlck);
+
+	(void) lxd_save_devattr(lxdm, devpath, va.va_uid, va.va_gid,
+	    va.va_mode & 0777);
+	lxd_save_db(lxdm);
+
+	mutex_exit(&lxdm->lxdm_attrlck);
+}
+
+/*
+ * Re-apply the persistent attribute settings to the devices when this lx
+ * devfs is mounted. As with lxd_save_attrs, this is done on a best effort and
+ * we won't prevent the mount if there is a problem. No locking is needed
+ * while reading the DB file since this action is performed during the
+ * mount of the devfs.
+ */
+void
+lxd_apply_db(lxd_mnt_t *lxdm)
+{
+	vnode_t *rvn;
+	char *buf, *entry, *bp, *ep;
+	struct uio auio;
+	struct iovec aiov;
+	size_t cnt, len, ecnt, roff;
+	char *devpath;
+	uid_t uid;
+	gid_t gid;
+	mode_t mode;
+	boolean_t needs_update = B_FALSE;
+
+	if (lxd_db_open(FREAD, &rvn) != 0)
+		return;
+
+	buf = kmem_alloc(RD_BUFSIZE, KM_SLEEP);
+	entry = kmem_alloc(ENTRY_BUFSIZE, KM_SLEEP);
+
+	roff = 0;
+	ep = entry;
+	ecnt = 0;
+	(void) VOP_RWLOCK(rvn, V_WRITELOCK_FALSE, NULL);
+loop:
+	aiov.iov_base = buf;
+	aiov.iov_len = RD_BUFSIZE;
+
+	auio.uio_iov = &aiov;
+	auio.uio_iovcnt = 1;
+	auio.uio_loffset = roff;
+	auio.uio_segflg = UIO_SYSSPACE;
+	auio.uio_resid = RD_BUFSIZE;
+	auio.uio_fmode = 0;
+	auio.uio_extflg = UIO_COPY_CACHED;
+
+	(void) VOP_READ(rvn, &auio, 0, CRED(), NULL);
+
+	len = RD_BUFSIZE - auio.uio_resid;
+	roff += len;
+
+	if (len > 0) {
+		for (bp = buf, cnt = 0; cnt < len; bp++, cnt++) {
+
+			/*
+			 * We have an improperly formed entry in the file (too
+			 * long). In an attempt to recover we reset the entry
+			 * pointer so we can read the rest of the line and try
+			 * to absorb the bad line. The code in lxd_apply_entry
+			 * will handle any malformed or inapplicable entries.
+			 */
+			if (ecnt >= (ENTRY_BUFSIZE - 1)) {
+				ep = entry;
+				ecnt = 0;
+				needs_update = B_TRUE;
+			}
+
+			if (*bp == '\n') {
+				*ep = '\0';
+
+				/* skip comments */
+				if (entry[0] != '#') {
+					if (lxd_apply_entry(entry, &devpath,
+					    &uid, &gid, &mode) != 0 ||
+					    lxd_save_devattr(lxdm, devpath,
+					    uid, gid, mode)) {
+						/*
+						 * An invalid entry, a
+						 * non-existent device node or
+						 * a duplicate entry.
+						 */
+						needs_update = B_TRUE;
+					}
+				}
+				ep = entry;
+				ecnt = 0;
+			} else {
+				*ep++ = *bp;
+				ecnt++;
+			}
+		}
+		goto loop;
+	}
+	VOP_RWUNLOCK(rvn, V_WRITELOCK_FALSE, NULL);
+
+	kmem_free(buf, RD_BUFSIZE);
+	kmem_free(entry, ENTRY_BUFSIZE);
+
+	(void) VOP_CLOSE(rvn, FREAD, 1, 0, CRED(), NULL);
+
+	if (needs_update)
+		lxd_save_db(lxdm);
+}
--- /dev/null	Wed Oct  5 00:32:04 2016
+++ new/usr/src/uts/common/brand/lx/devfs/lxd_node.c	Wed Oct  5 00:32:04 2016
@@ -0,0 +1,1003 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/vfs.h>
+#include <sys/vnode.h>
+#include <sys/errno.h>
+#include <sys/cmn_err.h>
+#include <sys/cred.h>
+#include <sys/stat.h>
+#include <sys/mode.h>
+#include <sys/policy.h>
+#include <sys/sdt.h>
+
+#include "lxd.h"
+
+#define	LXD_HASH_SIZE	8192		/* must be power of 2 */
+#define	LXD_MUTEX_SIZE	64
+
+
+#define	MODESHIFT	3
+
+typedef enum lxd_nodehold {
+	NOHOLD,
+	HOLD
+} lxd_nodehold_t;
+
+/*
+ * The following functions maintain the per-mount "front" files.
+ */
+static void
+lxd_save_dirent(lxd_dirent_t *de)
+{
+	lxd_mnt_t	*lxdm = VTOLXDM(LDNTOV(de->lddir_parent));
+	uint_t		hash;
+	kmutex_t	*hmtx;
+
+	LXD_NM_HASH(de->lddir_parent, de->lddir_name, hash);
+	de->lddir_hash = hash;
+
+	hmtx = &lxdm->lxdm_hash_mutex[hash];
+
+	mutex_enter(hmtx);
+	ASSERT(de->lddir_link == NULL);
+	de->lddir_link = lxdm->lxdm_dent_htable[hash];
+	lxdm->lxdm_dent_htable[hash] = de;
+	mutex_exit(hmtx);
+
+	atomic_inc_32(&lxdm->lxdm_dent_refcnt);
+}
+
+static void
+lxd_rm_dirent(lxd_dirent_t *de)
+{
+	lxd_mnt_t	*lxdm = VTOLXDM(LDNTOV(de->lddir_parent));
+	uint_t		hash;
+	lxd_dirent_t	**prevpp;
+	kmutex_t	*hmtx;
+
+	hash = de->lddir_hash;
+	hmtx = &lxdm->lxdm_hash_mutex[hash];
+
+	mutex_enter(hmtx);
+	prevpp = &lxdm->lxdm_dent_htable[hash];
+	while (*prevpp != de)
+		prevpp = &(*prevpp)->lddir_link;
+	*prevpp = de->lddir_link;
+	de->lddir_link = NULL;
+	mutex_exit(hmtx);
+
+	ASSERT(lxdm->lxdm_dent_refcnt > 0);
+	atomic_dec_32(&lxdm->lxdm_dent_refcnt);
+}
+
+static lxd_dirent_t *
+lxd_find_dirent(char *name, lxd_node_t *parent, lxd_nodehold_t do_hold,
+    lxd_node_t **found)
+{
+	lxd_mnt_t	*lxdm = VTOLXDM(LDNTOV(parent));
+	lxd_dirent_t	*de;
+	uint_t		hash;
+	kmutex_t	*hmtx;
+
+	LXD_NM_HASH(parent, name, hash);
+	hmtx = &lxdm->lxdm_hash_mutex[hash];
+
+	mutex_enter(hmtx);
+	de = lxdm->lxdm_dent_htable[hash];
+	while (de) {
+		if (de->lddir_hash == hash && de->lddir_parent == parent &&
+		    strcmp(de->lddir_name, name) == 0) {
+			lxd_node_t *ldn = de->lddir_node;
+
+			if (do_hold == HOLD) {
+				ASSERT(ldn != NULL);
+				ldnode_hold(ldn);
+			}
+			if (found != NULL)
+				*found = ldn;
+			mutex_exit(hmtx);
+			return (de);
+		}
+
+		de = de->lddir_link;
+	}
+	mutex_exit(hmtx);
+	return (NULL);
+}
+
+int
+lxd_naccess(void *vcp, int mode, cred_t *cr)
+{
+	lxd_node_t *ldn = vcp;
+	int shift = 0;
+	/*
+	 * Check access based on owner, group and public perms in lxd_node.
+	 */
+	if (crgetuid(cr) != ldn->lxdn_uid) {
+		shift += MODESHIFT;
+		if (groupmember(ldn->lxdn_gid, cr) == 0)
+			shift += MODESHIFT;
+	}
+
+	if (ldn->lxdn_type == LXDNT_FRONT)
+		return (secpolicy_vnode_access2(cr, LDNTOV(ldn),
+		    ldn->lxdn_uid, ldn->lxdn_mode << shift, mode));
+
+	ASSERT(ldn->lxdn_type == LXDNT_BACK);
+	return (VOP_ACCESS(ldn->lxdn_real_vp, mode, 0, cr, NULL));
+}
+
+static lxd_node_t *
+lxd_find_back(struct vnode *vp, uint_t hash, lxd_mnt_t *lxdm)
+{
+	lxd_node_t *l;
+
+	ASSERT(MUTEX_HELD(&lxdm->lxdm_hash_mutex[hash]));
+
+	for (l = lxdm->lxdm_back_htable[hash]; l != NULL; l = l->lxdn_hnxt) {
+		if (l->lxdn_real_vp == vp) {
+			ASSERT(l->lxdn_type == LXDNT_BACK);
+
+			VN_HOLD(LDNTOV(l));
+			return (l);
+		}
+	}
+	return (NULL);
+}
+
+static void
+lxd_save_back(lxd_node_t *l, uint_t hash, lxd_mnt_t *lxdm)
+{
+	ASSERT(l->lxdn_type == LXDNT_BACK);
+	ASSERT(l->lxdn_real_vp != NULL);
+	ASSERT(MUTEX_HELD(&lxdm->lxdm_hash_mutex[hash]));
+
+	atomic_inc_32(&lxdm->lxdm_back_refcnt);
+
+	l->lxdn_hnxt = lxdm->lxdm_back_htable[hash];
+	lxdm->lxdm_back_htable[hash] = l;
+}
+
+
+struct vnode *
+lxd_make_back_node(struct vnode *vp, lxd_mnt_t *lxdm)
+{
+	uint_t hash;
+	kmutex_t *hmtx;
+	lxd_node_t *l;
+
+	hash = LXD_BACK_HASH(vp);	/* Note: hashing with realvp */
+	hmtx = &lxdm->lxdm_hash_mutex[hash];
+	mutex_enter(hmtx);
+
+	l = lxd_find_back(vp, hash, lxdm);
+	if (l == NULL) {
+		vnode_t *nvp;
+
+		l = kmem_zalloc(sizeof (lxd_node_t), KM_SLEEP);
+		nvp = vn_alloc(KM_SLEEP);
+
+		rw_init(&l->lxdn_rwlock, NULL, RW_DEFAULT, NULL);
+		mutex_init(&l->lxdn_tlock, NULL, MUTEX_DEFAULT, NULL);
+
+		l->lxdn_vnode = nvp;
+		l->lxdn_type = LXDNT_BACK;
+		l->lxdn_real_vp = vp;
+
+		VN_SET_VFS_TYPE_DEV(nvp, lxdm->lxdm_vfsp, vp->v_type,
+		    vp->v_rdev);
+		nvp->v_flag |= (vp->v_flag & (VNOMOUNT|VNOMAP|VDIROPEN));
+		vn_setops(nvp, lxd_vnodeops);
+		nvp->v_data = (caddr_t)l;
+
+		lxd_save_back(l, hash, lxdm);
+		vn_exists(vp);
+	} else {
+		VN_RELE(vp);
+	}
+
+	mutex_exit(hmtx);
+	return (LDNTOV(l));
+}
+
+void
+lxd_free_back_node(lxd_node_t *lp)
+{
+	uint_t hash;
+	kmutex_t *hmtx;
+	lxd_node_t *l;
+	lxd_node_t *lprev = NULL;
+	vnode_t *vp = LDNTOV(lp);
+	vnode_t *realvp = REALVP(vp);
+	lxd_mnt_t *lxdm = VTOLXDM(vp);
+
+	/* in lxd_make_back_node we call lxd_find_back with the realvp */
+	hash = LXD_BACK_HASH(realvp);
+	hmtx = &lxdm->lxdm_hash_mutex[hash];
+	mutex_enter(hmtx);
+
+	mutex_enter(&vp->v_lock);
+	if (vp->v_count > 1) {
+		vp->v_count--;	/* release our hold from vn_rele */
+		mutex_exit(&vp->v_lock);
+		mutex_exit(hmtx);
+		return;
+	}
+	mutex_exit(&vp->v_lock);
+
+	for (l = lxdm->lxdm_back_htable[hash]; l != NULL;
+	    lprev = l, l = l->lxdn_hnxt) {
+
+		if (l != lp)
+			continue;
+
+		ASSERT(l->lxdn_type == LXDNT_BACK);
+		ASSERT(lxdm->lxdm_back_refcnt > 0);
+
+		atomic_dec_32(&lxdm->lxdm_back_refcnt);
+		vn_invalid(vp);
+
+		if (lprev == NULL) {
+			lxdm->lxdm_back_htable[hash] = l->lxdn_hnxt;
+		} else {
+			lprev->lxdn_hnxt = l->lxdn_hnxt;
+		}
+
+		mutex_exit(hmtx);
+		rw_destroy(&l->lxdn_rwlock);
+		mutex_destroy(&l->lxdn_tlock);
+		kmem_free(l, sizeof (lxd_node_t));
+		vn_free(vp);
+		VN_RELE(realvp);
+		return;
+	}
+
+	panic("lxd_free_back_node");
+	/*NOTREACHED*/
+}
+/*
+ * Search directory 'parent' for entry 'name'.
+ *
+ * 0 is returned on success and *foundcp points
+ * to the found lxd_node with its vnode held.
+ */
+int
+lxd_dirlookup(lxd_node_t *parent, char *name, lxd_node_t **foundnp, cred_t *cr)
+{
+	int error;
+
+	*foundnp = NULL;
+	if (parent->lxdn_vnode->v_type != VDIR)
+		return (ENOTDIR);
+
+	if ((error = lxd_naccess(parent, VEXEC, cr)))
+		return (error);
+
+	if (*name == '\0') {
+		ldnode_hold(parent);
+		*foundnp = parent;
+		return (0);
+	}
+
+	/*
+	 * Search the directory for the matching name
+	 * We need the lock protecting the lxdn_dir list
+	 * so that it doesn't change out from underneath us.
+	 * lxd_find_dirent() will pass back the lxd_node
+	 * with a hold on it.
+	 */
+
+	if (lxd_find_dirent(name, parent, HOLD, foundnp) != NULL) {
+		ASSERT(*foundnp);
+		return (0);
+	}
+
+	return (ENOENT);
+}
+
+/*
+ * Check if the source directory is in the path of the target directory.
+ * The target directory is locked by the caller.
+ */
+static int
+lxd_dircheckpath(lxd_node_t *fromnode, lxd_node_t *toparent)
+{
+	int error = 0;
+	lxd_node_t *dir, *dotdot;
+
+	ASSERT(RW_WRITE_HELD(&toparent->lxdn_rwlock));
+	ASSERT(toparent->lxdn_vnode->v_type == VDIR);
+
+	dotdot = toparent->lxdn_parent;
+	if (dotdot == NULL)
+		return (ENOENT);
+	ldnode_hold(dotdot);
+
+	if (dotdot == toparent) {
+		/* root of fs.  search trivially satisfied. */
+		ldnode_rele(dotdot);
+		return (0);
+	}
+
+	for (;;) {
+		/*
+		 * Return error for cases like "mv c c/d",
+		 * "mv c c/d/e" and so on.
+		 */
+		if (dotdot == fromnode) {
+			ldnode_rele(dotdot);
+			error = EINVAL;
+			break;
+		}
+
+		dir = dotdot;
+		dotdot = dir->lxdn_parent;
+		if (dotdot == NULL) {
+			ldnode_rele(dir);
+			error = ENOENT;
+			break;
+		}
+		ldnode_hold(dotdot);
+
+		/*
+		 * We're okay if we traverse the directory tree up to
+		 * the root directory and don't run into the
+		 * parent directory.
+		 */
+		if (dir == dotdot) {
+			ldnode_rele(dir);
+			ldnode_rele(dotdot);
+			break;
+		}
+		ldnode_rele(dir);
+	}
+
+	return (error);
+}
+
+static int
+lxd_dir_make_node(lxd_node_t *dir, lxd_mnt_t *lxdm, struct vattr *va,
+    enum de_op op, lxd_node_t **newnode, struct cred *cred)
+{
+	lxd_node_t *ldn;
+
+	ASSERT(va != NULL);
+
+	if (((va->va_mask & AT_ATIME) && TIMESPEC_OVERFLOW(&va->va_atime)) ||
+	    ((va->va_mask & AT_MTIME) && TIMESPEC_OVERFLOW(&va->va_mtime)))
+		return (EOVERFLOW);
+
+	ldn = kmem_zalloc(sizeof (lxd_node_t), KM_SLEEP);
+
+	ldn->lxdn_type = LXDNT_FRONT;
+	lxd_node_init(lxdm, ldn, NULL, va, cred);
+
+	ldn->lxdn_vnode->v_rdev = ldn->lxdn_rdev = NODEV;
+	ldn->lxdn_vnode->v_type = va->va_type;
+	ldn->lxdn_uid = crgetuid(cred);
+	ldn->lxdn_gid = crgetgid(cred);
+	ldn->lxdn_nodeid = lxdm->lxdm_gen++;
+
+	if (va->va_mask & AT_ATIME)
+		ldn->lxdn_atime = va->va_atime;
+	if (va->va_mask & AT_MTIME)
+		ldn->lxdn_mtime = va->va_mtime;
+
+	if (op == DE_MKDIR) {
+		lxd_dirinit(dir, ldn);
+	}
+
+	*newnode = ldn;
+	return (0);
+}
+
+static int
+lxd_diraddentry(lxd_node_t *dir, lxd_node_t *ldn, char *name)
+{
+	lxd_dirent_t	*dp, *pdp;
+	size_t		namelen, alloc_size;
+	timestruc_t	now;
+
+	/*
+	 * Make sure the parent directory wasn't removed from
+	 * underneath the caller.
+	 */
+	if (dir->lxdn_dir == NULL)
+		return (ENOENT);
+
+	/* Check that everything is on the same filesystem. */
+	if (ldn->lxdn_vnode->v_vfsp != dir->lxdn_vnode->v_vfsp)
+		return (EXDEV);
+
+	/* Allocate and initialize directory entry */
+	namelen = strlen(name) + 1;
+	alloc_size = namelen + sizeof (lxd_dirent_t);
+	dp = kmem_zalloc(alloc_size, KM_NOSLEEP | KM_NORMALPRI);
+	if (dp == NULL)
+		return (ENOSPC);
+
+	ldn->lxdn_parent = dir;
+
+	dir->lxdn_size += alloc_size;
+	dir->lxdn_dirents++;
+	dp->lddir_node = ldn;
+	dp->lddir_parent = dir;
+
+	/* The directory entry and its name were allocated sequentially. */
+	dp->lddir_name = (char *)dp + sizeof (lxd_dirent_t);
+	(void) strcpy(dp->lddir_name, name);
+
+	lxd_save_dirent(dp);
+
+	/*
+	 * Some utilities expect the size of a directory to remain
+	 * somewhat static.  For example, a routine which removes
+	 * subdirectories between calls to readdir(); the size of the
+	 * directory changes from underneath it and so the real
+	 * directory offset in bytes is invalid.  To circumvent
+	 * this problem, we initialize a directory entry with an
+	 * phony offset, and use this offset to determine end of
+	 * file in lxd_readdir.
+	 */
+	pdp = dir->lxdn_dir->lddir_prev;
+	/*
+	 * Install at first empty "slot" in directory list.
+	 */
+	while (pdp->lddir_next != NULL &&
+	    (pdp->lddir_next->lddir_offset - pdp->lddir_offset) <= 1) {
+		ASSERT(pdp->lddir_next != pdp);
+		ASSERT(pdp->lddir_prev != pdp);
+		ASSERT(pdp->lddir_next->lddir_offset > pdp->lddir_offset);
+		pdp = pdp->lddir_next;
+	}
+	dp->lddir_offset = pdp->lddir_offset + 1;
+
+	/*
+	 * If we're at the end of the dirent list and the offset (which
+	 * is necessarily the largest offset in this directory) is more
+	 * than twice the number of dirents, that means the directory is
+	 * 50% holes.  At this point we reset the slot pointer back to
+	 * the beginning of the directory so we start using the holes.
+	 * The idea is that if there are N dirents, there must also be
+	 * N holes, so we can satisfy the next N creates by walking at
+	 * most 2N entries; thus the average cost of a create is constant.
+	 * Note that we use the first dirent's lddir_prev as the roving
+	 * slot pointer; it's ugly, but it saves a word in every dirent.
+	 */
+	if (pdp->lddir_next == NULL &&
+	    pdp->lddir_offset > 2 * dir->lxdn_dirents)
+		dir->lxdn_dir->lddir_prev = dir->lxdn_dir->lddir_next;
+	else
+		dir->lxdn_dir->lddir_prev = dp;
+
+	ASSERT(pdp->lddir_next != pdp);
+	ASSERT(pdp->lddir_prev != pdp);
+
+	dp->lddir_next = pdp->lddir_next;
+	if (dp->lddir_next) {
+		dp->lddir_next->lddir_prev = dp;
+	}
+	dp->lddir_prev = pdp;
+	pdp->lddir_next = dp;
+
+	ASSERT(dp->lddir_next != dp);
+	ASSERT(dp->lddir_prev != dp);
+	ASSERT(pdp->lddir_next != pdp);
+	ASSERT(pdp->lddir_prev != pdp);
+
+	gethrestime(&now);
+	dir->lxdn_mtime = now;
+	dir->lxdn_ctime = now;
+
+	return (0);
+}
+
+/*
+ * Enter a directory entry for 'name' into directory 'dir'
+ *
+ * Returns 0 on success.
+ */
+int
+lxd_direnter(
+	lxd_mnt_t	*lxdm,
+	lxd_node_t	*dir,		/* target directory to make entry in */
+	char		*name,		/* name of entry */
+	enum de_op	op,		/* entry operation */
+	lxd_node_t	*fromparent,    /* original directory if rename */
+	lxd_node_t	*ldn,		/* existing lxd_node, if rename */
+	struct vattr	*va,
+	lxd_node_t	**rnp,		/* return lxd_node, if create/mkdir */
+	cred_t		*cr)
+{
+	lxd_dirent_t *dirp;
+	lxd_node_t *found = NULL;
+	int error = 0;
+	char *s;
+
+	/* lxdn_rwlock is held to serialize direnter and dirdeletes */
+	ASSERT(RW_WRITE_HELD(&dir->lxdn_rwlock));
+	ASSERT(dir->lxdn_vnode->v_type == VDIR);
+
+	/*
+	 * Don't allow '/' characters in pathname component,
+	 */
+	for (s = name; *s; s++)
+		if (*s == '/')
+			return (EACCES);
+
+	if (name[0] == '\0')
+		panic("lxd_direnter: NULL name");
+
+	/*
+	 * For rename lock the source entry and check the link count
+	 * to see if it has been removed while it was unlocked.
+	 */
+	if (op == DE_RENAME) {
+		mutex_enter(&ldn->lxdn_tlock);
+		if (ldn->lxdn_nlink == 0) {
+			mutex_exit(&ldn->lxdn_tlock);
+			return (ENOENT);
+		}
+
+		if (ldn->lxdn_nlink == MAXLINK) {
+			mutex_exit(&ldn->lxdn_tlock);
+			return (EMLINK);
+		}
+		ldn->lxdn_nlink++;
+		gethrestime(&ldn->lxdn_ctime);
+		mutex_exit(&ldn->lxdn_tlock);
+	}
+
+	/*
+	 * This might be a "dangling detached directory" (it could have been
+	 * removed, but a reference to it kept in u_cwd). Don't bother
+	 * searching it, and with any luck the user will get tired of dealing
+	 * with us and cd to some absolute pathway (thus in ufs, too).
+	 */
+	if (dir->lxdn_nlink == 0) {
+		error = ENOENT;
+		goto out;
+	}
+
+	/*
+	 * If this is a rename of a directory and the parent is different
+	 * (".." must be changed), then the source directory must not be in the
+	 * directory hierarchy above the target, as this would orphan
+	 * everything below the source directory.
+	 */
+	if (op == DE_RENAME) {
+		if (ldn == dir) {
+			error = EINVAL;
+			goto out;
+		}
+		if ((ldn->lxdn_vnode->v_type) == VDIR) {
+			if ((fromparent != dir) &&
+			    (error = lxd_dircheckpath(ldn, dir)) != 0) {
+				goto out;
+			}
+		}
+	}
+
+	/* Search for an existing entry. */
+	dirp = lxd_find_dirent(name, dir, HOLD, &found);
+	if (dirp != NULL) {
+		ASSERT(found != NULL);
+		switch (op) {
+		case DE_CREATE:
+		case DE_MKDIR:
+			if (rnp != NULL) {
+				*rnp = found;
+				error = EEXIST;
+			} else {
+				ldnode_rele(found);
+			}
+			break;
+
+		case DE_RENAME:
+			/*
+			 * Note that we only hit this path when we're renaming
+			 * a symlink from one directory to another and there is
+			 * a pre-existing symlink as the target. lxd_rename
+			 * will unlink the src from the original directory but
+			 * here we need to unlink the dest that we collided
+			 * with, then create the new directory entry as we do
+			 * below when there is no pre-existing symlink.
+			 */
+			if ((error = lxd_naccess(dir, VWRITE, cr)) != 0)
+				goto out;
+
+			ASSERT(found->lxdn_vnode->v_type == VLNK);
+			/* dir rw lock is already held and asserted above */
+			rw_enter(&found->lxdn_rwlock, RW_WRITER);
+			error = lxd_dirdelete(dir, found, name, DR_RENAME, cr);
+			rw_exit(&found->lxdn_rwlock);
+			ldnode_rele(found);
+			if (error != 0)
+				goto out;
+
+			error = lxd_diraddentry(dir, ldn, name);
+			if (error == 0 && rnp != NULL)
+				*rnp = ldn;
+			break;
+		}
+	} else {
+
+		/*
+		 * The directory entry does not exist, but the node might if
+		 * this is a rename. Check write permission in directory to
+		 * see if entry can be created.
+		 */
+		if ((error = lxd_naccess(dir, VWRITE, cr)) != 0)
+			goto out;
+		if (op == DE_CREATE || op == DE_MKDIR) {
+			/*
+			 * Make new lxd_node and directory entry as required.
+			 */
+			error = lxd_dir_make_node(dir, lxdm, va, op, &ldn, cr);
+			if (error)
+				goto out;
+		}
+
+		error = lxd_diraddentry(dir, ldn, name);
+		if (error != 0) {
+			if (op == DE_CREATE || op == DE_MKDIR) {
+				/*
+				 * Unmake the inode we just made.
+				 */
+				rw_enter(&ldn->lxdn_rwlock, RW_WRITER);
+				if ((ldn->lxdn_vnode->v_type) == VDIR) {
+					ASSERT(dirp == NULL);
+					/*
+					 * cleanup allocs made by lxd_dirinit
+					 */
+					lxd_dirtrunc(ldn);
+				}
+				mutex_enter(&ldn->lxdn_tlock);
+				ldn->lxdn_nlink = 0;
+				gethrestime(&ldn->lxdn_ctime);
+				mutex_exit(&ldn->lxdn_tlock);
+				rw_exit(&ldn->lxdn_rwlock);
+				ldnode_rele(ldn);
+				ldn = NULL;
+			}
+		} else if (rnp != NULL) {
+			*rnp = ldn;
+		} else if (op == DE_CREATE || op == DE_MKDIR) {
+			ldnode_rele(ldn);
+		}
+	}
+
+out:
+	if (error && op == DE_RENAME) {
+		/* Undo bumped link count. */
+		mutex_enter(&ldn->lxdn_tlock);
+		ldn->lxdn_nlink--;
+		gethrestime(&ldn->lxdn_ctime);
+		mutex_exit(&ldn->lxdn_tlock);
+	}
+	return (error);
+}
+
+/*
+ * Delete entry ldn of name "nm" from parent dir. This is used to both remove
+ * a directory and to remove file nodes within the directory (by recursively
+ * calling itself). It frees the dir entry space and decrements link count on
+ * lxd_node(s).
+ *
+ * Return 0 on success.
+ */
+int
+lxd_dirdelete(lxd_node_t *dir, lxd_node_t *ldn, char *nm, enum dr_op op,
+    cred_t *cred)
+{
+	lxd_dirent_t *dirp;
+	int error;
+	size_t namelen;
+	lxd_node_t *fndnp;
+	timestruc_t now;
+
+	ASSERT(RW_WRITE_HELD(&dir->lxdn_rwlock));
+	ASSERT(RW_WRITE_HELD(&ldn->lxdn_rwlock));
+	ASSERT(dir->lxdn_vnode->v_type == VDIR);
+
+	if (nm[0] == '\0')
+		panic("lxd_dirdelete: empty name for 0x%p", (void *)ldn);
+
+	/*
+	 * return error when removing . and ..
+	 */
+	if (nm[0] == '.') {
+		if (nm[1] == '\0')
+			return (EINVAL);
+		if (nm[1] == '.' && nm[2] == '\0')
+			return (EEXIST); /* thus in ufs */
+	}
+
+	if ((error = lxd_naccess(dir, VEXEC|VWRITE, cred)) != 0)
+		return (error);
+
+	if (dir->lxdn_dir == NULL)
+		return (ENOENT);
+
+	if (op == DR_RMDIR) {
+		/*
+		 * This is the top-level removal of a directory. Start by
+		 * removing any file entries from the dir. We do this by
+		 * recursively calling back into this function with a different
+		 * op code. The caller of this function has already verified
+		 * that it is safe to remove this directory.
+		 */
+		lxd_dirent_t *dirp;
+
+		ASSERT(ldn->lxdn_vnode->v_type == VDIR);
+
+		dirp = ldn->lxdn_dir;
+		while (dirp) {
+			lxd_node_t *dn;
+			lxd_dirent_t *nextp;
+
+			if (strcmp(dirp->lddir_name, ".") == 0 ||
+			    strcmp(dirp->lddir_name, "..") == 0) {
+				dirp = dirp->lddir_next;
+				continue;
+			}
+
+			dn = dirp->lddir_node;
+			nextp = dirp->lddir_next;
+
+			ldnode_hold(dn);
+			error = lxd_dirdelete(ldn, dn, dirp->lddir_name,
+			    DR_REMOVE, cred);
+			ldnode_rele(dn);
+
+			dirp = nextp;
+		}
+	}
+
+	dirp = lxd_find_dirent(nm, dir, NOHOLD, &fndnp);
+	VERIFY(dirp != NULL);
+	VERIFY(ldn == fndnp);
+
+	lxd_rm_dirent(dirp);
+
+	/* Take dirp out of the directory list. */
+	ASSERT(dirp->lddir_next != dirp);
+	ASSERT(dirp->lddir_prev != dirp);
+	if (dirp->lddir_prev) {
+		dirp->lddir_prev->lddir_next = dirp->lddir_next;
+	}
+	if (dirp->lddir_next) {
+		dirp->lddir_next->lddir_prev = dirp->lddir_prev;
+	}
+
+	/*
+	 * If the roving slot pointer happens to match dirp,
+	 * point it at the previous dirent.
+	 */
+	if (dir->lxdn_dir->lddir_prev == dirp) {
+		dir->lxdn_dir->lddir_prev = dirp->lddir_prev;
+	}
+	ASSERT(dirp->lddir_next != dirp);
+	ASSERT(dirp->lddir_prev != dirp);
+
+	/* dirp points to the correct directory entry */
+	namelen = strlen(dirp->lddir_name) + 1;
+
+	kmem_free(dirp, sizeof (lxd_dirent_t) + namelen);
+	dir->lxdn_size -= (sizeof (lxd_dirent_t) + namelen);
+	dir->lxdn_dirents--;
+
+	gethrestime(&now);
+	dir->lxdn_mtime = now;
+	dir->lxdn_ctime = now;
+	ldn->lxdn_ctime = now;
+
+	ASSERT(ldn->lxdn_nlink > 0);
+	mutex_enter(&ldn->lxdn_tlock);
+	ldn->lxdn_nlink--;
+	mutex_exit(&ldn->lxdn_tlock);
+	if (op == DR_RMDIR && ldn->lxdn_vnode->v_type == VDIR) {
+		lxd_dirtrunc(ldn);
+		ASSERT(ldn->lxdn_nlink == 0);
+	}
+	return (0);
+}
+
+/*
+ * Initialize a lxd_node and add it to file list under mount point.
+ */
+void
+lxd_node_init(lxd_mnt_t *lxdm, lxd_node_t *ldn, vnode_t *realvp, vattr_t *vap,
+    cred_t *cred)
+{
+	struct vnode *vp;
+	timestruc_t now;
+
+	ASSERT(vap != NULL);
+
+	rw_init(&ldn->lxdn_rwlock, NULL, RW_DEFAULT, NULL);
+	mutex_init(&ldn->lxdn_tlock, NULL, MUTEX_DEFAULT, NULL);
+	ldn->lxdn_mode = MAKEIMODE(vap->va_type, vap->va_mode);
+	ldn->lxdn_mask = 0;
+	ldn->lxdn_attr.va_type = vap->va_type;
+	ldn->lxdn_nlink = 1;
+	ldn->lxdn_size = 0;
+
+	if (cred == NULL) {
+		ldn->lxdn_uid = vap->va_uid;
+		ldn->lxdn_gid = vap->va_gid;
+	} else {
+		ldn->lxdn_uid = crgetuid(cred);
+		ldn->lxdn_gid = crgetgid(cred);
+	}
+
+	ldn->lxdn_fsid = lxdm->lxdm_dev;
+	ldn->lxdn_rdev = vap->va_rdev;
+	ldn->lxdn_blksize = PAGESIZE;
+	ldn->lxdn_nblocks = 0;
+	gethrestime(&now);
+	ldn->lxdn_atime = now;
+	ldn->lxdn_mtime = now;
+	ldn->lxdn_ctime = now;
+	ldn->lxdn_seq = 0;
+	ldn->lxdn_dir = NULL;
+
+	ldn->lxdn_real_vp = realvp;
+
+	ldn->lxdn_vnode = vn_alloc(KM_SLEEP);
+	vp = LDNTOV(ldn);
+	vn_setops(vp, lxd_vnodeops);
+	vp->v_vfsp = lxdm->lxdm_vfsp;
+	vp->v_type = vap->va_type;
+	vp->v_rdev = vap->va_rdev;
+	vp->v_data = (caddr_t)ldn;
+
+	mutex_enter(&lxdm->lxdm_contents);
+	ldn->lxdn_nodeid = lxdm->lxdm_gen++;
+
+	/*
+	 * Add new lxd_node to end of linked list of lxd_nodes for this
+	 * lxdevfs. Root directory is handled specially in lxd_mount.
+	 */
+	if (lxdm->lxdm_rootnode != (lxd_node_t *)NULL) {
+		ldn->lxdn_next = NULL;
+		ldn->lxdn_prev = lxdm->lxdm_rootnode->lxdn_prev;
+		ldn->lxdn_prev->lxdn_next = lxdm->lxdm_rootnode->lxdn_prev =
+		    ldn;
+	}
+	mutex_exit(&lxdm->lxdm_contents);
+	vn_exists(vp);
+}
+
+/*
+ * lxd_dirinit is used internally to initialize a directory (dir)
+ * with '.' and '..' entries without checking permissions and locking
+ * It also creates the entries for the pseudo file nodes that reside in the
+ * directory.
+ */
+void
+lxd_dirinit(lxd_node_t *parent, lxd_node_t *dir)
+{
+	lxd_dirent_t *dot, *dotdot;
+	timestruc_t now;
+	lxd_mnt_t *lxdm = VTOLXDM(dir->lxdn_vnode);
+	struct vattr nattr;
+
+	ASSERT(RW_WRITE_HELD(&parent->lxdn_rwlock));
+	ASSERT(dir->lxdn_vnode->v_type == VDIR);
+
+	dir->lxdn_nodeid = lxdm->lxdm_gen++;
+
+	/*
+	 * Initialize the entries
+	 */
+	dot = kmem_zalloc(sizeof (lxd_dirent_t) + 2, KM_SLEEP);
+	dot->lddir_node = dir;
+	dot->lddir_offset = 0;
+	dot->lddir_name = (char *)dot + sizeof (lxd_dirent_t);
+	dot->lddir_name[0] = '.';
+	dot->lddir_parent = dir;
+	lxd_save_dirent(dot);
+
+	dotdot = kmem_zalloc(sizeof (lxd_dirent_t) + 3, KM_SLEEP);
+	dotdot->lddir_node = parent;
+	dotdot->lddir_offset = 1;
+	dotdot->lddir_name = (char *)dotdot + sizeof (lxd_dirent_t);
+	dotdot->lddir_name[0] = '.';
+	dotdot->lddir_name[1] = '.';
+	dotdot->lddir_parent = dir;
+	lxd_save_dirent(dotdot);
+
+	/*
+	 * Initialize directory entry list.
+	 */
+	dot->lddir_next = dotdot;
+	dot->lddir_prev = dotdot; /* dot's lddir_prev holds roving slot ptr */
+	dotdot->lddir_next = NULL;
+	dotdot->lddir_prev = dot;
+
+	gethrestime(&now);
+	dir->lxdn_mtime = now;
+	dir->lxdn_ctime = now;
+
+	parent->lxdn_nlink++;
+	parent->lxdn_ctime = now;
+
+	dir->lxdn_dir = dot;
+	dir->lxdn_size = 2 * sizeof (lxd_dirent_t) + 5;	/* dot and dotdot */
+	dir->lxdn_dirents = 2;
+	dir->lxdn_nlink = 2;
+	dir->lxdn_parent = parent;
+
+	bzero(&nattr, sizeof (struct vattr));
+	nattr.va_mode = (mode_t)(0644);
+	nattr.va_type = VREG;
+	nattr.va_rdev = 0;
+}
+
+/*
+ * lxd_dirtrunc is called to remove all directory entries under this directory.
+ */
+void
+lxd_dirtrunc(lxd_node_t *dir)
+{
+	lxd_dirent_t *ldp;
+	timestruc_t now;
+
+	ASSERT(RW_WRITE_HELD(&dir->lxdn_rwlock));
+	ASSERT(dir->lxdn_vnode->v_type == VDIR);
+
+	for (ldp = dir->lxdn_dir; ldp; ldp = dir->lxdn_dir) {
+		size_t namelen;
+		lxd_node_t *ldn;
+
+		ASSERT(ldp->lddir_next != ldp);
+		ASSERT(ldp->lddir_prev != ldp);
+		ASSERT(ldp->lddir_node);
+
+		dir->lxdn_dir = ldp->lddir_next;
+		namelen = strlen(ldp->lddir_name) + 1;
+
+		/*
+		 * Adjust the link counts to account for this directory entry
+		 * removal. We do hold/rele operations to free up these nodes.
+		 */
+		ldn = ldp->lddir_node;
+
+		ASSERT(ldn->lxdn_nlink > 0);
+		mutex_enter(&ldn->lxdn_tlock);
+		ldn->lxdn_nlink--;
+		mutex_exit(&ldn->lxdn_tlock);
+
+		lxd_rm_dirent(ldp);
+		kmem_free(ldp, sizeof (lxd_dirent_t) + namelen);
+		dir->lxdn_size -= (sizeof (lxd_dirent_t) + namelen);
+		dir->lxdn_dirents--;
+	}
+
+	gethrestime(&now);
+	dir->lxdn_mtime = now;
+	dir->lxdn_ctime = now;
+
+	ASSERT(dir->lxdn_dir == NULL);
+	ASSERT(dir->lxdn_size == 0);
+	ASSERT(dir->lxdn_dirents == 0);
+}
--- /dev/null	Wed Oct  5 00:32:04 2016
+++ new/usr/src/uts/common/brand/lx/devfs/lxd_vfsops.c	Wed Oct  5 00:32:04 2016
@@ -0,0 +1,860 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+/*
+ * The lx devfs (lxd) file system is used within lx branded zones to provide
+ * the Linux view of /dev.
+ *
+ * In the past, the Linux /dev was simply a lofs mount pointing at /native/dev.
+ * lxd now provides the Linux /dev.
+ *
+ * The lxd file system is a hybrid of lofs and tmpfs. It supports a "back" file
+ * system which is the special device and corresponds to the special device in
+ * a lofs mount. As with lofs, all files in the special device are accessible
+ * through the lxd mount. Because the zone's devfs is not directly modifiable
+ * within the zone (also mknod(2) is not generally allowed within a zone) it is
+ * impossible to create files in devfs. For lx, in some cases it's useful to be
+ * able to make new symlinks or new directories under /dev. lxd implements
+ * these operations by creating "files" in memory in the same way as tmpfs
+ * does. Within lxd these are referred to as "front" files. For operations such
+ * as lookup or readdir, lxd provides a merged view of both the front and back
+ * files. lxd does not support regular front files or simple I/O (read/write)
+ * to front files, since there is no need for that. For back files, all
+ * operations are simply passed through to the real vnode, as is done with
+ * lofs. Front files are not allowed to mask back files.
+ *
+ * The Linux /dev is now a lxd mount with the special file (i.e. the back
+ * file system) as /native/dev.
+ *
+ * In addition, lx has a need for some illumos/Linux translation for the
+ * various *stat(2) system calls when used on a device. This translation can
+ * be centralized within lxd's getattr vnode entry point.
+ *
+ * Because the front file system only exists in memory and the back file
+ * system is the zone's devfs, which is not persistent across reboots, we
+ * track any device uid/gid/mode changes in a per-zone /etc/.lxd_dev_attr
+ * file and re-apply those changes when the lx devfs file system is mounted.
+ * Currently only changes to block device nodes are persistent.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <sys/kmem.h>
+#include <sys/time.h>
+#include <sys/pathname.h>
+#include <sys/vfs.h>
+#include <sys/vfs_opreg.h>
+#include <sys/vnode.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/cmn_err.h>
+#include <sys/cred.h>
+#include <sys/statvfs.h>
+#include <sys/mount.h>
+#include <sys/systm.h>
+#include <sys/mntent.h>
+#include <sys/policy.h>
+#include <sys/sdt.h>
+#include <sys/ddi.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_ptm.h>
+#include <sys/lx_impl.h>
+
+#include "lxd.h"
+
+/* Module level parameters */
+static int	lxd_fstype;
+static dev_t	lxd_dev;
+
+/*
+ * lxd_mountcount is used to prevent module unloads while there is still
+ * state from a former mount hanging around. The filesystem module must not be
+ * allowed to go away before the last VFS_FREEVFS() call has been made. Since
+ * this is just an atomic counter, there's no need for locking.
+ */
+static uint32_t lxd_mountcount;
+
+/*
+ * lxd_minfree is the minimum amount of swap space that lx devfs leaves for
+ * the rest of the zone.
+ */
+size_t lxd_minfree = 0;
+
+/*
+ * LXDMINFREE -- the value from which lxd_minfree is derived -- should be
+ * configured to a value that is roughly the smallest practical value for
+ * memory + swap minus the largest reasonable size for lxd in such
+ * a configuration. As of this writing, the smallest practical memory + swap
+ * configuration is 128MB, and it seems reasonable to allow lxd to consume
+ * no more than ~10% of this, yielding a LXDMINFREE of 12MB.
+ */
+#define	LXDMINFREE	12 * 1024 * 1024	/* 12 Megabytes */
+
+extern pgcnt_t swapfs_minfree;
+
+extern int lxd_symlink(vnode_t *, char *, struct vattr *, char *, cred_t *,
+    caller_context_t *, int);
+extern int stat64(char *, struct stat64 *);
+
+/*
+ * lxd vfs operations.
+ */
+static int lxd_init(int, char *);
+static int lxd_mount(vfs_t *, vnode_t *, struct mounta *, cred_t *);
+static int lxd_unmount(vfs_t *, int, cred_t *);
+static int lxd_root(vfs_t *, vnode_t **);
+static int lxd_statvfs(vfs_t *, statvfs64_t *);
+static void lxd_freevfs(vfs_t *vfsp);
+
+/*
+ * Loadable module wrapper
+ */
+#include <sys/modctl.h>
+
+static vfsdef_t vfw = {
+	VFSDEF_VERSION,
+	"lx_devfs",
+	lxd_init,
+	VSW_ZMOUNT,
+	NULL
+};
+
+/*
+ * Module linkage information
+ */
+static struct modlfs modlfs = {
+	&mod_fsops, "lx brand devfs", &vfw
+};
+
+static struct modlinkage modlinkage = {
+	MODREV_1, &modlfs, NULL
+};
+
+/*
+ * Definitions and translators for devt's.
+ */
+static void lxd_pts_devt_translator(dev_t, dev_t *);
+static void lxd_ptm_devt_translator(dev_t, dev_t *);
+
+static kmutex_t			lxd_xlate_lock;
+static boolean_t		lxd_xlate_initialized = B_FALSE;
+
+static lxd_minor_translator_t lxd_mtranslator_mm[] = {
+	{ "/dev/null",		0, 1, 3 },
+	{ "/dev/zero",		0, 1, 5 },
+	{ NULL,			0, 0, 0 }
+};
+static lxd_minor_translator_t lxd_mtranslator_random[] = {
+	{ "/dev/random",	0, 1, 8 },
+	{ "/dev/urandom",	0, 1, 9 },
+	{ NULL,			0, 0, 0 }
+};
+static lxd_minor_translator_t lxd_mtranslator_sy[] = {
+	{ "/dev/tty",		0, LX_TTY_MAJOR, 0 },
+	{ NULL,			0, 0, 0 }
+};
+static lxd_minor_translator_t lxd_mtranslator_zcons[] = {
+	{ "/dev/console",	0, LX_TTY_MAJOR, 1 },
+	{ NULL,			0, 0, 0 }
+};
+lxd_devt_translator_t lxd_devt_translators[] = {
+	{ "mm",		0, DTT_LIST,	(uintptr_t)&lxd_mtranslator_mm },
+	{ "random",	0, DTT_LIST,	(uintptr_t)&lxd_mtranslator_random },
+	{ "sy",		0, DTT_LIST,	(uintptr_t)&lxd_mtranslator_sy },
+	{ "zcons",	0, DTT_LIST,	(uintptr_t)&lxd_mtranslator_zcons },
+	{ LX_PTM_DRV,	0, DTT_CUSTOM,	(uintptr_t)lxd_ptm_devt_translator },
+	{ "pts",	0, DTT_CUSTOM,	(uintptr_t)lxd_pts_devt_translator },
+	{ NULL,		0, DTT_INVALID,	NULL }
+};
+
+int
+_init()
+{
+	return (mod_install(&modlinkage));
+}
+
+int
+_fini()
+{
+	int error;
+
+	if (lxd_mountcount > 0)
+		return (EBUSY);
+
+	if ((error = mod_remove(&modlinkage)) != 0)
+		return (error);
+
+	/*
+	 * Tear down the operations vectors
+	 */
+	(void) vfs_freevfsops_by_type(lxd_fstype);
+	vn_freevnodeops(lxd_vnodeops);
+	mutex_destroy(&lxd_xlate_lock);
+	return (0);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&modlinkage, modinfop));
+}
+
+/*
+ * Initialize global locks, etc. Called when loading lxd module.
+ */
+static int
+lxd_init(int fstype, char *name)
+{
+	static const fs_operation_def_t lxd_vfsops_template[] = {
+		VFSNAME_MOUNT,		{ .vfs_mount = lxd_mount },
+		VFSNAME_UNMOUNT,	{ .vfs_unmount = lxd_unmount },
+		VFSNAME_ROOT,		{ .vfs_root = lxd_root },
+		VFSNAME_STATVFS,	{ .vfs_statvfs = lxd_statvfs },
+		VFSNAME_FREEVFS,	{ .vfs_freevfs = lxd_freevfs },
+		NULL,			NULL
+	};
+	extern const struct fs_operation_def lxd_vnodeops_template[];
+	int error;
+	major_t dev;
+
+	lxd_fstype = fstype;
+	ASSERT(lxd_fstype != 0);
+
+	error = vfs_setfsops(fstype, lxd_vfsops_template, NULL);
+	if (error != 0) {
+		cmn_err(CE_WARN, "lxd_init: bad vfs ops template");
+		return (error);
+	}
+
+	error = vn_make_ops(name, lxd_vnodeops_template, &lxd_vnodeops);
+	if (error != 0) {
+		(void) vfs_freevfsops_by_type(fstype);
+		cmn_err(CE_WARN, "lxd_init: bad vnode ops template");
+		return (error);
+	}
+
+	/*
+	 * lxd_minfree doesn't need to be some function of configured
+	 * swap space since it really is an absolute limit of swap space
+	 * which still allows other processes to execute.
+	 */
+	if (lxd_minfree == 0) {
+		/* Set if not patched */
+		lxd_minfree = btopr(LXDMINFREE);
+	}
+
+	if ((dev = getudev()) == (major_t)-1) {
+		cmn_err(CE_WARN, "lxd_init: Can't get unique device number.");
+		dev = 0;
+	}
+
+	/*
+	 * Make the pseudo device
+	 */
+	lxd_dev = makedevice(dev, 0);
+
+	mutex_init(&lxd_xlate_lock, NULL, MUTEX_DEFAULT, NULL);
+
+	return (0);
+}
+
+/*
+ * Initialize device translator mapping table.
+ *
+ * Note that we cannot do this in lxd_init since that can lead to a recursive
+ * rw_enter while we're doing lookupnameat (via sdev_lookup/prof_make_maps/
+ * devi_attach_node/modload). Thus we do it in the mount path and keep track
+ * so that we only initialize the table once.
+ */
+static void
+lxd_xlate_init()
+{
+	int i;
+
+	mutex_enter(&lxd_xlate_lock);
+	if (lxd_xlate_initialized) {
+		mutex_exit(&lxd_xlate_lock);
+		return;
+	}
+
+	for (i = 0; lxd_devt_translators[i].lxd_xl_driver != NULL; i++) {
+		lxd_minor_translator_t	*mt;
+		int j;
+
+		lxd_devt_translators[i].lxd_xl_major =
+		    mod_name_to_major(lxd_devt_translators[i].lxd_xl_driver);
+
+		/* if this translator doesn't use a list mapping we're done. */
+		if (lxd_devt_translators[i].lxd_xl_type != DTT_LIST)
+			continue;
+
+		/* for each device listed, lookup the minor node number */
+		mt = lxd_devt_translators[i].xl_list;
+		for (j = 0; mt[j].lxd_mt_path != NULL; j++) {
+			vnode_t *vp;
+			struct vattr va;
+			char *tpath;
+			char tnm[MAXPATHLEN];
+
+			/*
+			 * The attach might be triggered in either the global
+			 * zone or in a non-global zone, so we may need to
+			 * adjust the path if we're in a NGZ.
+			 */
+			if (curproc->p_zone->zone_id == GLOBAL_ZONEUNIQID) {
+				tpath = mt[j].lxd_mt_path;
+			} else {
+				(void) snprintf(tnm, sizeof (tnm), "/native%s",
+				    mt[j].lxd_mt_path);
+				tpath = tnm;
+			}
+
+			if (lookupnameat(tpath, UIO_SYSSPACE, FOLLOW, NULL,
+			    &vp, NULL) != 0) {
+				mt[j].lxd_mt_minor = UINT_MAX;
+				continue;
+			}
+
+			va.va_mask = AT_RDEV;
+			if (VOP_GETATTR(vp, &va, 0, kcred, NULL) != 0) {
+				va.va_rdev = NODEV;
+			} else {
+				ASSERT(getmajor(va.va_rdev) ==
+				    lxd_devt_translators[i].lxd_xl_major);
+				ASSERT(mt[j].lxd_mt_lx_minor < LX_MAXMIN);
+			}
+
+			mt[j].lxd_mt_minor = getminor(va.va_rdev);
+
+			VN_RELE(vp);
+		}
+	}
+
+	lxd_xlate_initialized = B_TRUE;
+	mutex_exit(&lxd_xlate_lock);
+}
+
+static int
+lxd_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
+{
+	lxd_mnt_t *lxdm = NULL;
+	struct lxd_node *ldn;
+	struct pathname dpn;
+	int error;
+	int i;
+	int nodev;
+	struct vattr rattr;
+	vnode_t *realrootvp;
+	vnode_t *tvp;
+	lx_zone_data_t *lxzdata;
+	lx_virt_disk_t *vd;
+	vattr_t vattr;
+
+	nodev = vfs_optionisset(vfsp, MNTOPT_NODEVICES, NULL);
+
+	if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0)
+		return (error);
+
+	if (mvp->v_type != VDIR)
+		return (ENOTDIR);
+
+	lxd_xlate_init();
+
+	/*
+	 * This is the same behavior as with lofs.
+	 * Loopback devices which get "nodevices" added can be done without
+	 * "nodevices" set because we cannot import devices into a zone
+	 * with loopback.  Note that we have all zone privileges when
+	 * this happens; if not, we'd have gotten "nosuid".
+	 */
+	if (!nodev && vfs_optionisset(vfsp, MNTOPT_NODEVICES, NULL))
+		vfs_setmntopt(vfsp, MNTOPT_DEVICES, NULL, VFS_NODISPLAY);
+
+	/*
+	 * Only allow mounting within lx zones.
+	 */
+	if (curproc->p_zone->zone_brand != &lx_brand)
+		return (EINVAL);
+
+	/*
+	 * Ensure we don't allow overlaying mounts
+	 */
+	mutex_enter(&mvp->v_lock);
+	if ((uap->flags & MS_OVERLAY) == 0 &&
+	    (mvp->v_count > 1 || (mvp->v_flag & VROOT))) {
+		mutex_exit(&mvp->v_lock);
+		return (EBUSY);
+	}
+	mutex_exit(&mvp->v_lock);
+
+	/* lxd doesn't support read-only mounts */
+	if (vfs_optionisset(vfsp, MNTOPT_RO, NULL)) {
+		error = EINVAL;
+		goto out;
+	}
+
+	error = pn_get(uap->dir,
+	    (uap->flags & MS_SYSSPACE) ? UIO_SYSSPACE : UIO_USERSPACE, &dpn);
+	if (error != 0)
+		goto out;
+
+	/*
+	 * Find real root
+	 */
+	if ((error = lookupname(uap->spec, (uap->flags & MS_SYSSPACE) ?
+	    UIO_SYSSPACE : UIO_USERSPACE, FOLLOW, NULLVPP, &realrootvp))) {
+		pn_free(&dpn);
+		return (error);
+	}
+
+	if ((error = VOP_ACCESS(realrootvp, 0, 0, cr, NULL)) != 0) {
+		pn_free(&dpn);
+		VN_RELE(realrootvp);
+		return (error);
+	}
+
+	/* If realroot is not a devfs, error out */
+	if (strcmp(realrootvp->v_op->vnop_name, "dev") != 0) {
+		pn_free(&dpn);
+		VN_RELE(realrootvp);
+		return (EINVAL);
+	}
+
+	lxdm = kmem_zalloc(sizeof (*lxdm), KM_SLEEP);
+
+	/* init but don't bother entering the mutex (not on mount list yet) */
+	mutex_init(&lxdm->lxdm_contents, NULL, MUTEX_DEFAULT, NULL);
+	mutex_init(&lxdm->lxdm_renamelck, NULL, MUTEX_DEFAULT, NULL);
+	mutex_init(&lxdm->lxdm_attrlck, NULL, MUTEX_DEFAULT, NULL);
+
+	list_create(&lxdm->lxdm_devattrs, sizeof (lxd_dev_attr_t),
+	    offsetof(lxd_dev_attr_t, lxda_link));
+
+	/* Initialize the hash table mutexes */
+	for (i = 0; i < LXD_HASH_SZ; i++) {
+		mutex_init(&lxdm->lxdm_hash_mutex[i], NULL, MUTEX_DEFAULT,
+		    NULL);
+	}
+
+	lxdm->lxdm_vfsp = vfsp;
+	lxdm->lxdm_gen = 1;	/* start inode counter at 1 */
+
+	vfsp->vfs_data = (caddr_t)lxdm;
+	vfsp->vfs_fstype = lxd_fstype;
+	vfsp->vfs_dev = lxd_dev;
+	vfsp->vfs_bsize = PAGESIZE;
+	vfsp->vfs_flag |= VFS_NOTRUNC;
+	vfs_make_fsid(&vfsp->vfs_fsid, lxd_dev, lxd_fstype);
+	lxdm->lxdm_mntpath = kmem_zalloc(dpn.pn_pathlen + 1, KM_SLEEP);
+	(void) strcpy(lxdm->lxdm_mntpath, dpn.pn_path);
+
+	/* allocate and initialize root lxd_node structure */
+	bzero(&rattr, sizeof (struct vattr));
+	rattr.va_mode = (mode_t)(S_IFDIR | 0755);
+	rattr.va_type = VDIR;
+	rattr.va_rdev = 0;
+
+	tvp = lxd_make_back_node(realrootvp, lxdm);
+	ldn = VTOLDN(tvp);
+
+	rw_enter(&ldn->lxdn_rwlock, RW_WRITER);
+	LDNTOV(ldn)->v_flag |= VROOT;
+
+	/*
+	 * initialize linked list of lxd_nodes so that the back pointer of
+	 * the root lxd_node always points to the last one on the list
+	 * and the forward pointer of the last node is null
+	 */
+	ldn->lxdn_prev = ldn;
+	ldn->lxdn_next = NULL;
+	ldn->lxdn_nlink = 0;
+	lxdm->lxdm_rootnode = ldn;
+
+	ldn->lxdn_nodeid = lxdm->lxdm_gen++;
+	lxd_dirinit(ldn, ldn);
+
+	rw_exit(&ldn->lxdn_rwlock);
+
+	pn_free(&dpn);
+	error = 0;
+	atomic_inc_32(&lxd_mountcount);
+
+	lxzdata = ztolxzd(curproc->p_zone);
+	ASSERT(lxzdata->lxzd_vdisks != NULL);
+
+	vattr.va_mask = AT_TYPE | AT_MODE;
+	vattr.va_type = VLNK;
+	vattr.va_mode = 0777;
+
+	vd = list_head(lxzdata->lxzd_vdisks);
+	while (vd != NULL) {
+		if (vd->lxvd_type == LXVD_ZVOL) {
+			char lnknm[MAXPATHLEN];
+
+			/* Create a symlink for the actual zvol. */
+			(void) snprintf(lnknm, sizeof (lnknm),
+			    "./zvol/dsk/%s", vd->lxvd_real_name);
+			(void) lxd_symlink(LDNTOV(ldn), vd->lxvd_name, &vattr,
+			    lnknm, cr, NULL, 0);
+		} else if (vd->lxvd_type == LXVD_ZFS_DS) {
+			/*
+			 * Create a symlink for the root "disk" using /dev/zfs
+			 * as the target device.
+			 */
+			(void) lxd_symlink(LDNTOV(ldn), vd->lxvd_name, &vattr,
+			    "./zfs", cr, NULL, 0);
+		}
+
+		vd = list_next(lxzdata->lxzd_vdisks, vd);
+	}
+
+	/* Apply any persistent attribute changes. */
+	lxd_apply_db(lxdm);
+
+out:
+	if (error == 0)
+		vfs_set_feature(vfsp, VFSFT_SYSATTR_VIEWS);
+
+	return (error);
+}
+
+static int
+lxd_unmount(struct vfs *vfsp, int flag, struct cred *cr)
+{
+	lxd_mnt_t *lxdm = (lxd_mnt_t *)VFSTOLXDM(vfsp);
+	lxd_node_t *ldn, *cancel;
+	struct vnode	*vp;
+	int error;
+	uint_t cnt;
+
+	if ((error = secpolicy_fs_unmount(cr, vfsp)) != 0)
+		return (error);
+
+	mutex_enter(&lxdm->lxdm_contents);
+
+	/*
+	 * In the normal unmount case only the root node would have a reference
+	 * count.
+	 *
+	 * With lxdm_contents held, nothing can be added or removed.
+	 * If we find a previously referenced node, undo the holds we have
+	 * placed and fail EBUSY.
+	 */
+	ldn = lxdm->lxdm_rootnode;
+
+	vp = LDNTOV(ldn);
+	mutex_enter(&vp->v_lock);
+
+	if (flag & MS_FORCE) {
+		mutex_exit(&vp->v_lock);
+		mutex_exit(&lxdm->lxdm_contents);
+		return (EINVAL);
+	}
+
+	cnt = vp->v_count;
+	if (cnt > 1) {
+		mutex_exit(&vp->v_lock);
+		mutex_exit(&lxdm->lxdm_contents);
+		return (EBUSY);
+	}
+
+	mutex_exit(&vp->v_lock);
+
+	/*
+	 * Check for open files. An open file causes everything to unwind.
+	 */
+	for (ldn = ldn->lxdn_next; ldn; ldn = ldn->lxdn_next) {
+		vp = LDNTOV(ldn);
+		mutex_enter(&vp->v_lock);
+		cnt = vp->v_count;
+		if (cnt > 0) {
+			/* An open file; unwind the holds we've been adding. */
+			mutex_exit(&vp->v_lock);
+			cancel = lxdm->lxdm_rootnode->lxdn_next;
+			while (cancel != ldn) {
+				vp = LDNTOV(cancel);
+				ASSERT(vp->v_count > 0);
+				VN_RELE(vp);
+				cancel = cancel->lxdn_next;
+			}
+			mutex_exit(&lxdm->lxdm_contents);
+			return (EBUSY);
+		} else {
+			/*
+			 * It may seem incorrect for us to have a vnode with
+			 * a count of 0, but this is modeled on tmpfs and works
+			 * the same way. See lxd_front_inactive. There we allow
+			 * the v_count to go to 0 but rely on the link count to
+			 * keep the vnode alive. Since we now want to cleanup
+			 * these vnodes we manually add a VN_HOLD so that the
+			 * VN_RELEs that occur in the lxd_freevfs() cleanup
+			 * will take us down the lxd_inactive code path. We
+			 * can directly add a VN_HOLD since we have the lock.
+			 */
+			vp->v_count++;
+			mutex_exit(&vp->v_lock);
+		}
+	}
+
+	/*
+	 * We can drop the mutex now because
+	 * no one can find this mount anymore
+	 */
+	vfsp->vfs_flag |= VFS_UNMOUNTED;
+	mutex_exit(&lxdm->lxdm_contents);
+
+	return (0);
+}
+
+/*
+ * Implementation of VFS_FREEVFS(). This is called by the vfs framework after
+ * umount and the last VFS_RELE, to trigger the release of any resources still
+ * associated with the given vfs_t. This is normally called immediately after
+ * lxd_unmount.
+ */
+void
+lxd_freevfs(vfs_t *vfsp)
+{
+	lxd_mnt_t *lxdm = (lxd_mnt_t *)VFSTOLXDM(vfsp);
+	lxd_node_t *ldn;
+	struct vnode *vp;
+	lxd_dev_attr_t *da;
+
+	/*
+	 * Free all kmemalloc'd and anonalloc'd memory associated with
+	 * this filesystem.  To do this, we go through the file list twice,
+	 * once to remove all the directory entries, and then to remove
+	 * all the pseudo files.
+	 */
+
+	/*
+	 * Now that we are tearing ourselves down we need to remove the
+	 * UNMOUNTED flag. If we don't, we'll later hit a VN_RELE when we remove
+	 * files from the system causing us to have a negative value. Doing this
+	 * seems a bit better than trying to set a flag on the lxd_mnt_t that
+	 * says we're tearing down.
+	 */
+	vfsp->vfs_flag &= ~VFS_UNMOUNTED;
+
+	/*
+	 * Remove all directory entries (this doesn't remove top-level dirs).
+	 */
+	for (ldn = lxdm->lxdm_rootnode; ldn; ldn = ldn->lxdn_next) {
+		rw_enter(&ldn->lxdn_rwlock, RW_WRITER);
+		if (ldn->lxdn_vnode->v_type == VDIR)
+			lxd_dirtrunc(ldn);
+		rw_exit(&ldn->lxdn_rwlock);
+	}
+
+	ASSERT(lxdm->lxdm_rootnode != NULL);
+
+	/*
+	 * All links are gone, v_count is keeping nodes in place.
+	 * VN_RELE should make the node disappear, unless somebody
+	 * is holding pages against it.  Nap and retry until it disappears.
+	 *
+	 * We re-acquire the lock to prevent others who have a HOLD on a
+	 * lxd_node from blowing it away (in lxd_inactive) while we're trying
+	 * to get to it here. Once we have a HOLD on it we know it'll stick
+	 * around.
+	 */
+	mutex_enter(&lxdm->lxdm_contents);
+
+	/*
+	 * Remove all the files (except the rootnode) backwards.
+	 */
+	while ((ldn = lxdm->lxdm_rootnode->lxdn_prev) != lxdm->lxdm_rootnode) {
+		mutex_exit(&lxdm->lxdm_contents);
+		/*
+		 * All nodes will be released here. Note we handled the link
+		 * count above.
+		 */
+		vp = LDNTOV(ldn);
+		ASSERT(vp->v_type == VLNK || vp->v_type == VDIR ||
+		    vp->v_type == VSOCK);
+		VN_RELE(vp);
+		mutex_enter(&lxdm->lxdm_contents);
+		/*
+		 * It's still there after the RELE. Someone else like pageout
+		 * has a hold on it so wait a bit and then try again - we know
+		 * they'll give it up soon.
+		 */
+		if (ldn == lxdm->lxdm_rootnode->lxdn_prev) {
+			VN_HOLD(vp);
+			mutex_exit(&lxdm->lxdm_contents);
+			delay(hz / 4);
+			mutex_enter(&lxdm->lxdm_contents);
+		}
+	}
+	mutex_exit(&lxdm->lxdm_contents);
+
+	ASSERT(lxdm->lxdm_back_refcnt == 1);
+	ASSERT(lxdm->lxdm_dent_refcnt == 0);
+
+	VN_RELE(LDNTOV(lxdm->lxdm_rootnode));
+
+	ASSERT(lxdm->lxdm_mntpath != NULL);
+	kmem_free(lxdm->lxdm_mntpath, strlen(lxdm->lxdm_mntpath) + 1);
+
+	da = list_remove_head(&lxdm->lxdm_devattrs);
+	while (da != NULL) {
+		kmem_free(da, sizeof (lxd_dev_attr_t));
+		da = list_remove_head(&lxdm->lxdm_devattrs);
+	}
+	list_destroy(&lxdm->lxdm_devattrs);
+
+	mutex_destroy(&lxdm->lxdm_contents);
+	mutex_destroy(&lxdm->lxdm_renamelck);
+	mutex_destroy(&lxdm->lxdm_attrlck);
+	kmem_free(lxdm, sizeof (lxd_mnt_t));
+
+	/* Allow _fini() to succeed now */
+	atomic_dec_32(&lxd_mountcount);
+}
+
+/*
+ * return root lxdnode for given vnode
+ */
+static int
+lxd_root(struct vfs *vfsp, struct vnode **vpp)
+{
+	lxd_mnt_t *lxdm = (lxd_mnt_t *)VFSTOLXDM(vfsp);
+	lxd_node_t *ldn = lxdm->lxdm_rootnode;
+	struct vnode *vp;
+
+	ASSERT(ldn != NULL);
+
+	vp = LDNTOV(ldn);
+	VN_HOLD(vp);
+	*vpp = vp;
+	return (0);
+}
+
+static int
+lxd_statvfs(struct vfs *vfsp, statvfs64_t *sbp)
+{
+	lxd_mnt_t *lxdm = (lxd_mnt_t *)VFSTOLXDM(vfsp);
+	ulong_t	blocks;
+	dev32_t d32;
+	zoneid_t eff_zid;
+	struct zone *zp;
+
+	zp = lxdm->lxdm_vfsp->vfs_zone;
+
+	if (zp == NULL)
+		eff_zid = GLOBAL_ZONEUNIQID;
+	else
+		eff_zid = zp->zone_id;
+
+	sbp->f_bsize = PAGESIZE;
+	sbp->f_frsize = PAGESIZE;
+
+	/*
+	 * Find the amount of available physical and memory swap
+	 */
+	mutex_enter(&anoninfo_lock);
+	ASSERT(k_anoninfo.ani_max >= k_anoninfo.ani_phys_resv);
+	blocks = (ulong_t)CURRENT_TOTAL_AVAILABLE_SWAP;
+	mutex_exit(&anoninfo_lock);
+
+	if (blocks > lxd_minfree)
+		sbp->f_bfree = blocks - lxd_minfree;
+	else
+		sbp->f_bfree = 0;
+
+	sbp->f_bavail = sbp->f_bfree;
+
+	/*
+	 * Total number of blocks is just what's available
+	 */
+	sbp->f_blocks = (fsblkcnt64_t)(sbp->f_bfree);
+
+	if (eff_zid != GLOBAL_ZONEUNIQID &&
+	    zp->zone_max_swap_ctl != UINT64_MAX) {
+		/*
+		 * If the fs is used by a zone with a swap cap,
+		 * then report the capped size.
+		 */
+		rctl_qty_t cap, used;
+		pgcnt_t pgcap, pgused;
+
+		mutex_enter(&zp->zone_mem_lock);
+		cap = zp->zone_max_swap_ctl;
+		used = zp->zone_max_swap;
+		mutex_exit(&zp->zone_mem_lock);
+
+		pgcap = btop(cap);
+		pgused = btop(used);
+
+		sbp->f_bfree = MIN(pgcap - pgused, sbp->f_bfree);
+		sbp->f_bavail = sbp->f_bfree;
+		sbp->f_blocks = MIN(pgcap, sbp->f_blocks);
+	}
+
+	/*
+	 * The maximum number of files available is approximately the number
+	 * of lxd_nodes we can allocate from the remaining kernel memory
+	 * available to lxdevfs in this zone.  This is fairly inaccurate since
+	 * it doesn't take into account the names stored in the directory
+	 * entries.
+	 */
+	sbp->f_ffree = sbp->f_files = ptob(availrmem) /
+	    (sizeof (lxd_node_t) + sizeof (lxd_dirent_t));
+	sbp->f_favail = (fsfilcnt64_t)(sbp->f_ffree);
+	(void) cmpldev(&d32, vfsp->vfs_dev);
+	sbp->f_fsid = d32;
+	(void) strcpy(sbp->f_basetype, vfssw[lxd_fstype].vsw_name);
+	(void) strncpy(sbp->f_fstr, lxdm->lxdm_mntpath, sizeof (sbp->f_fstr));
+	/* ensure null termination */
+	sbp->f_fstr[sizeof (sbp->f_fstr) - 1] = '\0';
+	sbp->f_flag = vf_to_stf(vfsp->vfs_flag);
+	sbp->f_namemax = MAXNAMELEN - 1;
+	return (0);
+}
+
+static void
+lxd_pts_devt_translator(dev_t dev, dev_t *jdev)
+{
+	minor_t	min = getminor(dev);
+	int	lx_maj, lx_min;
+
+	/*
+	 * Linux uses a range of major numbers for pts devices to address the
+	 * relatively small minor number space (20 bits).
+	 */
+
+	lx_maj = LX_PTS_MAJOR_MIN + (min / LX_MAXMIN);
+	lx_min = min % LX_MAXMIN;
+	if (lx_maj > LX_PTS_MAJOR_MAX) {
+		/*
+		 * The major is outside the acceptable range but there's little
+		 * we can presently do about it short of overhauling the
+		 * translation logic.
+		 */
+		lx_unsupported("pts major out of translation range");
+	}
+
+	*jdev = LX_MAKEDEVICE(lx_maj, lx_min);
+}
+
+/* ARGSUSED */
+static void
+lxd_ptm_devt_translator(dev_t dev, dev_t *jdev)
+{
+	*jdev = LX_MAKEDEVICE(LX_PTM_MAJOR, LX_PTM_MINOR);
+}
--- /dev/null	Wed Oct  5 00:32:04 2016
+++ new/usr/src/uts/common/brand/lx/devfs/lxd_vnops.c	Wed Oct  5 00:32:04 2016
@@ -0,0 +1,1519 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/vnode.h>
+#include <sys/vfs.h>
+#include <sys/vfs_opreg.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/cred.h>
+#include <sys/pathname.h>
+#include <sys/debug.h>
+#include <sys/sdt.h>
+#include <fs/fs_subr.h>
+#include <vm/as.h>
+#include <vm/seg.h>
+#include <sys/lx_brand.h>
+#include <sys/brand.h>
+
+#include "lxd.h"
+
+static int
+lxd_open(vnode_t **vpp, int flag, struct cred *cr, caller_context_t *ct)
+{
+	lxd_node_t *ldn = VTOLDN(*vpp);
+	vnode_t *vp = *vpp;
+	vnode_t *rvp;
+	vnode_t *oldvp;
+	int error;
+
+	if (ldn->lxdn_type == LXDNT_FRONT)
+		return (0);
+
+	ASSERT(ldn->lxdn_type == LXDNT_BACK);
+	oldvp = vp;
+	vp = rvp = REALVP(vp);
+	/*
+	 * Need to hold new reference to vp since VOP_OPEN() may
+	 * decide to release it.
+	 */
+	VN_HOLD(vp);
+	error = VOP_OPEN(&rvp, flag, cr, ct);
+
+	if (!error && rvp != vp) {
+		/*
+		 * the FS which we called should have released the
+		 * new reference on vp
+		 */
+		*vpp = lxd_make_back_node(rvp, VFSTOLXDM(oldvp->v_vfsp));
+
+		if (IS_DEVVP(*vpp)) {
+			vnode_t *svp;
+
+			svp = specvp(*vpp, (*vpp)->v_rdev, (*vpp)->v_type, cr);
+			VN_RELE(*vpp);
+			if (svp == NULL)
+				error = ENOSYS;
+			else
+				*vpp = svp;
+		}
+		VN_RELE(oldvp);
+	} else {
+		ASSERT(rvp->v_count > 1);
+		VN_RELE(rvp);
+	}
+
+	return (error);
+}
+
+static int
+lxd_close(vnode_t *vp, int flag, int count, offset_t offset, struct cred *cr,
+    caller_context_t *ct)
+{
+	lxd_node_t *ldn = VTOLDN(vp);
+
+	if (ldn->lxdn_type == LXDNT_FRONT)
+		return (0);
+
+	ASSERT(ldn->lxdn_type == LXDNT_BACK);
+	vp = REALVP(vp);
+	return (VOP_CLOSE(vp, flag, count, offset, cr, ct));
+}
+
+static int
+lxd_read(vnode_t *vp, struct uio *uiop, int ioflag, struct cred *cr,
+    caller_context_t *ct)
+{
+	lxd_node_t *ldn = VTOLDN(vp);
+
+	if (ldn->lxdn_type == LXDNT_FRONT)
+		return (ENOTSUP);
+
+	ASSERT(ldn->lxdn_type == LXDNT_BACK);
+	vp = REALVP(vp);
+	return (VOP_READ(vp, uiop, ioflag, cr, ct));
+}
+
+static int
+lxd_write(vnode_t *vp, struct uio *uiop, int ioflag, struct cred *cr,
+    caller_context_t *ct)
+{
+	lxd_node_t *ldn = VTOLDN(vp);
+
+	if (ldn->lxdn_type == LXDNT_FRONT)
+		return (ENOTSUP);
+
+	ASSERT(ldn->lxdn_type == LXDNT_BACK);
+	vp = REALVP(vp);
+	return (VOP_WRITE(vp, uiop, ioflag, cr, ct));
+}
+
+static int
+lxd_ioctl(vnode_t *vp, int cmd, intptr_t arg, int flag, struct cred *cr,
+    int *rvalp, caller_context_t *ct)
+{
+	lxd_node_t *ldn = VTOLDN(vp);
+
+	if (ldn->lxdn_type == LXDNT_FRONT)
+		return (ENOTSUP);
+
+	ASSERT(ldn->lxdn_type == LXDNT_BACK);
+	vp = REALVP(vp);
+	return (VOP_IOCTL(vp, cmd, arg, flag, cr, rvalp, ct));
+}
+
+static int
+lxd_setfl(vnode_t *vp, int oflags, int nflags, cred_t *cr, caller_context_t *ct)
+{
+	lxd_node_t *ldn = VTOLDN(vp);
+
+	if (ldn->lxdn_type == LXDNT_FRONT)
+		return (ENOTSUP);
+
+	ASSERT(ldn->lxdn_type == LXDNT_BACK);
+	vp = REALVP(vp);
+	return (VOP_SETFL(vp, oflags, nflags, cr, ct));
+}
+
+/*
+ * Translate SunOS devt to Linux devt.
+ */
+static void
+lxd_s2l_devt(dev_t dev, dev_t *rdev)
+{
+	lxd_minor_translator_t	*mt;
+	int			i, j;
+	major_t			maj = getmajor(dev);
+	minor_t			min = getminor(dev);
+
+	/* look for a devt translator for this major number */
+	for (i = 0; lxd_devt_translators[i].lxd_xl_driver != NULL; i++) {
+		if (lxd_devt_translators[i].lxd_xl_major == maj)
+			break;
+	}
+
+	if (lxd_devt_translators[i].lxd_xl_driver != NULL) {
+		/* try to translate the illumos devt to a linux devt */
+		switch (lxd_devt_translators[i].lxd_xl_type) {
+		case DTT_INVALID:
+			ASSERT(0);
+			break;
+
+		case DTT_LIST:
+			mt = lxd_devt_translators[i].xl_list;
+			for (j = 0; mt[j].lxd_mt_path != NULL; j++) {
+				if (mt[j].lxd_mt_minor == min) {
+					ASSERT(mt[j].lxd_mt_minor < LX_MAXMIN);
+
+					/* found a translation */
+					*rdev = LX_MAKEDEVICE(
+					    mt[j].lxd_mt_lx_major,
+					    mt[j].lxd_mt_lx_minor);
+					return;
+				}
+			}
+			break;
+
+		case DTT_CUSTOM:
+			lxd_devt_translators[i].xl_custom(dev, rdev);
+			return;
+		}
+	}
+
+	/* we don't have a translator for this device */
+	*rdev = LX_MAKEDEVICE(maj, min);
+}
+
+static int
+lxd_getattr(vnode_t *vp, struct vattr *vap, int flags, struct cred *cr,
+    caller_context_t *ct)
+{
+	lxd_node_t *ldn = VTOLDN(vp);
+	int error;
+	vnode_t *rvp;
+
+	if (ldn->lxdn_type == LXDNT_FRONT) {
+		mutex_enter(&ldn->lxdn_tlock);
+
+		vap->va_type = vp->v_type;
+		vap->va_mode = ldn->lxdn_mode & MODEMASK;
+		vap->va_uid = ldn->lxdn_uid;
+		vap->va_gid = ldn->lxdn_gid;
+		vap->va_fsid = ldn->lxdn_fsid;
+		vap->va_nodeid = (ino64_t)ldn->lxdn_nodeid;
+		vap->va_nlink = ldn->lxdn_nlink;
+		vap->va_size = (u_offset_t)ldn->lxdn_size;
+		vap->va_atime = ldn->lxdn_atime;
+		vap->va_mtime = ldn->lxdn_mtime;
+		vap->va_ctime = ldn->lxdn_ctime;
+		vap->va_blksize = PAGESIZE;
+		vap->va_rdev = 0;	/* no devs in front */
+		vap->va_seq = ldn->lxdn_seq;
+
+		vap->va_nblocks = (fsblkcnt64_t)btodb(ptob(btopr(
+		    vap->va_size)));
+		mutex_exit(&ldn->lxdn_tlock);
+		return (0);
+	}
+
+	ASSERT(ldn->lxdn_type == LXDNT_BACK);
+	rvp = REALVP(vp);
+	if ((error = VOP_GETATTR(rvp, vap, flags, cr, ct)))
+		return (error);
+
+	/* Skip devt translation for native programs */
+	if (curproc->p_brand != &lx_brand) {
+		return (0);
+	} else {
+		/*
+		 * We also skip translation when called from the user-land
+		 * emulation code.
+		 */
+		lx_lwp_data_t *lwpd = ttolxlwp(curthread);
+
+		if (lwpd == NULL || lwpd->br_stack_mode != LX_STACK_MODE_BRAND)
+			return (0);
+	}
+
+	if (rvp->v_type == VCHR) {
+		dev_t ldev;
+
+		lxd_s2l_devt(vap->va_rdev, &ldev);
+		DTRACE_PROBE3(lxd__devxl, void *, rvp, void *, vap, int, ldev);
+		vap->va_rdev = ldev;
+	}
+
+	return (0);
+}
+
+static int
+lxd_setattr(vnode_t *vp, struct vattr *vap, int flags, struct cred *cr,
+    caller_context_t *ct)
+{
+	lxd_node_t *ldn = VTOLDN(vp);
+	lxd_mnt_t *lxdm = VTOLXDM(vp);
+	int res;
+
+	if (ldn->lxdn_type == LXDNT_FRONT) {
+		int error = 0;
+		struct vattr *set;
+		long mask = vap->va_mask;
+
+		/* Cannot set these attributes */
+		if ((mask & AT_NOSET) || (mask & AT_XVATTR) ||
+		    (mask & AT_MODE && vap->va_mode & (S_ISUID | S_ISGID)) ||
+		    (mask & AT_SIZE))
+			return (EINVAL);
+
+		mutex_enter(&ldn->lxdn_tlock);
+
+		set = &ldn->lxdn_attr;
+		/*
+		 * Change file access modes. Must be owner or have sufficient
+		 * privileges.
+		 */
+		error = secpolicy_vnode_setattr(cr, vp, vap, set, flags,
+		    lxd_naccess, ldn);
+		if (error) {
+			mutex_exit(&ldn->lxdn_tlock);
+			return (error);
+		}
+
+		if (mask & AT_MODE) {
+			set->va_mode &= S_IFMT;
+			set->va_mode |= vap->va_mode & ~S_IFMT;
+		}
+
+		if (mask & AT_UID)
+			set->va_uid = vap->va_uid;
+		if (mask & AT_GID)
+			set->va_gid = vap->va_gid;
+		if (mask & AT_ATIME)
+			set->va_atime = vap->va_atime;
+		if (mask & AT_MTIME)
+			set->va_mtime = vap->va_mtime;
+
+		if (mask & (AT_UID | AT_GID | AT_MODE | AT_MTIME))
+			gethrestime(&ldn->lxdn_ctime);
+
+		mutex_exit(&ldn->lxdn_tlock);
+		return (error);
+	}
+
+	ASSERT(ldn->lxdn_type == LXDNT_BACK);
+	vp = REALVP(vp);
+	res = VOP_SETATTR(vp, vap, flags, cr, ct);
+	if (res == 0 && (vap->va_mask & (AT_MODE | AT_UID | AT_GID))) {
+		lxd_save_attrs(lxdm, vp);
+	}
+	return (res);
+}
+
+static int
+lxd_access(vnode_t *vp, int mode, int flags, struct cred *cr,
+    caller_context_t *ct)
+{
+	lxd_node_t *ldn = VTOLDN(vp);
+
+	if (ldn->lxdn_type == LXDNT_FRONT) {
+		int error;
+
+		mutex_enter(&ldn->lxdn_tlock);
+		error = lxd_naccess(ldn, mode, cr);
+		mutex_exit(&ldn->lxdn_tlock);
+		return (error);
+	}
+
+	ASSERT(ldn->lxdn_type == LXDNT_BACK);
+	if (mode & VWRITE) {
+		if (vp->v_type == VREG && vn_is_readonly(vp))
+			return (EROFS);
+	}
+	vp = REALVP(vp);
+	return (VOP_ACCESS(vp, mode, flags, cr, ct));
+}
+
+static int
+lxd_fsync(vnode_t *vp, int syncflag, struct cred *cr, caller_context_t *ct)
+{
+	lxd_node_t *ldn = VTOLDN(vp);
+
+	if (ldn->lxdn_type == LXDNT_FRONT)
+		return (0);
+
+	ASSERT(ldn->lxdn_type == LXDNT_BACK);
+	vp = REALVP(vp);
+	return (VOP_FSYNC(vp, syncflag, cr, ct));
+}
+
+/* ARGSUSED */
+static void
+lxd_front_inactive(struct vnode *vp, struct cred *cred, caller_context_t *ct)
+{
+	lxd_node_t *ldn = VTOLDN(vp);
+	lxd_mnt_t *lxdm = VTOLXDM(vp);
+
+	ASSERT(ldn->lxdn_type == LXDNT_FRONT);
+	rw_enter(&ldn->lxdn_rwlock, RW_WRITER);
+
+	mutex_enter(&ldn->lxdn_tlock);
+	mutex_enter(&vp->v_lock);
+	ASSERT(vp->v_count >= 1);
+
+	/*
+	 * If we don't have the last hold or the link count is non-zero,
+	 * there's little to do -- just drop our hold.
+	 */
+	if (vp->v_count > 1 || ldn->lxdn_nlink != 0) {
+		vp->v_count--;
+
+		mutex_exit(&vp->v_lock);
+		mutex_exit(&ldn->lxdn_tlock);
+		rw_exit(&ldn->lxdn_rwlock);
+		return;
+	}
+
+	/*
+	 * We have the last hold *and* the link count is zero, so this node is
+	 * dead from the filesystem's viewpoint.
+	 */
+	if (ldn->lxdn_size != 0) {
+		if (ldn->lxdn_vnode->v_type == VLNK)
+			kmem_free(ldn->lxdn_symlink, ldn->lxdn_size + 1);
+	}
+
+	mutex_exit(&vp->v_lock);
+	mutex_exit(&ldn->lxdn_tlock);
+
+	vn_invalid(LDNTOV(ldn));
+
+	mutex_enter(&lxdm->lxdm_contents);
+	if (ldn->lxdn_next == NULL)
+		lxdm->lxdm_rootnode->lxdn_prev = ldn->lxdn_prev;
+	else
+		ldn->lxdn_next->lxdn_prev = ldn->lxdn_prev;
+	ldn->lxdn_prev->lxdn_next = ldn->lxdn_next;
+
+	mutex_exit(&lxdm->lxdm_contents);
+	rw_exit(&ldn->lxdn_rwlock);
+	rw_destroy(&ldn->lxdn_rwlock);
+	mutex_destroy(&ldn->lxdn_tlock);
+
+	vn_free(LDNTOV(ldn));
+	kmem_free(ldn, sizeof (lxd_node_t));
+}
+
+/*ARGSUSED*/
+static void
+lxd_inactive(vnode_t *vp, struct cred *cr, caller_context_t *ct)
+{
+	lxd_node_t *ldn = VTOLDN(vp);
+
+	if (ldn->lxdn_type == LXDNT_FRONT) {
+		lxd_front_inactive(vp, cr, ct);
+		return;
+	}
+
+	ASSERT(ldn->lxdn_type == LXDNT_BACK);
+	lxd_free_back_node(ldn);
+}
+
+/* ARGSUSED */
+static int
+lxd_fid(vnode_t *vp, struct fid *fidp, caller_context_t *ct)
+{
+	lxd_node_t *ldn = VTOLDN(vp);
+
+	if (ldn->lxdn_type == LXDNT_FRONT)
+		return (ENOTSUP);
+
+	ASSERT(ldn->lxdn_type == LXDNT_BACK);
+	vp = REALVP(vp);
+	return (VOP_FID(vp, fidp, ct));
+}
+
+/*
+ * For a front node lookup in the dirent hash table and return a shadow vnode
+ * (lxd_node_t type) of type LXDNT_FRONT.
+ *
+ * For a back node, lookup nm name and return a shadow vnode (lxd_node_t type)
+ * of the real vnode found.
+ */
+static int
+lxd_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, struct pathname *pnp,
+    int flags, vnode_t *rdir, struct cred *cr, caller_context_t *ct,
+    int *direntflags, pathname_t *realpnp)
+{
+	vnode_t *vp = NULL;
+	int error;
+	vnode_t *realdvp;
+	lxd_mnt_t *lxdm = VTOLXDM(dvp);
+	int doingdotdot = 0;
+	lxd_node_t *ldn = VTOLDN(dvp);
+	lxd_node_t *nldn = NULL;
+
+	/*
+	 * First check for front file which could be instantiated on either a
+	 * front or back node (e.g. the top-level moint point directory node is
+	 * a back node which can have front files created in it).
+	 */
+
+	/* disallow extended attrs */
+	if (flags & LOOKUP_XATTR)
+		return (EINVAL);
+
+	/* Null component name is a synonym for dir being searched. */
+	if (*nm == '\0') {
+		VN_HOLD(dvp);
+		*vpp = dvp;
+		return (0);
+	}
+
+	rw_enter(&ldn->lxdn_rwlock, RW_READER);
+	error = lxd_dirlookup(ldn, nm, &nldn, cr);
+	rw_exit(&ldn->lxdn_rwlock);
+
+	if (error == 0) {
+		/* found */
+		ASSERT(nldn != NULL);
+		*vpp = LDNTOV(nldn);
+		return (0);
+	}
+
+	/* At this point, if dir node is a front node, error */
+	if (ldn->lxdn_type == LXDNT_FRONT) {
+		return (ENOENT);
+	}
+
+	realdvp = REALVP(dvp);
+
+	if (nm[0] == '.' && nm[1] == '.' && nm[2] == '\0') {
+		doingdotdot++;
+		/*
+		 * Handle ".." out of mounted filesystem
+		 */
+		while ((realdvp->v_flag & VROOT) && realdvp != rootdir) {
+			realdvp = realdvp->v_vfsp->vfs_vnodecovered;
+			ASSERT(realdvp != NULL);
+		}
+	}
+
+	*vpp = NULL;	/* default(error) case */
+
+	/*
+	 * Do the normal lookup
+	 */
+	if ((error = VOP_LOOKUP(realdvp, nm, &vp, pnp, flags, rdir, cr,
+	    ct, direntflags, realpnp)) != 0) {
+		vp = NULL;
+		goto out;
+	}
+
+	/*
+	 * We do this check here to avoid returning a stale file handle to the
+	 * caller.
+	 */
+	if (nm[0] == '.' && nm[1] == '\0') {
+		ASSERT(vp == realdvp);
+		VN_HOLD(dvp);
+		VN_RELE(vp);
+		*vpp = dvp;
+		return (0);
+	}
+
+	if (doingdotdot) {
+		*vpp = lxd_make_back_node(vp, lxdm);
+		return (0);
+	}
+
+	/*
+	 * If this vnode is mounted on, then we
+	 * traverse to the vnode which is the root of
+	 * the mounted file system.
+	 */
+	if ((error = traverse(&vp)) != 0)
+		goto out;
+
+	/*
+	 * Make a lxd node for the real vnode.
+	 */
+	*vpp = lxd_make_back_node(vp, lxdm);
+	if (vp->v_type != VDIR) {
+		if (IS_DEVVP(*vpp)) {
+			vnode_t *svp;
+
+			svp = specvp(*vpp, (*vpp)->v_rdev, (*vpp)->v_type, cr);
+			VN_RELE(*vpp);
+			if (svp == NULL) {
+				VN_RELE(vp);
+				error = ENOSYS;
+			} else {
+				*vpp = svp;
+			}
+		}
+		return (error);
+	}
+
+out:
+	if (error != 0 && vp != NULL)
+		VN_RELE(vp);
+
+	return (error);
+}
+
+/*ARGSUSED*/
+static int
+lxd_create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive,
+    int mode, vnode_t **vpp, struct cred *cr, int flag, caller_context_t *ct,
+    vsecattr_t *vsecp)
+{
+	int error;
+	lxd_node_t *parent = VTOLDN(dvp);
+	lxd_node_t *lnp = NULL;
+
+	rw_enter(&parent->lxdn_rwlock, RW_READER);
+	error = lxd_dirlookup(parent, nm, &lnp, cr);
+	rw_exit(&parent->lxdn_rwlock);
+	/*
+	 * If this vnode already exists in lx devfs, we should pass the create
+	 * operation through to the underlying resource it represents.  For
+	 * existing back nodes, the VOP_CREATE is done directly against the
+	 * returned lxd node with an empty name (to avoid a redunant lookup).
+	 * For existing front nodes, an appropriate error must be chosen since
+	 * they cannot represent regular files
+	 */
+	if (error == 0) {
+		if (lnp->lxdn_type == LXDNT_BACK) {
+			error = VOP_CREATE(lnp->lxdn_real_vp, "\0", va,
+			    exclusive, mode, vpp, cr, flag, ct, vsecp);
+		} else {
+			if (exclusive == EXCL) {
+				error = EEXIST;
+			} else if (LDNTOV(lnp)->v_type == VDIR &&
+			    (mode & S_IWRITE)) {
+				error = EISDIR;
+			} else {
+				error = ENOTSUP;
+			}
+		}
+		if (error != 0) {
+			ldnode_rele(lnp);
+		}
+		return (error);
+	}
+
+	/*
+	 * We cannot create files in the back devfs but we want to allow for
+	 * O_CREAT on existing files.  Pass this through and let the back file
+	 * system allow or deny it.
+	 */
+	if (parent->lxdn_type == LXDNT_BACK) {
+		vnode_t *vp = NULL;
+
+		if (*nm == '\0') {
+			ASSERT(vpp && dvp == *vpp);
+			vp = REALVP(*vpp);
+		}
+		if ((error = VOP_CREATE(REALVP(dvp), nm, va, exclusive, mode,
+		    &vp, cr, flag, ct, vsecp)) == 0) {
+			*vpp = lxd_make_back_node(vp, VFSTOLXDM(dvp->v_vfsp));
+			if (IS_DEVVP(*vpp)) {
+				vnode_t *svp;
+
+				svp = specvp(*vpp, (*vpp)->v_rdev,
+				    (*vpp)->v_type, cr);
+				VN_RELE(*vpp);
+				if (svp == NULL) {
+					return (ENOSYS);
+				}
+				*vpp = svp;
+			}
+			return (0);
+		}
+		/*
+		 * If we were unable to perform the VOP_CREATE for any reason
+		 * other than sdev being read-only, we should bail.
+		 */
+		if (error != ENOTSUP && error != EROFS) {
+			return (error);
+		}
+	}
+
+	/*
+	 * While we don't allow create data-containing files under LX devfs, we
+	 * must allow VSOCK front nodes to be created so that paths such as
+	 * /dev/log can be used as AF_UNIX sockets.
+	 */
+	if (va->va_type == VSOCK) {
+		lxd_mnt_t *lxdm = VTOLXDM(parent->lxdn_vnode);
+
+		lnp = NULL;
+		rw_enter(&parent->lxdn_rwlock, RW_WRITER);
+		error = lxd_direnter(lxdm, parent, nm, DE_CREATE, NULL, NULL,
+		    va, &lnp, cr);
+		rw_exit(&parent->lxdn_rwlock);
+
+		if (error == 0) {
+			*vpp = LDNTOV(lnp);
+		} else if (lnp != NULL) {
+			/*
+			 * It's possible that a racing process created an entry
+			 * at this name since we last performed the lookup.
+			 */
+			ldnode_rele(lnp);
+		}
+	} else {
+		error = ENOTSUP;
+	}
+
+	return (error);
+}
+
+/* ARGSUSED */
+static int
+lxd_remove(vnode_t *dvp, char *nm, struct cred *cr, caller_context_t *ct,
+    int flags)
+{
+	lxd_node_t *parent = VTOLDN(dvp);
+	lxd_node_t *ldn = NULL;
+	int error;
+
+	/* can only remove existing front nodes */
+	error = lxd_dirlookup(parent, nm, &ldn, cr);
+	if (error) {
+		return (error);
+	}
+
+	ASSERT(ldn != NULL);
+	ASSERT(ldn->lxdn_type == LXDNT_FRONT);
+	rw_enter(&parent->lxdn_rwlock, RW_WRITER);
+	rw_enter(&ldn->lxdn_rwlock, RW_WRITER);
+
+	error = lxd_dirdelete(parent, ldn, nm, DR_REMOVE, cr);
+
+	rw_exit(&ldn->lxdn_rwlock);
+	rw_exit(&parent->lxdn_rwlock);
+
+	ldnode_rele(ldn);
+
+	return (error);
+}
+
+/* ARGSUSED */
+static int
+lxd_link(vnode_t *tdvp, vnode_t *vp, char *tnm, struct cred *cr,
+    caller_context_t *ct, int flags)
+{
+	return (ENOTSUP);
+}
+
+/* ARGSUSED */
+static int
+lxd_rename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, struct cred *cr,
+    caller_context_t *ct, int flags)
+{
+	lxd_node_t *oldparent = VTOLDN(odvp);
+	lxd_node_t *newparent;
+	lxd_mnt_t *lxdm = VTOLXDM(oldparent->lxdn_vnode);
+	lxd_node_t *fromnode = NULL;
+	int error;
+	int samedir = 0;
+
+	if (!vn_matchops(ndvp, lxd_vnodeops)) {
+		/* cannot rename out of this file system */
+		return (EACCES);
+	}
+
+	mutex_enter(&lxdm->lxdm_renamelck);
+
+	newparent = VTOLDN(ndvp);
+
+	/*
+	 * We can only rename front nodes.
+	 */
+	error = lxd_dirlookup(oldparent, onm, &fromnode, cr);
+	if (error != 0) {
+		/* not found in front */
+		mutex_exit(&lxdm->lxdm_renamelck);
+		return (error);
+	}
+
+	/*
+	 * Make sure we can delete the old (source) entry.  This
+	 * requires write permission on the containing directory.  If
+	 * that directory is "sticky" it requires further checks.
+	 */
+	if ((error = lxd_naccess(oldparent, VWRITE, cr)) != 0)
+		goto done;
+
+	/*
+	 * Check for renaming to or from '.' or '..' or that
+	 * fromnode == oldparent
+	 */
+	if ((onm[0] == '.' &&
+	    (onm[1] == '\0' || (onm[1] == '.' && onm[2] == '\0'))) ||
+	    (nnm[0] == '.' &&
+	    (nnm[1] == '\0' || (nnm[1] == '.' && nnm[2] == '\0'))) ||
+	    (oldparent == fromnode)) {
+		error = EINVAL;
+		goto done;
+	}
+
+	samedir = (oldparent == newparent);
+
+	/*
+	 * Make sure we can search and rename into the destination directory.
+	 */
+	if (!samedir) {
+		if ((error = lxd_naccess(newparent, VEXEC|VWRITE, cr)) != 0)
+			goto done;
+	}
+
+	/*
+	 * Link source to new target
+	 */
+	rw_enter(&newparent->lxdn_rwlock, RW_WRITER);
+	error = lxd_direnter(lxdm, newparent, nnm, DE_RENAME,
+	    oldparent, fromnode, (struct vattr *)NULL, (lxd_node_t **)NULL,
+	    cr);
+	rw_exit(&newparent->lxdn_rwlock);
+
+	if (error)
+		goto done;
+
+	/*
+	 * Unlink from source.
+	 */
+	rw_enter(&oldparent->lxdn_rwlock, RW_WRITER);
+	rw_enter(&fromnode->lxdn_rwlock, RW_WRITER);
+
+	error = lxd_dirdelete(oldparent, fromnode, onm, DR_RENAME, cr);
+
+	/*
+	 * The following handles the case where our source node was
+	 * removed before we got to it.
+	 */
+	if (error == ENOENT)
+		error = 0;
+
+	rw_exit(&fromnode->lxdn_rwlock);
+	rw_exit(&oldparent->lxdn_rwlock);
+
+done:
+	ldnode_rele(fromnode);
+	mutex_exit(&lxdm->lxdm_renamelck);
+	return (error);
+}
+
+/* ARGSUSED */
+static int
+lxd_mkdir(vnode_t *dvp, char *nm, struct vattr *va, vnode_t **vpp,
+    struct cred *cr, caller_context_t *ct, int flags, vsecattr_t *vsecp)
+{
+	int error;
+	vnode_t *tvp;
+	lxd_node_t *ndir = NULL;
+	lxd_node_t *parent = VTOLDN(dvp);
+	lxd_mnt_t *lxdm = VTOLXDM(parent->lxdn_vnode);
+
+	/* check for existence in both front and back */
+	if (lxd_lookup(dvp, nm, &tvp, NULL, 0, NULL, cr, ct, NULL, NULL) == 0) {
+		/* The entry already exists */
+		VN_RELE(tvp);
+		return (EEXIST);
+	}
+
+	/* make front directory */
+	rw_enter(&parent->lxdn_rwlock, RW_WRITER);
+	error = lxd_direnter(lxdm, parent, nm, DE_MKDIR, NULL, NULL,
+	    va, &ndir, cr);
+	rw_exit(&parent->lxdn_rwlock);
+
+	if (error != 0) {
+		if (ndir != NULL)
+			ldnode_rele(ndir);
+	} else {
+		*vpp = LDNTOV(ndir);
+	}
+
+	return (error);
+}
+
+static int
+lxd_realvp(vnode_t *vp, vnode_t **vpp, caller_context_t *ct)
+{
+	lxd_node_t *ldn = VTOLDN(vp);
+
+	if (ldn->lxdn_type == LXDNT_FRONT) {
+		*vpp = vp;
+		return (0);
+	}
+
+	ASSERT(ldn->lxdn_type == LXDNT_BACK);
+	while (vn_matchops(vp, lxd_vnodeops))
+		vp = REALVP(vp);
+
+	if (VOP_REALVP(vp, vpp, ct) != 0)
+		*vpp = vp;
+	return (0);
+}
+
+/* ARGSUSED */
+static int
+lxd_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, struct cred *cr,
+    caller_context_t *ct, int flags)
+{
+	int error;
+	lxd_node_t *ldn;
+	struct vnode *vp;
+	lxd_node_t *parent = VTOLDN(dvp);
+
+	/*
+	 * Return error if trying to remove . or ..
+	 */
+	if (strcmp(nm, ".") == 0)
+		return (EINVAL);
+	if (strcmp(nm, "..") == 0)
+		return (EEXIST);
+
+	error = lxd_dirlookup(VTOLDN(dvp), nm, &ldn, cr);
+	if (error != 0) {
+		/* not found in front */
+		return (error);
+	}
+
+	rw_enter(&parent->lxdn_rwlock, RW_WRITER);
+	rw_enter(&ldn->lxdn_rwlock, RW_WRITER);
+
+	vp = LDNTOV(ldn);
+	if (vp == dvp || vp == cdir) {
+		error = EINVAL;
+		goto err;
+	}
+
+	if (ldn->lxdn_vnode->v_type != VDIR) {
+		error = ENOTDIR;
+		goto err;
+	}
+
+	mutex_enter(&ldn->lxdn_tlock);
+	if (ldn->lxdn_nlink > 2) {
+		mutex_exit(&ldn->lxdn_tlock);
+		error = EEXIST;
+		goto err;
+	}
+	mutex_exit(&ldn->lxdn_tlock);
+
+	/* Check for an empty directory */
+	if (ldn->lxdn_dirents > 2) {
+		error = EEXIST;
+		gethrestime(&ldn->lxdn_atime);
+		goto err;
+	}
+
+	if (vn_vfswlock(vp)) {
+		error = EBUSY;
+		goto err;
+	}
+	if (vn_mountedvfs(vp) != NULL) {
+		error = EBUSY;
+		vn_vfsunlock(vp);
+		goto err;
+	}
+
+	error = lxd_dirdelete(parent, ldn, nm, DR_RMDIR, cr);
+	vn_vfsunlock(vp);
+
+err:
+	rw_exit(&ldn->lxdn_rwlock);
+	rw_exit(&parent->lxdn_rwlock);
+	ldnode_rele(ldn);
+
+	return (error);
+}
+
+/* Not static so it can be used during mount. */
+/* ARGSUSED */
+int
+lxd_symlink(vnode_t *dvp, char *nm, struct vattr *tva, char *tnm,
+    struct cred *cr, caller_context_t *ct, int flags)
+{
+	lxd_node_t *parent = VTOLDN(dvp);
+	lxd_mnt_t *lxdm = VTOLXDM(parent->lxdn_vnode);
+	lxd_node_t *self = NULL;
+	vnode_t *tvp;
+	char *cp = NULL;
+	int error;
+	size_t len;
+
+	/* this will check for existence in both front and back */
+	if (lxd_lookup(dvp, nm, &tvp, NULL, 0, NULL, cr, ct, NULL, NULL) == 0) {
+		/* The entry already exists */
+		VN_RELE(tvp);
+		return (EEXIST);
+	}
+
+	/* make symlink in the front */
+	rw_enter(&parent->lxdn_rwlock, RW_WRITER);
+	error = lxd_direnter(lxdm, parent, nm, DE_CREATE, NULL, NULL,
+	    tva, &self, cr);
+	rw_exit(&parent->lxdn_rwlock);
+
+	if (error) {
+		if (self != NULL)
+			ldnode_rele(self);
+		return (error);
+	}
+
+	len = strlen(tnm) + 1;
+	cp = kmem_alloc(len, KM_NOSLEEP | KM_NORMALPRI);
+	if (cp == NULL) {
+		ldnode_rele(self);
+		return (ENOSPC);
+	}
+	(void) strcpy(cp, tnm);
+
+	self->lxdn_symlink = cp;
+	self->lxdn_size = len - 1;
+	ldnode_rele(self);
+
+	return (error);
+}
+
+static int
+lxd_readlink(vnode_t *vp, struct uio *uiop, struct cred *cr,
+    caller_context_t *ct)
+{
+	lxd_node_t *ldn = VTOLDN(vp);
+
+	if (ldn->lxdn_type == LXDNT_FRONT) {
+		int error;
+
+		if (vp->v_type != VLNK)
+			return (EINVAL);
+
+		rw_enter(&ldn->lxdn_rwlock, RW_READER);
+		error = uiomove(ldn->lxdn_symlink, ldn->lxdn_size, UIO_READ,
+		    uiop);
+		gethrestime(&ldn->lxdn_atime);
+		rw_exit(&ldn->lxdn_rwlock);
+		return (error);
+	}
+
+	ASSERT(ldn->lxdn_type == LXDNT_BACK);
+	vp = REALVP(vp);
+	return (VOP_READLINK(vp, uiop, cr, ct));
+}
+
+static int
+lx_merge_front(vnode_t *vp, struct uio *uiop, off_t req_off, int *eofp)
+{
+	lxd_node_t *ldn = VTOLDN(vp);
+	struct dirent *sd;
+	lxd_dirent_t *ldp;
+	enum lxd_node_type type = ldn->lxdn_type;
+	ssize_t uresid;
+	off_t front_off;
+	int error = 0;
+	int sdlen;
+
+	/* skip the front entries if the back read was incomplete */
+	if (*eofp == 0)
+		return (0);
+
+	/*
+	 * If this was a back node then reading that node has completed and we
+	 * may have a partially full uio struct. eof should be set to true.
+	 * Leave it set since we're likely to hit eof for the front nodes (if
+	 * any).
+	 */
+
+	front_off = uiop->uio_offset + 1;
+	sdlen = sizeof (struct dirent) + MAXPATHLEN;
+	/* zalloc to ensure we don't have anything in the d_name buffer */
+	sd = (struct dirent *)kmem_zalloc(sdlen, KM_SLEEP);
+	ldp = ldn->lxdn_dir;
+	while (ldp != NULL && (uresid = uiop->uio_resid) > 0) {
+		int namelen;
+		int reclen;
+
+		/*
+		 * Skip dot and dotdot for back nodes since we have them
+		 * already.
+		 */
+		if (type == LXDNT_BACK &&
+		    (strcmp(ldp->lddir_name, ".") == 0 ||
+		    strcmp(ldp->lddir_name, "..") == 0)) {
+			ldp = ldp->lddir_next;
+			continue;
+		}
+
+		/*
+		 * Might have previously had a partial readdir of the front
+		 * nodes, and now we're back for more, or we may just be
+		 * be doing a follow-up readdir after we've previously
+		 * returned all front and back nodes.
+		 */
+		if (front_off > req_off) {
+			namelen = strlen(ldp->lddir_name); /* no +1 needed */
+			reclen = (int)DIRENT64_RECLEN(namelen);
+
+			/*
+			 * If the size of the data to transfer is greater
+			 * than that requested, then we can't do it this
+			 * transfer.
+			 */
+			if (reclen > uresid) {
+				*eofp = 0;
+				/* Buffer too small for any entries. */
+				if (front_off == 0)
+					error = EINVAL;
+				break;
+			}
+
+			(void) strncpy(sd->d_name, ldp->lddir_name,
+			    DIRENT64_NAMELEN(reclen));
+			sd->d_reclen = (ushort_t)reclen;
+			sd->d_ino = (ino_t)ldp->lddir_node->lxdn_nodeid;
+			sd->d_off = front_off;
+
+			/* uiomove will adjust iov_base properly */
+			if ((error = uiomove((caddr_t)sd, reclen, UIO_READ,
+			    uiop)) != 0) {
+				*eofp = 0;
+				break;
+			}
+		}
+
+		/*
+		 * uiomove() above updates both uio_resid and uio_offset by the
+		 * same amount but we want uio_offset to change in increments
+		 * of 1, which is different from the number of bytes being
+		 * returned to the caller, so we set uio_offset explicitly,
+		 * ignoring what uiomove() did.
+		 */
+		uiop->uio_offset = front_off;
+		front_off++;
+
+		ldp = ldp->lddir_next;
+	}
+
+	kmem_free(sd, sdlen);
+	return (error);
+}
+
+static int
+lxd_readdir(vnode_t *vp, struct uio *uiop, struct cred *cr, int *eofp,
+    caller_context_t *ct, int flags)
+{
+	lxd_node_t *ldn = VTOLDN(vp);
+	vnode_t *rvp;
+	int res;
+	off_t req_off;
+
+	if (uiop->uio_iovcnt != 1)
+		return (EINVAL);
+
+	if (vp->v_type != VDIR)
+		return (ENOTDIR);
+
+	req_off = uiop->uio_offset;
+
+	/* First read the back node (if it is one) */
+	if (ldn->lxdn_type == LXDNT_BACK) {
+		rvp = REALVP(vp);
+		res = VOP_READDIR(rvp, uiop, cr, eofp, ct, flags);
+		if (res != 0)
+			return (res);
+	} else {
+		/* setup for merge_front */
+		ASSERT(ldn->lxdn_type == LXDNT_FRONT);
+		/* caller should have already called lxd_rwlock */
+		ASSERT(RW_READ_HELD(&ldn->lxdn_rwlock));
+
+		*eofp = 1;
+		/*
+		 * The merge code starts the offset calculation from uio_offset,
+		 * which is normally already set to the high value by the back
+		 * code, but in this case we need to count up from 0.
+		 */
+		uiop->uio_offset = 0;
+	}
+
+	/*
+	 * Our back nodes can also have front entries hanging on them so we
+	 * need to merge those in. Or, we may simply have a front node (i.e. a
+	 * front subdir).
+	 */
+	res = lx_merge_front(vp, uiop, req_off, eofp);
+	return (res);
+}
+
+static int
+lxd_rwlock(vnode_t *vp, int write_lock, caller_context_t *ct)
+{
+	lxd_node_t *ldn = VTOLDN(vp);
+
+	if (ldn->lxdn_type == LXDNT_FRONT) {
+		if (write_lock) {
+			rw_enter(&ldn->lxdn_rwlock, RW_WRITER);
+		} else {
+			rw_enter(&ldn->lxdn_rwlock, RW_READER);
+		}
+		return (write_lock);
+	}
+
+	ASSERT(ldn->lxdn_type == LXDNT_BACK);
+	vp = REALVP(vp);
+	return (VOP_RWLOCK(vp, write_lock, ct));
+}
+
+static void
+lxd_rwunlock(vnode_t *vp, int write_lock, caller_context_t *ct)
+{
+	lxd_node_t *ldn = VTOLDN(vp);
+
+	if (ldn->lxdn_type == LXDNT_FRONT) {
+		rw_exit(&ldn->lxdn_rwlock);
+		return;
+	}
+
+	ASSERT(ldn->lxdn_type == LXDNT_BACK);
+	vp = REALVP(vp);
+	VOP_RWUNLOCK(vp, write_lock, ct);
+}
+
+static int
+lxd_seek(vnode_t *vp, offset_t ooff, offset_t *noffp, caller_context_t *ct)
+{
+	lxd_node_t *ldn = VTOLDN(vp);
+
+	if (ldn->lxdn_type == LXDNT_FRONT) {
+		return ((*noffp < 0 || *noffp > MAXOFFSET_T) ? EINVAL : 0);
+	}
+
+	ASSERT(ldn->lxdn_type == LXDNT_BACK);
+	vp = REALVP(vp);
+	return (VOP_SEEK(vp, ooff, noffp, ct));
+}
+
+static int
+lxd_cmp(vnode_t *vp1, vnode_t *vp2, caller_context_t *ct)
+{
+	while (vn_matchops(vp1, lxd_vnodeops) &&
+	    VTOLDN(vp1)->lxdn_type == LXDNT_BACK) {
+		vp1 = REALVP(vp1);
+	}
+	while (vn_matchops(vp2, lxd_vnodeops) &&
+	    VTOLDN(vp2)->lxdn_type == LXDNT_BACK) {
+		vp2 = REALVP(vp2);
+	}
+
+	if (vn_matchops(vp1, lxd_vnodeops) || vn_matchops(vp2, lxd_vnodeops))
+		return (vp1 == vp2);
+
+	return (VOP_CMP(vp1, vp2, ct));
+}
+
+static int
+lxd_frlock(vnode_t *vp, int cmd, struct flock64 *bfp, int flag, offset_t offset,
+    struct flk_callback *flk_cbp, cred_t *cr, caller_context_t *ct)
+{
+	lxd_node_t *ldn = VTOLDN(vp);
+
+	if (ldn->lxdn_type == LXDNT_FRONT) {
+		return (EINVAL);
+	}
+
+	ASSERT(ldn->lxdn_type == LXDNT_BACK);
+	vp = REALVP(vp);
+	return (VOP_FRLOCK(vp, cmd, bfp, flag, offset, flk_cbp, cr, ct));
+}
+
+static int
+lxd_space(vnode_t *vp, int cmd, struct flock64 *bfp, int flag, offset_t offset,
+    struct cred *cr, caller_context_t *ct)
+{
+	lxd_node_t *ldn = VTOLDN(vp);
+
+	if (ldn->lxdn_type == LXDNT_FRONT) {
+		return (EINVAL);
+	}
+
+	ASSERT(ldn->lxdn_type == LXDNT_BACK);
+	vp = REALVP(vp);
+	return (VOP_SPACE(vp, cmd, bfp, flag, offset, cr, ct));
+}
+
+static int
+lxd_getpage(vnode_t *vp, offset_t off, size_t len, uint_t *prot,
+    struct page *parr[], size_t psz, struct seg *seg, caddr_t addr,
+    enum seg_rw rw, struct cred *cr, caller_context_t *ct)
+{
+	lxd_node_t *ldn = VTOLDN(vp);
+
+	if (ldn->lxdn_type == LXDNT_FRONT) {
+		return (EINVAL);
+	}
+
+	ASSERT(ldn->lxdn_type == LXDNT_BACK);
+	vp = REALVP(vp);
+	return (VOP_GETPAGE(vp, off, len, prot, parr, psz, seg, addr, rw, cr,
+	    ct));
+}
+
+static int
+lxd_putpage(vnode_t *vp, offset_t off, size_t len, int flags, struct cred *cr,
+    caller_context_t *ct)
+{
+	lxd_node_t *ldn = VTOLDN(vp);
+
+	if (ldn->lxdn_type == LXDNT_FRONT) {
+		return (EINVAL);
+	}
+
+	ASSERT(ldn->lxdn_type == LXDNT_BACK);
+	vp = REALVP(vp);
+	return (VOP_PUTPAGE(vp, off, len, flags, cr, ct));
+}
+
+static int
+lxd_map(vnode_t *vp, offset_t off, struct as *as, caddr_t *addrp, size_t len,
+    uchar_t prot, uchar_t maxprot, uint_t flags, struct cred *cr,
+    caller_context_t *ct)
+{
+	lxd_node_t *ldn = VTOLDN(vp);
+
+	if (ldn->lxdn_type == LXDNT_FRONT) {
+		return (EINVAL);
+	}
+
+	ASSERT(ldn->lxdn_type == LXDNT_BACK);
+	vp = REALVP(vp);
+	return (VOP_MAP(vp, off, as, addrp, len, prot, maxprot, flags, cr, ct));
+}
+
+static int
+lxd_addmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr, size_t len,
+    uchar_t prot, uchar_t maxprot, uint_t flags, struct cred *cr,
+    caller_context_t *ct)
+{
+	lxd_node_t *ldn = VTOLDN(vp);
+
+	if (ldn->lxdn_type == LXDNT_FRONT) {
+		return (EINVAL);
+	}
+
+	ASSERT(ldn->lxdn_type == LXDNT_BACK);
+	vp = REALVP(vp);
+	return (VOP_ADDMAP(vp, off, as, addr, len, prot, maxprot, flags, cr,
+	    ct));
+}
+
+static int
+lxd_delmap(vnode_t *vp, offset_t off, struct as *as, caddr_t addr, size_t len,
+    uint_t prot, uint_t maxprot, uint_t flags, struct cred *cr,
+    caller_context_t *ct)
+{
+	lxd_node_t *ldn = VTOLDN(vp);
+
+	if (ldn->lxdn_type == LXDNT_FRONT) {
+		return (EINVAL);
+	}
+
+	ASSERT(ldn->lxdn_type == LXDNT_BACK);
+	vp = REALVP(vp);
+	return (VOP_DELMAP(vp, off, as, addr, len, prot, maxprot, flags, cr,
+	    ct));
+}
+
+static int
+lxd_poll(vnode_t *vp, short events, int anyyet, short *reventsp,
+    struct pollhead **phpp, caller_context_t *ct)
+{
+	lxd_node_t *ldn = VTOLDN(vp);
+
+	if (ldn->lxdn_type == LXDNT_FRONT) {
+		return (EINVAL);
+	}
+
+	ASSERT(ldn->lxdn_type == LXDNT_BACK);
+	vp = REALVP(vp);
+	return (VOP_POLL(vp, events, anyyet, reventsp, phpp, ct));
+}
+
+static int
+lxd_dump(vnode_t *vp, caddr_t addr, offset_t bn, offset_t count,
+    caller_context_t *ct)
+{
+	lxd_node_t *ldn = VTOLDN(vp);
+
+	if (ldn->lxdn_type == LXDNT_FRONT) {
+		return (EINVAL);
+	}
+
+	ASSERT(ldn->lxdn_type == LXDNT_BACK);
+	vp = REALVP(vp);
+	return (VOP_DUMP(vp, addr, bn, count, ct));
+}
+
+static int
+lxd_pathconf(vnode_t *vp, int cmd, ulong_t *valp, struct cred *cr,
+    caller_context_t *ct)
+{
+	lxd_node_t *ldn = VTOLDN(vp);
+
+	if (ldn->lxdn_type == LXDNT_FRONT) {
+		return (EINVAL);
+	}
+
+	ASSERT(ldn->lxdn_type == LXDNT_BACK);
+	vp = REALVP(vp);
+	return (VOP_PATHCONF(vp, cmd, valp, cr, ct));
+}
+
+static int
+lxd_pageio(vnode_t *vp, struct page *pp, u_offset_t io_off, size_t io_len,
+    int flags, cred_t *cr, caller_context_t *ct)
+{
+	lxd_node_t *ldn = VTOLDN(vp);
+
+	if (ldn->lxdn_type == LXDNT_FRONT) {
+		return (EINVAL);
+	}
+
+	ASSERT(ldn->lxdn_type == LXDNT_BACK);
+	vp = REALVP(vp);
+	return (VOP_PAGEIO(vp, pp, io_off, io_len, flags, cr, ct));
+}
+
+static void
+lxd_dispose(vnode_t *vp, page_t *pp, int fl, int dn, cred_t *cr,
+    caller_context_t *ct)
+{
+	lxd_node_t *ldn = VTOLDN(vp);
+
+	if (ldn->lxdn_type == LXDNT_FRONT) {
+		return;
+	}
+
+	ASSERT(ldn->lxdn_type == LXDNT_BACK);
+	vp = REALVP(vp);
+	if (vp != NULL && !VN_ISKAS(vp))
+		VOP_DISPOSE(vp, pp, fl, dn, cr, ct);
+}
+
+static int
+lxd_setsecattr(vnode_t *vp, vsecattr_t *secattr, int flags, struct cred *cr,
+    caller_context_t *ct)
+{
+	lxd_node_t *ldn = VTOLDN(vp);
+	if (ldn->lxdn_type == LXDNT_FRONT) {
+		return (ENOSYS);
+	}
+
+	ASSERT(ldn->lxdn_type == LXDNT_BACK);
+	if (vn_is_readonly(vp))
+		return (EROFS);
+
+	vp = REALVP(vp);
+	return (VOP_SETSECATTR(vp, secattr, flags, cr, ct));
+}
+
+static int
+lxd_getsecattr(vnode_t *vp, vsecattr_t *secattr, int flags, struct cred *cr,
+    caller_context_t *ct)
+{
+	lxd_node_t *ldn = VTOLDN(vp);
+
+	if (ldn->lxdn_type == LXDNT_FRONT) {
+		return (ENOSYS);
+	}
+
+	ASSERT(ldn->lxdn_type == LXDNT_BACK);
+	vp = REALVP(vp);
+	return (VOP_GETSECATTR(vp, secattr, flags, cr, ct));
+}
+
+static int
+lxd_shrlock(vnode_t *vp, int cmd, struct shrlock *shr, int flag, cred_t *cr,
+    caller_context_t *ct)
+{
+	lxd_node_t *ldn = VTOLDN(vp);
+
+	if (ldn->lxdn_type == LXDNT_FRONT) {
+		return (EINVAL);
+	}
+
+	ASSERT(ldn->lxdn_type == LXDNT_BACK);
+	vp = REALVP(vp);
+	return (VOP_SHRLOCK(vp, cmd, shr, flag, cr, ct));
+}
+
+/*
+ * Loopback vnode operations vector.
+ */
+
+struct vnodeops *lxd_vnodeops;
+
+const fs_operation_def_t lxd_vnodeops_template[] = {
+	VOPNAME_OPEN,		{ .vop_open = lxd_open },
+	VOPNAME_CLOSE,		{ .vop_close = lxd_close },
+	VOPNAME_READ,		{ .vop_read = lxd_read },
+	VOPNAME_WRITE,		{ .vop_write = lxd_write },
+	VOPNAME_IOCTL,		{ .vop_ioctl = lxd_ioctl },
+	VOPNAME_SETFL,		{ .vop_setfl = lxd_setfl },
+	VOPNAME_GETATTR,	{ .vop_getattr = lxd_getattr },
+	VOPNAME_SETATTR,	{ .vop_setattr = lxd_setattr },
+	VOPNAME_ACCESS,		{ .vop_access = lxd_access },
+	VOPNAME_LOOKUP,		{ .vop_lookup = lxd_lookup },
+	VOPNAME_CREATE,		{ .vop_create = lxd_create },
+	VOPNAME_REMOVE,		{ .vop_remove = lxd_remove },
+	VOPNAME_LINK,		{ .vop_link = lxd_link },
+	VOPNAME_RENAME,		{ .vop_rename = lxd_rename },
+	VOPNAME_MKDIR,		{ .vop_mkdir = lxd_mkdir },
+	VOPNAME_RMDIR,		{ .vop_rmdir = lxd_rmdir },
+	VOPNAME_READDIR,	{ .vop_readdir = lxd_readdir },
+	VOPNAME_SYMLINK,	{ .vop_symlink = lxd_symlink },
+	VOPNAME_READLINK,	{ .vop_readlink = lxd_readlink },
+	VOPNAME_FSYNC,		{ .vop_fsync = lxd_fsync },
+	VOPNAME_INACTIVE,	{ .vop_inactive = lxd_inactive },
+	VOPNAME_FID,		{ .vop_fid = lxd_fid },
+	VOPNAME_RWLOCK,		{ .vop_rwlock = lxd_rwlock },
+	VOPNAME_RWUNLOCK,	{ .vop_rwunlock = lxd_rwunlock },
+	VOPNAME_SEEK,		{ .vop_seek = lxd_seek },
+	VOPNAME_CMP,		{ .vop_cmp = lxd_cmp },
+	VOPNAME_FRLOCK,		{ .vop_frlock = lxd_frlock },
+	VOPNAME_SPACE,		{ .vop_space = lxd_space },
+	VOPNAME_REALVP,		{ .vop_realvp = lxd_realvp },
+	VOPNAME_GETPAGE,	{ .vop_getpage = lxd_getpage },
+	VOPNAME_PUTPAGE,	{ .vop_putpage = lxd_putpage },
+	VOPNAME_MAP,		{ .vop_map = lxd_map },
+	VOPNAME_ADDMAP,		{ .vop_addmap = lxd_addmap },
+	VOPNAME_DELMAP,		{ .vop_delmap = lxd_delmap },
+	VOPNAME_POLL,		{ .vop_poll = lxd_poll },
+	VOPNAME_DUMP,		{ .vop_dump = lxd_dump },
+	VOPNAME_DUMPCTL,	{ .error = fs_error },
+	VOPNAME_PATHCONF,	{ .vop_pathconf = lxd_pathconf },
+	VOPNAME_PAGEIO,		{ .vop_pageio = lxd_pageio },
+	VOPNAME_DISPOSE,	{ .vop_dispose = lxd_dispose },
+	VOPNAME_SETSECATTR,	{ .vop_setsecattr = lxd_setsecattr },
+	VOPNAME_GETSECATTR,	{ .vop_getsecattr = lxd_getsecattr },
+	VOPNAME_SHRLOCK,	{ .vop_shrlock = lxd_shrlock },
+	NULL,			NULL
+};
--- /dev/null	Wed Oct  5 00:32:04 2016
+++ new/usr/src/uts/common/brand/lx/dtrace/lx_systrace.c	Wed Oct  5 00:32:04 2016
@@ -0,0 +1,499 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+
+#include <sys/modctl.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/stat.h>
+#include <sys/conf.h>
+#include <sys/frame.h>
+#include <sys/dtrace.h>
+#include <sys/dtrace_impl.h>
+
+#include <sys/lx_brand.h>
+#include <sys/lx_impl.h>
+
+/*
+ * We store the syscall number in the low 16 bits (which limits us to 64k
+ * syscalls). The next bit indicates entry/return probe and the next bit
+ * indicates 64bit/32bit syscall.
+ */
+#define	SCALL_MASK	0xffff
+#define	ENTRY_FLAG	0x10000
+#define	SYSC_64_BIT	0x100000
+
+#define	LX_SYSTRACE_IS64BIT(x)	((int)(x) & SYSC_64_BIT)
+#define	LX_SYSTRACE_ISENTRY(x)	((int)(x) & ENTRY_FLAG)
+#define	LX_SYSTRACE_SYSNUM(x)	((int)(x) & SCALL_MASK)
+
+#define	LX_SYSTRACE32_ENTRY(id)	(ENTRY_FLAG | (id))
+#define	LX_SYSTRACE32_RETURN(id) (id)
+
+#define	LX_SYSTRACE64_ENTRY(id)	(SYSC_64_BIT | ENTRY_FLAG | (id))
+#define	LX_SYSTRACE64_RETURN(id) (SYSC_64_BIT | id)
+
+#define	LX_SYSTRACE_ENTRY_AFRAMES	2
+#define	LX_SYSTRACE_RETURN_AFRAMES	4
+
+typedef struct lx_systrace_sysent {
+	const char *lss_name;
+	dtrace_id_t lss_entry;
+	dtrace_id_t lss_return;
+} lx_systrace_sysent_t;
+
+static dev_info_t *lx_systrace_devi;
+static dtrace_provider_id_t lx_systrace_id;
+static kmutex_t lx_systrace_lock;
+static uint_t lx_systrace_nenabled;
+
+static int lx_systrace_nsysent32;
+static lx_systrace_sysent_t *lx_systrace_sysent32;
+
+#if defined(_LP64)
+static int lx_systrace_nsysent64;
+static lx_systrace_sysent_t *lx_systrace_sysent64;
+#endif
+
+/*ARGSUSED*/
+static void
+lx_systrace_entry(ulong_t sysnum, ulong_t arg0, ulong_t arg1, ulong_t arg2,
+    ulong_t arg3, ulong_t arg4, ulong_t arg5)
+{
+	dtrace_id_t id;
+
+#if defined(_LP64)
+	if ((ttoproc(curthread))->p_model == DATAMODEL_NATIVE) {
+		if (sysnum >= lx_systrace_nsysent64)
+			return;
+		id = lx_systrace_sysent64[sysnum].lss_entry;
+	} else
+#endif
+	{
+		if (sysnum >= lx_systrace_nsysent32)
+			return;
+		id = lx_systrace_sysent32[sysnum].lss_entry;
+	}
+
+	if (id == DTRACE_IDNONE)
+		return;
+	dtrace_probe(id, arg0, arg1, arg2, arg3, arg4);
+}
+
+/*ARGSUSED*/
+static void
+lx_systrace_return(ulong_t sysnum, ulong_t arg0, ulong_t arg1, ulong_t arg2,
+    ulong_t arg3, ulong_t arg4, ulong_t arg5)
+{
+	dtrace_id_t id;
+
+#if defined(_LP64)
+	if ((ttoproc(curthread))->p_model == DATAMODEL_NATIVE) {
+		if (sysnum >= lx_systrace_nsysent64)
+			return;
+		id = lx_systrace_sysent64[sysnum].lss_return;
+	} else
+#endif
+	{
+		if (sysnum >= lx_systrace_nsysent32)
+			return;
+		id = lx_systrace_sysent32[sysnum].lss_return;
+	}
+
+	if (id == DTRACE_IDNONE)
+		return;
+	dtrace_probe(id, arg0, arg1, arg2, arg3, arg4);
+}
+
+/*ARGSUSED*/
+static void
+lx_systrace_provide(void *arg, const dtrace_probedesc_t *desc)
+{
+	int i;
+
+	if (desc != NULL)
+		return;
+
+	for (i = 0; i < lx_systrace_nsysent32; i++) {
+		if (dtrace_probe_lookup(lx_systrace_id, "sys32",
+		    lx_systrace_sysent32[i].lss_name, "entry") != 0)
+			continue;
+
+		(void) dtrace_probe_create(lx_systrace_id, "sys32",
+		    lx_systrace_sysent32[i].lss_name, "entry",
+		    LX_SYSTRACE_ENTRY_AFRAMES,
+		    (void *)((uintptr_t)LX_SYSTRACE32_ENTRY(i)));
+
+		(void) dtrace_probe_create(lx_systrace_id, "sys32",
+		    lx_systrace_sysent32[i].lss_name, "return",
+		    LX_SYSTRACE_RETURN_AFRAMES,
+		    (void *)((uintptr_t)LX_SYSTRACE32_RETURN(i)));
+
+		lx_systrace_sysent32[i].lss_entry = DTRACE_IDNONE;
+		lx_systrace_sysent32[i].lss_return = DTRACE_IDNONE;
+	}
+
+#if defined(_LP64)
+	for (i = 0; i < lx_systrace_nsysent64; i++) {
+		if (dtrace_probe_lookup(lx_systrace_id, "sys64",
+		    lx_systrace_sysent64[i].lss_name, "entry") != 0)
+			continue;
+
+		(void) dtrace_probe_create(lx_systrace_id, "sys64",
+		    lx_systrace_sysent64[i].lss_name, "entry",
+		    LX_SYSTRACE_ENTRY_AFRAMES,
+		    (void *)((uintptr_t)LX_SYSTRACE64_ENTRY(i)));
+
+		(void) dtrace_probe_create(lx_systrace_id, "sys64",
+		    lx_systrace_sysent64[i].lss_name, "return",
+		    LX_SYSTRACE_RETURN_AFRAMES,
+		    (void *)((uintptr_t)LX_SYSTRACE64_RETURN(i)));
+
+		lx_systrace_sysent64[i].lss_entry = DTRACE_IDNONE;
+		lx_systrace_sysent64[i].lss_return = DTRACE_IDNONE;
+	}
+#endif
+}
+
+/*ARGSUSED*/
+static int
+lx_systrace_enable(void *arg, dtrace_id_t id, void *parg)
+{
+	int sysnum = LX_SYSTRACE_SYSNUM((uintptr_t)parg);
+
+	mutex_enter(&lx_systrace_lock);
+	if (lx_systrace_nenabled++ == 0)
+		lx_brand_systrace_enable();
+	mutex_exit(&lx_systrace_lock);
+
+#if defined(_LP64)
+	if (LX_SYSTRACE_IS64BIT((uintptr_t)parg)) {
+		ASSERT(sysnum < lx_systrace_nsysent64);
+
+		if (LX_SYSTRACE_ISENTRY((uintptr_t)parg)) {
+			lx_systrace_sysent64[sysnum].lss_entry = id;
+		} else {
+			lx_systrace_sysent64[sysnum].lss_return = id;
+		}
+	} else
+#endif
+	{
+		ASSERT(sysnum < lx_systrace_nsysent32);
+
+		if (LX_SYSTRACE_ISENTRY((uintptr_t)parg)) {
+			lx_systrace_sysent32[sysnum].lss_entry = id;
+		} else {
+			lx_systrace_sysent32[sysnum].lss_return = id;
+		}
+	}
+	return (0);
+}
+
+/*ARGSUSED*/
+static void
+lx_systrace_disable(void *arg, dtrace_id_t id, void *parg)
+{
+	int sysnum = LX_SYSTRACE_SYSNUM((uintptr_t)parg);
+
+#if defined(_LP64)
+	if (LX_SYSTRACE_IS64BIT((uintptr_t)parg)) {
+		ASSERT(sysnum < lx_systrace_nsysent64);
+
+		if (LX_SYSTRACE_ISENTRY((uintptr_t)parg)) {
+			lx_systrace_sysent64[sysnum].lss_entry = DTRACE_IDNONE;
+		} else {
+			lx_systrace_sysent64[sysnum].lss_return = DTRACE_IDNONE;
+		}
+	} else
+#endif
+	{
+		ASSERT(sysnum < lx_systrace_nsysent32);
+
+		if (LX_SYSTRACE_ISENTRY((uintptr_t)parg)) {
+			lx_systrace_sysent32[sysnum].lss_entry = DTRACE_IDNONE;
+		} else {
+			lx_systrace_sysent32[sysnum].lss_return = DTRACE_IDNONE;
+		}
+	}
+
+	mutex_enter(&lx_systrace_lock);
+	if (--lx_systrace_nenabled == 0)
+		lx_brand_systrace_disable();
+	mutex_exit(&lx_systrace_lock);
+}
+
+/*ARGSUSED*/
+static void
+lx_systrace_destroy(void *arg, dtrace_id_t id, void *parg)
+{
+}
+
+/*ARGSUSED*/
+static uint64_t
+lx_systrace_getarg(void *arg, dtrace_id_t id, void *parg, int argno,
+    int aframes)
+{
+	struct frame *fp = (struct frame *)dtrace_getfp();
+	uintptr_t *stack;
+	uint64_t val = 0;
+	int i;
+
+	if (argno >= 6)
+		return (0);
+
+	/*
+	 * Walk the four frames down the stack to the entry or return callback.
+	 * Our callback calls dtrace_probe() which calls dtrace_dif_variable()
+	 * which invokes this function to get the extended arguments. We get
+	 * the frame pointer in via call to dtrace_getfp() above which makes for
+	 * four frames.
+	 */
+	for (i = 0; i < 4; i++) {
+		fp = (struct frame *)fp->fr_savfp;
+	}
+
+	stack = (uintptr_t *)&fp[1];
+
+	/*
+	 * Skip the first argument to the callback -- the system call number.
+	 */
+	argno++;
+
+#ifdef __amd64
+	/*
+	 * On amd64, the first 6 arguments are passed in registers while
+	 * subsequent arguments are on the stack.
+	 */
+	argno -= 6;
+#endif
+
+	DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
+	val = stack[argno];
+	DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT);
+
+	return (val);
+}
+
+
+static const dtrace_pattr_t lx_systrace_attr = {
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
+{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_COMMON },
+{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
+};
+
+static dtrace_pops_t lx_systrace_pops = {
+	lx_systrace_provide,
+	NULL,
+	lx_systrace_enable,
+	lx_systrace_disable,
+	NULL,
+	NULL,
+	NULL,
+	lx_systrace_getarg,
+	NULL,
+	lx_systrace_destroy
+};
+
+static int
+lx_systrace_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
+{
+	int i;
+
+	switch (cmd) {
+	case DDI_ATTACH:
+		break;
+	case DDI_RESUME:
+		return (DDI_SUCCESS);
+	default:
+		return (DDI_FAILURE);
+	}
+
+	if (ddi_create_minor_node(devi, "lx_systrace", S_IFCHR,
+	    0, DDI_PSEUDO, NULL) == DDI_FAILURE ||
+	    dtrace_register("lx-syscall", &lx_systrace_attr,
+	    DTRACE_PRIV_USER, 0, &lx_systrace_pops, NULL,
+	    &lx_systrace_id) != 0) {
+		ddi_remove_minor_node(devi, NULL);
+		return (DDI_FAILURE);
+	}
+
+	ddi_report_dev(devi);
+	lx_systrace_devi = devi;
+
+	/*
+	 * Initialize the 32-bit table.
+	 */
+	VERIFY(lx_nsysent32 > 0);
+	lx_systrace_nsysent32 = lx_nsysent32;
+	lx_systrace_sysent32 = kmem_zalloc(lx_systrace_nsysent32 *
+	    sizeof (lx_systrace_sysent_t), KM_SLEEP);
+
+	for (i = 0; i < lx_systrace_nsysent32; i++) {
+		lx_systrace_sysent32[i].lss_name = lx_sysent32[i].sy_name;
+		lx_systrace_sysent32[i].lss_entry = DTRACE_IDNONE;
+		lx_systrace_sysent32[i].lss_return = DTRACE_IDNONE;
+	}
+
+#if defined(_LP64)
+	/*
+	 * Initialize the 64-bit table.
+	 */
+	VERIFY(lx_nsysent64 > 0);
+	lx_systrace_nsysent64 = lx_nsysent64;
+	lx_systrace_sysent64 = kmem_zalloc(lx_systrace_nsysent64 *
+	    sizeof (lx_systrace_sysent_t), KM_SLEEP);
+
+	for (i = 0; i < lx_systrace_nsysent64; i++) {
+		lx_systrace_sysent64[i].lss_name = lx_sysent64[i].sy_name;
+		lx_systrace_sysent64[i].lss_entry = DTRACE_IDNONE;
+		lx_systrace_sysent64[i].lss_return = DTRACE_IDNONE;
+	}
+#endif
+
+	/*
+	 * Install probe triggers.
+	 */
+	lx_systrace_entry_ptr = lx_systrace_entry;
+	lx_systrace_return_ptr = lx_systrace_return;
+
+	return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+lx_systrace_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
+{
+	switch (cmd) {
+	case DDI_DETACH:
+		break;
+	case DDI_SUSPEND:
+		return (DDI_SUCCESS);
+	default:
+		return (DDI_FAILURE);
+	}
+
+	if (dtrace_unregister(lx_systrace_id) != 0)
+		return (DDI_FAILURE);
+
+	/*
+	 * Free tables.
+	 */
+	kmem_free(lx_systrace_sysent32, lx_systrace_nsysent32 *
+	    sizeof (lx_systrace_sysent_t));
+	lx_systrace_sysent32 = NULL;
+	lx_systrace_nsysent32 = 0;
+
+#if defined(_LP64)
+	kmem_free(lx_systrace_sysent64, lx_systrace_nsysent64 *
+	    sizeof (lx_systrace_sysent_t));
+	lx_systrace_sysent64 = NULL;
+	lx_systrace_nsysent64 = 0;
+#endif
+
+	/*
+	 * Reset probe triggers.
+	 */
+	lx_systrace_entry_ptr = NULL;
+	lx_systrace_return_ptr = NULL;
+
+	return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+lx_systrace_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
+{
+	return (0);
+}
+
+static struct cb_ops lx_systrace_cb_ops = {
+	lx_systrace_open,	/* open */
+	nodev,			/* close */
+	nulldev,		/* strategy */
+	nulldev,		/* print */
+	nodev,			/* dump */
+	nodev,			/* read */
+	nodev,			/* write */
+	nodev,			/* ioctl */
+	nodev,			/* devmap */
+	nodev,			/* mmap */
+	nodev,			/* segmap */
+	nochpoll,		/* poll */
+	ddi_prop_op,		/* cb_prop_op */
+	0,			/* streamtab */
+	D_NEW | D_MP		/* Driver compatibility flag */
+};
+
+static struct dev_ops lx_systrace_ops = {
+	DEVO_REV,		/* devo_rev */
+	0,			/* refcnt */
+	ddi_getinfo_1to1,	/* get_dev_info */
+	nulldev,		/* identify */
+	nulldev,		/* probe */
+	lx_systrace_attach,	/* attach */
+	lx_systrace_detach,	/* detach */
+	nodev,			/* reset */
+	&lx_systrace_cb_ops,	/* driver operations */
+	NULL,			/* bus operations */
+	nodev,			/* dev power */
+	ddi_quiesce_not_needed,		/* quiesce */
+};
+
+/*
+ * Module linkage information for the kernel.
+ */
+static struct modldrv modldrv = {
+	&mod_driverops,		/* module type (this is a pseudo driver) */
+	"Linux Brand System Call Tracing", /* name of module */
+	&lx_systrace_ops	/* driver ops */
+};
+
+static struct modlinkage modlinkage = {
+	MODREV_1,
+	(void *)&modldrv,
+	NULL
+};
+
+int
+_init(void)
+{
+	return (mod_install(&modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+	return (mod_remove(&modlinkage));
+}
--- /dev/null	Wed Oct  5 00:32:04 2016
+++ new/usr/src/uts/common/brand/lx/dtrace/lx_systrace.conf	Wed Oct  5 00:32:04 2016
@@ -0,0 +1,27 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#ident	"%Z%%M%	%I%	%E% SMI"
+
+name="lx_systrace" parent="pseudo" instance=0;
--- /dev/null	Wed Oct  5 00:32:05 2016
+++ new/usr/src/uts/common/brand/lx/io/lx_netlink.c	Wed Oct  5 00:32:05 2016
@@ -0,0 +1,1686 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+/*
+ * Compatibility for the Linux netlink(7) kernel/user transport, as well as
+ * for in-kernel netlink(7) providers like rtnetlink(7).  See RFC 3549 for
+ * details of the protocol, and the Linux man pages for details of the Linux
+ * implementation that we're mimicking.
+ */
+
+#include <sys/strsubr.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sunddi.h>
+#include <sys/sunldi.h>
+#include <sys/strsun.h>
+#include <sys/tihdr.h>
+#include <sys/sockio.h>
+#include <sys/brand.h>
+#include <sys/debug.h>
+#include <sys/ucred.h>
+#include <inet/ip.h>
+#include <inet/ip6.h>
+#include <inet/ip_impl.h>
+#include <inet/ip_ire.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_socket.h>
+#include <sys/ethernet.h>
+#include <sys/dlpi.h>
+#include <sys/policy.h>
+
+/*
+ * Flags in netlink header
+ */
+#define	LX_NETLINK_NLM_F_REQUEST		1
+#define	LX_NETLINK_NLM_F_MULTI			2
+#define	LX_NETLINK_NLM_F_ACK			4
+#define	LX_NETLINK_NLM_F_ECHO			8
+#define	LX_NETLINK_NLM_F_DUMP_INTR		16
+#define	LX_NETLINK_NLM_F_ROOT			0x100
+#define	LX_NETLINK_NLM_F_MATCH			0x200
+#define	LX_NETLINK_NLM_F_ATOMIC			0x400
+
+/*
+ * Generic message type constants
+ */
+#define	LX_NETLINK_NLMSG_NONE			0
+#define	LX_NETLINK_NLMSG_NOOP			1
+#define	LX_NETLINK_NLMSG_ERROR			2
+#define	LX_NETLINK_NLMSG_DONE			3
+#define	LX_NETLINK_NLMSG_OVERRUN		4
+
+/*
+ * Protocol constants.
+ */
+#define	LX_NETLINK_ROUTE			0
+#define	LX_NETLINK_UNUSED			1
+#define	LX_NETLINK_USERSOCK			2
+#define	LX_NETLINK_FIREWALL			3
+#define	LX_NETLINK_SOCK_DIAG			4
+#define	LX_NETLINK_NFLOG			5
+#define	LX_NETLINK_XFRM				6
+#define	LX_NETLINK_SELINUX			7
+#define	LX_NETLINK_ISCSI			8
+#define	LX_NETLINK_AUDIT			9
+#define	LX_NETLINK_FIB_LOOKUP			10
+#define	LX_NETLINK_CONNECTOR			11
+#define	LX_NETLINK_NETFILTER			12
+#define	LX_NETLINK_IP6_FW			13
+#define	LX_NETLINK_DNRTMSG			14
+#define	LX_NETLINK_KOBJECT_UEVENT		15
+#define	LX_NETLINK_GENERIC			16
+#define	LX_NETLINK_SCSITRANSPORT		18
+#define	LX_NETLINK_ECRYPTFS			19
+#define	LX_NETLINK_RDMA				20
+#define	LX_NETLINK_CRYPTO			21
+
+/*
+ * rtnetlink(7) attribute-related constants
+ */
+#define	LX_NETLINK_NLA_ALIGNTO			4
+
+#define	LX_NETLINK_RTM_NEWLINK			16
+#define	LX_NETLINK_RTM_DELLINK			17
+#define	LX_NETLINK_RTM_GETLINK			18
+#define	LX_NETLINK_RTM_SETLINK			19
+#define	LX_NETLINK_RTM_NEWADDR			20
+#define	LX_NETLINK_RTM_DELADDR			21
+#define	LX_NETLINK_RTM_GETADDR			22
+#define	LX_NETLINK_RTM_NEWROUTE			24
+#define	LX_NETLINK_RTM_DELROUTE			25
+#define	LX_NETLINK_RTM_GETROUTE			26
+#define	LX_NETLINK_RTM_NEWNEIGH			28
+#define	LX_NETLINK_RTM_DELNEIGH			29
+#define	LX_NETLINK_RTM_GETNEIGH			30
+#define	LX_NETLINK_RTM_NEWRULE			32
+#define	LX_NETLINK_RTM_DELRULE			33
+#define	LX_NETLINK_RTM_GETRULE			34
+#define	LX_NETLINK_RTM_NEWQDISC			36
+#define	LX_NETLINK_RTM_DELQDISC			37
+#define	LX_NETLINK_RTM_GETQDISC			38
+#define	LX_NETLINK_RTM_NEWTCLASS		40
+#define	LX_NETLINK_RTM_DELTCLASS		41
+#define	LX_NETLINK_RTM_GETTCLASS		42
+#define	LX_NETLINK_RTM_NEWTFILTER		44
+#define	LX_NETLINK_RTM_DELTFILTER		45
+#define	LX_NETLINK_RTM_GETTFILTER		46
+#define	LX_NETLINK_RTM_NEWACTION		48
+#define	LX_NETLINK_RTM_DELACTION		49
+#define	LX_NETLINK_RTM_GETACTION		50
+#define	LX_NETLINK_RTM_NEWPREFIX		52
+#define	LX_NETLINK_RTM_GETMULTICAST		58
+#define	LX_NETLINK_RTM_GETANYCAST		62
+#define	LX_NETLINK_RTM_NEWNEIGHTBL		64
+#define	LX_NETLINK_RTM_GETNEIGHTBL		66
+#define	LX_NETLINK_RTM_SETNEIGHTBL		67
+#define	LX_NETLINK_RTM_NEWNDUSEROPT		68
+#define	LX_NETLINK_RTM_NEWADDRLABEL		72
+#define	LX_NETLINK_RTM_DELADDRLABEL		73
+#define	LX_NETLINK_RTM_GETADDRLABEL		74
+#define	LX_NETLINK_RTM_GETDCB			78
+#define	LX_NETLINK_RTM_SETDCB			79
+#define	LX_NETLINK_RTM_NEWNETCONF		80
+#define	LX_NETLINK_RTM_GETNETCONF		82
+#define	LX_NETLINK_RTM_NEWMDB			84
+#define	LX_NETLINK_RTM_DELMDB			85
+#define	LX_NETLINK_RTM_GETMDB			86
+#define	LX_NETLINK_RTM_MAX			87
+
+/*
+ * rtnetlink(7) attribute constants
+ */
+#define	LX_NETLINK_RTA_UNSPEC		0
+#define	LX_NETLINK_RTA_DST		1
+#define	LX_NETLINK_RTA_SRC		2
+#define	LX_NETLINK_RTA_IIF		3
+#define	LX_NETLINK_RTA_OIF		4
+#define	LX_NETLINK_RTA_GATEWAY		5
+#define	LX_NETLINK_RTA_PRIORITY		6
+#define	LX_NETLINK_RTA_PREFSRC		7
+#define	LX_NETLINK_RTA_METRICS		8
+#define	LX_NETLINK_RTA_MULTIPATH	9
+#define	LX_NETLINK_RTA_PROTOINFO	10
+#define	LX_NETLINK_RTA_FLOW		11
+#define	LX_NETLINK_RTA_CACHEINFO	12
+#define	LX_NETLINK_RTA_SESSION		13
+#define	LX_NETLINK_RTA_MP_ALGO		14
+#define	LX_NETLINK_RTA_TABLE		15
+#define	LX_NETLINK_RTA_MARK		16
+#define	LX_NETLINK_RTA_MFC_STATS	17
+#define	LX_NETLINK_MAX_RTA	LX_NETLINK_RTA_MFC_STATS
+
+/*
+ * rtnetlink(7) NEWLINK/DELLINK/GETLINK constants
+ */
+#define	LX_NETLINK_IFLA_UNSPEC			0
+#define	LX_NETLINK_IFLA_ADDRESS			1
+#define	LX_NETLINK_IFLA_BROADCAST		2
+#define	LX_NETLINK_IFLA_IFNAME			3
+#define	LX_NETLINK_IFLA_MTU			4
+#define	LX_NETLINK_IFLA_LINK			5
+#define	LX_NETLINK_IFLA_QDISC			6
+#define	LX_NETLINK_IFLA_STATS			7
+#define	LX_NETLINK_IFLA_COST			8
+#define	LX_NETLINK_IFLA_PRIORITY		9
+#define	LX_NETLINK_IFLA_MASTER			10
+#define	LX_NETLINK_IFLA_WIRELESS		11
+#define	LX_NETLINK_IFLA_PROTINFO		12
+#define	LX_NETLINK_IFLA_TXQLEN			13
+#define	LX_NETLINK_IFLA_MAP			14
+#define	LX_NETLINK_IFLA_WEIGHT			15
+#define	LX_NETLINK_IFLA_OPERSTATE		16
+#define	LX_NETLINK_IFLA_LINKMODE		17
+#define	LX_NETLINK_IFLA_LINKINFO		18
+#define	LX_NETLINK_IFLA_NET_NS_PID		19
+#define	LX_NETLINK_IFLA_IFALIAS			20
+#define	LX_NETLINK_IFLA_NUM_VF			21
+#define	LX_NETLINK_IFLA_VFINFO_LIST		22
+#define	LX_NETLINK_IFLA_STATS64			23
+#define	LX_NETLINK_IFLA_VF_PORTS		24
+#define	LX_NETLINK_IFLA_PORT_SELF		25
+#define	LX_NETLINK_IFLA_AF_SPEC			26
+#define	LX_NETLINK_IFLA_GROUP			27
+#define	LX_NETLINK_IFLA_NET_NS_FD		28
+#define	LX_NETLINK_IFLA_EXT_MASK		29
+#define	LX_NETLINK_IFLA_PROMISCUITY		30
+#define	LX_NETLINK_IFLA_NUM_TX_QUEUES		31
+#define	LX_NETLINK_IFLA_NUM_RX_QUEUES		32
+#define	LX_NETLINK_IFLA_CARRIER			33
+#define	LX_NETLINK_IFLA_PHYS_PORT_ID		34
+#define	LX_NETLINK_IFLA_CARRIER_CHANGES		35
+#define	LX_NETLINK_IFLA_MAX			36
+
+/*
+ * rtnetlink(7) NEWADDR/DELADDR/GETADDR constants
+ */
+#define	LX_NETLINK_IFA_UNSPEC			0
+#define	LX_NETLINK_IFA_ADDRESS			1
+#define	LX_NETLINK_IFA_LOCAL			2
+#define	LX_NETLINK_IFA_LABEL			3
+#define	LX_NETLINK_IFA_BROADCAST		4
+#define	LX_NETLINK_IFA_ANYCAST			5
+#define	LX_NETLINK_IFA_CACHEINFO		6
+#define	LX_NETLINK_IFA_MULTICAST		7
+#define	LX_NETLINK_IFA_FLAGS			8
+#define	LX_NETLINK_IFA_MAX			9
+
+#define	LX_NETLINK_IFA_F_SECONDARY		0x01
+#define	LX_NETLINK_IFA_F_TEMPORARY		LX_NETLINK_IFA_F_SECONDARY
+#define	LX_NETLINK_IFA_F_NODAD			0x02
+#define	LX_NETLINK_IFA_F_OPTIMISTIC		0x04
+#define	LX_NETLINK_IFA_F_DADFAILED		0x08
+#define	LX_NETLINK_IFA_F_HOMEADDRESS		0x10
+#define	LX_NETLINK_IFA_F_DEPRECATED		0x20
+#define	LX_NETLINK_IFA_F_TENTATIVE		0x40
+#define	LX_NETLINK_IFA_F_PERMANENT		0x80
+#define	LX_NETLINK_IFA_F_MANAGETEMPADDR		0x100
+#define	LX_NETLINK_IFA_F_NOPREFIXROUTE		0x200
+
+/*
+ * Linux interface flags.
+ */
+#define	LX_IFF_UP		(1<<0)
+#define	LX_IFF_BROADCAST	(1<<1)
+#define	LX_IFF_DEBUG		(1<<2)
+#define	LX_IFF_LOOPBACK		(1<<3)
+#define	LX_IFF_POINTOPOINT	(1<<4)
+#define	LX_IFF_NOTRAILERS	(1<<5)
+#define	LX_IFF_RUNNING		(1<<6)
+#define	LX_IFF_NOARP		(1<<7)
+#define	LX_IFF_PROMISC		(1<<8)
+#define	LX_IFF_ALLMULTI		(1<<9)
+#define	LX_IFF_MASTER		(1<<10)
+#define	LX_IFF_SLAVE		(1<<11)
+#define	LX_IFF_MULTICAST	(1<<12)
+#define	LX_IFF_PORTSEL		(1<<13)
+#define	LX_IFF_AUTOMEDIA	(1<<14)
+#define	LX_IFF_DYNAMIC		(1<<15)
+#define	LX_IFF_LOWER_UP		(1<<16)
+#define	LX_IFF_DORMANT		(1<<17)
+#define	LX_IFF_ECHO		(1<<18)
+
+/* rtm_table */
+#define	LX_ROUTE_TABLE_MAIN	254
+
+/* rtm_type */
+#define	LX_RTN_UNSPEC		0
+#define	LX_RTN_UNICAST		1
+#define	LX_RTN_LOCAL		2
+#define	LX_RTN_BROADCAST	3
+#define	LX_RTN_ANYCAST		4
+#define	LX_RTN_MULTICAST	5
+#define	LX_RTN_BLACKHOLE	6
+#define	LX_RTN_UNREACHABLE	7
+#define	LX_RTN_PROHIBIT		8
+#define	LX_RTN_THROW		9
+#define	LX_RTN_NAT		10
+#define	LX_RTN_XRESOLVE		11
+
+/* rtm_protocol */
+#define	LX_RTPROT_UNSPEC	0
+#define	LX_RTPROT_REDIRECT	1	/* From ICMP redir	*/
+#define	LX_RTPROT_KERNEL	2	/* From kernel		*/
+#define	LX_RTPROT_BOOT		3	/* From boot		*/
+#define	LX_RTPROT_STATIC	4	/* From administrator	*/
+#define	LX_RTPROT_NULL		0xff	/* Uninitialized	*/
+
+/* rtm_scope */
+#define	LX_RTSCOPE_UNIVERSE	0
+#define	LX_RTSCOPE_SITE		200
+#define	LX_RTSCOPE_LINK		253
+#define	LX_RTSCOPE_HOST		254
+#define	LX_RTSCOPE_NOWHERE	255
+
+
+/*
+ * Netlink sockopts
+ */
+#define	SOL_LX_NETLINK	270
+
+#define	LX_NETLINK_SO_ADD_MEMBERSHIP	1
+#define	LX_NETLINK_SO_DROP_MEMBERSHIP	2
+#define	LX_NETLINK_SO_PKTINFO		3
+#define	LX_NETLINK_SO_BROADCAST_ERROR	4
+#define	LX_NETLINK_SO_NO_ENOBUFS	5
+#define	LX_NETLINK_SO_RX_RING		6
+#define	LX_NETLINK_SO_TX_RING		7
+
+/* Internal socket flags */
+#define	LXNLF_RECVUCRED			0x1
+
+/* nlmsg structure macros */
+#define	LXNLMSG_ALIGNTO	4
+#define	LXNLMSG_ALIGN(len)	\
+	(((len) + LXNLMSG_ALIGNTO - 1) & ~(LXNLMSG_ALIGNTO - 1))
+#define	LXNLMSG_HDRLEN	\
+	((int)LXNLMSG_ALIGN(sizeof (lx_netlink_hdr_t)))
+#define	LXNLMSG_LENGTH(len)	((len) + NLMSG_HDRLEN)
+#define	LXNLMSG_SPACE(len)	NLMSG_ALIGN(NLMSG_LENGTH(len))
+#define	LXNLMSG_DATA(nlh)	((void*)(((char *)nlh) + NLMSG_LENGTH(0)))
+#define	LXNLMSG_PAYLOAD(nlh, len)	\
+	((nlh)->nlmsg_len - NLMSG_SPACE((len)))
+
+#define	LXATTR_PAYLOAD(lxa)	\
+	((void*)((caddr_t)(lxa) + sizeof (lx_netlink_attr_t)))
+#define	LXATTR_HDRLEN	LXNLMSG_ALIGN(sizeof (lx_netlink_attr_t))
+#define	LXATTR_LEN(len)	(LXATTR_HDRLEN + LXNLMSG_ALIGN(len))
+
+typedef struct lx_netlink_hdr {
+	uint32_t lxnh_len;			/* length of message */
+	uint16_t lxnh_type;			/* type of message */
+	uint16_t lxnh_flags;			/* flags */
+	uint32_t lxnh_seq;			/* sequence number */
+	uint32_t lxnh_pid;			/* sending pid */
+} lx_netlink_hdr_t;
+
+typedef struct lx_netlink_err {
+	lx_netlink_hdr_t	lxne_hdr;	/* header */
+	int32_t			lxne_errno;	/* errno */
+	lx_netlink_hdr_t	lxne_failed;	/* header of err */
+} lx_netlink_err_t;
+
+typedef struct lx_netlink_attr {
+	uint16_t	lxna_len;		/* length of attribute */
+	uint16_t	lxna_type;		/* type of attribute */
+} lx_netlink_attr_t;
+
+typedef struct lx_netlink_ifinfomsg {
+	uint8_t		lxnl_ifi_family;	/* family: AF_UNSPEC */
+	uint8_t		lxnl_ifi__pad;
+	uint16_t	lxnl_ifi_type;		/* device type */
+	uint32_t	lxnl_ifi_index;		/* interface index */
+	uint32_t	lxnl_ifi_flags;		/* device flags */
+	uint32_t 	lxnl_ifi_change;	/* unused; must be -1 */
+} lx_netlink_ifinfomsg_t;
+
+typedef struct lx_netlink_ifaddrmsg {
+	uint8_t		lxnl_ifa_family;	/* address type */
+	uint8_t		lxnl_ifa_prefixlen;	/* prefix length of address */
+	uint8_t		lxnl_ifa_flags;		/* address flags */
+	uint8_t		lxnl_ifa_scope;		/* address scope */
+	uint8_t		lxnl_ifa_index;		/* interface index */
+} lx_netlink_ifaddrmsg_t;
+
+typedef struct lx_netlink_rtmsg {
+	uint8_t		rtm_family;	/* route AF			*/
+	uint8_t		rtm_dst_len;	/* destination addr length	*/
+	uint8_t		rtm_src_len;	/* source addr length		*/
+	uint8_t		rtm_tos;	/* TOS filter			*/
+	uint8_t		rtm_table;	/* routing table ID		*/
+	uint8_t		rtm_protocol;	/* routing protocol		*/
+	uint8_t		rtm_scope;
+	uint8_t		rtm_type;
+	uint32_t	rtm_flags;
+} lx_netlink_rtmsg_t;
+
+typedef struct lx_netlink_sockaddr {
+	sa_family_t	lxnl_family;		/* AF_LX_NETLINK */
+	uint16_t	lxnl_pad;		/* padding */
+	uint32_t	lxnl_port;		/* port id */
+	uint32_t	lxnl_groups;		/* multicast groups mask */
+} lx_netlink_sockaddr_t;
+
+typedef struct lx_netlink_sock {
+	struct lx_netlink_sock *lxns_next;	/* list of lx_netlink sockets */
+	sock_upcalls_t *lxns_upcalls;		/* pointer to socket upcalls */
+	sock_upper_handle_t lxns_uphandle;	/* socket upcall handle */
+	ldi_handle_t lxns_iphandle;		/* handle to /dev/ip */
+	ldi_handle_t lxns_ip6handle;		/* handle to /dev/ip6 */
+	ldi_handle_t lxns_current;		/* current ip handle */
+	int lxns_proto;				/* protocol */
+	uint32_t lxns_port;			/* port identifier */
+	uint32_t lxns_groups;			/* group subscriptions */
+	uint32_t lxns_bufsize;			/* buffer size */
+	uint32_t lxns_flags;			/* socket flags */
+} lx_netlink_sock_t;
+
+typedef struct lx_netlink_reply {
+	lx_netlink_hdr_t lxnr_hdr;		/* header that we're reply to */
+	lx_netlink_sock_t *lxnr_sock;		/* socket */
+	uint32_t lxnr_seq;			/* sequence number */
+	uint16_t lxnr_type;			/* type of reply */
+	mblk_t *lxnr_mp;			/* current mblk */
+	mblk_t *lxnr_err;			/* error mblk */
+	mblk_t *lxnr_mp1;			/* T_UNITDATA_IND mblk */
+	int lxnr_errno;				/* errno, if any */
+} lx_netlink_reply_t;
+
+static lx_netlink_sock_t *lx_netlink_head;	/* head of lx_netlink sockets */
+static kmutex_t lx_netlink_lock;		/* lock to protect state */
+static ldi_ident_t lx_netlink_ldi;		/* LDI handle */
+static int lx_netlink_bufsize = 4096;		/* default buffer size */
+static int lx_netlink_flowctrld;		/* # of times flow controlled */
+
+/*ARGSUSED*/
+static void
+lx_netlink_activate(sock_lower_handle_t handle,
+    sock_upper_handle_t sock_handle, sock_upcalls_t *sock_upcalls,
+    int flags, cred_t *cr)
+{
+	lx_netlink_sock_t *lxsock = (lx_netlink_sock_t *)handle;
+	struct sock_proto_props sopp;
+
+	sopp.sopp_flags = SOCKOPT_WROFF | SOCKOPT_RCVHIWAT |
+	    SOCKOPT_RCVLOWAT | SOCKOPT_MAXADDRLEN | SOCKOPT_MAXPSZ |
+	    SOCKOPT_MAXBLK | SOCKOPT_MINPSZ;
+	sopp.sopp_wroff = 0;
+	sopp.sopp_rxhiwat = SOCKET_RECVHIWATER;
+	sopp.sopp_rxlowat = SOCKET_RECVLOWATER;
+	sopp.sopp_maxaddrlen = sizeof (struct sockaddr_dl);
+	sopp.sopp_maxpsz = INFPSZ;
+	sopp.sopp_maxblk = INFPSZ;
+	sopp.sopp_minpsz = 0;
+
+	lxsock->lxns_upcalls = sock_upcalls;
+	lxsock->lxns_uphandle = sock_handle;
+
+	sock_upcalls->su_set_proto_props(sock_handle, &sopp);
+}
+
+/*ARGSUSED*/
+static int
+lx_netlink_setsockopt(sock_lower_handle_t handle, int level,
+    int option_name, const void *optval, socklen_t optlen, struct cred *cr)
+{
+	lx_netlink_sock_t *lxsock = (lx_netlink_sock_t *)handle;
+
+	if (level == SOL_SOCKET && option_name == SO_RECVUCRED) {
+		int *ival;
+		if (optlen != sizeof (int)) {
+			return (EINVAL);
+		}
+		ival = (int *)optval;
+		if (*ival == 0) {
+			lxsock->lxns_flags &= ~LXNLF_RECVUCRED;
+		} else {
+			lxsock->lxns_flags |= LXNLF_RECVUCRED;
+		}
+		return (0);
+	} else if (level == SOL_SOCKET) {
+		/* Punt on the other SOL_SOCKET options */
+		return (0);
+	} else if (level != SOL_LX_NETLINK) {
+		return (EOPNOTSUPP);
+	}
+
+	switch (option_name) {
+	case LX_NETLINK_SO_ADD_MEMBERSHIP:
+	case LX_NETLINK_SO_DROP_MEMBERSHIP:
+	case LX_NETLINK_SO_PKTINFO:
+	case LX_NETLINK_SO_BROADCAST_ERROR:
+	case LX_NETLINK_SO_NO_ENOBUFS:
+	case LX_NETLINK_SO_RX_RING:
+	case LX_NETLINK_SO_TX_RING:
+		/* Blatant lie */
+		return (0);
+	default:
+		return (EINVAL);
+	}
+}
+
+/*ARGSUSED*/
+static int
+lx_netlink_bind(sock_lower_handle_t handle, struct sockaddr *name,
+    socklen_t namelen, struct cred *cr)
+{
+	lx_netlink_sock_t *lxsock = (lx_netlink_sock_t *)handle;
+	lx_netlink_sockaddr_t *lxsa = (lx_netlink_sockaddr_t *)name;
+
+	if (namelen != sizeof (lx_netlink_sockaddr_t) ||
+	    lxsa->lxnl_family != AF_LX_NETLINK) {
+		return (EINVAL);
+	}
+
+
+	if (lxsa->lxnl_groups != 0) {
+		/*
+		 * On linux, CAP_NET_ADMIN is needed to bind to netlink groups.
+		 * This roughly maps to PRIV_SYS_IP_CONFIG.
+		 */
+		if (secpolicy_ip_config(cr, B_FALSE) != 0) {
+			return (EACCES);
+		}
+
+		/* Lie about group subscription for now */
+		lxsock->lxns_groups = lxsa->lxnl_groups;
+	}
+
+	/*
+	 * Linux netlink uses nl_port to identify distinct netlink sockets.
+	 * Binding to an address of nl_port=0 triggers the kernel to
+	 * automatically assign a free nl_port identifier.  Originally,
+	 * consumers of lx_netlink were required to bind with that automatic
+	 * address.  We now support non-zero values for nl_port although strict
+	 * checking to identify conflicts is not performed.  Use of the
+	 * id_space facility could be a convenient solution, if a need arose.
+	 */
+	if (lxsa->lxnl_port == 0) {
+		/*
+		 * Because we are not doing conflict detection, there is no
+		 * need to expend effort selecting a unique port for automatic
+		 * addressing during bind.
+		 */
+		lxsock->lxns_port = curproc->p_pid;
+	} else {
+		lxsock->lxns_port = lxsa->lxnl_port;
+	}
+
+	return (0);
+}
+
+/*ARGSUSED*/
+static int
+lx_netlink_getsockname(sock_lower_handle_t handle, struct sockaddr *sa,
+    socklen_t *len, struct cred *cr)
+{
+	lx_netlink_sock_t *lxsock = (lx_netlink_sock_t *)handle;
+	lx_netlink_sockaddr_t *lxsa = (lx_netlink_sockaddr_t *)sa;
+
+	if (*len < sizeof (lx_netlink_sockaddr_t))
+		return (EINVAL);
+
+	lxsa->lxnl_family = AF_LX_NETLINK;
+	lxsa->lxnl_pad = 0;
+	lxsa->lxnl_port = lxsock->lxns_port;
+	lxsa->lxnl_groups = lxsock->lxns_groups;
+
+	*len = sizeof (lx_netlink_sockaddr_t);
+
+	return (0);
+}
+
+static mblk_t *
+lx_netlink_alloc_mp1(lx_netlink_sock_t *lxsock)
+{
+	mblk_t *mp;
+	size_t size;
+	struct T_unitdata_ind *tunit;
+	lx_netlink_sockaddr_t *lxsa;
+	boolean_t send_ucred;
+
+	/*
+	 * Certain netlink clients (such as systemd) will set SO_RECVUCRED
+	 * (via the Linux SCM_CREDENTIALS) on the expectation that all replies
+	 * will contain credentials passed via cmsg.  They require this to
+	 * authenticate those messages as having originated in the kernel by
+	 * checking uc_pid == 0.
+	 */
+	VERIFY(lxsock != NULL);
+	send_ucred = ((lxsock->lxns_flags & LXNLF_RECVUCRED) != 0);
+
+	/*
+	 * Message structure:
+	 * +----------------------------+
+	 * | struct T_unit_data_ind	|
+	 * +----------------------------+
+	 * | lx_netlink_sockaddr_t	|
+	 * +----------------------------+  -+
+	 * | struct cmsghdr (SCM_UCRED)	|   |
+	 * +----------------------------+   +-(optional)
+	 * | struct ucred_s (cmsg data)	|   |
+	 * +----------------------------+  -+
+	 */
+	size = sizeof (*tunit) + sizeof (*lxsa);
+	if (send_ucred) {
+		size += sizeof (struct cmsghdr) +
+		    ROUNDUP_cmsglen(sizeof (struct ucred_s));
+	}
+	mp = allocb(size, 0);
+	if (mp == NULL) {
+		return (NULL);
+	}
+
+	tunit = (struct T_unitdata_ind *)mp->b_rptr;
+	lxsa = (lx_netlink_sockaddr_t *)((caddr_t)tunit + sizeof (*tunit));
+	mp->b_wptr += size;
+
+	mp->b_datap->db_type = M_PROTO;
+	tunit->PRIM_type = T_UNITDATA_IND;
+	tunit->SRC_length = sizeof (*lxsa);
+	tunit->SRC_offset = sizeof (*tunit);
+
+	lxsa->lxnl_family = AF_LX_NETLINK;
+	lxsa->lxnl_port = 0;
+	lxsa->lxnl_groups = 0;
+	lxsa->lxnl_pad = 0;
+
+	if (send_ucred) {
+		struct cmsghdr *cmsg;
+		struct ucred_s *ucred;
+
+		cmsg = (struct cmsghdr *)((caddr_t)lxsa + sizeof (*lxsa));
+		ucred = (struct ucred_s *)CMSG_CONTENT(cmsg);
+		cmsg->cmsg_len = sizeof (*cmsg) + sizeof (*ucred);
+		cmsg->cmsg_level = SOL_SOCKET;
+		cmsg->cmsg_type = SCM_UCRED;
+		bzero(ucred, sizeof (*ucred));
+		ucred->uc_size = sizeof (*ucred);
+		ucred->uc_zoneid = getzoneid();
+
+		tunit->OPT_length = sizeof (*cmsg) +
+		    ROUNDUP_cmsglen(sizeof (*ucred));
+		tunit->OPT_offset = tunit->SRC_offset + tunit->SRC_length;
+	} else {
+		tunit->OPT_length = 0;
+		tunit->OPT_offset = 0;
+	}
+
+	return (mp);
+}
+
+static lx_netlink_reply_t *
+lx_netlink_reply(lx_netlink_sock_t *lxsock,
+    lx_netlink_hdr_t *hdr, uint16_t type)
+{
+	lx_netlink_reply_t *reply;
+	mblk_t *err, *mp1;
+
+	/*
+	 * We always allocate an error block to assure that even if subsequent
+	 * allocations fail, we can return an error.
+	 */
+	if ((err = allocb(sizeof (lx_netlink_err_t), 0)) == NULL)
+		return (NULL);
+
+	if ((mp1 = lx_netlink_alloc_mp1(lxsock)) == NULL) {
+		freeb(err);
+		return (NULL);
+	}
+
+	reply = kmem_zalloc(sizeof (lx_netlink_reply_t), KM_SLEEP);
+	reply->lxnr_err = err;
+	reply->lxnr_sock = lxsock;
+	reply->lxnr_hdr = *hdr;
+	reply->lxnr_type = type;
+	reply->lxnr_mp1 = mp1;
+
+	return (reply);
+}
+
+static void
+lx_netlink_reply_add(lx_netlink_reply_t *reply, void *payload, uint32_t size)
+{
+	lx_netlink_hdr_t *hdr;
+	lx_netlink_sock_t *lxsock = reply->lxnr_sock;
+	uint32_t aligned;
+	mblk_t *mp = reply->lxnr_mp;
+
+	if (reply->lxnr_errno)
+		return;
+
+	aligned = LXNLMSG_ALIGN(size);
+	hdr = (lx_netlink_hdr_t *)mp->b_rptr;
+
+	if (hdr->lxnh_len + aligned > lxsock->lxns_bufsize) {
+		reply->lxnr_errno = E2BIG;
+		return;
+	}
+
+	bcopy(payload, mp->b_wptr, size);
+	hdr->lxnh_len += aligned;
+	mp->b_wptr += aligned;
+}
+
+static void
+lx_netlink_reply_msg(lx_netlink_reply_t *reply, void *payload, uint32_t size)
+{
+	lx_netlink_hdr_t *hdr;
+	lx_netlink_sock_t *lxsock = reply->lxnr_sock;
+	mblk_t *mp;
+
+	if (reply->lxnr_errno)
+		return;
+
+	VERIFY(reply->lxnr_mp == NULL);
+
+	if ((reply->lxnr_mp = mp = allocb(lxsock->lxns_bufsize, 0)) == NULL) {
+		reply->lxnr_errno = ENOMEM;
+		return;
+	}
+
+	bzero(mp->b_rptr, lxsock->lxns_bufsize);
+	hdr = (lx_netlink_hdr_t *)mp->b_rptr;
+	hdr->lxnh_flags = LX_NETLINK_NLM_F_MULTI;
+	hdr->lxnh_len = LXNLMSG_ALIGN(sizeof (lx_netlink_hdr_t));
+	hdr->lxnh_seq = reply->lxnr_hdr.lxnh_seq;
+	hdr->lxnh_pid = lxsock->lxns_port;
+
+	mp->b_wptr += LXNLMSG_ALIGN(sizeof (lx_netlink_hdr_t));
+
+	if (payload == NULL) {
+		/*
+		 * A NULL payload denotes a "done" message.
+		 */
+		hdr->lxnh_type = LX_NETLINK_NLMSG_DONE;
+	} else {
+		hdr->lxnh_type = reply->lxnr_type;
+		lx_netlink_reply_add(reply, payload, size);
+	}
+}
+
+static void
+lx_netlink_reply_attr(lx_netlink_reply_t *reply, uint16_t type,
+    void *payload, uint32_t size)
+{
+	lx_netlink_attr_t attr;
+
+	attr.lxna_len = size + sizeof (lx_netlink_attr_t);
+	attr.lxna_type = type;
+
+	lx_netlink_reply_add(reply, &attr, sizeof (attr));
+	lx_netlink_reply_add(reply, payload, size);
+}
+
+static void
+lx_netlink_reply_attr_string(lx_netlink_reply_t *reply,
+    uint16_t type, const char *str)
+{
+	lx_netlink_reply_attr(reply, type, (void *)str, strlen(str) + 1);
+}
+
+static void
+lx_netlink_reply_attr_int32(lx_netlink_reply_t *reply,
+    uint16_t type, int32_t val)
+{
+	int32_t v = val;
+
+	lx_netlink_reply_attr(reply, type, &v, sizeof (int32_t));
+}
+
+static int
+lx_netlink_reply_ioctl(lx_netlink_reply_t *reply, int cmd, void *arg)
+{
+	int rval;
+
+	if (reply->lxnr_errno != 0)
+		return (reply->lxnr_errno);
+
+	if ((rval = ldi_ioctl(reply->lxnr_sock->lxns_current,
+	    cmd, (intptr_t)arg, FKIOCTL, kcred, NULL)) != 0) {
+		reply->lxnr_errno = rval;
+	}
+
+	return (rval);
+}
+
+static void
+lx_netlink_reply_sendup(lx_netlink_reply_t *reply, mblk_t *mp, mblk_t *mp1)
+{
+	lx_netlink_sock_t *lxsock = reply->lxnr_sock;
+	int error;
+
+	/*
+	 * To prevent the stream head from coalescing messages and to indicate
+	 * their origin, we send them as T_UNITDATA_IND messages, not as raw
+	 * M_DATA.
+	 */
+	mp1->b_cont = mp;
+
+	lxsock->lxns_upcalls->su_recv(lxsock->lxns_uphandle, mp1,
+	    msgdsize(mp1), 0, &error, NULL);
+
+	if (error != 0)
+		lx_netlink_flowctrld++;
+}
+
+static void
+lx_netlink_reply_send(lx_netlink_reply_t *reply)
+{
+	mblk_t *mp1;
+
+	if (reply->lxnr_errno)
+		return;
+
+	if ((mp1 = lx_netlink_alloc_mp1(reply->lxnr_sock)) == NULL) {
+		reply->lxnr_errno = ENOMEM;
+		return;
+	}
+
+	lx_netlink_reply_sendup(reply, reply->lxnr_mp, mp1);
+	reply->lxnr_mp = NULL;
+}
+
+static void
+lx_netlink_reply_done(lx_netlink_reply_t *reply)
+{
+	lx_netlink_sock_t *lxsock = reply->lxnr_sock;
+	mblk_t *mp;
+
+	/*
+	 * Denote that we're done via a message with a NULL payload.
+	 */
+	lx_netlink_reply_msg(reply, NULL, 0);
+
+	if (reply->lxnr_errno) {
+		/*
+		 * If anything failed, we'll send up an error message.
+		 */
+		lx_netlink_hdr_t *hdr;
+		lx_netlink_err_t *err;
+
+		if (reply->lxnr_mp != NULL) {
+			freeb(reply->lxnr_mp);
+			reply->lxnr_mp = NULL;
+		}
+
+		mp = reply->lxnr_err;
+		VERIFY(mp != NULL);
+		reply->lxnr_err = NULL;
+		err = (lx_netlink_err_t *)mp->b_rptr;
+		hdr = &err->lxne_hdr;
+		mp->b_wptr += sizeof (lx_netlink_err_t);
+
+		err->lxne_failed = reply->lxnr_hdr;
+		err->lxne_errno = reply->lxnr_errno;
+		hdr->lxnh_type = LX_NETLINK_NLMSG_ERROR;
+		hdr->lxnh_seq = reply->lxnr_hdr.lxnh_seq;
+		hdr->lxnh_len = sizeof (lx_netlink_err_t);
+		hdr->lxnh_seq = reply->lxnr_hdr.lxnh_seq;
+		hdr->lxnh_pid = lxsock->lxns_port;
+	} else {
+		mp = reply->lxnr_mp;
+		VERIFY(mp != NULL);
+		reply->lxnr_mp = NULL;
+	}
+
+	lx_netlink_reply_sendup(reply, mp, reply->lxnr_mp1);
+
+	if (reply->lxnr_mp != NULL)
+		freeb(reply->lxnr_mp);
+
+	if (reply->lxnr_err != NULL)
+		freeb(reply->lxnr_err);
+
+	kmem_free(reply, sizeof (lx_netlink_reply_t));
+}
+
+static int
+lx_netlink_reply_error(lx_netlink_sock_t *lxsock,
+    lx_netlink_hdr_t *hdr, int errno)
+{
+	/*
+	 * The type of the message doesn't matter, as we're going to explicitly
+	 * set lxnr_errno and therefore send only an error message.
+	 */
+	lx_netlink_reply_t *reply = lx_netlink_reply(lxsock, hdr, 0);
+
+	if (reply == NULL)
+		return (ENOMEM);
+
+	reply->lxnr_errno = errno;
+	lx_netlink_reply_done(reply);
+
+	return (0);
+}
+
+static int
+lx_netlink_parse_msg_attrs(mblk_t *mp, void **msgp, unsigned int msg_size,
+    lx_netlink_attr_t **attrp, unsigned int *attr_max)
+{
+	lx_netlink_hdr_t *hdr = (lx_netlink_hdr_t *)mp->b_rptr;
+	lx_netlink_attr_t *lxa;
+	unsigned char *buf = mp->b_rptr + LXNLMSG_HDRLEN;
+	unsigned int i;
+	uint32_t buf_left = MBLKL(mp) - LXNLMSG_HDRLEN;
+	uint32_t msg_left = hdr->lxnh_len;
+
+	msg_size = LXNLMSG_ALIGN(msg_size);
+	if (msg_size > buf_left || msg_size > msg_left) {
+		return (-1);
+	}
+
+	*msgp = (void *)buf;
+	buf += msg_size;
+	buf_left -= msg_size;
+	msg_left -= msg_size;
+
+	/* Do not bother with attr parsing if not requested */
+	if (attrp == NULL || *attr_max == 0) {
+		return (0);
+	}
+
+	for (i = 0; i < *attr_max; i++) {
+		if (buf_left < LXATTR_HDRLEN || msg_left < LXATTR_HDRLEN) {
+			break;
+		}
+
+		lxa = (lx_netlink_attr_t *)buf;
+		if (lxa->lxna_len > buf_left || lxa->lxna_len > msg_left) {
+			return (-1);
+		}
+
+		attrp[i] = lxa;
+		buf += lxa->lxna_len;
+		buf_left -= lxa->lxna_len;
+		msg_left -= lxa->lxna_len;
+	}
+	*attr_max = i;
+
+	return (0);
+}
+
+/*
+ * Takes an IPv4 address (in network byte order) and returns the address scope.
+ */
+static uint8_t
+lx_ipv4_rtscope(in_addr_t nbo_addr)
+{
+	in_addr_t addr = ntohl(nbo_addr);
+	if ((addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) {
+		return (LX_RTSCOPE_HOST);
+	} else if ((addr & IN_AUTOCONF_MASK) == IN_AUTOCONF_NET) {
+		return (LX_RTSCOPE_LINK);
+	} else if ((addr & IN_PRIVATE8_MASK) == IN_PRIVATE8_NET ||
+	    (addr & IN_PRIVATE12_MASK) == IN_PRIVATE12_NET ||
+	    (addr & IN_PRIVATE16_MASK) == IN_PRIVATE16_NET) {
+		return (LX_RTSCOPE_SITE);
+	} else {
+		return (LX_RTSCOPE_UNIVERSE);
+	}
+}
+
+/*
+ * Takes an IPv6 address and returns the address scope.
+ */
+static uint8_t
+lx_ipv6_rtscope(const in6_addr_t *addr)
+{
+	if (IN6_ARE_ADDR_EQUAL(addr, &ipv6_loopback)) {
+		return (LX_RTSCOPE_HOST);
+	} else if (IN6_IS_ADDR_LINKLOCAL(addr)) {
+		return (LX_RTSCOPE_LINK);
+	} else if (IN6_IS_ADDR_SITELOCAL(addr)) {
+		return (LX_RTSCOPE_SITE);
+	} else {
+		return (LX_RTSCOPE_UNIVERSE);
+	}
+}
+
+static void
+lx_netlink_getlink_lifreq(lx_netlink_reply_t *reply, struct lifreq *lifr)
+{
+	lx_netlink_ifinfomsg_t ifi;
+	int i;
+	char if_name[IFNAMSIZ];
+	struct sockaddr_dl *sdl;
+	struct sockaddr hwaddr;
+	int hwaddr_size;
+	boolean_t is_loopback;
+
+	struct {
+		int native;
+		int lx;
+	} flags[] = {
+		{ IFF_UP, LX_IFF_UP },
+		{ IFF_BROADCAST, LX_IFF_BROADCAST },
+		{ IFF_DEBUG, LX_IFF_DEBUG },
+		{ IFF_LOOPBACK, LX_IFF_LOOPBACK },
+		{ IFF_POINTOPOINT, LX_IFF_POINTOPOINT },
+		{ IFF_NOTRAILERS, LX_IFF_NOTRAILERS },
+		{ IFF_RUNNING, LX_IFF_RUNNING },
+		{ IFF_NOARP, LX_IFF_NOARP },
+		{ IFF_PROMISC, LX_IFF_PROMISC },
+		{ IFF_ALLMULTI, LX_IFF_ALLMULTI },
+		{ IFF_MULTICAST, LX_IFF_MULTICAST },
+		{ 0 }
+	};
+
+	/*
+	 * illumos interfaces that contain a ':' are non-zero logical
+	 * interfaces. We should only emit the name of the zeroth logical
+	 * interface, since RTM_GETLINK only expects to see the name of
+	 * devices. The addresses of all logical devices will be
+	 * returned via an RTM_GETADDR.
+	 */
+	if (strchr(lifr->lifr_name, ':') != NULL)
+		return;
+
+	/*
+	 * Most of the lx_netlink module is architected to emit information in
+	 * an illumos-native manner.  Socket syscalls such as getsockname will
+	 * not translate fields to values Linux programs would expect since
+	 * that conversion is performed by the generic socket emulation.
+	 *
+	 * This is _not_ true of the actual protocol output from lx_netlink.
+	 * Since translating it at the socket layer would be onerous, all
+	 * output (including constants and names) is pre-translated to values
+	 * valid for Linux.
+	 */
+
+	bzero(&ifi, sizeof (ifi));
+	ifi.lxnl_ifi_family = AF_UNSPEC;
+	ifi.lxnl_ifi_change = (uint32_t)-1;
+
+	/* Convert the name to be Linux-friendly */
+	(void) strlcpy(if_name, lifr->lifr_name, IFNAMSIZ);
+	lx_ifname_convert(if_name, LX_IF_FROMNATIVE);
+	is_loopback = (strncmp(if_name, "lo", 2) == 0);
+
+	if (lx_netlink_reply_ioctl(reply, SIOCGLIFINDEX, lifr) != 0)
+		return;
+
+	ifi.lxnl_ifi_index = lifr->lifr_index;
+
+	if (lx_netlink_reply_ioctl(reply, SIOCGLIFFLAGS, lifr) != 0)
+		return;
+
+	for (i = 0; flags[i].native; i++) {
+		if (lifr->lifr_flags & flags[i].native)
+			ifi.lxnl_ifi_flags |= flags[i].lx;
+	}
+
+	/*
+	 * Query the datalink address.
+	 * The interface type will be included in the outgoing infomsg while
+	 * the address itself will be output separately.
+	 */
+	sdl = (struct sockaddr_dl *)&lifr->lifr_addr;
+	bzero(sdl, sizeof (*sdl));
+	if (!is_loopback) {
+		(void) lx_netlink_reply_ioctl(reply, SIOCGLIFHWADDR, lifr);
+	} else {
+		/* Simulate an empty hwaddr for loopback */
+		sdl->sdl_type = DL_LOOP;
+		sdl->sdl_alen = ETHERADDRL;
+	}
+	lx_stol_hwaddr(sdl, &hwaddr, &hwaddr_size);
+
+	ifi.lxnl_ifi_type = hwaddr.sa_family;
+	lx_netlink_reply_msg(reply, &ifi, sizeof (lx_netlink_ifinfomsg_t));
+
+	lx_netlink_reply_attr_string(reply, LX_NETLINK_IFLA_IFNAME, if_name);
+
+	if (lx_netlink_reply_ioctl(reply, SIOCGLIFMTU, lifr) != 0)
+		return;
+
+	lx_netlink_reply_attr_int32(reply, LX_NETLINK_IFLA_MTU, lifr->lifr_mtu);
+
+	if (hwaddr_size != 0) {
+		lx_netlink_reply_attr(reply, LX_NETLINK_IFLA_ADDRESS,
+		    hwaddr.sa_data, hwaddr_size);
+	}
+
+	/* Emulate a txqlen of 1. (0 for loopbacks) */
+	lx_netlink_reply_attr_int32(reply, LX_NETLINK_IFLA_TXQLEN,
+	    (is_loopback) ? 0 : 1);
+
+	lx_netlink_reply_send(reply);
+}
+
+static void
+lx_netlink_reply_eachfamily(lx_netlink_reply_t *reply,
+    void (*func)(lx_netlink_reply_t *, struct lifreq *), boolean_t distinct)
+{
+	lx_netlink_sock_t *sock = reply->lxnr_sock;
+	int nlifr, i;
+
+	struct {
+		int family;
+		ldi_handle_t handle;
+		struct lifconf lifc;
+		struct lifnum lifn;
+	} families[] = {
+		{ AF_INET, sock->lxns_iphandle },
+		{ AF_INET6, sock->lxns_ip6handle },
+		{ AF_UNSPEC }
+	}, *family, *check;
+
+	for (family = families; family->family != AF_UNSPEC; family++) {
+		struct lifconf *lifc = &family->lifc;
+		struct lifnum *lifn = &family->lifn;
+
+		lifn->lifn_family = family->family;
+		sock->lxns_current = family->handle;
+
+		if (lx_netlink_reply_ioctl(reply, SIOCGLIFNUM, lifn) != 0)
+			break;
+
+		lifc->lifc_family = lifn->lifn_family;
+		lifc->lifc_flags = 0;
+		lifc->lifc_len = lifn->lifn_count * sizeof (struct lifreq);
+		if (lifn->lifn_count == 0) {
+			lifc->lifc_buf = NULL;
+			continue;
+		}
+		lifc->lifc_buf = kmem_alloc(lifc->lifc_len, KM_SLEEP);
+
+		if (lx_netlink_reply_ioctl(reply, SIOCGLIFCONF, lifc) != 0)
+			break;
+
+		nlifr = lifc->lifc_len / sizeof (lifc->lifc_req[0]);
+
+		for (i = 0; i < nlifr; i++) {
+			if (!distinct) {
+				func(reply, &lifc->lifc_req[i]);
+				continue;
+			}
+
+			/*
+			 * If we have been asked to provide each interface
+			 * exactly once, we need to (annoyingly) check this
+			 * name against others that we've already processed for
+			 * other families.  Yes, this is quadratic time -- but
+			 * the number of interfaces per family is expected to
+			 * be very small.
+			 */
+			for (check = families; check != family; check++) {
+				struct lifconf *clifc = &check->lifc;
+				int cnlifr = clifc->lifc_len /
+				    sizeof (clifc->lifc_req[0]), j;
+				char *nm = lifc->lifc_req[i].lifr_name, *cnm;
+
+				for (j = 0; j < cnlifr; j++) {
+					cnm = clifc->lifc_req[j].lifr_name;
+
+					if (strcmp(nm, cnm) == 0)
+						break;
+				}
+
+				if (j != cnlifr)
+					break;
+			}
+
+			if (check != family)
+				continue;
+
+			func(reply, &lifc->lifc_req[i]);
+		}
+	}
+
+	for (family = families; family->family != AF_UNSPEC; family++) {
+		struct lifconf *lifc = &family->lifc;
+
+		if (lifc->lifc_buf != NULL)
+			kmem_free(lifc->lifc_buf, lifc->lifc_len);
+	}
+}
+
+/*ARGSUSED*/
+static int
+lx_netlink_getlink(lx_netlink_sock_t *lxsock, lx_netlink_hdr_t *hdr, mblk_t *mp)
+{
+	lx_netlink_reply_t *reply;
+
+	reply = lx_netlink_reply(lxsock, hdr, LX_NETLINK_RTM_NEWLINK);
+
+	if (reply == NULL)
+		return (ENOMEM);
+
+	lx_netlink_reply_eachfamily(reply, lx_netlink_getlink_lifreq, B_TRUE);
+	lx_netlink_reply_done(reply);
+
+	return (0);
+}
+
+static void
+lx_netlink_getaddr_lifreq(lx_netlink_reply_t *reply, struct lifreq *lifr)
+{
+	lx_netlink_ifaddrmsg_t ifa;
+
+	bzero(&ifa, sizeof (ifa));
+
+	if (lx_netlink_reply_ioctl(reply, SIOCGLIFINDEX, lifr) != 0)
+		return;
+
+	ifa.lxnl_ifa_index = lifr->lifr_index;
+
+	if (lx_netlink_reply_ioctl(reply, SIOCGLIFFLAGS, lifr) != 0)
+		return;
+
+	/*
+	 * Don't report on-link subnets
+	 */
+	if ((lifr->lifr_flags & IFF_NOLOCAL) != 0)
+		return;
+
+	if (lx_netlink_reply_ioctl(reply, SIOCGLIFSUBNET, lifr) != 0)
+		return;
+
+	ifa.lxnl_ifa_prefixlen = lifr->lifr_addrlen;
+
+	if (lx_netlink_reply_ioctl(reply, SIOCGLIFADDR, lifr) != 0)
+		return;
+
+	if (lifr->lifr_addr.ss_family == AF_INET) {
+		struct sockaddr_in *sin;
+
+		ifa.lxnl_ifa_family = LX_AF_INET;
+
+		sin = (struct sockaddr_in *)&lifr->lifr_addr;
+		ifa.lxnl_ifa_scope = lx_ipv4_rtscope(
+		    sin->sin_addr.s_addr);
+
+		lx_netlink_reply_msg(reply, &ifa,
+		    sizeof (lx_netlink_ifaddrmsg_t));
+
+		lx_netlink_reply_attr_int32(reply,
+		    LX_NETLINK_IFA_ADDRESS, sin->sin_addr.s_addr);
+	} else {
+		struct sockaddr_in6 *sin;
+
+		ifa.lxnl_ifa_family = LX_AF_INET6;
+
+		sin = (struct sockaddr_in6 *)&lifr->lifr_addr;
+		ifa.lxnl_ifa_scope = lx_ipv6_rtscope(&sin->sin6_addr);
+
+		lx_netlink_reply_msg(reply, &ifa,
+		    sizeof (lx_netlink_ifaddrmsg_t));
+
+		lx_netlink_reply_attr(reply, LX_NETLINK_IFA_ADDRESS,
+		    &sin->sin6_addr, sizeof (sin->sin6_addr));
+	}
+
+	lx_netlink_reply_send(reply);
+}
+
+/*ARGSUSED*/
+static int
+lx_netlink_getaddr(lx_netlink_sock_t *lxsock, lx_netlink_hdr_t *hdr, mblk_t *mp)
+{
+	lx_netlink_reply_t *reply;
+
+	reply = lx_netlink_reply(lxsock, hdr, LX_NETLINK_RTM_NEWADDR);
+
+	if (reply == NULL)
+		return (ENOMEM);
+
+	lx_netlink_reply_eachfamily(reply, lx_netlink_getaddr_lifreq, B_FALSE);
+	lx_netlink_reply_done(reply);
+
+	return (0);
+}
+
+struct lx_getroute_ctx {
+	lx_netlink_reply_t *lgrtctx_reply;
+	lx_netlink_rtmsg_t *lgrtctx_rtmsg;
+	lx_netlink_attr_t *lgrtctx_attrs[LX_NETLINK_MAX_RTA];
+	unsigned int lgrtctx_max_attr;
+	lx_netlink_attr_t *lgrtctx_rtadst;
+};
+
+static void
+lx_netlink_getroute_ipv4(ire_t *ire, struct lx_getroute_ctx *ctx)
+{
+	lx_netlink_reply_t *reply = ctx->lgrtctx_reply;
+	lx_netlink_rtmsg_t *rtmsg = ctx->lgrtctx_rtmsg;
+	lx_netlink_attr_t *rtadst = ctx->lgrtctx_rtadst;
+	lx_netlink_rtmsg_t res;
+	ill_t *ill = NULL;
+
+	/* Certain IREs are too specific for netlink */
+	if ((ire->ire_type & (IRE_BROADCAST | IRE_MULTICAST | IRE_NOROUTE |
+	    IRE_LOOPBACK | IRE_LOCAL)) != 0 || ire->ire_testhidden != 0) {
+		return;
+	}
+	/*
+	 * When listing routes, CLONE entries are undesired.
+	 * They are required for 'ip route get' on a local address.
+	 */
+	if (rtmsg->rtm_dst_len == 0 && (ire->ire_type & IRE_IF_CLONE) != 0) {
+		return;
+	}
+
+	bzero(&res, sizeof (res));
+	res.rtm_family = LX_AF_INET;
+	res.rtm_table = LX_ROUTE_TABLE_MAIN;
+	res.rtm_type = LX_RTN_UNICAST;
+	res.rtm_dst_len = ire->ire_masklen;
+
+	if (ire->ire_type & (IRE_IF_NORESOLVER|IRE_IF_RESOLVER)) {
+		/* Interface-local networks considered kernel-created */
+		res.rtm_protocol = LX_RTPROT_KERNEL;
+		res.rtm_scope = LX_RTSCOPE_LINK;
+	} else if (ire->ire_flags & RTF_STATIC) {
+		res.rtm_protocol = LX_RTPROT_STATIC;
+	}
+
+	if (rtmsg->rtm_dst_len == 0x20 && rtadst != NULL) {
+		/*
+		 * SpecifY single-destination route.
+		 * RTA_DST details will be added later
+		 */
+		res.rtm_dst_len = rtmsg->rtm_dst_len;
+	}
+
+
+	lx_netlink_reply_msg(reply, &res, sizeof (res));
+
+	if (rtmsg->rtm_dst_len == 0x20 && rtadst != NULL) {
+		/* Add RTA_DST details for single-destination route. */
+		lx_netlink_reply_attr(reply, LX_NETLINK_RTA_DST,
+		    LXATTR_PAYLOAD(rtadst), sizeof (ipaddr_t));
+	} else if (ire->ire_masklen != 0) {
+		lx_netlink_reply_attr(reply, LX_NETLINK_RTA_DST,
+		    &ire->ire_addr, sizeof (ire->ire_addr));
+	}
+
+	if (ire->ire_ill != NULL) {
+		ill = ire->ire_ill;
+	} else if (ire->ire_dep_parent != NULL) {
+		ill = ire->ire_dep_parent->ire_ill;
+	}
+
+	if (ill != NULL) {
+		uint32_t ifindex, addr_src;
+
+		ifindex = ill->ill_phyint->phyint_ifindex;
+		lx_netlink_reply_attr(reply, LX_NETLINK_RTA_OIF,
+		    &ifindex, sizeof (ifindex));
+
+		addr_src = ill->ill_ipif->ipif_lcl_addr;
+		lx_netlink_reply_attr(reply, LX_NETLINK_RTA_PREFSRC,
+		    &addr_src, sizeof (addr_src));
+	}
+
+	if (ire->ire_flags & RTF_GATEWAY) {
+		lx_netlink_reply_attr(reply, LX_NETLINK_RTA_GATEWAY,
+		    &ire->ire_gateway_addr, sizeof (ire->ire_gateway_addr));
+	}
+
+	lx_netlink_reply_send(reply);
+}
+
+/*ARGSUSED*/
+static int
+lx_netlink_getroute(lx_netlink_sock_t *lxsock, lx_netlink_hdr_t *hdr,
+    mblk_t *mp)
+{
+	struct lx_getroute_ctx ctx;
+	lx_netlink_reply_t *reply;
+	lx_netlink_rtmsg_t rtmsg, *rtmsgp;
+	int rtmsg_size = sizeof (rtmsg);
+	netstack_t *ns;
+	int i;
+
+	bzero(&ctx, sizeof (ctx));
+	ctx.lgrtctx_max_attr = LX_NETLINK_MAX_RTA;
+
+	if (lx_netlink_parse_msg_attrs(mp, (void **)&rtmsgp,
+	    rtmsg_size, ctx.lgrtctx_attrs, &ctx.lgrtctx_max_attr) != 0) {
+		return (EPROTO);
+	}
+
+	/*
+	 * Older version of libnetlink send a truncated rtmsg struct for
+	 * certain RTM_GETROUTE queries.  We must detect this condition and
+	 * truncate our input to prevent later confusion.
+	 */
+	if (curproc->p_zone->zone_brand == &lx_brand &&
+	    lx_kern_release_cmp(curproc->p_zone, "2.6.32") <= 0 &&
+	    rtmsgp->rtm_dst_len == 0) {
+		rtmsg_size = sizeof (rtmsg.rtm_family);
+	}
+	bzero(&rtmsg, sizeof (rtmsg));
+	bcopy(rtmsgp, &rtmsg, rtmsg_size);
+	ctx.lgrtctx_rtmsg = &rtmsg;
+
+	/* If RTA_DST was passed, it effects later decisions */
+	for (i = 0; i < ctx.lgrtctx_max_attr; i++) {
+		lx_netlink_attr_t *attr = ctx.lgrtctx_attrs[i];
+
+		if (attr->lxna_type == LX_NETLINK_RTA_DST &&
+		    attr->lxna_len == LXATTR_LEN(sizeof (ipaddr_t))) {
+			ctx.lgrtctx_rtadst = attr;
+			break;
+		}
+	}
+
+	reply = lx_netlink_reply(lxsock, hdr, LX_NETLINK_RTM_NEWROUTE);
+	if (reply == NULL) {
+		return (ENOMEM);
+	}
+	ctx.lgrtctx_reply = reply;
+
+	/* Do not report anything outside the main table */
+	if (rtmsg.rtm_table != LX_ROUTE_TABLE_MAIN &&
+	    rtmsg.rtm_table != 0) {
+		lx_netlink_reply_done(reply);
+		return (0);
+	}
+
+	ns = netstack_get_current();
+	if (ns == NULL) {
+		lx_netlink_reply_done(reply);
+		return (0);
+	}
+	if (rtmsg.rtm_family == LX_AF_INET || rtmsg.rtm_family == 0) {
+		if (rtmsg.rtm_dst_len == 0x20 && ctx.lgrtctx_rtadst != NULL) {
+			/* resolve route for host */
+			ipaddr_t *dst = LXATTR_PAYLOAD(ctx.lgrtctx_rtadst);
+			ire_t *ire_dst;
+
+			ire_dst = ire_route_recursive_dstonly_v4(*dst, 0, 0,
+			    ns->netstack_ip);
+			lx_netlink_getroute_ipv4(ire_dst, &ctx);
+			ire_refrele(ire_dst);
+		} else {
+			/* get route listing */
+			ire_walk_v4(&lx_netlink_getroute_ipv4, &ctx, ALL_ZONES,
+			    ns->netstack_ip);
+		}
+	}
+	if (rtmsg.rtm_family == LX_AF_INET6) {
+		/* punt on ipv6 for now */
+		netstack_rele(ns);
+		lx_netlink_reply_done(reply);
+		return (EPROTO);
+	}
+	netstack_rele(ns);
+
+	lx_netlink_reply_done(reply);
+	return (0);
+}
+
+
+/*ARGSUSED*/
+static int
+lx_netlink_audit(lx_netlink_sock_t *lxsock, lx_netlink_hdr_t *hdr, mblk_t *mp)
+{
+	/*
+	 * For all auditing messages, we return ECONNREFUSED, which seems to
+	 * keep user-level auditing happy.  (Or at least, non-suicidal.)
+	 */
+	return (ECONNREFUSED);
+}
+
+/*ARGSUSED*/
+static int
+lx_netlink_kobject_uevent(lx_netlink_sock_t *lxsock,
+    lx_netlink_hdr_t *hdr, mblk_t *mp)
+{
+	/*
+	 * For udev, we just silently accept all writes and never actually
+	 * reply with anything -- which appears to be sufficient for things
+	 * to work.
+	 */
+	return (0);
+}
+
+/*ARGSUSED*/
+static int
+lx_netlink_send(sock_lower_handle_t handle, mblk_t *mp,
+    struct nmsghdr *msg, cred_t *cr)
+{
+	lx_netlink_sock_t *lxsock = (lx_netlink_sock_t *)handle;
+	lx_netlink_hdr_t *hdr = (lx_netlink_hdr_t *)mp->b_rptr;
+	int i, rval;
+
+	static struct {
+		int proto;
+		uint16_t type;
+		int (*func)(lx_netlink_sock_t *, lx_netlink_hdr_t *, mblk_t *);
+	} handlers[] = {
+		{ LX_NETLINK_ROUTE,
+		    LX_NETLINK_RTM_GETLINK, lx_netlink_getlink },
+		{ LX_NETLINK_ROUTE,
+		    LX_NETLINK_RTM_GETADDR, lx_netlink_getaddr },
+		{ LX_NETLINK_ROUTE,
+		    LX_NETLINK_RTM_GETROUTE, lx_netlink_getroute },
+		{ LX_NETLINK_AUDIT,
+		    LX_NETLINK_NLMSG_NONE, lx_netlink_audit },
+		{ LX_NETLINK_KOBJECT_UEVENT,
+		    LX_NETLINK_NLMSG_NONE, lx_netlink_kobject_uevent },
+		{ LX_NETLINK_NLMSG_NOOP, LX_NETLINK_NLMSG_NONE, NULL }
+	};
+
+	if (DB_TYPE(mp) != M_DATA || MBLKL(mp) < sizeof (lx_netlink_hdr_t)) {
+		freemsg(mp);
+		return (EPROTO);
+	}
+
+	for (i = 0; handlers[i].func != NULL; i++) {
+		if (lxsock->lxns_proto != handlers[i].proto)
+			continue;
+
+		if (handlers[i].type != LX_NETLINK_NLMSG_NONE &&
+		    hdr->lxnh_type != handlers[i].type)
+			continue;
+
+		rval = handlers[i].func(lxsock, hdr, mp);
+		freemsg(mp);
+
+		return (rval);
+	}
+
+	/*
+	 * An unrecognized message.  We will bounce up an EOPNOTSUPP reply.
+	 */
+	rval = lx_netlink_reply_error(lxsock, hdr, EOPNOTSUPP);
+	freemsg(mp);
+
+	return (rval);
+}
+
+/*ARGSUSED*/
+static int
+lx_netlink_close(sock_lower_handle_t handle, int flags, cred_t *cr)
+{
+	lx_netlink_sock_t *lxsock = (lx_netlink_sock_t *)handle, *sock, **prev;
+
+	mutex_enter(&lx_netlink_lock);
+
+	prev = &lx_netlink_head;
+
+	for (sock = *prev; sock != lxsock; sock = sock->lxns_next)
+		prev = &sock->lxns_next;
+
+	*prev = sock->lxns_next;
+
+	mutex_exit(&lx_netlink_lock);
+
+	(void) ldi_close(lxsock->lxns_iphandle, FREAD, kcred);
+	(void) ldi_close(lxsock->lxns_ip6handle, FREAD, kcred);
+	kmem_free(lxsock, sizeof (lx_netlink_sock_t));
+
+	return (0);
+}
+
+static sock_downcalls_t sock_lx_netlink_downcalls = {
+	lx_netlink_activate,		/* sd_activate */
+	sock_accept_notsupp,		/* sd_accept */
+	lx_netlink_bind,		/* sd_bind */
+	sock_listen_notsupp,		/* sd_listen */
+	sock_connect_notsupp,		/* sd_connect */
+	sock_getpeername_notsupp,	/* sd_getpeername */
+	lx_netlink_getsockname,		/* sd_getsockname */
+	sock_getsockopt_notsupp,	/* sd_getsockopt */
+	lx_netlink_setsockopt,		/* sd_setsockopt */
+	lx_netlink_send,		/* sd_send */
+	NULL,				/* sd_send_uio */
+	NULL,				/* sd_recv_uio */
+	NULL,				/* sd_poll */
+	sock_shutdown_notsupp,		/* sd_shutdown */
+	sock_clr_flowctrl_notsupp,	/* sd_setflowctrl */
+	sock_ioctl_notsupp,		/* sd_ioctl */
+	lx_netlink_close		/* sd_close */
+};
+
+/*ARGSUSED*/
+static sock_lower_handle_t
+lx_netlink_create(int family, int type, int proto,
+    sock_downcalls_t **sock_downcalls, uint_t *smodep, int *errorp,
+    int flags, cred_t *credp)
+{
+	lx_netlink_sock_t *lxsock;
+	ldi_handle_t handle, handle6;
+	cred_t *kcred = zone_kcred();
+	int err;
+
+	if (family != AF_LX_NETLINK ||
+	    (type != SOCK_DGRAM && type != SOCK_RAW)) {
+		*errorp = EPROTONOSUPPORT;
+		return (NULL);
+	}
+
+	switch (proto) {
+	case LX_NETLINK_ROUTE:
+	case LX_NETLINK_AUDIT:
+	case LX_NETLINK_KOBJECT_UEVENT:
+		break;
+
+	default:
+		*errorp = EPROTONOSUPPORT;
+		return (NULL);
+	}
+
+	if ((err = ldi_open_by_name(DEV_IP, FREAD, kcred,
+	    &handle, lx_netlink_ldi)) != 0) {
+		*errorp = err;
+		return (NULL);
+	}
+
+	if ((err = ldi_open_by_name(DEV_IP6, FREAD, kcred,
+	    &handle6, lx_netlink_ldi)) != 0) {
+		(void) ldi_close(handle, FREAD, kcred);
+		*errorp = err;
+		return (NULL);
+	}
+
+	*sock_downcalls = &sock_lx_netlink_downcalls;
+	*smodep = SM_ATOMIC;
+
+	lxsock = kmem_zalloc(sizeof (lx_netlink_sock_t), KM_SLEEP);
+	lxsock->lxns_iphandle = handle;
+	lxsock->lxns_ip6handle = handle6;
+	lxsock->lxns_bufsize = lx_netlink_bufsize;
+	lxsock->lxns_proto = proto;
+
+	mutex_enter(&lx_netlink_lock);
+
+	lxsock->lxns_next = lx_netlink_head;
+	lx_netlink_head = lxsock;
+
+	mutex_exit(&lx_netlink_lock);
+
+	return ((sock_lower_handle_t)lxsock);
+}
+
+static void
+lx_netlink_init(void)
+{
+	major_t major = mod_name_to_major("ip");
+	int err;
+
+	VERIFY(major != DDI_MAJOR_T_NONE);
+
+	err = ldi_ident_from_major(major, &lx_netlink_ldi);
+	VERIFY(err == 0);
+}
+
+static void
+lx_netlink_fini(void)
+{
+	ldi_ident_release(lx_netlink_ldi);
+}
+
+static smod_reg_t sinfo = {
+	SOCKMOD_VERSION,
+	"lx_netlink",
+	SOCK_UC_VERSION,
+	SOCK_DC_VERSION,
+	lx_netlink_create,
+	NULL
+};
+
+/* modldrv structure */
+static struct modlsockmod sockmod = {
+	&mod_sockmodops, "AF_LX_NETLINK socket module", &sinfo
+};
+
+/* modlinkage structure */
+static struct modlinkage ml = {
+	MODREV_1,
+	&sockmod,
+	NULL
+};
+
+int
+_init(void)
+{
+	int err;
+
+	lx_netlink_init();
+
+	if ((err = mod_install(&ml)) != 0)
+		lx_netlink_fini();
+
+	return (err);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&ml, modinfop));
+}
+
+int
+_fini(void)
+{
+	int err = 0;
+
+	mutex_enter(&lx_netlink_lock);
+
+	if (lx_netlink_head != NULL)
+		err = EBUSY;
+
+	mutex_exit(&lx_netlink_lock);
+
+	if (err == 0 && (err = mod_remove(&ml)) == 0)
+		lx_netlink_fini();
+
+	return (err);
+}
--- /dev/null	Wed Oct  5 00:32:05 2016
+++ new/usr/src/uts/common/brand/lx/io/lx_ptm.c	Wed Oct  5 00:32:05 2016
@@ -0,0 +1,1188 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * Copyright 2016 Joyent, Inc.  All rights reserved.
+ */
+
+
+/*
+ * This driver attempts to emulate some of the the behaviors of
+ * Linux terminal devices (/dev/ptmx and /dev/pts/[0-9][0-9]*) on Solaris
+ *
+ * It does this by layering over the /dev/ptmx device and intercepting
+ * opens to it.
+ *
+ * This driver makes the following assumptions about the way the ptm/pts
+ * drivers on Solaris work:
+ *
+ *    - all opens of the /dev/ptmx device node return a unique dev_t.
+ *
+ *    - the dev_t minor node value for each open ptm instance corrospondes
+ *      to it's associated slave terminal device number.  ie. the path to
+ *      the slave terminal device associated with an open ptm instance
+ *      who's dev_t minor node vaue is 5, is /dev/pts/5.
+ *
+ *    - the ptm driver always allocates the lowest numbered slave terminal
+ *      device possible.
+ */
+
+#include <sys/conf.h>
+#include <sys/ddi.h>
+#include <sys/devops.h>
+#include <sys/file.h>
+#include <sys/filio.h>
+#include <sys/kstr.h>
+#include <sys/lx_ptm.h>
+#include <sys/modctl.h>
+#include <sys/pathname.h>
+#include <sys/ptms.h>
+#include <sys/ptyvar.h>
+#include <sys/stat.h>
+#include <sys/stropts.h>
+#include <sys/sunddi.h>
+#include <sys/sunldi.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <sys/sdt.h>
+
+#define	LP_PTM_PATH		"/dev/ptmx"
+#define	LP_PTS_PATH		"/dev/pts/"
+#define	LP_PTS_DRV_NAME		"pts"
+#define	LP_PTS_USEC_DELAY	(5 * 1000)	/* 5 ms */
+#define	LP_PTS_USEC_DELAY_MAX	(5 * MILLISEC)	/* 5 ms */
+
+/*
+ * this driver is layered on top of the ptm driver.  we'd like to
+ * make this drivers minor name space a mirror of the ptm drivers
+ * namespace, but we can't actually do this.  the reason is that the
+ * ptm driver is opened via the clone driver.  there for no minor nodes
+ * of the ptm driver are actually accessible via the filesystem.
+ * since we're not a streams device we can't be opened by the clone
+ * driver.  there for we need to have at least minor node accessible
+ * via the filesystem so that consumers can open it.  we use the device
+ * node with a minor number of 0 for this purpose.  what this means is
+ * that minor node 0 can't be used to map ptm minor node 0.  since this
+ * minor node is now reserved we need to shift our ptm minor node
+ * mappings by one.  ie. a ptm minor node with a value of 0 will
+ * corrospond to our minor node with a value of 1.  these mappings are
+ * managed with the following macros.
+ */
+#define	DEVT_TO_INDEX(x)	LX_PTM_DEV_TO_PTS(x)
+#define	INDEX_TO_MINOR(x)	((x) + 1)
+
+/*
+ * grow our layered handle array by the same size increment that the ptm
+ * driver uses to grow the pty device space - PTY_MAXDELTA
+ */
+#define	LP_PTY_INC	128
+
+/*
+ * lx_ptm_ops contains state information about outstanding operations on the
+ * underlying master terminal device.  Currently we only track information
+ * for read operations.
+ *
+ * Note that this data has not been rolled directly into the lx_ptm_handle
+ * structure because we can't put mutex's of condition variables into
+ * lx_ptm_handle structure.  The reason is that the array of lx_ptm_handle
+ * structures linked to from the global lx_ptm state can be resized
+ * dynamically, and when it's resized, the new array is at a different
+ * memory location and the old array memory is discarded.  Mutexs and cvs
+ * are accessed based off their address, so if this array was re-sized while
+ * there were outstanding operations on any mutexs or cvs in the array
+ * then the system would tip over.  In the future the lx_ptm_handle structure
+ * array should probably be replaced with either an array of pointers to
+ * lx_ptm_handle structures or some other kind of data structure containing
+ * pointers to lx_ptm_handle structures.  Then the lx_ptm_ops structure
+ * could be folded directly into the lx_ptm_handle structures.  (This will
+ * also require the definition of a new locking mechanism to protect the
+ * contents of lx_ptm_handle structures.)
+ */
+typedef struct lx_ptm_ops {
+	int			lpo_rops;
+	kcondvar_t		lpo_rops_cv;
+	kmutex_t		lpo_rops_lock;
+} lx_ptm_ops_t;
+
+/*
+ * Every open of the master terminal device in a zone results in a new
+ * lx_ptm_handle handle allocation.  These handles are stored in an array
+ * hanging off the lx_ptm_state structure.
+ */
+typedef struct lx_ptm_handle {
+	/* Device handle to the underlying real /dev/ptmx master terminal. */
+	ldi_handle_t		lph_handle;
+
+	/* Flag to indicate if TIOCPKT mode has been enabled. */
+	int			lph_pktio;
+
+	/* Number of times the slave device has been opened/closed. */
+	int			lph_eofed;
+
+	/* Callback handler in the ptm driver to check if slave is open. */
+	ptmptsopencb_t		lph_ppocb;
+
+	/* Pointer to state for operations on underlying device. */
+	lx_ptm_ops_t		*lph_lpo;
+} lx_ptm_handle_t;
+
+/*
+ * Global state for the lx_ptm driver.
+ */
+typedef struct lx_ptm_state {
+	/* lx_ptm device devinfo pointer */
+	dev_info_t		*lps_dip;
+
+	/* LDI ident used to open underlying real /dev/ptmx master terminals. */
+	ldi_ident_t		lps_li;
+
+	/* pts drivers major number */
+	major_t			lps_pts_major;
+
+	/* rw lock used to manage access and growth of lps_lh_array */
+	krwlock_t		lps_lh_rwlock;
+
+	/* number of elements in lps_lh_array */
+	uint_t			lps_lh_count;
+
+	/* Array of handles to underlying real /dev/ptmx master terminals. */
+	lx_ptm_handle_t		*lps_lh_array;
+} lx_ptm_state_t;
+
+/* Pointer to the lx_ptm global state structure. */
+static lx_ptm_state_t	lps;
+
+/*
+ * List of modules to be autopushed onto slave terminal devices when they
+ * are opened in an lx branded zone.
+ */
+static char *lx_pts_mods[] = {
+	"ptem",
+	"ldterm",
+	"ttcompat",
+	NULL
+};
+
+static void
+lx_ptm_lh_grow(uint_t index)
+{
+	uint_t			new_lh_count, old_lh_count;
+	lx_ptm_handle_t		*new_lh_array, *old_lh_array;
+
+	/*
+	 * allocate a new array.  we drop the rw lock on the array so that
+	 * readers can still access devices in case our memory allocation
+	 * blocks.
+	 */
+	new_lh_count = MAX(lps.lps_lh_count + LP_PTY_INC, index + 1);
+	new_lh_array =
+	    kmem_zalloc(sizeof (lx_ptm_handle_t) * new_lh_count, KM_SLEEP);
+
+	/*
+	 * double check that we still actually need to increase the size
+	 * of the array
+	 */
+	rw_enter(&lps.lps_lh_rwlock, RW_WRITER);
+	if (index < lps.lps_lh_count) {
+		/* someone beat us to it so there's nothing more to do */
+		rw_exit(&lps.lps_lh_rwlock);
+		kmem_free(new_lh_array,
+		    sizeof (lx_ptm_handle_t) * new_lh_count);
+		return;
+	}
+
+	/* copy the existing data into the new array */
+	ASSERT((lps.lps_lh_count != 0) || (lps.lps_lh_array == NULL));
+	ASSERT((lps.lps_lh_count == 0) || (lps.lps_lh_array != NULL));
+	if (lps.lps_lh_count != 0) {
+		bcopy(lps.lps_lh_array, new_lh_array,
+		    sizeof (lx_ptm_handle_t) * lps.lps_lh_count);
+	}
+
+	/* save info on the old array */
+	old_lh_array = lps.lps_lh_array;
+	old_lh_count = lps.lps_lh_count;
+
+	/* install the new array */
+	lps.lps_lh_array = new_lh_array;
+	lps.lps_lh_count = new_lh_count;
+
+	rw_exit(&lps.lps_lh_rwlock);
+
+	/* free the old array */
+	if (old_lh_array != NULL) {
+		kmem_free(old_lh_array,
+		    sizeof (lx_ptm_handle_t) * old_lh_count);
+	}
+}
+
+static void
+lx_ptm_lh_insert(uint_t index, ldi_handle_t lh)
+{
+	lx_ptm_ops_t *lpo;
+
+	ASSERT(lh != NULL);
+
+	/* Allocate and initialize the ops structure */
+	lpo = kmem_zalloc(sizeof (lx_ptm_ops_t), KM_SLEEP);
+	mutex_init(&lpo->lpo_rops_lock, NULL, MUTEX_DEFAULT, NULL);
+	cv_init(&lpo->lpo_rops_cv, NULL, CV_DEFAULT, NULL);
+
+	rw_enter(&lps.lps_lh_rwlock, RW_WRITER);
+
+	/* check if we need to grow the size of the layered handle array */
+	if (index >= lps.lps_lh_count) {
+		rw_exit(&lps.lps_lh_rwlock);
+		lx_ptm_lh_grow(index);
+		rw_enter(&lps.lps_lh_rwlock, RW_WRITER);
+	}
+
+	ASSERT(index < lps.lps_lh_count);
+	ASSERT(lps.lps_lh_array[index].lph_handle == NULL);
+	ASSERT(lps.lps_lh_array[index].lph_pktio == 0);
+	ASSERT(lps.lps_lh_array[index].lph_eofed == 0);
+	ASSERT(lps.lps_lh_array[index].lph_lpo == NULL);
+
+	/* insert the new handle and return */
+	lps.lps_lh_array[index].lph_handle = lh;
+	lps.lps_lh_array[index].lph_pktio = 0;
+	lps.lps_lh_array[index].lph_eofed = 0;
+	lps.lps_lh_array[index].lph_lpo = lpo;
+
+	rw_exit(&lps.lps_lh_rwlock);
+}
+
+static ldi_handle_t
+lx_ptm_lh_remove(uint_t index)
+{
+	ldi_handle_t	lh;
+
+	rw_enter(&lps.lps_lh_rwlock, RW_WRITER);
+
+	ASSERT(index < lps.lps_lh_count);
+	ASSERT(lps.lps_lh_array[index].lph_handle != NULL);
+	ASSERT(lps.lps_lh_array[index].lph_lpo->lpo_rops == 0);
+	ASSERT(!MUTEX_HELD(&lps.lps_lh_array[index].lph_lpo->lpo_rops_lock));
+
+	/* free the write handle */
+	kmem_free(lps.lps_lh_array[index].lph_lpo, sizeof (lx_ptm_ops_t));
+	lps.lps_lh_array[index].lph_lpo = NULL;
+
+	/* remove the handle and return it */
+	lh = lps.lps_lh_array[index].lph_handle;
+	lps.lps_lh_array[index].lph_handle = NULL;
+	lps.lps_lh_array[index].lph_pktio = 0;
+	lps.lps_lh_array[index].lph_eofed = 0;
+	rw_exit(&lps.lps_lh_rwlock);
+	return (lh);
+}
+
+static void
+lx_ptm_lh_get_ppocb(uint_t index, ptmptsopencb_t *ppocb)
+{
+	rw_enter(&lps.lps_lh_rwlock, RW_WRITER);
+
+	ASSERT(index < lps.lps_lh_count);
+	ASSERT(lps.lps_lh_array[index].lph_handle != NULL);
+
+	*ppocb = lps.lps_lh_array[index].lph_ppocb;
+	rw_exit(&lps.lps_lh_rwlock);
+}
+
+static void
+lx_ptm_lh_set_ppocb(uint_t index, ptmptsopencb_t *ppocb)
+{
+	rw_enter(&lps.lps_lh_rwlock, RW_WRITER);
+
+	ASSERT(index < lps.lps_lh_count);
+	ASSERT(lps.lps_lh_array[index].lph_handle != NULL);
+
+	lps.lps_lh_array[index].lph_ppocb = *ppocb;
+	rw_exit(&lps.lps_lh_rwlock);
+}
+
+static ldi_handle_t
+lx_ptm_lh_lookup(uint_t index)
+{
+	ldi_handle_t	lh;
+
+	rw_enter(&lps.lps_lh_rwlock, RW_READER);
+
+	ASSERT(index < lps.lps_lh_count);
+	ASSERT(lps.lps_lh_array[index].lph_handle != NULL);
+
+	/* return the handle */
+	lh = lps.lps_lh_array[index].lph_handle;
+	rw_exit(&lps.lps_lh_rwlock);
+	return (lh);
+}
+
+static lx_ptm_ops_t *
+lx_ptm_lpo_lookup(uint_t index)
+{
+	lx_ptm_ops_t	*lpo;
+
+	rw_enter(&lps.lps_lh_rwlock, RW_READER);
+
+	ASSERT(index < lps.lps_lh_count);
+	ASSERT(lps.lps_lh_array[index].lph_lpo != NULL);
+
+	/* return the handle */
+	lpo = lps.lps_lh_array[index].lph_lpo;
+	rw_exit(&lps.lps_lh_rwlock);
+	return (lpo);
+}
+
+static int
+lx_ptm_lh_pktio_get(uint_t index)
+{
+	int		pktio;
+
+	rw_enter(&lps.lps_lh_rwlock, RW_READER);
+
+	ASSERT(index < lps.lps_lh_count);
+	ASSERT(lps.lps_lh_array[index].lph_handle != NULL);
+
+	/* return the pktio state */
+	pktio = lps.lps_lh_array[index].lph_pktio;
+	rw_exit(&lps.lps_lh_rwlock);
+	return (pktio);
+}
+
+static void
+lx_ptm_lh_pktio_set(uint_t index, int pktio)
+{
+	rw_enter(&lps.lps_lh_rwlock, RW_WRITER);
+
+	ASSERT(index < lps.lps_lh_count);
+	ASSERT(lps.lps_lh_array[index].lph_handle != NULL);
+
+	/* set the pktio state */
+	lps.lps_lh_array[index].lph_pktio = pktio;
+	rw_exit(&lps.lps_lh_rwlock);
+}
+
+static int
+lx_ptm_lh_eofed_get(uint_t index)
+{
+	int		eofed;
+
+	rw_enter(&lps.lps_lh_rwlock, RW_READER);
+
+	ASSERT(index < lps.lps_lh_count);
+	ASSERT(lps.lps_lh_array[index].lph_handle != NULL);
+
+	/* return the eofed state */
+	eofed = lps.lps_lh_array[index].lph_eofed;
+	rw_exit(&lps.lps_lh_rwlock);
+	return (eofed);
+}
+
+static void
+lx_ptm_lh_eofed_set(uint_t index)
+{
+	rw_enter(&lps.lps_lh_rwlock, RW_WRITER);
+
+	ASSERT(index < lps.lps_lh_count);
+	ASSERT(lps.lps_lh_array[index].lph_handle != NULL);
+
+	/* set the eofed state */
+	lps.lps_lh_array[index].lph_eofed++;
+	rw_exit(&lps.lps_lh_rwlock);
+}
+
+static int
+lx_ptm_read_start(dev_t dev)
+{
+	lx_ptm_ops_t	*lpo = lx_ptm_lpo_lookup(DEVT_TO_INDEX(dev));
+
+	mutex_enter(&lpo->lpo_rops_lock);
+	ASSERT(lpo->lpo_rops >= 0);
+
+	/* Wait for other read operations to finish */
+	while (lpo->lpo_rops != 0) {
+		if (cv_wait_sig(&lpo->lpo_rops_cv, &lpo->lpo_rops_lock) == 0) {
+			mutex_exit(&lpo->lpo_rops_lock);
+			return (-1);
+		}
+	}
+
+	/* Start a read operation */
+	VERIFY(++lpo->lpo_rops == 1);
+	mutex_exit(&lpo->lpo_rops_lock);
+	return (0);
+}
+
+static void
+lx_ptm_read_end(dev_t dev)
+{
+	lx_ptm_ops_t	*lpo = lx_ptm_lpo_lookup(DEVT_TO_INDEX(dev));
+
+	mutex_enter(&lpo->lpo_rops_lock);
+	ASSERT(lpo->lpo_rops >= 0);
+
+	/* End a read operation */
+	VERIFY(--lpo->lpo_rops == 0);
+	cv_signal(&lpo->lpo_rops_cv);
+
+	mutex_exit(&lpo->lpo_rops_lock);
+}
+
+static int
+lx_ptm_pts_isopen(dev_t dev)
+{
+	ptmptsopencb_t	ppocb;
+
+	lx_ptm_lh_get_ppocb(DEVT_TO_INDEX(dev), &ppocb);
+	return (ppocb.ppocb_func(ppocb.ppocb_arg));
+}
+
+static void
+lx_ptm_eof_read(ldi_handle_t lh)
+{
+	struct uio	uio;
+	iovec_t		iov;
+	char		junk[1];
+
+	/*
+	 * We can remove any EOF message from the head of the stream by
+	 * doing a zero byte read from the stream.
+	 */
+	iov.iov_len = 0;
+	iov.iov_base = junk;
+	uio.uio_iovcnt = 1;
+	uio.uio_iov = &iov;
+	uio.uio_resid = iov.iov_len;
+	uio.uio_offset = 0;
+	uio.uio_segflg = UIO_SYSSPACE;
+	uio.uio_fmode = 0;
+	uio.uio_extflg = 0;
+	uio.uio_llimit = MAXOFFSET_T;
+	(void) ldi_read(lh, &uio, kcred);
+}
+
+static int
+lx_ptm_eof_drop_1(dev_t dev, int *rvalp)
+{
+	ldi_handle_t	lh = lx_ptm_lh_lookup(DEVT_TO_INDEX(dev));
+	int		err, msg_size, msg_count;
+
+	*rvalp = 0;
+
+	/*
+	 * Check if there is an EOF message (represented by a zero length
+	 * data message) at the head of the stream.  Note that the
+	 * I_NREAD ioctl is a streams framework ioctl so it will succeed
+	 * even if there have been previous write errors on this stream.
+	 */
+	if ((err = ldi_ioctl(lh, I_NREAD, (intptr_t)&msg_size,
+	    FKIOCTL, kcred, &msg_count)) != 0)
+		return (err);
+
+	if ((msg_count == 0) || (msg_size != 0)) {
+		/* No EOF message found */
+		return (0);
+	}
+
+	/* Record the fact that the slave device has been closed. */
+	lx_ptm_lh_eofed_set(DEVT_TO_INDEX(dev));
+
+	/* drop the EOF */
+	lx_ptm_eof_read(lh);
+	*rvalp = 1;
+	return (0);
+}
+
+static int
+lx_ptm_eof_drop(dev_t dev, int *rvalp)
+{
+	int rval, err;
+
+	if (rvalp != NULL)
+		*rvalp = 0;
+	for (;;) {
+		if ((err = lx_ptm_eof_drop_1(dev, &rval)) != 0)
+			return (err);
+		if (rval == 0)
+			return (0);
+		if (rvalp != NULL)
+			*rvalp = 1;
+	}
+}
+
+static int
+lx_ptm_data_check(dev_t dev, int ignore_eof, int *rvalp)
+{
+	ldi_handle_t	lh = lx_ptm_lh_lookup(DEVT_TO_INDEX(dev));
+	int		err;
+
+	*rvalp = 0;
+	if (ignore_eof) {
+		int	size, rval;
+
+		if ((err = ldi_ioctl(lh, FIONREAD, (intptr_t)&size,
+		    FKIOCTL, kcred, &rval)) != 0)
+			return (err);
+		if (size != 0)
+			*rvalp = 1;
+	} else {
+		int	msg_size, msg_count;
+
+		if ((err = ldi_ioctl(lh, I_NREAD, (intptr_t)&msg_size,
+		    FKIOCTL, kcred, &msg_count)) != 0)
+			return (err);
+		if (msg_count != 0)
+			*rvalp = 1;
+	}
+	return (0);
+}
+
+static int
+lx_ptm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+	int err;
+
+	if (cmd != DDI_ATTACH)
+		return (DDI_FAILURE);
+
+	if (ddi_create_minor_node(dip, LX_PTM_MINOR_NODE, S_IFCHR,
+	    ddi_get_instance(dip), DDI_PSEUDO, 0) != DDI_SUCCESS)
+		return (DDI_FAILURE);
+
+	err = ldi_ident_from_dip(dip, &lps.lps_li);
+	if (err != 0) {
+		ddi_remove_minor_node(dip, ddi_get_name(dip));
+		return (DDI_FAILURE);
+	}
+
+	lps.lps_dip = dip;
+	lps.lps_pts_major = ddi_name_to_major(LP_PTS_DRV_NAME);
+
+	rw_init(&lps.lps_lh_rwlock, NULL, RW_DRIVER, NULL);
+	lps.lps_lh_count = 0;
+	lps.lps_lh_array = NULL;
+
+	return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+lx_ptm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+	if (cmd != DDI_DETACH)
+		return (DDI_FAILURE);
+
+	ldi_ident_release(lps.lps_li);
+	lps.lps_dip = NULL;
+
+	ASSERT((lps.lps_lh_count != 0) || (lps.lps_lh_array == NULL));
+	ASSERT((lps.lps_lh_count == 0) || (lps.lps_lh_array != NULL));
+	if (lps.lps_lh_array != NULL) {
+		kmem_free(lps.lps_lh_array,
+		    sizeof (lx_ptm_handle_t) * lps.lps_lh_count);
+		lps.lps_lh_array = NULL;
+		lps.lps_lh_count = 0;
+	}
+
+	return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+lx_ptm_open(dev_t *devp, int flag, int otyp, cred_t *credp)
+{
+	struct strioctl	iocb;
+	ptmptsopencb_t	ppocb = { NULL, NULL };
+	ldi_handle_t	lh;
+	major_t		maj, our_major = getmajor(*devp);
+	minor_t		min, lastmin;
+	uint_t		index, anchor = 1;
+	dev_t		ptm_dev;
+	int		err, rval = 0;
+
+	/*
+	 * Don't support the FNDELAY flag and FNONBLOCK until we either
+	 * find a Linux app that opens /dev/ptmx with the O_NDELAY
+	 * or O_NONBLOCK flags explicitly, or until we create test cases
+	 * to determine how reads of master terminal devices opened with
+	 * these flags behave in different situations on Linux.  Supporting
+	 * these flags will involve enhancing our read implementation
+	 * and changing the way it deals with EOF notifications.
+	 */
+	if (flag & (FNDELAY | FNONBLOCK))
+		return (ENOTSUP);
+
+	/*
+	 * we're layered on top of the ptm driver so open that driver
+	 * first.  (note that we're opening /dev/ptmx in the global
+	 * zone, not ourselves in the lx zone.)
+	 */
+	err = ldi_open_by_name(LP_PTM_PATH, flag, credp, &lh, lps.lps_li);
+	if (err != 0)
+		return (err);
+
+	/* get the devt returned by the ptmx open */
+	err = ldi_get_dev(lh, &ptm_dev);
+	if (err != 0) {
+		(void) ldi_close(lh, flag, credp);
+		return (err);
+	}
+
+	/*
+	 * we're a cloning driver so here's where we'll change the devt that we
+	 * return.  the ptmx is also a cloning driver so we'll just use
+	 * it's minor number as our minor number (it already manages it's
+	 * minor name space so no reason to duplicate the effort.)
+	 */
+	index = getminor(ptm_dev);
+	*devp = makedevice(our_major, INDEX_TO_MINOR(index));
+
+	/* Get a callback function to query if the pts device is open. */
+	iocb.ic_cmd = PTMPTSOPENCB;
+	iocb.ic_timout = 0;
+	iocb.ic_len = sizeof (ppocb);
+	iocb.ic_dp = (char *)&ppocb;
+
+	err = ldi_ioctl(lh, I_STR, (intptr_t)&iocb, FKIOCTL, kcred, &rval);
+	if ((err != 0) || (rval != 0)) {
+		(void) ldi_close(lh, flag, credp);
+		return (EIO); /* XXX return something else here? */
+	}
+	ASSERT(ppocb.ppocb_func != NULL);
+
+	/*
+	 * now setup autopush for the terminal slave device.  this is
+	 * necessary so that when a Linux program opens the device we
+	 * can push required strmod modules onto the stream.  in Solaris
+	 * this is normally done by the application that actually
+	 * allocates the terminal.
+	 */
+	maj = lps.lps_pts_major;
+	min = index;
+	lastmin = 0;
+	err = kstr_autopush(SET_AUTOPUSH, &maj, &min, &lastmin,
+	    &anchor, lx_pts_mods);
+	if (err != 0 && err != EEXIST) {
+		(void) ldi_close(lh, flag, credp);
+		return (EIO); /* XXX return something else here? */
+	}
+
+	/* save off this layered handle for future accesses */
+	lx_ptm_lh_insert(index, lh);
+	lx_ptm_lh_set_ppocb(index, &ppocb);
+	return (0);
+}
+
+/*ARGSUSED*/
+static int
+lx_ptm_close(dev_t dev, int flag, int otyp, cred_t *credp)
+{
+	ldi_handle_t	lh;
+	major_t		maj;
+	minor_t		min, lastmin;
+	uint_t		index;
+	int		err;
+	int		i;
+
+	index = DEVT_TO_INDEX(dev);
+
+	/*
+	 * we must cleanup all the state associated with this major/minor
+	 * terminal pair before actually closing the ptm master device.
+	 * this is required because once the close of the ptm device is
+	 * complete major/minor terminal pair is immediatly available for
+	 * re-use in any zone.
+	 */
+
+	/* free up our saved reference for this layered handle */
+	lh = lx_ptm_lh_remove(index);
+
+	/* unconfigure autopush for the associated terminal slave device */
+	maj = lps.lps_pts_major;
+	min = index;
+	lastmin = 0;
+	for (i = 0; i < 5; i++) {
+		/*
+		 * we loop here because we don't want to release this ptm
+		 * node if autopush can't be disabled on the associated
+		 * slave device because then bad things could happen if
+		 * another brand were to get this terminal allocated
+		 * to them. If we keep failing we eventually drive on so that
+		 * things don't hang.
+		 */
+		err = kstr_autopush(CLR_AUTOPUSH, &maj, &min, &lastmin,
+		    0, NULL);
+		if (err == 0)
+			break;
+
+		cmn_err(CE_WARN, "lx zoneid %d: error %d on kstr_autopush",
+		    getzoneid(), err);
+
+		/* wait one second and try again */
+		delay(drv_usectohz(1000000));
+	}
+
+	err = ldi_close(lh, flag, credp);
+
+	/*
+	 * note that we don't have to bother with changing the permissions
+	 * on the associated slave device here.  the reason is that no one
+	 * can actually open the device untill it's associated master
+	 * device is re-opened, which will result in the permissions on
+	 * it being reset.
+	 */
+	return (err);
+}
+
+static int
+lx_ptm_read_loop(dev_t dev, struct uio *uiop, cred_t *credp, int *loop)
+{
+	ldi_handle_t	lh = lx_ptm_lh_lookup(DEVT_TO_INDEX(dev));
+	int		err, rval;
+	struct uio	uio = *uiop;
+
+	*loop = 0;
+
+	/*
+	 * Here's another way that Linux master terminals behave differently
+	 * from Solaris master terminals.  If you do a read on a Linux
+	 * master terminal (that was opened witout NDELAY and NONBLOCK)
+	 * who's corrosponding slave terminal is currently closed and
+	 * has been opened and closed at least once, Linux return -1 and
+	 * set errno to EIO where as Solaris blocks.
+	 */
+	if (lx_ptm_lh_eofed_get(DEVT_TO_INDEX(dev))) {
+		/* Slave has been opened and closed at least once. */
+		if (lx_ptm_pts_isopen(dev) == 0) {
+			/*
+			 * Slave is closed.  Make sure that data is avaliable
+			 * before attempting a read.
+			 */
+			if ((err = lx_ptm_data_check(dev, 0, &rval)) != 0)
+				return (err);
+
+			/* If there is no data available then return. */
+			if (rval == 0)
+				return (EIO);
+		}
+	}
+
+	/* Actually do the read operation. */
+	if ((err = ldi_read(lh, uiop, credp)) != 0)
+		return (err);
+
+	/* If read returned actual data then return. */
+	if (uio.uio_resid != uiop->uio_resid)
+		return (0);
+
+	/*
+	 * This was a zero byte read (ie, an EOF).  This indicates
+	 * that the slave terinal device has been closed.  Record
+	 * the fact that the slave device has been closed and retry
+	 * the read operation.
+	 */
+	lx_ptm_lh_eofed_set(DEVT_TO_INDEX(dev));
+	*loop = 1;
+	return (0);
+}
+
+static int
+lx_ptm_read(dev_t dev, struct uio *uiop, cred_t *credp)
+{
+	int		pktio = lx_ptm_lh_pktio_get(DEVT_TO_INDEX(dev));
+	int		err, loop;
+	struct uio	uio;
+	struct iovec	iovp;
+
+	ASSERT(uiop->uio_iovcnt > 0);
+
+	/*
+	 * If packet mode has been enabled (via TIOCPKT) we need to pad
+	 * all read requests with a leading byte that indicates any
+	 * relevant control status information.
+	 */
+	if (pktio != 0) {
+		/*
+		 * We'd like to write the control information into
+		 * the current buffer but we can't yet.  We don't
+		 * want to modify userspace memory here only to have
+		 * the read operation fail later.  So instead
+		 * what we'll do here is read one character from the
+		 * beginning of the memory pointed to by the uio
+		 * structure.  This will advance the output pointer
+		 * by one.  Then when the read completes successfully
+		 * we can update the byte that we passed over.  Before
+		 * we do the read make a copy of the current uiop and
+		 * iovec structs so we can write to them later.
+		 */
+		uio = *uiop;
+		iovp = *uiop->uio_iov;
+		uio.uio_iov = &iovp;
+
+		if (uwritec(uiop) == -1)
+			return (EFAULT);
+	}
+
+	do {
+		/*
+		 * Before we actually attempt a read operation we need
+		 * to make sure there's some buffer space to actually
+		 * read in some data.  We do this because if we're in
+		 * pktio mode and the caller only requested one byte,
+		 * then we've already used up that one byte and we
+		 * don't want to pass this read request.  Doing a 0
+		 * byte read (unless there is a problem with the stream
+		 * head) always returns succcess.  Normally when a streams
+		 * read returns 0 bytes we interpret that as an EOF on
+		 * the stream (ie, the slave side has been opened and
+		 * closed) and we ignore it and re-try the read operation.
+		 * So if we pass on a 0 byte read here lx_ptm_read_loop()
+		 * will tell us to loop around and we'll end up in an
+		 * infinite loop.
+		 */
+		if (uiop->uio_resid == 0)
+			break;
+
+		/*
+		 * Serialize all reads.  We need to do this so that we can
+		 * properly emulate the behavior of master terminals on Linux.
+		 * In reality this serializaion should not pose any kind of
+		 * performance problem since it would be very strange to have
+		 * multiple threads trying to read from the same master
+		 * terminal device concurrently.
+		 */
+		if (lx_ptm_read_start(dev) != 0)
+			return (EINTR);
+
+		err = lx_ptm_read_loop(dev, uiop, credp, &loop);
+		lx_ptm_read_end(dev);
+		if (err != 0)
+			return (err);
+	} while (loop != 0);
+
+	if (pktio != 0) {
+		uint8_t		pktio_data = TIOCPKT_DATA;
+
+		/*
+		 * Note that the control status information we
+		 * pass back is faked up in the sense that we
+		 * don't actually report any events, we always
+		 * report a status of 0.
+		 */
+		if (uiomove(&pktio_data, 1, UIO_READ, &uio) != 0)
+			return (EFAULT);
+	}
+
+	return (0);
+}
+
+static int
+lx_ptm_write(dev_t dev, struct uio *uiop, cred_t *credp)
+{
+	ldi_handle_t		lh = lx_ptm_lh_lookup(DEVT_TO_INDEX(dev));
+	int		err;
+
+	err = ldi_write(lh, uiop, credp);
+
+	return (err);
+}
+
+static int
+lx_ptm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
+    int *rvalp)
+{
+	ldi_handle_t	lh = lx_ptm_lh_lookup(DEVT_TO_INDEX(dev));
+	int		err;
+
+	/*
+	 * here we need to make sure that we never allow the
+	 * I_SETSIG and I_ESETSIG ioctls to pass through.  we
+	 * do this because we can't support them.
+	 *
+	 * the native Solaris ptm device supports these ioctls because
+	 * they are streams framework ioctls and all streams devices
+	 * support them by default.  these ioctls cause the current
+	 * process to be registered with a stream and receive signals
+	 * when certain stream events occur.
+	 *
+	 * a problem arises with cleanup of these registrations
+	 * for layered drivers.
+	 *
+	 * normally the streams framework is notified whenever a
+	 * process closes any reference to a stream and it goes ahead
+	 * and cleans up these registrations.  but actual device drivers
+	 * are not notified when a process performs a close operation
+	 * unless the process is closing the last opened reference to
+	 * the device on the entire system.
+	 *
+	 * so while we could pass these ioctls on and allow processes
+	 * to register for signal delivery, we would never receive
+	 * any notification when those processes exit (or close a
+	 * stream) and we wouldn't be able to unregister them.
+	 *
+	 * luckily these operations are streams specific and Linux
+	 * doesn't support streams devices.  so it doesn't actually
+	 * seem like we need to support these ioctls.  if it turns
+	 * out that we do need to support them for some reason in
+	 * the future, the current driver model will have to be
+	 * enhanced to better support streams device layering.
+	 */
+	if ((cmd == I_SETSIG) || (cmd == I_ESETSIG))
+		return (EINVAL);
+
+	/*
+	 * here we fake up support for TIOCPKT.  Linux applications expect
+	 * /etc/ptmx to support this ioctl, but on Solaris it doesn't.
+	 * (it is supported on older bsd style ptys.)  so we'll fake
+	 * up support for it here.
+	 *
+	 * the reason that this ioctl is emulated here instead of in
+	 * userland is that this ioctl affects the results returned
+	 * from read() operations.  if this ioctl was emulated in
+	 * userland the brand library would need to intercept all
+	 * read operations and check to see if pktio was enabled
+	 * for the fd being read from.  since this ioctl only needs
+	 * to be supported on the ptmx device it makes more sense
+	 * to support it here where we can easily update the results
+	 * returned for read() operations performed on ourselves.
+	 */
+	if (cmd == TIOCPKT) {
+		int	pktio;
+
+		if (ddi_copyin((void *)arg, &pktio, sizeof (pktio),
+		    mode) != DDI_SUCCESS)
+			return (EFAULT);
+
+		if (pktio == 0)
+			lx_ptm_lh_pktio_set(DEVT_TO_INDEX(dev), 0);
+		else
+			lx_ptm_lh_pktio_set(DEVT_TO_INDEX(dev), 1);
+
+		return (0);
+	}
+
+	err = ldi_ioctl(lh, cmd, arg, mode, credp, rvalp);
+
+	/*
+	 * On recent versions of Linux some apps issue the following ioctls to
+	 * the master side of the ptm before opening the slave side. Because
+	 * our streams modules (specifically ptem) aren't autopushed until the
+	 * slave side has been opened, these ioctls will fail. To alleviate the
+	 * issue we simply pretend that these ioctls have succeeded.
+	 *
+	 * We could push our own "lx_ptem" module onto the master side of the
+	 * stream in lx_ptm_open if we need better emulation, but that would
+	 * require an "lx_ptem" module which duplicates most of ptem. ptem
+	 * doesn't work properly when pushed on the master side.
+	 */
+	if (err == EINVAL && (cmd == TIOCSWINSZ || cmd == TCSETS) &&
+	    lx_ptm_pts_isopen(dev) == 0) {
+		/* slave side not open, assume we need to succeed */
+		DTRACE_PROBE1(lx_ptm_ioctl__override, int, cmd);
+		return (0);
+	}
+
+	return (err);
+}
+
+static int
+lx_ptm_poll_loop(dev_t dev, short events, int anyyet, short *reventsp,
+    struct pollhead **phpp, int *loop)
+{
+	ldi_handle_t	lh = lx_ptm_lh_lookup(DEVT_TO_INDEX(dev));
+	short		reventsp2;
+	int		err, rval;
+
+	*loop = 0;
+
+	/*
+	 * If the slave device has been opened and closed at least
+	 * once and the slave device is currently closed, then poll
+	 * always needs to returns immediatly.
+	 */
+	if ((lx_ptm_lh_eofed_get(DEVT_TO_INDEX(dev)) != 0) &&
+	    (lx_ptm_pts_isopen(dev) == 0)) {
+		/* In this case always return POLLHUP */
+		*reventsp = POLLHUP;
+
+		/*
+		 * Check if there really is data on the stream.
+		 * If so set the correct return flags.
+		 */
+		if ((err = lx_ptm_data_check(dev, 1, &rval)) != 0) {
+			/* Something went wrong. */
+			return (err);
+		}
+		if (rval != 0)
+			*reventsp |= (events & (POLLIN | POLLRDNORM));
+
+		/*
+		 * Is the user checking for writability?  Note that for ptm
+		 * devices Linux seems to ignore the POLLWRBAND write flag.
+		 */
+		if ((events & POLLWRNORM) == 0)
+			return (0);
+
+		/*
+		 * To check if the stream is writable we have to actually
+		 * call poll, but make sure to set anyyet to 1 to prevent
+		 * the streams framework from setting up callbacks.
+		 */
+		if ((err = ldi_poll(lh, POLLWRNORM, 1, &reventsp2, NULL)) != 0)
+			return (err);
+
+		*reventsp |= (reventsp2 & POLLWRNORM);
+	} else {
+		int lockstate;
+
+		/* The slave device is open, do the poll */
+		if ((err = ldi_poll(lh, events, anyyet, reventsp, phpp)) != 0)
+			return (err);
+
+		/*
+		 * Drop any leading EOFs on the stream.
+		 *
+		 * Note that we have to use pollunlock() here to avoid
+		 * recursive mutex enters in the poll framework.  The
+		 * reason is that if there is an EOF message on the stream
+		 * then the act of reading from the queue to remove the
+		 * message can cause the ptm drivers event service
+		 * routine to be invoked, and if there is no open
+		 * slave device then the ptm driver may generate
+		 * error messages and put them on the stream.  This
+		 * in turn will generate a poll event and the poll
+		 * framework will try to invoke any poll callbacks
+		 * associated with the stream.  In the process of
+		 * doing that the poll framework will try to aquire
+		 * locks that we are already holding.  So we need to
+		 * drop those locks here before we do our read.
+		 */
+		if (pollunlock(&lockstate) != 0) {
+			*reventsp = POLLNVAL;
+			return (0);
+		}
+		err = lx_ptm_eof_drop(dev, &rval);
+		pollrelock(lockstate);
+		if (err)
+			return (err);
+
+		/* If no EOF was dropped then return */
+		if (rval == 0)
+			return (0);
+
+		/*
+		 * An EOF was removed from the stream.  Retry the entire
+		 * poll operation from the top because polls on the ptm
+		 * device should behave differently now.
+		 */
+		*loop = 1;
+	}
+	return (0);
+}
+
+static int
+lx_ptm_poll(dev_t dev, short events, int anyyet, short *reventsp,
+    struct pollhead **phpp)
+{
+	int loop, err;
+
+	do {
+		/* Serialize ourself wrt read operations. */
+		if (lx_ptm_read_start(dev) != 0)
+			return (EINTR);
+
+		err = lx_ptm_poll_loop(dev,
+		    events, anyyet, reventsp, phpp, &loop);
+		lx_ptm_read_end(dev);
+		if (err != 0)
+			return (err);
+	} while (loop != 0);
+	return (0);
+}
+
+static struct cb_ops lx_ptm_cb_ops = {
+	lx_ptm_open,		/* open */
+	lx_ptm_close,		/* close */
+	nodev,			/* strategy */
+	nodev,			/* print */
+	nodev,			/* dump */
+	lx_ptm_read,		/* read */
+	lx_ptm_write,		/* write */
+	lx_ptm_ioctl,		/* ioctl */
+	nodev,			/* devmap */
+	nodev,			/* mmap */
+	nodev,			/* segmap */
+	lx_ptm_poll,		/* chpoll */
+	ddi_prop_op,		/* prop_op */
+	NULL,			/* cb_str */
+	D_NEW | D_MP,
+	CB_REV,
+	NULL,
+	NULL
+};
+
+static struct dev_ops lx_ptm_ops = {
+	DEVO_REV,
+	0,
+	ddi_getinfo_1to1,
+	nulldev,
+	nulldev,
+	lx_ptm_attach,
+	lx_ptm_detach,
+	nodev,
+	&lx_ptm_cb_ops,
+	NULL,
+	NULL,
+	ddi_quiesce_not_needed,		/* quiesce */
+};
+
+static struct modldrv modldrv = {
+	&mod_driverops,			/* type of module */
+	"Linux master terminal driver",	/* description of module */
+	&lx_ptm_ops			/* driver ops */
+};
+
+static struct modlinkage modlinkage = {
+	MODREV_1,
+	&modldrv,
+	NULL
+};
+
+int
+_init(void)
+{
+	return (mod_install(&modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+	return (mod_remove(&modlinkage));
+}
--- /dev/null	Wed Oct  5 00:32:05 2016
+++ new/usr/src/uts/common/brand/lx/io/lx_ptm.conf	Wed Oct  5 00:32:05 2016
@@ -0,0 +1,27 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#ident	"%Z%%M%	%I%	%E% SMI"
+
+name="lx_ptm" parent="pseudo" instance=0;
--- /dev/null	Wed Oct  5 00:32:05 2016
+++ new/usr/src/uts/common/brand/lx/os/lx_brand.c	Wed Oct  5 00:32:05 2016
@@ -0,0 +1,2630 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright 2016, Joyent, Inc. All rights reserved.
+ */
+
+/*
+ * The LX Brand: emulation of a Linux operating environment within a zone.
+ *
+ * OVERVIEW
+ *
+ * The LX brand enables a full Linux userland -- including a C library,
+ * init(1) framework, and some set of applications -- to run unmodified
+ * within an illumos zone.  Unlike illumos, where applications are expected
+ * to link against and consume functions exported from libraries, the
+ * supported Linux binary compatibility boundary is the system call
+ * interface.  By accurately emulating the behaviour of Linux system calls,
+ * Linux software can be executed in this environment as if it were running
+ * on a native Linux system.
+ *
+ * EMULATING LINUX SYSTEM CALLS
+ *
+ * Linux system calls are made in 32-bit processes via the "int 0x80"
+ * instruction; in 64-bit processes the "syscall" instruction is used, as it
+ * is with native illumos processes.  In both cases, arguments to system
+ * calls are generally passed in registers and the usermode stack is not
+ * interpreted or modified by the Linux kernel.
+ *
+ * When the emulated Linux process makes a system call, it traps into the
+ * illumos kernel.  The in-kernel brand module contains various emulation
+ * routines, and can fully service some emulated system calls; e.g. read(2)
+ * and write(2).  Other system calls require assistance from the illumos
+ * libc, bouncing back out to the brand library ("lx_brand.so.1") for
+ * emulation.
+ *
+ * The brand mechanism allows for the provision of an alternative trap
+ * handler for the various system call mechanisms.  Traditionally this was
+ * used to immediately revector execution to the usermode emulation library,
+ * which was responsible for handling all system calls.  In the interests of
+ * more accurate emulation and increased performance, much of the regular
+ * illumos system call path is now invoked.  Only the argument processing and
+ * handler dispatch are replaced by the brand, via the per-LWP
+ * "lwp_brand_syscall" interposition function pointer.
+ *
+ * THE NATIVE AND BRAND STACKS
+ *
+ * Some runtime environments (e.g. the Go language) allocate very small
+ * thread stacks, preferring to grow or split the stack as necessary.  The
+ * Linux kernel generally does not use the usermode stack when servicing
+ * system calls, so this is not a problem.  In order for our emulation to
+ * have the same zero stack impact, we must execute usermode emulation
+ * routines on an _alternate_ stack.  This is similar, in principle, to the
+ * use of sigaltstack(3C) to run signal handlers off the main thread stack.
+ *
+ * To this end, the brand library allocates and installs an alternate stack
+ * (called the "native" stack) for each LWP.  The in-kernel brand code uses
+ * this stack for usermode emulation calls and interposed signal delivery,
+ * while the emulated Linux process sees only the data on the main thread
+ * stack, known as the "brand" stack.  The stack mode is tracked in the
+ * per-LWP brand-private data, using the LX_STACK_MODE_* enum.
+ *
+ * The stack mode doubles as a system call "mode bit".  When in the
+ * LX_STACK_MODE_BRAND mode, system calls are processed as emulated Linux
+ * system calls.  In other modes, system calls are assumed to be native
+ * illumos system calls as made during brand library initialisation and
+ * usermode emulation.
+ *
+ * USERMODE EMULATION
+ *
+ * When a Linux system call cannot be emulated within the kernel, we preserve
+ * the register state of the Linux process and revector the LWP to the brand
+ * library usermode emulation handler: the "lx_emulate()" function in
+ * "lx_brand.so.1".  This revectoring is modelled on the delivery of signals,
+ * and is performed in "lx_emulate_user()".
+ *
+ * First, the emulated process state is written out to the usermode stack of
+ * the process as a "ucontext_t" object.  Arguments to the emulation routine
+ * are passed on the stack or in registers, depending on the ABI.  When the
+ * usermode emulation is complete, the result is passed back to the kernel
+ * (via the "B_EMULATION_DONE" brandsys subcommand) with the saved context
+ * for restoration.
+ *
+ * SIGNAL DELIVERY, SETCONTEXT AND GETCONTEXT
+ *
+ * When servicing emulated system calls in the usermode brand library, or
+ * during signal delivery, various state is preserved by the kernel so that
+ * the running LWP may be revectored to a handling routine.  The context
+ * allows the kernel to restart the program at the point of interruption,
+ * either at the return of the signal handler, via setcontext(3C); or after
+ * the usermode emulation request has been serviced, via B_EMULATION_DONE.
+ *
+ * In illumos native processes, the saved context (a "ucontext_t" object)
+ * includes the state of registers and the current signal mask at the point
+ * of interruption.  The context also includes a link to the most recently
+ * saved context, forming a chain to be unwound as requests complete.  The LX
+ * brand requires additional book-keeping to describe the machine state: in
+ * particular, the current stack mode and the occupied extent of the native
+ * stack.
+ *
+ * The brand code is able to interpose on the context save and restore
+ * operations in the kernel -- see "lx_savecontext()" and
+ * "lx_restorecontext()" -- to enable getcontext(3C) and setcontext(3C) to
+ * function correctly in the face of a dual stack LWP.  The brand also
+ * interposes on the signal delivery mechanism -- see "lx_sendsig()" and
+ * "lx_sendsig_stack()" -- to allow all signals to be delivered to the brand
+ * library interposer on the native stack, regardless of the interrupted
+ * execution mode.  Linux sigaltstack(2) emulation is performed entirely by
+ * the usermode brand library during signal handler interposition.
+ */
+
+#include <sys/types.h>
+#include <sys/kmem.h>
+#include <sys/errno.h>
+#include <sys/thread.h>
+#include <sys/systm.h>
+#include <sys/syscall.h>
+#include <sys/proc.h>
+#include <sys/modctl.h>
+#include <sys/cmn_err.h>
+#include <sys/model.h>
+#include <sys/exec.h>
+#include <sys/lx_impl.h>
+#include <sys/machbrand.h>
+#include <sys/lx_syscalls.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_futex.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_types.h>
+#include <sys/param.h>
+#include <sys/termios.h>
+#include <sys/sunddi.h>
+#include <sys/ddi.h>
+#include <sys/vnode.h>
+#include <sys/pathname.h>
+#include <sys/auxv.h>
+#include <sys/priv.h>
+#include <sys/regset.h>
+#include <sys/privregs.h>
+#include <sys/archsystm.h>
+#include <sys/zone.h>
+#include <sys/brand.h>
+#include <sys/sdt.h>
+#include <sys/x86_archext.h>
+#include <sys/controlregs.h>
+#include <sys/core.h>
+#include <sys/stack.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <lx_signum.h>
+#include <util/sscanf.h>
+#include <sys/lx_brand.h>
+#include <sys/zfs_ioctl.h>
+#include <inet/tcp_impl.h>
+#include <inet/udp_impl.h>
+
+int	lx_debug = 0;
+
+void	lx_init_brand_data(zone_t *, kmutex_t *);
+void	lx_free_brand_data(zone_t *);
+void	lx_setbrand(proc_t *);
+int	lx_getattr(zone_t *, int, void *, size_t *);
+int	lx_setattr(zone_t *, int, void *, size_t);
+int	lx_brandsys(int, int64_t *, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+void	lx_set_kern_version(zone_t *, char *);
+void	lx_copy_procdata(proc_t *, proc_t *);
+
+extern int getsetcontext(int, void *);
+extern int waitsys(idtype_t, id_t, siginfo_t *, int);
+#if defined(_SYSCALL32_IMPL)
+extern int getsetcontext32(int, void *);
+extern int waitsys32(idtype_t, id_t, siginfo_t *, int);
+#endif
+
+extern int zvol_name2minor(const char *, minor_t *);
+extern int zvol_create_minor(const char *);
+
+extern void lx_proc_exit(proc_t *);
+extern int lx_sched_affinity(int, uintptr_t, int, uintptr_t, int64_t *);
+
+extern void lx_ioctl_init();
+extern void lx_ioctl_fini();
+extern void lx_socket_init();
+extern void lx_socket_fini();
+
+lx_systrace_f *lx_systrace_entry_ptr;
+lx_systrace_f *lx_systrace_return_ptr;
+
+static int lx_systrace_enabled;
+
+/*
+ * cgroup file system maintenance functions which are set when cgroups loads.
+ */
+void (*lx_cgrp_initlwp)(vfs_t *, uint_t, id_t, pid_t);
+void (*lx_cgrp_freelwp)(vfs_t *, uint_t, id_t, pid_t);
+
+/*
+ * While this is effectively mmu.hole_start - PAGESIZE, we don't particularly
+ * want an MMU dependency here (and should there be a microprocessor without
+ * a hole, we don't want to start allocating from the top of the VA range).
+ */
+#define	LX_MAXSTACK64	0x7ffffff00000
+
+uint64_t lx_maxstack64 = LX_MAXSTACK64;
+
+static int lx_elfexec(struct vnode *vp, struct execa *uap, struct uarg *args,
+    struct intpdata *idata, int level, long *execsz, int setid,
+    caddr_t exec_file, struct cred *cred, int *brand_action);
+
+static boolean_t lx_native_exec(uint8_t, const char **);
+static uint32_t lx_map32limit(proc_t *);
+
+static void lx_savecontext(ucontext_t *);
+static void lx_restorecontext(ucontext_t *);
+static caddr_t lx_sendsig_stack(int);
+static void lx_sendsig(int);
+#if defined(_SYSCALL32_IMPL)
+static void lx_savecontext32(ucontext32_t *);
+#endif
+static int lx_setid_clear(vattr_t *, cred_t *);
+#if defined(_LP64)
+static int lx_pagefault(proc_t *, klwp_t *, caddr_t, enum fault_type,
+    enum seg_rw);
+#endif
+
+typedef struct lx_zfs_ds {
+	list_node_t	ds_link;
+	char		ds_name[MAXPATHLEN];
+	uint64_t	ds_cookie;
+} lx_zfs_ds_t;
+
+/* lx brand */
+struct brand_ops lx_brops = {
+	lx_init_brand_data,		/* b_init_brand_data */
+	lx_free_brand_data,		/* b_free_brand_data */
+	lx_brandsys,			/* b_brandsys */
+	lx_setbrand,			/* b_setbrand */
+	lx_getattr,			/* b_getattr */
+	lx_setattr,			/* b_setattr */
+	lx_copy_procdata,		/* b_copy_procdata */
+	lx_proc_exit,			/* b_proc_exit */
+	lx_exec,			/* b_exec */
+	lx_setrval,			/* b_lwp_setrval */
+	lx_lwpdata_alloc,		/* b_lwpdata_alloc */
+	lx_lwpdata_free,		/* b_lwpdata_free */
+	lx_initlwp,			/* b_initlwp */
+	lx_initlwp_post,		/* b_initlwp_post */
+	lx_forklwp,			/* b_forklwp */
+	lx_freelwp,			/* b_freelwp */
+	lx_exitlwp,			/* b_lwpexit */
+	lx_elfexec,			/* b_elfexec */
+	NULL,				/* b_sigset_native_to_brand */
+	NULL,				/* b_sigset_brand_to_native */
+	lx_sigfd_translate,		/* b_sigfd_translate */
+	NSIG,				/* b_nsig */
+	lx_exit_with_sig,		/* b_exit_with_sig */
+	lx_wait_filter,			/* b_wait_filter */
+	lx_native_exec,			/* b_native_exec */
+	lx_map32limit,			/* b_map32limit */
+	lx_stop_notify,			/* b_stop_notify */
+	lx_waitid_helper,		/* b_waitid_helper */
+	lx_sigcld_repost,		/* b_sigcld_repost */
+	lx_ptrace_issig_stop,		/* b_issig_stop */
+	lx_ptrace_sig_ignorable,	/* b_sig_ignorable */
+	lx_savecontext,			/* b_savecontext */
+#if defined(_SYSCALL32_IMPL)
+	lx_savecontext32,		/* b_savecontext32 */
+#endif
+	lx_restorecontext,		/* b_restorecontext */
+	lx_sendsig_stack,		/* b_sendsig_stack */
+	lx_sendsig,			/* b_sendsig */
+	lx_setid_clear,			/* b_setid_clear */
+#if defined(_LP64)
+	lx_pagefault,			/* b_pagefault */
+#else
+	NULL,
+#endif
+	B_FALSE				/* b_intp_parse_arg */
+};
+
+struct brand_mach_ops lx_mops = {
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+	lx_fixsegreg,
+	lx_fsbase
+};
+
+struct brand lx_brand = {
+	BRAND_VER_1,
+	"lx",
+	&lx_brops,
+	&lx_mops,
+	sizeof (struct lx_proc_data)
+};
+
+static struct modlbrand modlbrand = {
+	&mod_brandops, "lx brand", &lx_brand
+};
+
+static struct modlinkage modlinkage = {
+	MODREV_1, (void *)&modlbrand, NULL
+};
+
+void
+lx_proc_exit(proc_t *p)
+{
+	lx_proc_data_t *lxpd;
+	proc_t *cp;
+
+	mutex_enter(&p->p_lock);
+	VERIFY((lxpd = ptolxproc(p)) != NULL);
+	VERIFY(lxpd->l_ptrace == 0);
+	if ((lxpd->l_flags & LX_PROC_CHILD_DEATHSIG) == 0) {
+		mutex_exit(&p->p_lock);
+		return;
+	}
+	mutex_exit(&p->p_lock);
+
+	/* Check for children which desire notification of parental death. */
+	mutex_enter(&pidlock);
+	for (cp = p->p_child; cp != NULL; cp = cp->p_sibling) {
+		mutex_enter(&cp->p_lock);
+		if ((lxpd = ptolxproc(cp)) == NULL) {
+			mutex_exit(&cp->p_lock);
+			continue;
+		}
+		if (lxpd->l_parent_deathsig != 0) {
+			sigtoproc(cp, NULL, lxpd->l_parent_deathsig);
+		}
+		mutex_exit(&cp->p_lock);
+	}
+	mutex_exit(&pidlock);
+}
+
+void
+lx_setbrand(proc_t *p)
+{
+	/* Send SIGCHLD to parent by default when child exits */
+	ptolxproc(p)->l_signal = stol_signo[SIGCHLD];
+
+	lx_read_argv_bounds(p);
+}
+
+/* ARGSUSED */
+int
+lx_setattr(zone_t *zone, int attr, void *ubuf, size_t ubufsz)
+{
+	lx_zone_data_t *lxzd = (lx_zone_data_t *)zone->zone_brand_data;
+
+	switch (attr) {
+	case LX_ATTR_KERN_RELEASE: {
+		char buf[LX_KERN_RELEASE_MAX];
+		bzero(buf, LX_KERN_RELEASE_MAX);
+		if (ubufsz >= LX_KERN_RELEASE_MAX) {
+			return (ERANGE);
+		}
+		if (copyin(ubuf, buf, ubufsz) != 0) {
+			return (EFAULT);
+		}
+		mutex_enter(&lxzd->lxzd_lock);
+		(void) strlcpy(lxzd->lxzd_kernel_release, buf,
+		    LX_KERN_RELEASE_MAX);
+		mutex_exit(&lxzd->lxzd_lock);
+		return (0);
+	}
+	case LX_ATTR_KERN_VERSION: {
+		char buf[LX_KERN_VERSION_MAX];
+		bzero(buf, LX_KERN_VERSION_MAX);
+		if (ubufsz >= LX_KERN_VERSION_MAX) {
+			return (ERANGE);
+		}
+		if (copyin(ubuf, buf, ubufsz) != 0) {
+			return (EFAULT);
+		}
+		mutex_enter(&lxzd->lxzd_lock);
+		(void) strlcpy(lxzd->lxzd_kernel_version, buf,
+		    LX_KERN_VERSION_MAX);
+		mutex_exit(&lxzd->lxzd_lock);
+		return (0);
+	}
+	default:
+		return (EINVAL);
+	}
+}
+
+/* ARGSUSED */
+int
+lx_getattr(zone_t *zone, int attr, void *ubuf, size_t *ubufsz)
+{
+	lx_zone_data_t *lxzd = (lx_zone_data_t *)zone->zone_brand_data;
+	int len;
+
+	switch (attr) {
+	case LX_ATTR_KERN_RELEASE: {
+		char buf[LX_KERN_RELEASE_MAX];
+
+		mutex_enter(&lxzd->lxzd_lock);
+		len = strnlen(lxzd->lxzd_kernel_release, LX_KERN_RELEASE_MAX);
+		len++;
+		if (*ubufsz < len) {
+			mutex_exit(&lxzd->lxzd_lock);
+			return (ERANGE);
+		}
+		bzero(buf, sizeof (buf));
+		(void) strncpy(buf, lxzd->lxzd_kernel_release, sizeof (buf));
+		mutex_exit(&lxzd->lxzd_lock);
+		if (copyout(buf, ubuf, len) != 0) {
+			return (EFAULT);
+		}
+		*ubufsz = len;
+		return (0);
+	}
+	case LX_ATTR_KERN_VERSION: {
+		char buf[LX_KERN_VERSION_MAX];
+
+		mutex_enter(&lxzd->lxzd_lock);
+		len = strnlen(lxzd->lxzd_kernel_version, LX_KERN_VERSION_MAX);
+		len++;
+		if (*ubufsz < len) {
+			mutex_exit(&lxzd->lxzd_lock);
+			return (ERANGE);
+		}
+		bzero(buf, sizeof (buf));
+		(void) strncpy(buf, lxzd->lxzd_kernel_version, sizeof (buf));
+		mutex_exit(&lxzd->lxzd_lock);
+		if (copyout(buf, ubuf, len) != 0) {
+			return (EFAULT);
+		}
+		*ubufsz = len;
+		return (0);
+	}
+	default:
+		return (EINVAL);
+	}
+}
+
+uint32_t
+lx_map32limit(proc_t *p)
+{
+	/*
+	 * To be bug-for-bug compatible with Linux, we have MAP_32BIT only
+	 * allow mappings in the first 31 bits.  This was a nuance in the
+	 * original Linux implementation circa 2002, and applications have
+	 * come to depend on its behavior.
+	 *
+	 * This is only relevant for 64-bit processes.
+	 */
+	if (p->p_model == DATAMODEL_LP64)
+		return ((uint32_t)1 << 31);
+
+	return ((uint32_t)USERLIMIT32);
+}
+
+void
+lx_brand_systrace_enable(void)
+{
+	VERIFY(!lx_systrace_enabled);
+
+	lx_systrace_enabled = 1;
+}
+
+void
+lx_brand_systrace_disable(void)
+{
+	VERIFY(lx_systrace_enabled);
+
+	lx_systrace_enabled = 0;
+}
+
+void
+lx_lwp_set_native_stack_current(lx_lwp_data_t *lwpd, uintptr_t new_sp)
+{
+	VERIFY(lwpd->br_ntv_stack != 0);
+
+	/*
+	 * The "brand-lx-set-ntv-stack-current" probe has arguments:
+	 *   arg0: stack pointer before change
+	 *   arg1: stack pointer after change
+	 *   arg2: current stack base
+	 */
+	DTRACE_PROBE3(brand__lx__set__ntv__stack__current,
+	    uintptr_t, lwpd->br_ntv_stack_current,
+	    uintptr_t, new_sp,
+	    uintptr_t, lwpd->br_ntv_stack);
+
+	lwpd->br_ntv_stack_current = new_sp;
+}
+
+#if defined(_LP64)
+static int
+lx_pagefault(proc_t *p, klwp_t *lwp, caddr_t addr, enum fault_type type,
+    enum seg_rw rw)
+{
+	int syscall_num;
+
+	/*
+	 * We only want to handle a very specific set of circumstances.
+	 * Namely: this is a 64-bit LX-branded process attempting to execute an
+	 * address in a page for which it does not have a valid mapping.  If
+	 * this is not the case, we bail out as fast as possible.
+	 */
+	VERIFY(PROC_IS_BRANDED(p));
+	if (type != F_INVAL || rw != S_EXEC || lwp_getdatamodel(lwp) !=
+	    DATAMODEL_NATIVE) {
+		return (-1);
+	}
+
+	if (!lx_vsyscall_iscall(lwp, (uintptr_t)addr, &syscall_num)) {
+		return (-1);
+	}
+
+	/*
+	 * This is a valid vsyscall address.  We service the system call and
+	 * return 0 to signal that the pagefault has been handled completely.
+	 */
+	lx_vsyscall_enter(p, lwp, syscall_num);
+	return (0);
+}
+#endif
+
+/*
+ * This hook runs prior to sendsig() processing and allows us to nominate
+ * an alternative stack pointer for delivery of the signal handling frame.
+ * Critically, this routine should _not_ modify any LWP state as the
+ * savecontext() does not run until after this hook.
+ */
+/* ARGSUSED */
+static caddr_t
+lx_sendsig_stack(int sig)
+{
+	klwp_t *lwp = ttolwp(curthread);
+	lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+
+	/*
+	 * We want to take signal delivery on the native stack, but only if
+	 * one has been allocated and installed for this LWP.
+	 */
+	if (lwpd->br_stack_mode == LX_STACK_MODE_BRAND) {
+		/*
+		 * The program is not running on the native stack.  Return
+		 * the native stack pointer from our brand-private data so
+		 * that we may switch to it for signal handling.
+		 */
+		return ((caddr_t)lwpd->br_ntv_stack_current);
+	} else {
+		struct regs *rp = lwptoregs(lwp);
+
+		/*
+		 * Either the program is already running on the native stack,
+		 * or one has not yet been allocated for this LWP.  Use the
+		 * current stack pointer value.
+		 */
+		return ((caddr_t)rp->r_sp);
+	}
+}
+
+/*
+ * This hook runs after sendsig() processing and allows us to update the
+ * per-LWP mode flags for system calls and stacks.  The pre-signal
+ * context has already been saved and delivered to the user at this point.
+ */
+/* ARGSUSED */
+static void
+lx_sendsig(int sig)
+{
+	klwp_t *lwp = ttolwp(curthread);
+	lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+	struct regs *rp = lwptoregs(lwp);
+
+	switch (lwpd->br_stack_mode) {
+	case LX_STACK_MODE_BRAND:
+	case LX_STACK_MODE_NATIVE:
+		/*
+		 * In lx_sendsig_stack(), we nominated a stack pointer from the
+		 * native stack.  Update the stack mode, and the current in-use
+		 * extent of the native stack, accordingly:
+		 */
+		lwpd->br_stack_mode = LX_STACK_MODE_NATIVE;
+		lx_lwp_set_native_stack_current(lwpd, rp->r_sp);
+
+		/*
+		 * Fix up segment registers, etc.
+		 */
+		lx_switch_to_native(lwp);
+		break;
+
+	default:
+		/*
+		 * Otherwise, the brand library has not yet installed the
+		 * alternate stack for this LWP.  Signals will be handled on
+		 * the regular stack thread.
+		 */
+		return;
+	}
+}
+
+/*
+ * This hook runs prior to the context restoration, allowing us to take action
+ * or modify the context before it is loaded.
+ */
+static void
+lx_restorecontext(ucontext_t *ucp)
+{
+	klwp_t *lwp = ttolwp(curthread);
+	lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+	uintptr_t flags = (uintptr_t)ucp->uc_brand_data[0];
+	caddr_t sp = ucp->uc_brand_data[1];
+
+	/*
+	 * We have a saved native stack pointer value that we must restore
+	 * into the per-LWP data.
+	 */
+	if (flags & LX_UC_RESTORE_NATIVE_SP) {
+		lx_lwp_set_native_stack_current(lwpd, (uintptr_t)sp);
+	}
+
+	/*
+	 * We do not wish to restore the value of uc_link in this context,
+	 * so replace it with the value currently in the LWP.
+	 */
+	if (flags & LX_UC_IGNORE_LINK) {
+		ucp->uc_link = (ucontext_t *)lwp->lwp_oldcontext;
+	}
+
+	/*
+	 * Restore the stack mode:
+	 */
+	if (flags & LX_UC_STACK_NATIVE) {
+		lwpd->br_stack_mode = LX_STACK_MODE_NATIVE;
+	} else if (flags & LX_UC_STACK_BRAND) {
+		lwpd->br_stack_mode = LX_STACK_MODE_BRAND;
+	}
+
+#if defined(__amd64)
+	/*
+	 * Override the fs/gsbase in the context with the value provided
+	 * through the Linux arch_prctl(2) system call.
+	 */
+	if (flags & LX_UC_STACK_BRAND) {
+		if (lwpd->br_lx_fsbase != 0) {
+			ucp->uc_mcontext.gregs[REG_FSBASE] = lwpd->br_lx_fsbase;
+		}
+		if (lwpd->br_lx_gsbase != 0) {
+			ucp->uc_mcontext.gregs[REG_GSBASE] = lwpd->br_lx_gsbase;
+		}
+	}
+#endif
+}
+
+static void
+lx_savecontext(ucontext_t *ucp)
+{
+	klwp_t *lwp = ttolwp(curthread);
+	lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+	uintptr_t flags = 0;
+
+	/*
+	 * The ucontext_t affords us three private pointer-sized members in
+	 * "uc_brand_data".  We pack a variety of flags into the first element,
+	 * and an optional stack pointer in the second element.  The flags
+	 * determine which stack pointer (native or brand), if any, is stored
+	 * in the second element.  The third element may contain the system
+	 * call number; this is analogous to the "orig_[er]ax" member of a
+	 * Linux "user_regs_struct".
+	 */
+
+	if (lwpd->br_stack_mode != LX_STACK_MODE_INIT &&
+	    lwpd->br_stack_mode != LX_STACK_MODE_PREINIT) {
+		/*
+		 * Record the value of the native stack pointer to restore
+		 * when returning to this branded context:
+		 */
+		flags |= LX_UC_RESTORE_NATIVE_SP;
+		ucp->uc_brand_data[1] = (void *)lwpd->br_ntv_stack_current;
+	}
+
+	/*
+	 * Save the stack mode:
+	 */
+	if (lwpd->br_stack_mode == LX_STACK_MODE_NATIVE) {
+		flags |= LX_UC_STACK_NATIVE;
+	} else if (lwpd->br_stack_mode == LX_STACK_MODE_BRAND) {
+		flags |= LX_UC_STACK_BRAND;
+	}
+
+	/*
+	 * If we might need to restart this system call, save that information
+	 * in the context:
+	 */
+	if (lwpd->br_stack_mode == LX_STACK_MODE_BRAND) {
+		ucp->uc_brand_data[2] =
+		    (void *)(uintptr_t)lwpd->br_syscall_num;
+		if (lwpd->br_syscall_restart) {
+			flags |= LX_UC_RESTART_SYSCALL;
+		}
+	} else {
+		ucp->uc_brand_data[2] = NULL;
+	}
+
+	ucp->uc_brand_data[0] = (void *)flags;
+}
+
+#if defined(_SYSCALL32_IMPL)
+static void
+lx_savecontext32(ucontext32_t *ucp)
+{
+	klwp_t *lwp = ttolwp(curthread);
+	lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+	unsigned int flags = 0;
+
+	/*
+	 * The ucontext_t affords us three private pointer-sized members in
+	 * "uc_brand_data".  We pack a variety of flags into the first element,
+	 * and an optional stack pointer in the second element.  The flags
+	 * determine which stack pointer (native or brand), if any, is stored
+	 * in the second element.  The third element may contain the system
+	 * call number; this is analogous to the "orig_[er]ax" member of a
+	 * Linux "user_regs_struct".
+	 */
+
+	if (lwpd->br_stack_mode != LX_STACK_MODE_INIT &&
+	    lwpd->br_stack_mode != LX_STACK_MODE_PREINIT) {
+		/*
+		 * Record the value of the native stack pointer to restore
+		 * when returning to this branded context:
+		 */
+		flags |= LX_UC_RESTORE_NATIVE_SP;
+		ucp->uc_brand_data[1] = (caddr32_t)lwpd->br_ntv_stack_current;
+	}
+
+	/*
+	 * Save the stack mode:
+	 */
+	if (lwpd->br_stack_mode == LX_STACK_MODE_NATIVE) {
+		flags |= LX_UC_STACK_NATIVE;
+	} else if (lwpd->br_stack_mode == LX_STACK_MODE_BRAND) {
+		flags |= LX_UC_STACK_BRAND;
+	}
+
+	/*
+	 * If we might need to restart this system call, save that information
+	 * in the context:
+	 */
+	if (lwpd->br_stack_mode == LX_STACK_MODE_BRAND) {
+		ucp->uc_brand_data[2] = (caddr32_t)lwpd->br_syscall_num;
+		if (lwpd->br_syscall_restart) {
+			flags |= LX_UC_RESTART_SYSCALL;
+		}
+	} else {
+		ucp->uc_brand_data[2] = NULL;
+	}
+
+	ucp->uc_brand_data[0] = flags;
+}
+#endif
+
+static int
+lx_zfs_ioctl(ldi_handle_t lh, int cmd, zfs_cmd_t *zc, size_t *dst_alloc_size)
+{
+	uint64_t	cookie;
+	size_t		dstsize;
+	int		rc, unused;
+
+	cookie = zc->zc_cookie;
+
+	dstsize = (dst_alloc_size == NULL ? 0 : 8192);
+
+again:
+	if (dst_alloc_size != NULL) {
+		zc->zc_nvlist_dst = (uint64_t)(intptr_t)kmem_alloc(dstsize,
+		    KM_SLEEP);
+		zc->zc_nvlist_dst_size = dstsize;
+	}
+
+	rc = ldi_ioctl(lh, cmd, (intptr_t)zc, FKIOCTL, kcred, &unused);
+	if (rc == ENOMEM && dst_alloc_size != NULL) {
+		/*
+		 * Our nvlist_dst buffer was too small, retry with a bigger
+		 * buffer. ZFS will tell us the exact needed size.
+		 */
+		size_t newsize = zc->zc_nvlist_dst_size;
+		ASSERT(newsize > dstsize);
+
+		kmem_free((void *)(uintptr_t)zc->zc_nvlist_dst, dstsize);
+		dstsize = newsize;
+		zc->zc_cookie = cookie;
+
+		goto again;
+	}
+
+	if (dst_alloc_size != NULL) {
+		*dst_alloc_size = dstsize;
+	}
+
+	return (rc);
+}
+
+static int
+lx_zone_zfs_open(ldi_handle_t *lh, dev_t *zfs_dev)
+{
+	ldi_ident_t li;
+
+	if (ldi_ident_from_mod(&modlinkage, &li) != 0) {
+		return (-1);
+	}
+	if (ldi_open_by_name("/dev/zfs", FREAD|FWRITE, kcred, lh, li) != 0) {
+		ldi_ident_release(li);
+		return (-1);
+	}
+	ldi_ident_release(li);
+	if (ldi_get_dev(*lh, zfs_dev) != 0) {
+		(void) ldi_close(*lh, FREAD|FWRITE, kcred);
+		return (-1);
+	}
+	return (0);
+}
+
+/*
+ * We only get the relevant properties for zvols. This is because we're
+ * essentially iterating all of the ZFS datasets/zvols on the entire system
+ * when we boot the zone and there is a significant performance penalty if we
+ * have to retrieve all of the properties for everything. Especially since we
+ * don't care about any of them except the zvols actually in our delegated
+ * datasets.
+ *
+ * Note that the two properties we care about, volsize & volblocksize, are
+ * mandatory for zvols and should always be present. Also, note that the
+ * blocksize property value cannot change after the zvol has been created.
+ */
+static void
+lx_zvol_props(ldi_handle_t lh, zfs_cmd_t *zc, uint64_t *vsz, uint64_t *bsz)
+{
+	int		rc;
+	size_t		size;
+	nvlist_t	*nv = NULL, *nv2;
+
+	rc = lx_zfs_ioctl(lh, ZFS_IOC_OBJSET_STATS, zc, &size);
+	if (rc != 0)
+		return;
+
+	rc = nvlist_unpack((char *)(uintptr_t)zc->zc_nvlist_dst,
+	    zc->zc_nvlist_dst_size, &nv, 0);
+	ASSERT(rc == 0);
+
+	kmem_free((void *)(uintptr_t)zc->zc_nvlist_dst, size);
+	zc->zc_nvlist_dst = NULL;
+	zc->zc_nvlist_dst_size = 0;
+
+	if ((rc = nvlist_lookup_nvlist(nv, "volsize", &nv2)) == 0) {
+		uint64_t val;
+
+		rc = nvlist_lookup_uint64(nv2, ZPROP_VALUE, &val);
+		if (rc == 0) {
+			*vsz = val;
+		}
+	}
+
+	if ((rc = nvlist_lookup_nvlist(nv, "volblocksize", &nv2)) == 0) {
+		uint64_t val;
+
+		rc = nvlist_lookup_uint64(nv2, ZPROP_VALUE, &val);
+		if (rc == 0) {
+			*bsz = val;
+		}
+	}
+
+	nvlist_free(nv);
+}
+
+/*
+ * Unlike ZFS proper, which does dynamic zvols, we currently only generate the
+ * zone's "disk" list once at zone boot time and use that consistently in all
+ * of the various subsystems (devfs, sysfs, procfs).  This allows us to avoid
+ * re-iterating the datasets every time one of those subsystems accesses a
+ * "disk" and allows us to keep the view consistent across all subsystems, but
+ * it does mean a reboot is required to see new "disks". This is somewhat
+ * mitigated by its similarity to actual disk drives on a real system.
+ */
+static void
+lx_zone_get_zvols(zone_t *zone, ldi_handle_t lh, minor_t *emul_minor)
+{
+	lx_zone_data_t *lxzd;
+	list_t *zvol_lst, ds_lst;
+	int rc;
+	unsigned int devnum = 0;
+	size_t size;
+	zfs_cmd_t *zc;
+	nvpair_t *elem = NULL;
+	nvlist_t *pnv = NULL;
+
+	lxzd = ztolxzd(zone);
+	ASSERT(lxzd != NULL);
+	zvol_lst = lxzd->lxzd_vdisks;
+
+	zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
+	if (lx_zfs_ioctl(lh, ZFS_IOC_POOL_CONFIGS, zc, &size) != 0) {
+		goto out;
+	}
+	ASSERT(zc->zc_cookie > 0);
+
+	rc = nvlist_unpack((char *)(uintptr_t)zc->zc_nvlist_dst,
+	    zc->zc_nvlist_dst_size, &pnv, 0);
+	kmem_free((void *)(uintptr_t)zc->zc_nvlist_dst, size);
+	if (rc != 0)
+		goto out;
+
+	/*
+	 * We use a dataset list to process all of the datasets in the pool
+	 * without doing recursion so that we don't risk blowing the kernel
+	 * stack.
+	 */
+	list_create(&ds_lst, sizeof (lx_zfs_ds_t),
+	    offsetof(lx_zfs_ds_t, ds_link));
+
+	while ((elem = nvlist_next_nvpair(pnv, elem)) != NULL) {
+		lx_zfs_ds_t *ds;
+
+		ds = kmem_zalloc(sizeof (lx_zfs_ds_t), KM_SLEEP);
+		(void) strcpy(ds->ds_name, nvpair_name(elem));
+		list_insert_head(&ds_lst, ds);
+
+		while (ds != NULL) {
+			int w;		/* dummy variable */
+
+			bzero(zc, sizeof (zfs_cmd_t));
+			zc->zc_cookie = ds->ds_cookie;
+			(void) strcpy(zc->zc_name, ds->ds_name);
+
+			rc = lx_zfs_ioctl(lh, ZFS_IOC_DATASET_LIST_NEXT,
+			    zc, NULL);
+			/* Update the cookie before doing anything else. */
+			ds->ds_cookie = zc->zc_cookie;
+
+			if (rc != 0) {
+				list_remove(&ds_lst, ds);
+				kmem_free(ds, sizeof (lx_zfs_ds_t));
+				ds = list_tail(&ds_lst);
+				continue;
+			}
+
+			/* Reserved internal names, skip over these. */
+			if (strchr(zc->zc_name, '$') != NULL ||
+			    strchr(zc->zc_name, '%') != NULL)
+				continue;
+
+			if (!zone_dataset_visible_inzone(zone, zc->zc_name, &w))
+				continue;
+
+			if (zc->zc_objset_stats.dds_type == DMU_OST_ZVOL) {
+				lx_virt_disk_t *vd;
+				minor_t m = 0;
+				char *znm = zc->zc_name;
+
+				/* Create a virtual disk entry for the zvol */
+				vd = kmem_zalloc(sizeof (lx_virt_disk_t),
+				    KM_SLEEP);
+				vd->lxvd_type = LXVD_ZVOL;
+				(void) snprintf(vd->lxvd_name,
+				    sizeof (vd->lxvd_name),
+				    "zvol%u", devnum++);
+				(void) strlcpy(vd->lxvd_real_name,
+				    zc->zc_name,
+				    sizeof (vd->lxvd_real_name));
+
+				/* Record emulated and real dev_t values */
+				vd->lxvd_emul_dev = makedevice(LX_MAJOR_DISK,
+				    (*emul_minor)++);
+				if (zvol_name2minor(znm, &m) != 0) {
+					(void) zvol_create_minor(znm);
+					VERIFY(zvol_name2minor(znm, &m) == 0);
+				}
+				if (m != 0) {
+					vd->lxvd_real_dev = makedevice(
+					    getmajor(lxzd->lxzd_zfs_dev), m);
+				}
+
+				/* Query volume size properties */
+				lx_zvol_props(lh, zc, &vd->lxvd_volsize,
+				    &vd->lxvd_blksize);
+
+				list_insert_tail(zvol_lst, vd);
+			} else {
+				lx_zfs_ds_t *nds;
+
+				/* Create a new ds_t for the child. */
+				nds = kmem_zalloc(sizeof (lx_zfs_ds_t),
+				    KM_SLEEP);
+				(void) strcpy(nds->ds_name, zc->zc_name);
+				list_insert_after(&ds_lst, ds, nds);
+
+				/* Depth-first, so do the one just created. */
+				ds = nds;
+			}
+		}
+
+		ASSERT(list_is_empty(&ds_lst));
+	}
+
+	list_destroy(&ds_lst);
+
+out:
+	nvlist_free(pnv);
+	kmem_free(zc, sizeof (zfs_cmd_t));
+}
+
+static void
+lx_zone_get_zfsds(zone_t *zone, minor_t *emul_minor)
+{
+	lx_zone_data_t *lxzd = ztolxzd(zone);
+	vfs_t *vfsp = zone->zone_rootvp->v_vfsp;
+
+	/*
+	 * Only the root will be mounted at zone init time.
+	 * Finding means of discovering other datasets mounted in the zone
+	 * would be a good enhancement later.
+	 */
+	if (getmajor(vfsp->vfs_dev) == getmajor(lxzd->lxzd_zfs_dev)) {
+		lx_virt_disk_t *vd;
+
+		vd = kmem_zalloc(sizeof (lx_virt_disk_t), KM_SLEEP);
+		vd->lxvd_type = LXVD_ZFS_DS;
+		vd->lxvd_real_dev = vfsp->vfs_dev;
+		vd->lxvd_emul_dev = makedevice(LX_MAJOR_DISK, (*emul_minor)++);
+		(void) snprintf(vd->lxvd_name, sizeof (vd->lxvd_name),
+		    "zfsds%u", 0);
+		(void) strlcpy(vd->lxvd_real_name,
+		    refstr_value(vfsp->vfs_resource),
+		    sizeof (vd->lxvd_real_name));
+
+		list_insert_tail(lxzd->lxzd_vdisks, vd);
+	}
+}
+
+/* Cleanup virtual disk list */
+static void
+lx_zone_cleanup_vdisks(lx_zone_data_t *lxzd)
+{
+	lx_virt_disk_t *vd;
+
+	ASSERT(lxzd->lxzd_vdisks != NULL);
+	vd = (list_remove_head(lxzd->lxzd_vdisks));
+	while (vd != NULL) {
+		kmem_free(vd, sizeof (lx_virt_disk_t));
+		vd = list_remove_head(lxzd->lxzd_vdisks);
+	}
+
+	list_destroy(lxzd->lxzd_vdisks);
+	kmem_free(lxzd->lxzd_vdisks, sizeof (list_t));
+	lxzd->lxzd_vdisks = NULL;
+}
+
+/*
+ * See mod_set_extra_privports. By default illumos restricts access to
+ * ULP_DEF_EPRIV_PORT1 and ULP_DEF_EPRIV_PORT2 for TCP and UDP, even though
+ * these ports are outside of the privileged port range. Linux does not do
+ * this, so we need to remove these defaults.
+ */
+static void
+lx_fix_netstack()
+{
+	netstack_t	*ns;
+	tcp_stack_t	*tcps;
+	udp_stack_t	*udps;
+	in_port_t	*ports;
+	uint_t		i, nports;
+	kmutex_t	*lock;
+
+	ns = netstack_get_current();
+	if (ns == NULL)
+		return;
+
+	tcps = ns->netstack_tcp;
+	ports = tcps->tcps_g_epriv_ports;
+	nports = tcps->tcps_g_num_epriv_ports;
+	lock = &tcps->tcps_epriv_port_lock;
+
+	mutex_enter(lock);
+	for (i = 0; i < nports; i++)
+		ports[i] = 0;
+	mutex_exit(lock);
+
+	udps = ns->netstack_udp;
+	ports = udps->us_epriv_ports;
+	nports = udps->us_num_epriv_ports;
+	lock = &udps->us_epriv_port_lock;
+
+	mutex_enter(lock);
+	for (i = 0; i < nports; i++)
+		ports[i] = 0;
+	mutex_exit(lock);
+
+	netstack_rele(ns);
+}
+
+void
+lx_init_brand_data(zone_t *zone, kmutex_t *zsl)
+{
+	lx_zone_data_t *data;
+	ldi_handle_t lh;
+
+	ASSERT(MUTEX_HELD(zsl));
+	ASSERT(zone->zone_brand == &lx_brand);
+	ASSERT(zone->zone_brand_data == NULL);
+
+	data = (lx_zone_data_t *)kmem_zalloc(sizeof (lx_zone_data_t), KM_SLEEP);
+	mutex_init(&data->lxzd_lock, NULL, MUTEX_DEFAULT, NULL);
+
+	/* No need to hold mutex now since zone_brand_data is not set yet. */
+
+	/*
+	 * Set the default lxzd_kernel_version to 2.4.
+	 * This can be changed by a call to setattr() during zone boot.
+	 */
+	(void) strlcpy(data->lxzd_kernel_release, "2.4.21",
+	    LX_KERN_RELEASE_MAX);
+	(void) strlcpy(data->lxzd_kernel_version, "BrandZ virtual linux",
+	    LX_KERN_VERSION_MAX);
+
+	zone->zone_brand_data = data;
+
+	/*
+	 * In Linux, if the init(1) process terminates the system panics.
+	 * The zone must reboot to simulate this behaviour.
+	 */
+	zone->zone_reboot_on_init_exit = B_TRUE;
+
+	/*
+	 * We cannot hold the zone_status_lock while performing zfs operations
+	 * so we drop the lock, get the zfs devs as the last step in this
+	 * function, then reaquire the lock. Don't add any code after this
+	 * which requires that the zone_status_lock was continuously held.
+	 */
+	mutex_exit(zsl);
+
+	data->lxzd_vdisks = kmem_alloc(sizeof (list_t), KM_SLEEP);
+	list_create(data->lxzd_vdisks, sizeof (lx_virt_disk_t),
+	    offsetof(lx_virt_disk_t, lxvd_link));
+
+	if (lx_zone_zfs_open(&lh, &data->lxzd_zfs_dev) == 0) {
+		minor_t emul_minor = 1;
+
+		lx_zone_get_zfsds(zone, &emul_minor);
+		lx_zone_get_zvols(zone, lh, &emul_minor);
+		(void) ldi_close(lh, FREAD|FWRITE, kcred);
+	} else {
+		/* Avoid matching any devices */
+		data->lxzd_zfs_dev = makedevice(-1, 0);
+	}
+	mutex_enter(zsl);
+}
+
+void
+lx_free_brand_data(zone_t *zone)
+{
+	lx_zone_data_t *data = ztolxzd(zone);
+	ASSERT(data != NULL);
+	mutex_enter(&data->lxzd_lock);
+	if (data->lxzd_ioctl_sock != NULL) {
+		/*
+		 * Since zone_kcred has been cleaned up already, close the
+		 * socket using the global kcred.
+		 */
+		(void) ksocket_close(data->lxzd_ioctl_sock, kcred);
+		data->lxzd_ioctl_sock = NULL;
+	}
+	ASSERT(data->lxzd_cgroup == NULL);
+
+	lx_zone_cleanup_vdisks(data);
+
+	mutex_exit(&data->lxzd_lock);
+	zone->zone_brand_data = NULL;
+	mutex_destroy(&data->lxzd_lock);
+	kmem_free(data, sizeof (*data));
+}
+
+void
+lx_unsupported(char *dmsg)
+{
+	lx_proc_data_t *pd = ttolxproc(curthread);
+
+	DTRACE_PROBE1(brand__lx__unsupported, char *, dmsg);
+
+	if (pd != NULL && (pd->l_flags & LX_PROC_STRICT_MODE) != 0) {
+		/*
+		 * If this process was run with strict mode enabled
+		 * (via LX_STRICT in the environment), we mark this
+		 * LWP as having triggered an unsupported behaviour.
+		 * This flag will be checked at an appropriate point
+		 * by lx_check_strict_failure().
+		 */
+		lx_lwp_data_t *lwpd = ttolxlwp(curthread);
+
+		lwpd->br_strict_failure = B_TRUE;
+	}
+}
+
+void
+lx_check_strict_failure(lx_lwp_data_t *lwpd)
+{
+	proc_t *p;
+
+	if (!lwpd->br_strict_failure) {
+		return;
+	}
+
+	lwpd->br_strict_failure = B_FALSE;
+
+	/*
+	 * If this process is operating in strict mode (via LX_STRICT in
+	 * the environment), and has triggered a call to
+	 * lx_unsupported(), we drop SIGSYS on it as we return.
+	 */
+	p = curproc;
+	mutex_enter(&p->p_lock);
+	sigtoproc(p, curthread, SIGSYS);
+	mutex_exit(&p->p_lock);
+}
+
+void
+lx_trace_sysenter(int syscall_num, uintptr_t *args)
+{
+	if (lx_systrace_enabled) {
+		VERIFY(lx_systrace_entry_ptr != NULL);
+
+		(*lx_systrace_entry_ptr)(syscall_num, args[0], args[1],
+		    args[2], args[3], args[4], args[5]);
+	}
+}
+
+void
+lx_trace_sysreturn(int syscall_num, long ret)
+{
+	if (lx_systrace_enabled) {
+		VERIFY(lx_systrace_return_ptr != NULL);
+
+		(*lx_systrace_return_ptr)(syscall_num, ret, ret, 0, 0, 0, 0);
+	}
+}
+
+/*
+ * Get the addresses of the user-space system call handler and attach it to
+ * the proc structure. Returning 0 indicates success; the value returned
+ * by the system call is the value stored in rval. Returning a non-zero
+ * value indicates a failure; the value returned is used to set errno, -1
+ * is returned from the syscall and the contents of rval are ignored. To
+ * set errno and have the syscall return a value other than -1 we can
+ * manually set errno and rval and return 0.
+ */
+int
+lx_brandsys(int cmd, int64_t *rval, uintptr_t arg1, uintptr_t arg2,
+    uintptr_t arg3, uintptr_t arg4)
+{
+	kthread_t *t = curthread;
+	klwp_t *lwp = ttolwp(t);
+	proc_t *p = ttoproc(t);
+	lx_proc_data_t *pd;
+	struct termios *termios;
+	uint_t termios_len;
+	int error;
+	int code;
+	int sig;
+	lx_brand_registration_t reg;
+	lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+
+	/*
+	 * There is one operation that is suppored for non-branded
+	 * process.  B_EXEC_BRAND.  This is the equilivant of an
+	 * exec call, but the new process that is created will be
+	 * a branded process.
+	 */
+	if (cmd == B_EXEC_BRAND) {
+		VERIFY(p->p_zone != NULL);
+		VERIFY(p->p_zone->zone_brand == &lx_brand);
+		return (exec_common(
+		    (char *)arg1, (const char **)arg2, (const char **)arg3,
+		    EBA_BRAND));
+	}
+
+	/* For all other operations this must be a branded process. */
+	if (p->p_brand == NULL)
+		return (ENOSYS);
+
+	VERIFY(p->p_brand == &lx_brand);
+	VERIFY(p->p_brand_data != NULL);
+
+	switch (cmd) {
+	case B_REGISTER:
+		if (lwpd->br_stack_mode != LX_STACK_MODE_PREINIT) {
+			lx_print("stack mode was not PREINIT during "
+			    "REGISTER\n");
+			return (EINVAL);
+		}
+
+		if (p->p_model == DATAMODEL_NATIVE) {
+			if (copyin((void *)arg1, &reg, sizeof (reg)) != 0) {
+				lx_print("Failed to copyin brand registration "
+				    "at 0x%p\n", (void *)arg1);
+				return (EFAULT);
+			}
+		}
+#ifdef _LP64
+		else {
+			/* 32-bit userland on 64-bit kernel */
+			lx_brand_registration32_t reg32;
+
+			if (copyin((void *)arg1, &reg32, sizeof (reg32)) != 0) {
+				lx_print("Failed to copyin brand registration "
+				    "at 0x%p\n", (void *)arg1);
+				return (EFAULT);
+			}
+
+			reg.lxbr_version = (uint_t)reg32.lxbr_version;
+			reg.lxbr_handler =
+			    (void *)(uintptr_t)reg32.lxbr_handler;
+			reg.lxbr_flags = reg32.lxbr_flags;
+		}
+#endif
+
+		if (reg.lxbr_version != LX_VERSION_1) {
+			lx_print("Invalid brand library version (%u)\n",
+			    reg.lxbr_version);
+			return (EINVAL);
+		}
+
+		if ((reg.lxbr_flags & ~LX_PROC_ALL) != 0) {
+			lx_print("Invalid brand flags (%u)\n",
+			    reg.lxbr_flags);
+			return (EINVAL);
+		}
+
+		lx_print("Assigning brand 0x%p and handler 0x%p to proc 0x%p\n",
+		    (void *)&lx_brand, (void *)reg.lxbr_handler, (void *)p);
+		pd = p->p_brand_data;
+		pd->l_handler = (uintptr_t)reg.lxbr_handler;
+		pd->l_flags = reg.lxbr_flags & LX_PROC_ALL;
+
+		/*
+		 * We can't fix up our netstack from the lx_init_brand_data
+		 * hook since that hook is run by zoneadmd (which has the GZ's
+		 * stack). Instead, we fix it up when the init process starts
+		 * inside the zone since it will have the proper stack.
+		 * Note that it is conceivable that a Linux init could be
+		 * illumos-aware and re-enable additional privileged ports,
+		 * then exec(2) over itself. This would cause those settings to
+		 * be lost, but this scenario is considered unlikely so we
+		 * don't worry about it.
+		 */
+		if (p->p_pid == p->p_zone->zone_proc_initpid)
+			lx_fix_netstack();
+
+		return (0);
+
+	case B_TTYMODES:
+		/* This is necessary for emulating TCGETS ioctls. */
+		if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, ddi_root_node(),
+		    DDI_PROP_NOTPROM, "ttymodes", (uchar_t **)&termios,
+		    &termios_len) != DDI_SUCCESS)
+			return (EIO);
+
+		ASSERT(termios_len == sizeof (*termios));
+
+		if (copyout(&termios, (void *)arg1, sizeof (termios)) != 0) {
+			ddi_prop_free(termios);
+			return (EFAULT);
+		}
+
+		ddi_prop_free(termios);
+		return (0);
+
+	case B_ELFDATA: {
+		mutex_enter(&p->p_lock);
+		pd = curproc->p_brand_data;
+		if (get_udatamodel() == DATAMODEL_NATIVE) {
+			lx_elf_data_t led;
+
+			bcopy(&pd->l_elf_data, &led, sizeof (led));
+			mutex_exit(&p->p_lock);
+
+			if (copyout(&led, (void *)arg1,
+			    sizeof (lx_elf_data_t)) != 0) {
+				return (EFAULT);
+			}
+		}
+#if defined(_LP64)
+		else {
+			/* 32-bit userland on 64-bit kernel */
+			lx_elf_data32_t led32;
+
+			led32.ed_phdr = (int)pd->l_elf_data.ed_phdr;
+			led32.ed_phent = (int)pd->l_elf_data.ed_phent;
+			led32.ed_phnum = (int)pd->l_elf_data.ed_phnum;
+			led32.ed_entry = (int)pd->l_elf_data.ed_entry;
+			led32.ed_base = (int)pd->l_elf_data.ed_base;
+			led32.ed_ldentry = (int)pd->l_elf_data.ed_ldentry;
+			mutex_exit(&p->p_lock);
+
+			if (copyout(&led32, (void *)arg1,
+			    sizeof (led32)) != 0) {
+				return (EFAULT);
+			}
+		}
+#endif
+		return (0);
+	}
+
+	case B_EXEC_NATIVE:
+		return (exec_common((char *)arg1, (const char **)arg2,
+		    (const char **)arg3, EBA_NATIVE));
+
+	/*
+	 * The B_TRUSS_POINT subcommand is used so that we can make a no-op
+	 * syscall for debugging purposes (dtracing) from within the user-level
+	 * emulation.
+	 */
+	case B_TRUSS_POINT:
+		return (0);
+
+	case B_LPID_TO_SPAIR: {
+		/*
+		 * Given a Linux pid as arg1, return the Solaris pid in arg2 and
+		 * the Solaris LWP in arg3.  We also translate pid 1 (which is
+		 * hardcoded in many applications) to the zone's init process.
+		 */
+		pid_t s_pid;
+		id_t s_tid;
+
+		if ((pid_t)arg1 == 1) {
+			s_pid = p->p_zone->zone_proc_initpid;
+			/* handle the dead/missing init(1M) case */
+			if (s_pid == -1)
+				s_pid = 1;
+			s_tid = 1;
+		} else if (lx_lpid_to_spair((pid_t)arg1, &s_pid, &s_tid) < 0) {
+			return (ESRCH);
+		}
+
+		if (copyout(&s_pid, (void *)arg2, sizeof (s_pid)) != 0 ||
+		    copyout(&s_tid, (void *)arg3, sizeof (s_tid)) != 0) {
+			return (EFAULT);
+		}
+
+		return (0);
+	}
+
+	case B_SIGEV_THREAD_ID: {
+		/*
+		 * Emulate Linux's timer_create(2) SIGEV_THREAD_ID
+		 * notification method. This mechanism is only meant
+		 * for userland threading libraries such as glibc and
+		 * is documented as such. Therefore, assume this is
+		 * only ever invoked for the purpose of alerting a
+		 * Linux threading library. Assume that the tid is a
+		 * member of the caller's process and the signal
+		 * number is valid. See lx_sigev_thread_id() for the
+		 * userland side of this emulation.
+		 *
+		 * The return code from this function is not checked
+		 * by the caller since it executes in an asynchronous
+		 * context and there is nothing much to be done. If
+		 * this function does fail then it will manifest as
+		 * Linux threads waiting for a signal they will never
+		 * receive.
+		 *
+		 * arg1 -- Linux tid
+		 * arg2 -- Linux signal number
+		 * arg3 -- sigval pointer
+		 */
+
+		int native_sig = lx_ltos_signo((int)arg2, 0);
+		pid_t spid;
+		int stid;
+		sigqueue_t *sqp;
+
+		if (native_sig == 0)
+			return (EINVAL);
+
+		if (lx_lpid_to_spair((pid_t)arg1, &spid, &stid) != 0) {
+			return (ESRCH);
+		}
+		sqp = kmem_zalloc(sizeof (sigqueue_t), KM_SLEEP);
+		mutex_enter(&curproc->p_lock);
+		if ((t = idtot(curproc, stid)) == NULL) {
+			mutex_exit(&curproc->p_lock);
+			kmem_free(sqp, sizeof (sigqueue_t));
+			return (ESRCH);
+		}
+
+		sqp->sq_info.si_signo = native_sig;
+		sqp->sq_info.si_code = SI_TIMER;
+		sqp->sq_info.si_pid = curproc->p_pid;
+		sqp->sq_info.si_zoneid = getzoneid();
+		sqp->sq_info.si_uid = crgetruid(CRED());
+		sqp->sq_info.si_value.sival_ptr = (void *)arg3;
+		sigaddqa(curproc, t, sqp);
+
+		mutex_exit(&curproc->p_lock);
+
+		return (0);
+	}
+
+	case B_SET_AFFINITY_MASK:
+	case B_GET_AFFINITY_MASK:
+		/*
+		 * Retrieve or store the CPU affinity mask for the
+		 * requested linux pid.
+		 *
+		 * arg1 is a linux PID (0 means curthread).
+		 * arg2 is the size of the given mask.
+		 * arg3 is the address of the affinity mask.
+		 */
+		return (lx_sched_affinity(cmd, arg1, arg2, arg3, rval));
+
+	case B_PTRACE_STOP_FOR_OPT:
+		return (lx_ptrace_stop_for_option((int)arg1, arg2 == 0 ?
+		    B_FALSE : B_TRUE, (ulong_t)arg3, arg4));
+
+	case B_PTRACE_CLONE_BEGIN:
+		return (lx_ptrace_set_clone_inherit((int)arg1, arg2 == 0 ?
+		    B_FALSE : B_TRUE));
+
+	case B_PTRACE_SIG_RETURN: {
+		/*
+		 * Our ptrace emulation must emit PR_SYSEXIT for rt_sigreturn.
+		 * Since that syscall does not pass through the normal
+		 * emulation, which would call lx_syscall_return, the event is
+		 * emitted manually. A successful result of the syscall is
+		 * assumed since there is little to be done in the face of
+		 * failure.
+		 */
+		struct regs *rp = lwptoregs(lwp);
+
+		rp->r_r0 = 0;
+		(void) lx_ptrace_stop(LX_PR_SYSEXIT);
+		return (0);
+	}
+
+	case B_UNSUPPORTED: {
+		char dmsg[256];
+
+		if (copyin((void *)arg1, &dmsg, sizeof (dmsg)) != 0) {
+			lx_print("Failed to copyin unsupported msg "
+			    "at 0x%p\n", (void *)arg1);
+			return (EFAULT);
+		}
+		dmsg[255] = '\0';
+		lx_unsupported(dmsg);
+
+		lx_check_strict_failure(lwpd);
+
+		return (0);
+	}
+
+	case B_STORE_ARGS: {
+		/*
+		 * B_STORE_ARGS subcommand
+		 * arg1 = address of struct to be copied in
+		 * arg2 = size of the struct being copied in
+		 * arg3-arg6 ignored
+		 * rval = the amount of data copied.
+		 */
+		void *buf;
+
+		/* only have upper limit because arg2 is unsigned */
+		if (arg2 > LX_BR_ARGS_SIZE_MAX) {
+			return (EINVAL);
+		}
+
+		buf = kmem_alloc(arg2, KM_SLEEP);
+		if (copyin((void *)arg1, buf, arg2) != 0) {
+			lx_print("Failed to copyin scall arg at 0x%p\n",
+			    (void *) arg1);
+			kmem_free(buf, arg2);
+			/*
+			 * Purposely not setting br_scall_args to NULL
+			 * to preserve data for debugging.
+			 */
+			return (EFAULT);
+		}
+
+		if (lwpd->br_scall_args != NULL) {
+			ASSERT(lwpd->br_args_size > 0);
+			kmem_free(lwpd->br_scall_args,
+			    lwpd->br_args_size);
+		}
+
+		lwpd->br_scall_args = buf;
+		lwpd->br_args_size = arg2;
+		*rval = arg2;
+		return (0);
+	}
+
+	case B_HELPER_CLONE:
+		return (lx_helper_clone(rval, arg1, (void *)arg2, (void *)arg3,
+		    (void *)arg4));
+
+	case B_HELPER_SETGROUPS:
+		return (lx_helper_setgroups(arg1, (gid_t *)arg2));
+
+	case B_HELPER_SIGQUEUE:
+		return (lx_helper_rt_sigqueueinfo(arg1, arg2,
+		    (siginfo_t *)arg3));
+
+	case B_HELPER_TGSIGQUEUE:
+		return (lx_helper_rt_tgsigqueueinfo(arg1, arg2, arg3,
+		    (siginfo_t *)arg4));
+
+	case B_SET_THUNK_PID:
+		lwpd->br_lx_thunk_pid = arg1;
+		return (0);
+
+	case B_GETPID:
+		/*
+		 * The usermode clone(2) code needs to be able to call
+		 * lx_getpid() from native code:
+		 */
+		*rval = lx_getpid();
+		return (0);
+
+	case B_SET_NATIVE_STACK:
+		/*
+		 * B_SET_NATIVE_STACK subcommand
+		 * arg1 = the base of the stack to use for emulation
+		 */
+		if (lwpd->br_stack_mode != LX_STACK_MODE_PREINIT) {
+			lx_print("B_SET_NATIVE_STACK when stack was already "
+			    "set to %p\n", (void *)arg1);
+			return (EEXIST);
+		}
+
+		/*
+		 * We move from the PREINIT state, where we have no brand
+		 * emulation stack, to the INIT state.  Here, we are still
+		 * running on what will become the BRAND stack, but are running
+		 * emulation (i.e. native) code.  Once the initialisation
+		 * process for this thread has finished, we will jump to
+		 * brand-specific code, while moving to the BRAND mode.
+		 *
+		 * When a new LWP is created, lx_initlwp() will clear the
+		 * stack data.  If that LWP is actually being duplicated
+		 * into a child process by fork(2), lx_forklwp() will copy
+		 * it so that the cloned thread will keep using the same
+		 * alternate stack.
+		 */
+		lwpd->br_ntv_stack = arg1;
+		lwpd->br_stack_mode = LX_STACK_MODE_INIT;
+		lx_lwp_set_native_stack_current(lwpd, arg1);
+
+		return (0);
+
+	case B_GET_CURRENT_CONTEXT:
+		/*
+		 * B_GET_CURRENT_CONTEXT subcommand:
+		 * arg1 = address for pointer to current ucontext_t
+		 */
+
+#if defined(_SYSCALL32_IMPL)
+		if (get_udatamodel() != DATAMODEL_NATIVE) {
+			caddr32_t addr = (caddr32_t)lwp->lwp_oldcontext;
+
+			error = copyout(&addr, (void *)arg1, sizeof (addr));
+		} else
+#endif
+		{
+			error = copyout(&lwp->lwp_oldcontext, (void *)arg1,
+			    sizeof (lwp->lwp_oldcontext));
+		}
+
+		return (error != 0 ? EFAULT : 0);
+
+	case B_JUMP_TO_LINUX:
+		/*
+		 * B_JUMP_TO_LINUX subcommand:
+		 * arg1 = ucontext_t pointer for jump state
+		 */
+
+		if (arg1 == NULL)
+			return (EINVAL);
+
+		switch (lwpd->br_stack_mode) {
+		case LX_STACK_MODE_NATIVE: {
+			struct regs *rp = lwptoregs(lwp);
+
+			/*
+			 * We are on the NATIVE stack, so we must preserve
+			 * the extent of that stack.  The pointer will be
+			 * reset by a future setcontext().
+			 */
+			lx_lwp_set_native_stack_current(lwpd,
+			    (uintptr_t)rp->r_sp);
+			break;
+		}
+
+		case LX_STACK_MODE_INIT:
+			/*
+			 * The LWP is transitioning to Linux code for the first
+			 * time.
+			 */
+			break;
+
+		case LX_STACK_MODE_PREINIT:
+			/*
+			 * This LWP has not installed an alternate stack for
+			 * usermode emulation handling.
+			 */
+			return (ENOENT);
+
+		case LX_STACK_MODE_BRAND:
+			/*
+			 * The LWP should not be on the BRAND stack.
+			 */
+			exit(CLD_KILLED, SIGSYS);
+			return (0);
+		}
+
+		/*
+		 * Transfer control to Linux:
+		 */
+		return (lx_runexe(lwp, (void *)arg1));
+
+	case B_EMULATION_DONE:
+		/*
+		 * B_EMULATION_DONE subcommand:
+		 * arg1 = ucontext_t * to restore
+		 * arg2 = system call number
+		 * arg3 = return code
+		 * arg4 = if operation failed, the errno value
+		 */
+
+		/*
+		 * The first part of this operation is a setcontext() to
+		 * restore the register state to the copy we preserved
+		 * before vectoring to the usermode emulation routine.
+		 * If that fails, we return (hopefully) to the emulation
+		 * routine and it will handle the error.
+		 */
+#if (_SYSCALL32_IMPL)
+		if (get_udatamodel() != DATAMODEL_NATIVE) {
+			error = getsetcontext32(SETCONTEXT, (void *)arg1);
+		} else
+#endif
+		{
+			error = getsetcontext(SETCONTEXT, (void *)arg1);
+		}
+
+		if (error != 0) {
+			return (error);
+		}
+
+		/*
+		 * The saved Linux context has been restored.  We handle the
+		 * return value or errno with code common to the in-kernel
+		 * system call emulation.
+		 */
+		if ((error = (int)arg4) != 0) {
+			/*
+			 * lx_syscall_return() looks at the errno in the LWP,
+			 * so set it here:
+			 */
+			(void) set_errno(error);
+		}
+		lx_syscall_return(ttolwp(curthread), (int)arg2, (long)arg3);
+
+		return (0);
+
+	case B_EXIT_AS_SIG:
+		code = CLD_KILLED;
+		sig = (int)arg1;
+		proc_is_exiting(p);
+		if (exitlwps(1) != 0) {
+			mutex_enter(&p->p_lock);
+			lwp_exit();
+		}
+		ttolwp(curthread)->lwp_cursig = sig;
+		if (sig == SIGSEGV) {
+			if (core(sig, 0) == 0)
+				code = CLD_DUMPED;
+		}
+		exit(code, sig);
+		/* NOTREACHED */
+		break;
+
+	case B_OVERRIDE_KERN_VER: {
+		void *urel = (void *)arg1;
+		void *uver = (void *)arg2;
+		size_t len;
+
+		pd = ptolxproc(p);
+		if (urel != NULL) {
+			if (copyinstr(urel, pd->l_uname_release,
+			    LX_KERN_RELEASE_MAX, &len) != 0) {
+				return (EFAULT);
+			}
+			pd->l_uname_release[LX_KERN_RELEASE_MAX - 1] = '\0';
+		}
+		if (uver != NULL) {
+			if (copyinstr(uver, pd->l_uname_version,
+			    LX_KERN_VERSION_MAX, &len) != 0) {
+				return (EFAULT);
+			}
+			pd->l_uname_version[LX_KERN_VERSION_MAX - 1] = '\0';
+		}
+
+		return (0);
+	}
+
+	case B_GET_PERSONALITY: {
+		unsigned int result;
+
+		mutex_enter(&p->p_lock);
+		pd = ptolxproc(p);
+		result = pd->l_personality;
+		mutex_exit(&p->p_lock);
+		return (result);
+	}
+
+	}
+
+	return (EINVAL);
+}
+
+/*
+ * Compare linux kernel version to the one set for the zone.
+ * Returns greater than 0 if zone version is higher, less than 0 if the zone
+ * version is lower, and 0 if the versions are equal.
+ */
+int
+lx_kern_release_cmp(zone_t *zone, const char *vers)
+{
+	int zvers[3] = {0, 0, 0};
+	int cvers[3] = {0, 0, 0};
+	int i;
+	lx_zone_data_t *lxzd = (lx_zone_data_t *)zone->zone_brand_data;
+
+	VERIFY(zone->zone_brand == &lx_brand);
+
+	mutex_enter(&lxzd->lxzd_lock);
+	(void) sscanf(lxzd->lxzd_kernel_release, "%d.%d.%d", &zvers[0],
+	    &zvers[1], &zvers[2]);
+	mutex_exit(&lxzd->lxzd_lock);
+	(void) sscanf(vers, "%d.%d.%d", &cvers[0], &cvers[1], &cvers[2]);
+
+	for (i = 0; i < 3; i++) {
+		if (zvers[i] > cvers[i]) {
+			return (1);
+		} else if (zvers[i] < cvers[i]) {
+			return (-1);
+		}
+	}
+	return (0);
+}
+
+/*
+ * Linux unconditionally removes the setuid and setgid bits when changing
+ * file ownership.  This brand hook overrides the illumos native behaviour,
+ * which is based on the PRIV_FILE_SETID privilege.
+ */
+/* ARGSUSED */
+static int
+lx_setid_clear(vattr_t *vap, cred_t *cr)
+{
+	if (S_ISDIR(vap->va_mode)) {
+		return (0);
+	}
+
+	if (vap->va_mode & S_ISUID) {
+		vap->va_mask |= AT_MODE;
+		vap->va_mode &= ~S_ISUID;
+	}
+	if ((vap->va_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
+		vap->va_mask |= AT_MODE;
+		vap->va_mode &= ~S_ISGID;
+	}
+
+	return (0);
+}
+
+/*
+ * Copy the per-process brand data from a parent proc to a child.
+ */
+void
+lx_copy_procdata(proc_t *cp, proc_t *pp)
+{
+	lx_proc_data_t *cpd, *ppd;
+
+	/*
+	 * Since b_copy_procdata is called during getproc(), while the child
+	 * process is still being initialized, acquiring cp->p_lock should not
+	 * be required.
+	 */
+	VERIFY(cp->p_brand == &lx_brand);
+	VERIFY((cpd = cp->p_brand_data) != NULL);
+
+	mutex_enter(&pp->p_lock);
+	VERIFY(pp->p_brand == &lx_brand);
+	VERIFY((ppd = pp->p_brand_data) != NULL);
+
+	bcopy(ppd, cpd, sizeof (lx_proc_data_t));
+	mutex_exit(&pp->p_lock);
+
+	/*
+	 * The l_ptrace count is normally manipulated only while under holding
+	 * p_lock.  Since this is a freshly created process, it's safe to zero
+	 * out.  If it is to be inherited, the attach will occur later.
+	 */
+	cpd->l_ptrace = 0;
+
+	cpd->l_fake_limits[LX_RLFAKE_LOCKS].rlim_cur = LX_RLIM64_INFINITY;
+	cpd->l_fake_limits[LX_RLFAKE_LOCKS].rlim_max = LX_RLIM64_INFINITY;
+
+	cpd->l_fake_limits[LX_RLFAKE_NICE].rlim_cur = 20;
+	cpd->l_fake_limits[LX_RLFAKE_NICE].rlim_max = 20;
+
+	cpd->l_fake_limits[LX_RLFAKE_RTPRIO].rlim_cur = LX_RLIM64_INFINITY;
+	cpd->l_fake_limits[LX_RLFAKE_RTPRIO].rlim_max = LX_RLIM64_INFINITY;
+
+	cpd->l_fake_limits[LX_RLFAKE_RTTIME].rlim_cur = LX_RLIM64_INFINITY;
+	cpd->l_fake_limits[LX_RLFAKE_RTTIME].rlim_max = LX_RLIM64_INFINITY;
+}
+
+#if defined(_LP64)
+static void
+Ehdr32to64(Elf32_Ehdr *src, Ehdr *dst)
+{
+	bcopy(src->e_ident, dst->e_ident, sizeof (src->e_ident));
+	dst->e_type =		src->e_type;
+	dst->e_machine =	src->e_machine;
+	dst->e_version =	src->e_version;
+	dst->e_entry =		src->e_entry;
+	dst->e_phoff =		src->e_phoff;
+	dst->e_shoff =		src->e_shoff;
+	dst->e_flags =		src->e_flags;
+	dst->e_ehsize =		src->e_ehsize;
+	dst->e_phentsize =	src->e_phentsize;
+	dst->e_phnum =		src->e_phnum;
+	dst->e_shentsize =	src->e_shentsize;
+	dst->e_shnum =		src->e_shnum;
+	dst->e_shstrndx =	src->e_shstrndx;
+}
+#endif /* _LP64 */
+
+static void
+restoreexecenv(struct execenv *ep, stack_t *sp)
+{
+	klwp_t *lwp = ttolwp(curthread);
+
+	setexecenv(ep);
+	lwp->lwp_sigaltstack.ss_sp = sp->ss_sp;
+	lwp->lwp_sigaltstack.ss_size = sp->ss_size;
+	lwp->lwp_sigaltstack.ss_flags = sp->ss_flags;
+}
+
+extern int elfexec(vnode_t *, execa_t *, uarg_t *, intpdata_t *, int,
+    long *, int, caddr_t, cred_t *, int *);
+
+extern int elf32exec(struct vnode *, execa_t *, uarg_t *, intpdata_t *, int,
+    long *, int, caddr_t, cred_t *, int *);
+
+static uintptr_t
+lx_map_vdso(struct uarg *args, struct cred *cred)
+{
+	int err;
+	char *fpath = LX_VDSO_PATH;
+	vnode_t *vp;
+	vattr_t attr;
+	caddr_t addr;
+
+#if defined(_LP64)
+	if (args->to_model != DATAMODEL_NATIVE) {
+		fpath = LX_VDSO_PATH32;
+	}
+#endif
+
+	/*
+	 * The comm page should have been mapped in already.
+	 */
+	if (args->commpage == NULL) {
+		return (NULL);
+	}
+
+	/*
+	 * Ensure the VDSO library is present and appropriately sized.
+	 * This lookup is started at the zone root to avoid complications for
+	 * processes which have chrooted.  For the specified lookup root to be
+	 * used, the leading slash must be dropped from the path.
+	 */
+	ASSERT(fpath[0] == '/');
+	fpath++;
+	if (lookupnameat(fpath, UIO_SYSSPACE, FOLLOW, NULLVPP, &vp,
+	    curzone->zone_rootvp) != 0) {
+		return (NULL);
+	}
+
+	/*
+	 * The VDSO requires data exposed via the comm page in order to
+	 * function properly.  The VDSO is always mapped in at a fixed known
+	 * offset from the comm page, providing an easy means to locate it.
+	 */
+	addr = (caddr_t)(args->commpage - LX_VDSO_SIZE);
+	attr.va_mask = AT_SIZE;
+	if (VOP_GETATTR(vp, &attr, 0, cred, NULL) != 0 ||
+	    attr.va_size > LX_VDSO_SIZE) {
+		VN_RELE(vp);
+		return (NULL);
+	}
+
+	err = execmap(vp, addr, attr.va_size, 0, 0,
+	    PROT_USER|PROT_READ|PROT_EXEC, 1, 0);
+	VN_RELE(vp);
+	if (err != 0) {
+		return (NULL);
+	}
+	return ((uintptr_t)addr);
+}
+
+/*
+ * Exec routine called by elfexec() to load either 32-bit or 64-bit Linux
+ * binaries.
+ */
+/* ARGSUSED4 */
+static int
+lx_elfexec(struct vnode *vp, struct execa *uap, struct uarg *args,
+    struct intpdata *idata, int level, long *execsz, int setid,
+    caddr_t exec_file, struct cred *cred, int *brand_action)
+{
+	int		error, i;
+	vnode_t		*nvp;
+	Ehdr		ehdr;
+	Addr		uphdr_vaddr;
+	intptr_t	voffset;
+	char		*interp = NULL;
+	uintptr_t	ldaddr = NULL;
+	proc_t		*p = ttoproc(curthread);
+	klwp_t		*lwp = ttolwp(curthread);
+	lx_proc_data_t	*lxpd = ptolxproc(p);
+	struct execenv	env, origenv;
+	stack_t		orig_sigaltstack;
+	struct user	*up = PTOU(ttoproc(curthread));
+	lx_elf_data_t	edp;
+	char		*lib_path = LX_LIB_PATH;
+	boolean_t	execstk = B_TRUE;
+	unsigned int	personality;
+
+	ASSERT(p->p_brand == &lx_brand);
+	ASSERT(lxpd != NULL);
+
+	/*
+	 * Start with a separate struct for ELF data instead of inheriting
+	 * values from the currently running binary.  This ensures that fields
+	 * such as ed_base are cleared if the new binary does not utilize an
+	 * interpreter.
+	 */
+	bzero(&edp, sizeof (edp));
+
+#if defined(_LP64)
+	if (args->to_model != DATAMODEL_NATIVE) {
+		lib_path = LX_LIB_PATH32;
+	}
+#endif
+
+	/*
+	 * Set the brandname and library name for the new process so that
+	 * elfexec() puts them onto the stack.
+	 */
+	args->brandname = LX_BRANDNAME;
+	args->emulator = lib_path;
+
+#if defined(_LP64)
+	/*
+	 * To conform with the way Linux lays out the address space, we clamp
+	 * the stack to be the top of the lower region of the x86-64 canonical
+	 * form address space -- which has the side-effect of laying out the
+	 * entire address space in that lower region.  Note that this only
+	 * matters on 64-bit processes (this value will always be greater than
+	 * the size of a 32-bit address space) and doesn't actually affect
+	 * USERLIMIT:  if a Linux-branded processes wishes to map something
+	 * into the top half of the address space, it can do so -- but with
+	 * the user stack starting at the top of the bottom region, those high
+	 * virtual addresses won't be used unless explicitly directed.
+	 */
+	args->maxstack = lx_maxstack64;
+#endif
+
+	/*
+	 * Search the binary for a PT_GNU_STACK header.  The PF_X bit contained
+	 * within is used to dictate protection defaults for the stack, among
+	 * other things.
+	 */
+	if (args->to_model == DATAMODEL_NATIVE) {
+		Ehdr ehdr;
+		Phdr *phdrp;
+		caddr_t phdrbase = NULL;
+		ssize_t phdrsize = 0;
+		int nphdrs, hsize;
+
+		if ((error = elfreadhdr(vp, cred, &ehdr, &nphdrs, &phdrbase,
+		    &phdrsize)) != 0) {
+			return (error);
+		}
+
+		hsize = ehdr.e_phentsize;
+		/* LINTED: alignment */
+		phdrp = (Phdr *)phdrbase;
+		for (i = nphdrs; i > 0; i--) {
+			switch (phdrp->p_type) {
+			case PT_GNU_STACK:
+				if ((phdrp->p_flags & PF_X) == 0) {
+					execstk = B_FALSE;
+				}
+				break;
+			}
+			/* LINTED: alignment */
+			phdrp = (Phdr *)((caddr_t)phdrp + hsize);
+		}
+		kmem_free(phdrbase, phdrsize);
+	}
+#if defined(_LP64)
+	else {
+		Elf32_Ehdr ehdr;
+		Elf32_Phdr *phdrp;
+		caddr_t phdrbase = NULL;
+		ssize_t phdrsize = 0;
+		int nphdrs, hsize;
+
+		if ((error = elf32readhdr(vp, cred, &ehdr, &nphdrs, &phdrbase,
+		    &phdrsize)) != 0) {
+			return (error);
+		}
+
+		hsize = ehdr.e_phentsize;
+		/* LINTED: alignment */
+		phdrp = (Elf32_Phdr *)phdrbase;
+		for (i = nphdrs; i > 0; i--) {
+			switch (phdrp->p_type) {
+			case PT_GNU_STACK:
+				if ((phdrp->p_flags & PF_X) == 0) {
+					execstk = B_FALSE;
+				}
+				break;
+			}
+			/* LINTED: alignment */
+			phdrp = (Elf32_Phdr *)((caddr_t)phdrp + hsize);
+		}
+		kmem_free(phdrbase, phdrsize);
+	}
+#endif
+
+	/*
+	 * Revert the base personality while maintaining any existing flags.
+	 */
+	personality = LX_PER_LINUX | (lxpd->l_personality & ~LX_PER_MASK);
+
+	/*
+	 * Linux defaults to an executable stack unless the aformentioned
+	 * PT_GNU_STACK entry in the elf header dictates otherwise.  Enabling
+	 * the READ_IMPLIES_EXEC personality flag is also implied in this case.
+	 */
+	if (execstk) {
+		args->stk_prot |= PROT_EXEC;
+		args->stk_prot_override = B_TRUE;
+		personality |= LX_PER_READ_IMPLIES_EXEC;
+	}
+
+	/*
+	 * We will first exec the brand library, then map in the linux
+	 * executable and the linux linker.
+	 */
+	if ((error = lookupname(lib_path, UIO_SYSSPACE, FOLLOW, NULLVPP,
+	    &nvp))) {
+		uprintf("%s: not found.", lib_path);
+		return (error);
+	}
+
+	/*
+	 * We will eventually set the p_exec member to be the vnode for the new
+	 * executable when we call setexecenv(). However, if we get an error
+	 * before that call we need to restore the execenv to its original
+	 * values so that when we return to the caller fop_close() works
+	 * properly while cleaning up from the failed exec().  Restoring the
+	 * original value will also properly decrement the 2nd VN_RELE that we
+	 * took on the brand library.
+	 */
+	origenv.ex_bssbase = p->p_bssbase;
+	origenv.ex_brkbase = p->p_brkbase;
+	origenv.ex_brksize = p->p_brksize;
+	origenv.ex_vp = p->p_exec;
+	orig_sigaltstack.ss_sp = lwp->lwp_sigaltstack.ss_sp;
+	orig_sigaltstack.ss_size = lwp->lwp_sigaltstack.ss_size;
+	orig_sigaltstack.ss_flags = lwp->lwp_sigaltstack.ss_flags;
+
+	if (args->to_model == DATAMODEL_NATIVE) {
+		error = elfexec(nvp, uap, args, idata, INTP_MAXDEPTH + 1,
+		    execsz, setid, exec_file, cred, brand_action);
+	}
+#if defined(_LP64)
+	else {
+		error = elf32exec(nvp, uap, args, idata, INTP_MAXDEPTH + 1,
+		    execsz, setid, exec_file, cred, brand_action);
+	}
+#endif
+	VN_RELE(nvp);
+	if (error != 0) {
+		restoreexecenv(&origenv, &orig_sigaltstack);
+		return (error);
+	}
+
+	/*
+	 * exec-ed in the brand library above.
+	 * The u_auxv vectors are now setup by elfexec to point to the
+	 * brand emulation library and its linker.
+	 */
+
+	/*
+	 * After execing the brand library (which should have implicitly mapped
+	 * in the comm page), map the VDSO into the approprate place in the AS.
+	 */
+	lxpd->l_vdso = lx_map_vdso(args, cred);
+
+	bzero(&env, sizeof (env));
+
+	/*
+	 * map in the the Linux executable
+	 */
+	if (args->to_model == DATAMODEL_NATIVE) {
+		error = mapexec_brand(vp, args, &ehdr, &uphdr_vaddr,
+		    &voffset, exec_file, &interp, &env.ex_bssbase,
+		    &env.ex_brkbase, &env.ex_brksize, NULL, NULL);
+	}
+#if defined(_LP64)
+	else {
+		Elf32_Ehdr	ehdr32;
+		Elf32_Addr	uphdr_vaddr32;
+
+		error = mapexec32_brand(vp, args, &ehdr32, &uphdr_vaddr32,
+		    &voffset, exec_file, &interp, &env.ex_bssbase,
+		    &env.ex_brkbase, &env.ex_brksize, NULL, NULL);
+
+		Ehdr32to64(&ehdr32, &ehdr);
+
+		if (uphdr_vaddr32 == (Elf32_Addr)-1)
+			uphdr_vaddr = (Addr)-1;
+		else
+			uphdr_vaddr = uphdr_vaddr32;
+	}
+#endif
+	if (error != 0) {
+		restoreexecenv(&origenv, &orig_sigaltstack);
+
+		if (interp != NULL)
+			kmem_free(interp, MAXPATHLEN);
+
+		return (error);
+	}
+
+	/*
+	 * Save off the important properties of the lx executable. The brand
+	 * library will ask us for this data later, when it is ready to set
+	 * things up for the lx executable.
+	 */
+	edp.ed_phdr = (uphdr_vaddr == -1) ? voffset + ehdr.e_phoff :
+	    voffset + uphdr_vaddr;
+	edp.ed_entry = voffset + ehdr.e_entry;
+	edp.ed_phent = ehdr.e_phentsize;
+	edp.ed_phnum = ehdr.e_phnum;
+
+	if (interp != NULL) {
+		if (ehdr.e_type == ET_DYN) {
+			/*
+			 * This is a shared object executable, so we need to
+			 * pick a reasonable place to put the heap. Just don't
+			 * use the first page.
+			 */
+			env.ex_brkbase = (caddr_t)PAGESIZE;
+			env.ex_bssbase = (caddr_t)PAGESIZE;
+		}
+
+		/*
+		 * If the program needs an interpreter (most do), map it in and
+		 * store relevant information about it in the aux vector, where
+		 * the brand library can find it.
+		 */
+		if ((error = lookupname(interp, UIO_SYSSPACE, FOLLOW,
+		    NULLVPP, &nvp))) {
+			uprintf("%s: not found.", interp);
+			restoreexecenv(&origenv, &orig_sigaltstack);
+			kmem_free(interp, MAXPATHLEN);
+			return (error);
+		}
+
+		kmem_free(interp, MAXPATHLEN);
+		interp = NULL;
+
+		/*
+		 * map in the Linux linker
+		 */
+		if (args->to_model == DATAMODEL_NATIVE) {
+			error = mapexec_brand(nvp, args, &ehdr,
+			    &uphdr_vaddr, &voffset, exec_file, NULL, NULL,
+			    NULL, NULL, NULL, &ldaddr);
+		}
+#if defined(_LP64)
+		else {
+			Elf32_Ehdr	ehdr32;
+			Elf32_Addr	uphdr_vaddr32;
+
+			error = mapexec32_brand(nvp, args, &ehdr32,
+			    &uphdr_vaddr32, &voffset, exec_file, NULL, NULL,
+			    NULL, NULL, NULL, &ldaddr);
+
+			Ehdr32to64(&ehdr32, &ehdr);
+
+			if (uphdr_vaddr32 == (Elf32_Addr)-1)
+				uphdr_vaddr = (Addr)-1;
+			else
+				uphdr_vaddr = uphdr_vaddr32;
+		}
+#endif
+
+		VN_RELE(nvp);
+		if (error != 0) {
+			restoreexecenv(&origenv, &orig_sigaltstack);
+			return (error);
+		}
+
+		/*
+		 * Now that we know the base address of the brand's linker,
+		 * we also save this for later use by the brand library.
+		 */
+		edp.ed_base = voffset;
+		edp.ed_ldentry = voffset + ehdr.e_entry;
+	} else {
+		/*
+		 * This program has no interpreter. The lx brand library will
+		 * jump to the address in the AT_SUN_BRAND_LDENTRY aux vector,
+		 * so in this case, put the entry point of the main executable
+		 * there.
+		 */
+		if (ehdr.e_type == ET_EXEC) {
+			/*
+			 * An executable with no interpreter, this must be a
+			 * statically linked executable, which means we loaded
+			 * it at the address specified in the elf header, in
+			 * which case the e_entry field of the elf header is an
+			 * absolute address.
+			 */
+			edp.ed_ldentry = ehdr.e_entry;
+			edp.ed_entry = ehdr.e_entry;
+		} else {
+			/*
+			 * A shared object with no interpreter, we use the
+			 * calculated address from above.
+			 */
+			edp.ed_ldentry = edp.ed_entry;
+
+			/*
+			 * In all situations except an ET_DYN elf object with no
+			 * interpreter, we want to leave the brk and base
+			 * values set by mapexec_brand alone. Normally when
+			 * running ET_DYN objects on Solaris (most likely
+			 * /lib/ld.so.1) the kernel sets brk and base to 0 since
+			 * it doesn't know where to put the heap, and later the
+			 * linker will call brk() to initialize the heap in:
+			 *	usr/src/cmd/sgs/rtld/common/setup.c:setup()
+			 * after it has determined where to put it.  (This
+			 * decision is made after the linker loads and inspects
+			 * elf properties of the target executable being run.)
+			 *
+			 * So for ET_DYN Linux executables, we also don't know
+			 * where the heap should go, so we'll set the brk and
+			 * base to 0.  But in this case the Solaris linker will
+			 * not initialize the heap, so when the Linux linker
+			 * starts running there is no heap allocated.  This
+			 * seems to be ok on Linux 2.4 based systems because the
+			 * Linux linker/libc fall back to using mmap() to
+			 * allocate memory. But on 2.6 systems, running
+			 * applications by specifying them as command line
+			 * arguments to the linker results in segfaults for an
+			 * as yet undetermined reason (which seems to indicatej
+			 * that a more permanent fix for heap initalization in
+			 * these cases may be necessary).
+			 */
+			if (ehdr.e_type == ET_DYN) {
+				env.ex_bssbase = (caddr_t)0;
+				env.ex_brkbase = (caddr_t)0;
+				env.ex_brksize = 0;
+			}
+		}
+	}
+
+	env.ex_vp = vp;
+	setexecenv(&env);
+
+	/*
+	 * We try to keep /proc's view of the aux vector consistent with
+	 * what's on the process stack.
+	 */
+	if (args->to_model == DATAMODEL_NATIVE) {
+		auxv_t phdr_auxv[4] = {
+		    { AT_SUN_BRAND_LX_PHDR, 0 },
+		    { AT_SUN_BRAND_LX_INTERP, 0 },
+		    { AT_SUN_BRAND_LX_CLKTCK, 0 },
+		    { AT_SUN_BRAND_LX_SYSINFO_EHDR, 0 }
+		};
+		phdr_auxv[0].a_un.a_val = edp.ed_phdr;
+		phdr_auxv[1].a_un.a_val = ldaddr;
+		phdr_auxv[2].a_un.a_val = hz;
+		phdr_auxv[3].a_un.a_val = lxpd->l_vdso;
+
+		if (copyout(&phdr_auxv, args->auxp_brand,
+		    sizeof (phdr_auxv)) == -1)
+			return (EFAULT);
+	}
+#if defined(_LP64)
+	else {
+		auxv32_t phdr_auxv32[4] = {
+		    { AT_SUN_BRAND_LX_PHDR, 0 },
+		    { AT_SUN_BRAND_LX_INTERP, 0 },
+		    { AT_SUN_BRAND_LX_CLKTCK, 0 },
+		    { AT_SUN_BRAND_LX_SYSINFO_EHDR, 0 }
+		};
+		phdr_auxv32[0].a_un.a_val = edp.ed_phdr;
+		phdr_auxv32[1].a_un.a_val = ldaddr;
+		phdr_auxv32[2].a_un.a_val = hz;
+		phdr_auxv32[3].a_un.a_val = lxpd->l_vdso;
+
+		if (copyout(&phdr_auxv32, args->auxp_brand,
+		    sizeof (phdr_auxv32)) == -1)
+			return (EFAULT);
+	}
+#endif
+
+	/*
+	 * /proc uses the AT_ENTRY aux vector entry to deduce
+	 * the location of the executable in the address space. The user
+	 * structure contains a copy of the aux vector that needs to have those
+	 * entries patched with the values of the real lx executable (they
+	 * currently contain the values from the lx brand library that was
+	 * elfexec'd, above).
+	 *
+	 * For live processes, AT_BASE is used to locate the linker segment,
+	 * which /proc and friends will later use to find Solaris symbols
+	 * (such as rtld_db_preinit). However, for core files, /proc uses
+	 * AT_ENTRY to find the right segment to label as the executable.
+	 * So we set AT_ENTRY to be the entry point of the linux executable,
+	 * but leave AT_BASE to be the address of the Solaris linker.
+	 */
+	for (i = 0; i < __KERN_NAUXV_IMPL; i++) {
+		switch (up->u_auxv[i].a_type) {
+		case AT_ENTRY:
+			up->u_auxv[i].a_un.a_val = edp.ed_entry;
+			break;
+
+		case AT_SUN_BRAND_LX_PHDR:
+			up->u_auxv[i].a_un.a_val = edp.ed_phdr;
+			break;
+
+		case AT_SUN_BRAND_LX_INTERP:
+			up->u_auxv[i].a_un.a_val = ldaddr;
+			break;
+
+		case AT_SUN_BRAND_LX_CLKTCK:
+			up->u_auxv[i].a_un.a_val = hz;
+			break;
+
+		default:
+			break;
+		}
+	}
+
+	/*
+	 * Record the brand ELF data and new personality now that the exec has
+	 * proceeded successfully.
+	 */
+	bcopy(&edp, &lxpd->l_elf_data, sizeof (edp));
+	lxpd->l_personality = personality;
+
+	return (0);
+}
+
+boolean_t
+lx_native_exec(uint8_t osabi, const char **interp)
+{
+	if (osabi != ELFOSABI_SOLARIS)
+		return (B_FALSE);
+
+	/*
+	 * If the process root matches the zone root, prepend /native to the
+	 * interpreter path for native executables.  Absolute precision from
+	 * VN_CMP is not necessary since any change of process root is likely
+	 * to make native binaries inaccessible via /native.
+	 *
+	 * Processes which chroot directly into /native will be able to
+	 * function as expected with no need for the prefix.
+	 */
+	if (VN_CMP(curproc->p_user.u_rdir, curproc->p_zone->zone_rootvp)) {
+		*interp = "/native";
+	}
+
+	return (B_TRUE);
+}
+
+static void
+lx_syscall_init(void)
+{
+	int i;
+
+	/*
+	 * Count up the 32-bit Linux system calls.  Note that lx_sysent32
+	 * has (LX_NSYSCALLS + 1) entries.
+	 */
+	for (i = 0; i <= LX_NSYSCALLS && lx_sysent32[i].sy_name != NULL; i++)
+		continue;
+	lx_nsysent32 = i;
+
+#if defined(_LP64)
+	/*
+	 * Count up the 64-bit Linux system calls.  Note that lx_sysent64
+	 * has (LX_NSYSCALLS + 1) entries.
+	 */
+	for (i = 0; i <= LX_NSYSCALLS && lx_sysent64[i].sy_name != NULL; i++)
+		continue;
+	lx_nsysent64 = i;
+#endif
+}
+
+int
+_init(void)
+{
+	int err = 0;
+
+	lx_syscall_init();
+	lx_pid_init();
+	lx_ioctl_init();
+	lx_futex_init();
+	lx_ptrace_init();
+	lx_socket_init();
+
+	err = mod_install(&modlinkage);
+	if (err != 0) {
+		cmn_err(CE_WARN, "Couldn't install lx brand module");
+
+		/*
+		 * This looks drastic, but it should never happen.  These
+		 * two data structures should be completely free-able until
+		 * they are used by Linux processes.  Since the brand
+		 * wasn't loaded there should be no Linux processes, and
+		 * thus no way for these data structures to be modified.
+		 */
+		lx_pid_fini();
+		lx_ioctl_fini();
+		if (lx_futex_fini())
+			panic("lx brand module cannot be loaded or unloaded.");
+	}
+	return (err);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+	int err;
+	int futex_done = 0;
+
+	/*
+	 * If there are any zones using this brand, we can't allow it to be
+	 * unloaded.
+	 */
+	if (brand_zone_count(&lx_brand))
+		return (EBUSY);
+
+	lx_ptrace_fini();
+	lx_pid_fini();
+	lx_ioctl_fini();
+	lx_socket_fini();
+
+	if ((err = lx_futex_fini()) != 0) {
+		goto done;
+	}
+	futex_done = 1;
+
+	err = mod_remove(&modlinkage);
+
+done:
+	if (err) {
+		/*
+		 * If we can't unload the module, then we have to get it
+		 * back into a sane state.
+		 */
+		lx_ptrace_init();
+		lx_pid_init();
+		lx_ioctl_init();
+		lx_socket_init();
+
+		if (futex_done) {
+			lx_futex_init();
+		}
+	}
+
+	return (err);
+}
--- /dev/null	Wed Oct  5 00:32:05 2016
+++ new/usr/src/uts/common/brand/lx/os/lx_misc.c	Wed Oct  5 00:32:05 2016
@@ -0,0 +1,1124 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright 2016, Joyent, Inc.
+ */
+
+#include <sys/errno.h>
+#include <sys/systm.h>
+#include <sys/archsystm.h>
+#include <sys/privregs.h>
+#include <sys/exec.h>
+#include <sys/lwp.h>
+#include <sys/sem.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_siginfo.h>
+#include <sys/lx_futex.h>
+#include <lx_errno.h>
+#include <sys/cmn_err.h>
+#include <sys/siginfo.h>
+#include <sys/contract/process_impl.h>
+#include <sys/x86_archext.h>
+#include <sys/sdt.h>
+#include <lx_signum.h>
+#include <lx_syscall.h>
+#include <sys/proc.h>
+#include <sys/procfs.h>
+#include <net/if.h>
+#include <inet/ip6.h>
+#include <sys/sunddi.h>
+#include <sys/dlpi.h>
+#include <sys/sysmacros.h>
+
+/* Linux specific functions and definitions */
+static void lx_save(klwp_t *);
+static void lx_restore(klwp_t *);
+
+/*
+ * Set the return code for the forked child, always zero
+ */
+/*ARGSUSED*/
+void
+lx_setrval(klwp_t *lwp, int v1, int v2)
+{
+	lwptoregs(lwp)->r_r0 = 0;
+}
+
+/*
+ * Reset process state on exec(2)
+ */
+void
+lx_exec()
+{
+	klwp_t *lwp = ttolwp(curthread);
+	struct lx_lwp_data *lwpd = lwptolxlwp(lwp);
+	proc_t *p = ttoproc(curthread);
+	lx_proc_data_t *pd = ptolxproc(p);
+	struct regs *rp = lwptoregs(lwp);
+
+	/* b_exec is called without p_lock held */
+	VERIFY(MUTEX_NOT_HELD(&p->p_lock));
+
+	/*
+	 * Any l_handler handlers set as a result of B_REGISTER are now
+	 * invalid; clear them.
+	 */
+	pd->l_handler = NULL;
+
+	/*
+	 * If this was a multi-threaded Linux process and this lwp wasn't the
+	 * main lwp, then we need to make its Illumos and Linux PIDs match.
+	 */
+	if (curthread->t_tid != 1) {
+		lx_pid_reassign(curthread);
+	}
+
+	/*
+	 * Inform ptrace(2) that we are processing an execve(2) call so that if
+	 * we are traced we can post either the PTRACE_EVENT_EXEC event or the
+	 * legacy SIGTRAP.
+	 */
+	(void) lx_ptrace_stop_for_option(LX_PTRACE_O_TRACEEXEC, B_FALSE, 0, 0);
+
+	/* clear the fs/gsbase values until the app. can reinitialize them */
+	lwpd->br_lx_fsbase = NULL;
+	lwpd->br_ntv_fsbase = NULL;
+	lwpd->br_lx_gsbase = NULL;
+	lwpd->br_ntv_gsbase = NULL;
+
+	/*
+	 * Clear the native stack flags.  This will be reinitialised by
+	 * lx_init() in the new process image.
+	 */
+	lwpd->br_stack_mode = LX_STACK_MODE_PREINIT;
+	lwpd->br_ntv_stack = 0;
+	lwpd->br_ntv_stack_current = 0;
+
+	installctx(lwptot(lwp), lwp, lx_save, lx_restore, NULL, NULL, lx_save,
+	    NULL);
+
+	/*
+	 * clear out the tls array
+	 */
+	bzero(lwpd->br_tls, sizeof (lwpd->br_tls));
+
+	/*
+	 * reset the tls entries in the gdt
+	 */
+	kpreempt_disable();
+	lx_restore(lwp);
+	kpreempt_enable();
+
+	/* Grab the updated argv bounds */
+	mutex_enter(&p->p_lock);
+	lx_read_argv_bounds(p);
+	mutex_exit(&p->p_lock);
+
+	/*
+	 * The exec syscall doesn't return (so we don't call lx_syscall_return)
+	 * but for our ptrace emulation we need to do this so that a tracer
+	 * does not get out of sync. We know that by the time this lx_exec
+	 * function is called that the exec has succeeded.
+	 */
+	rp->r_r0 = 0;
+	(void) lx_ptrace_stop(LX_PR_SYSEXIT);
+}
+
+static void
+lx_cleanlwp(klwp_t *lwp, proc_t *p)
+{
+	struct lx_lwp_data *lwpd = lwptolxlwp(lwp);
+	void *rb_list = NULL;
+
+	VERIFY(lwpd != NULL);
+
+	mutex_enter(&p->p_lock);
+	if ((lwpd->br_ptrace_flags & LX_PTF_EXITING) == 0) {
+		lx_ptrace_exit(p, lwp);
+	}
+
+	/*
+	 * While we have p_lock, safely grab any robust_list references and
+	 * clear the lwp field.
+	 */
+	sprlock_proc(p);
+	rb_list = lwpd->br_robust_list;
+	lwpd->br_robust_list = NULL;
+	sprunlock(p);
+
+	if (rb_list != NULL) {
+		lx_futex_robust_exit((uintptr_t)rb_list, lwpd->br_pid);
+	}
+}
+
+void
+lx_exitlwp(klwp_t *lwp)
+{
+	struct lx_lwp_data *lwpd = lwptolxlwp(lwp);
+	proc_t *p = lwptoproc(lwp);
+	kthread_t *t;
+	sigqueue_t *sqp = NULL;
+	pid_t ppid;
+	id_t ptid;
+
+	VERIFY(MUTEX_NOT_HELD(&p->p_lock));
+
+	if (lwpd == NULL) {
+		/* second time thru' */
+		return;
+	}
+
+	lx_cleanlwp(lwp, p);
+
+	if (lwpd->br_clear_ctidp != NULL) {
+		(void) suword32(lwpd->br_clear_ctidp, 0);
+		(void) lx_futex((uintptr_t)lwpd->br_clear_ctidp, FUTEX_WAKE, 1,
+		    NULL, NULL, 0);
+		lwpd->br_clear_ctidp = NULL;
+	}
+
+	if (lwpd->br_signal != 0) {
+		/*
+		 * The first thread in a process doesn't cause a signal to
+		 * be sent when it exits.  It was created by a fork(), not
+		 * a clone(), so the parent should get signalled when the
+		 * process exits.
+		 */
+		if (lwpd->br_ptid == -1)
+			goto free;
+
+		sqp = kmem_zalloc(sizeof (sigqueue_t), KM_SLEEP);
+		/*
+		 * If br_ppid is 0, it means this is a CLONE_PARENT thread,
+		 * so the signal goes to the parent process - not to a
+		 * specific thread in this process.
+		 */
+		p = lwptoproc(lwp);
+		if (lwpd->br_ppid == 0) {
+			mutex_enter(&p->p_lock);
+			ppid = p->p_ppid;
+			t = NULL;
+		} else {
+			/*
+			 * If we have been reparented to init or if our
+			 * parent thread is gone, then nobody gets
+			 * signaled.
+			 */
+			if ((lx_lwp_ppid(lwp, &ppid, &ptid) == 1) ||
+			    (ptid == -1))
+				goto free;
+
+			mutex_enter(&pidlock);
+			if ((p = prfind(ppid)) == NULL || p->p_stat == SIDL) {
+				mutex_exit(&pidlock);
+				goto free;
+			}
+			mutex_enter(&p->p_lock);
+			mutex_exit(&pidlock);
+
+			if ((t = idtot(p, ptid)) == NULL) {
+				mutex_exit(&p->p_lock);
+				goto free;
+			}
+		}
+
+		sqp->sq_info.si_signo = lwpd->br_signal;
+		sqp->sq_info.si_code = lwpd->br_exitwhy;
+		sqp->sq_info.si_status = lwpd->br_exitwhat;
+		sqp->sq_info.si_pid = lwpd->br_pid;
+		sqp->sq_info.si_uid = crgetruid(CRED());
+		sigaddqa(p, t, sqp);
+		mutex_exit(&p->p_lock);
+		sqp = NULL;
+	}
+
+free:
+	if (lwpd->br_scall_args != NULL) {
+		ASSERT(lwpd->br_args_size > 0);
+		kmem_free(lwpd->br_scall_args, lwpd->br_args_size);
+	}
+	if (sqp)
+		kmem_free(sqp, sizeof (sigqueue_t));
+}
+
+void
+lx_freelwp(klwp_t *lwp)
+{
+	struct lx_lwp_data *lwpd = lwptolxlwp(lwp);
+	proc_t *p = lwptoproc(lwp);
+	lx_zone_data_t *lxzdata;
+	vfs_t *cgrp;
+
+	VERIFY(MUTEX_NOT_HELD(&p->p_lock));
+
+	if (lwpd == NULL) {
+		/*
+		 * There is one case where an LX branded process will possess
+		 * LWPs which lack their own brand data.  During the course of
+		 * executing native binary, the process will be preemptively
+		 * branded to allow hooks such as b_native_exec to function.
+		 * If that process possesses multiple LWPS, they will _not_ be
+		 * branded since they will exit if the exec succeeds.  It's
+		 * during this LWP exit that lx_freelwp would be called on an
+		 * unbranded LWP.  When that is the case, it is acceptable to
+		 * bypass the hook.
+		 */
+		return;
+	}
+
+	/* cgroup integration */
+	lxzdata = ztolxzd(p->p_zone);
+	mutex_enter(&lxzdata->lxzd_lock);
+	cgrp = lxzdata->lxzd_cgroup;
+	if (cgrp != NULL) {
+		VFS_HOLD(cgrp);
+		mutex_exit(&lxzdata->lxzd_lock);
+		ASSERT(lx_cgrp_freelwp != NULL);
+		(*lx_cgrp_freelwp)(cgrp, lwpd->br_cgroupid, lwptot(lwp)->t_tid,
+		    lwpd->br_pid);
+		VFS_RELE(cgrp);
+	} else {
+		mutex_exit(&lxzdata->lxzd_lock);
+	}
+
+	/*
+	 * It is possible for the lx_freelwp hook to be called without a prior
+	 * call to lx_exitlwp being made.  This happens as part of lwp
+	 * de-branding when a native binary is executed from a branded process.
+	 *
+	 * To cover all cases, lx_cleanlwp is called from lx_exitlwp as well
+	 * here in lx_freelwp.  When the second call is redundant, the
+	 * resources will already be freed and no work will be needed.
+	 */
+	lx_cleanlwp(lwp, p);
+
+	/*
+	 * Remove our system call interposer.
+	 */
+	lwp->lwp_brand_syscall = NULL;
+
+	(void) removectx(lwptot(lwp), lwp, lx_save, lx_restore, NULL, NULL,
+	    lx_save, NULL);
+	if (lwpd->br_pid != 0) {
+		lx_pid_rele(lwptoproc(lwp)->p_pid, lwptot(lwp)->t_tid);
+	}
+
+	/*
+	 * Ensure that lx_ptrace_exit() has been called to detach
+	 * ptrace(2) tracers and tracees.
+	 */
+	VERIFY(lwpd->br_ptrace_tracer == NULL);
+	VERIFY(lwpd->br_ptrace_accord == NULL);
+
+	lwp->lwp_brand = NULL;
+	kmem_free(lwpd, sizeof (struct lx_lwp_data));
+}
+
+void *
+lx_lwpdata_alloc(proc_t *p)
+{
+	lx_lwp_data_t *lwpd;
+	struct lx_pid *lpidp;
+	pid_t newpid = 0;
+	struct pid *pidp = NULL;
+
+	VERIFY(MUTEX_NOT_HELD(&p->p_lock));
+
+	/*
+	 * LWPs beyond the first will require a pid to be allocated to emulate
+	 * Linux's goofy thread model.  While this  allocation may be
+	 * unnecessary when a single-lwp process undergoes branding, it cannot
+	 * be performed during b_initlwp due to p_lock being held.
+	 */
+	if (p->p_lwpcnt > 0) {
+		if ((newpid = pid_allocate(p, 0, 0)) < 0) {
+			return (NULL);
+		}
+		pidp = pid_find(newpid);
+	}
+
+	lwpd = kmem_zalloc(sizeof (struct lx_lwp_data), KM_SLEEP);
+	lpidp = kmem_zalloc(sizeof (struct lx_pid), KM_SLEEP);
+
+	lpidp->l_pid = newpid;
+	lpidp->l_pidp = pidp;
+	lwpd->br_lpid = lpidp;
+	return (lwpd);
+}
+
+/*
+ * Free lwp brand data if an error occurred during lwp_create.
+ * Otherwise, lx_freelwp will be used to free the resources after they're
+ * associated with the lwp via lx_initlwp.
+ */
+void
+lx_lwpdata_free(void *lwpbd)
+{
+	lx_lwp_data_t *lwpd = (lx_lwp_data_t *)lwpbd;
+	VERIFY(lwpd != NULL);
+	VERIFY(lwpd->br_lpid != NULL);
+
+	if (lwpd->br_lpid->l_pidp != NULL) {
+		(void) pid_rele(lwpd->br_lpid->l_pidp);
+	}
+	kmem_free(lwpd->br_lpid, sizeof (*lwpd->br_lpid));
+	kmem_free(lwpd, sizeof (*lwpd));
+}
+
+void
+lx_initlwp(klwp_t *lwp, void *lwpbd)
+{
+	lx_lwp_data_t *lwpd = (lx_lwp_data_t *)lwpbd;
+	lx_lwp_data_t *plwpd = ttolxlwp(curthread);
+	kthread_t *tp = lwptot(lwp);
+	proc_t *p = lwptoproc(lwp);
+	lx_zone_data_t *lxzdata;
+	vfs_t *cgrp;
+
+	VERIFY(MUTEX_HELD(&p->p_lock));
+	VERIFY(lwp->lwp_brand == NULL);
+
+	lwpd->br_exitwhy = CLD_EXITED;
+	lwpd->br_lwp = lwp;
+	lwpd->br_clear_ctidp = NULL;
+	lwpd->br_set_ctidp = NULL;
+	lwpd->br_signal = 0;
+	lwpd->br_stack_mode = LX_STACK_MODE_PREINIT;
+	/*
+	 * lwpd->br_affinitymask was zeroed by kmem_zalloc()
+	 * as was lwpd->br_scall_args and lwpd->br_args_size.
+	 */
+
+	/*
+	 * The first thread in a process has ppid set to the parent
+	 * process's pid, and ptid set to -1.  Subsequent threads in the
+	 * process have their ppid set to the pid of the thread that
+	 * created them, and their ptid to that thread's tid.
+	 */
+	if (tp->t_next == tp) {
+		lwpd->br_ppid = tp->t_procp->p_ppid;
+		lwpd->br_ptid = -1;
+	} else if (plwpd != NULL) {
+		bcopy(plwpd->br_tls, lwpd->br_tls, sizeof (lwpd->br_tls));
+		lwpd->br_ppid = plwpd->br_pid;
+		lwpd->br_ptid = curthread->t_tid;
+		/* The child inherits the fs/gsbase values from the parent */
+		lwpd->br_lx_fsbase = plwpd->br_lx_fsbase;
+		lwpd->br_ntv_fsbase = plwpd->br_ntv_fsbase;
+		lwpd->br_lx_gsbase = plwpd->br_lx_gsbase;
+		lwpd->br_ntv_gsbase = plwpd->br_ntv_gsbase;
+	} else {
+		/*
+		 * Oddball case: the parent thread isn't a Linux process.
+		 */
+		lwpd->br_ppid = 0;
+		lwpd->br_ptid = -1;
+	}
+	lwp->lwp_brand = lwpd;
+
+	/*
+	 * When during lx_lwpdata_alloc, we must decide whether or not to
+	 * allocate a new pid to associate with the lwp. Since p_lock is not
+	 * held at that point, the only time we can guarantee a new pid isn't
+	 * needed is when p_lwpcnt == 0.  This is because other lwps won't be
+	 * present to race with us with regards to pid allocation.
+	 *
+	 * This means that in all other cases (where p_lwpcnt > 0), we expect
+	 * that lx_lwpdata_alloc will allocate a pid for us to use here, even
+	 * if it is uneeded.  If this process is undergoing an exec, for
+	 * example, the single existing lwp will not need a new pid when it is
+	 * rebranded.  In that case, lx_pid_assign will free the uneeded pid.
+	 */
+	VERIFY(lwpd->br_lpid->l_pidp != NULL || p->p_lwpcnt == 0);
+
+	lx_pid_assign(tp, lwpd->br_lpid);
+	lwpd->br_tgid = lwpd->br_pid;
+	/*
+	 * Having performed the lx pid assignement, the lpid reference is no
+	 * longer needed.  The underlying data will be freed during lx_freelwp.
+	 */
+	lwpd->br_lpid = NULL;
+
+	installctx(lwptot(lwp), lwp, lx_save, lx_restore, NULL, NULL,
+	    lx_save, NULL);
+
+	/*
+	 * Install branded system call hooks for this LWP:
+	 */
+	lwp->lwp_brand_syscall = lx_syscall_enter;
+
+	/*
+	 * The new LWP inherits the parent LWP cgroup ID.
+	 */
+	if (plwpd != NULL) {
+		lwpd->br_cgroupid = plwpd->br_cgroupid;
+	}
+	lxzdata = ztolxzd(p->p_zone);
+	mutex_enter(&lxzdata->lxzd_lock);
+	cgrp = lxzdata->lxzd_cgroup;
+	if (cgrp != NULL) {
+		VFS_HOLD(cgrp);
+		mutex_exit(&lxzdata->lxzd_lock);
+		ASSERT(lx_cgrp_initlwp != NULL);
+		(*lx_cgrp_initlwp)(cgrp, lwpd->br_cgroupid, lwptot(lwp)->t_tid,
+		    lwpd->br_pid);
+		VFS_RELE(cgrp);
+	} else {
+		mutex_exit(&lxzdata->lxzd_lock);
+	}
+}
+
+void
+lx_initlwp_post(klwp_t *lwp)
+{
+	lx_lwp_data_t *plwpd = ttolxlwp(curthread);
+	/*
+	 * If the parent LWP has a ptrace(2) tracer, the new LWP may
+	 * need to inherit that same tracer.
+	 */
+	if (plwpd != NULL) {
+		lx_ptrace_inherit_tracer(plwpd, lwptolxlwp(lwp));
+	}
+}
+
+/*
+ * There is no need to have any locking for either the source or
+ * destination struct lx_lwp_data structs.  This is always run in the
+ * thread context of the source thread, and the destination thread is
+ * always newly created and not referred to from anywhere else.
+ */
+void
+lx_forklwp(klwp_t *srclwp, klwp_t *dstlwp)
+{
+	struct lx_lwp_data *src = srclwp->lwp_brand;
+	struct lx_lwp_data *dst = dstlwp->lwp_brand;
+
+	dst->br_ppid = src->br_pid;
+	dst->br_ptid = lwptot(srclwp)->t_tid;
+	bcopy(src->br_tls, dst->br_tls, sizeof (dst->br_tls));
+
+	switch (src->br_stack_mode) {
+	case LX_STACK_MODE_BRAND:
+	case LX_STACK_MODE_NATIVE:
+		/*
+		 * The parent LWP has an alternate stack installed.
+		 * The child LWP should have the same stack base and extent.
+		 */
+		dst->br_stack_mode = src->br_stack_mode;
+		dst->br_ntv_stack = src->br_ntv_stack;
+		dst->br_ntv_stack_current = src->br_ntv_stack_current;
+		break;
+
+	default:
+		/*
+		 * Otherwise, clear the stack data for this LWP.
+		 */
+		dst->br_stack_mode = LX_STACK_MODE_PREINIT;
+		dst->br_ntv_stack = 0;
+		dst->br_ntv_stack_current = 0;
+	}
+
+	/*
+	 * copy only these flags
+	 */
+	dst->br_lwp_flags = src->br_lwp_flags & BR_CPU_BOUND;
+	dst->br_scall_args = NULL;
+
+	/*
+	 * Flag so child doesn't ptrace-stop on syscall exit.
+	 */
+	dst->br_ptrace_flags |= LX_PTF_NOSTOP;
+}
+
+/*
+ * When switching a Linux process off the CPU, clear its GDT entries.
+ */
+/* ARGSUSED */
+static void
+lx_save(klwp_t *t)
+{
+	int i;
+
+#if defined(__amd64)
+	reset_sregs();
+#endif
+	for (i = 0; i < LX_TLSNUM; i++)
+		gdt_update_usegd(GDT_TLSMIN + i, &null_udesc);
+}
+
+/*
+ * When switching a Linux process on the CPU, set its GDT entries.
+ *
+ * For 64-bit code we don't have to worry about explicitly setting the
+ * %fsbase via wrmsr(MSR_AMD_FSBASE) here. Instead, that should happen
+ * automatically in update_sregs if we are executing in user-land. If this
+ * is the case then pcb_rupdate should be set.
+ */
+static void
+lx_restore(klwp_t *t)
+{
+	struct lx_lwp_data *lwpd = lwptolxlwp(t);
+	user_desc_t *tls;
+	int i;
+
+	ASSERT(lwpd);
+
+	tls = lwpd->br_tls;
+	for (i = 0; i < LX_TLSNUM; i++)
+		gdt_update_usegd(GDT_TLSMIN + i, &tls[i]);
+}
+
+void
+lx_set_gdt(int entry, user_desc_t *descrp)
+{
+
+	gdt_update_usegd(entry, descrp);
+}
+
+void
+lx_clear_gdt(int entry)
+{
+	gdt_update_usegd(entry, &null_udesc);
+}
+
+longlong_t
+lx_nosys()
+{
+	return (set_errno(ENOSYS));
+}
+
+/*
+ * Brand-specific routine to check if given non-Solaris standard segment
+ * register values should be modified to other values.
+ */
+/*ARGSUSED*/
+greg_t
+lx_fixsegreg(greg_t sr, model_t datamodel)
+{
+	uint16_t idx = SELTOIDX(sr);
+
+	ASSERT(sr == (sr & 0xffff));
+
+	/*
+	 * If the segment selector is a valid TLS selector, just return it.
+	 */
+	if (!SELISLDT(sr) && idx >= GDT_TLSMIN && idx <= GDT_TLSMAX)
+		return (sr | SEL_UPL);
+
+	/*
+	 * Force the SR into the LDT in ring 3 for 32-bit processes.
+	 *
+	 * 64-bit processes get the null GDT selector since they are not
+	 * allowed to have a private LDT.
+	 */
+#if defined(__amd64)
+	return (datamodel == DATAMODEL_ILP32 ? (sr | SEL_TI_LDT | SEL_UPL) : 0);
+#elif defined(__i386)
+	datamodel = datamodel;  /* datamodel currently unused for 32-bit */
+	return (sr | SEL_TI_LDT | SEL_UPL);
+#endif	/* __amd64 */
+}
+
+/*
+ * Brand-specific function to convert the fsbase as pulled from the register
+ * into a native fsbase suitable for locating the ulwp_t from the kernel.
+ */
+uintptr_t
+lx_fsbase(klwp_t *lwp, uintptr_t fsbase)
+{
+	lx_lwp_data_t *lwpd = lwp->lwp_brand;
+
+	if (lwpd->br_stack_mode != LX_STACK_MODE_BRAND ||
+	    lwpd->br_ntv_fsbase == NULL) {
+		return (fsbase);
+	}
+
+	return (lwpd->br_ntv_fsbase);
+}
+
+/*
+ * These two functions simulate winfo and post_sigcld for the lx brand. The
+ * difference is delivering a designated signal as opposed to always SIGCLD.
+ */
+static void
+lx_winfo(proc_t *pp, k_siginfo_t *ip, struct lx_proc_data *dat)
+{
+	ASSERT(MUTEX_HELD(&pidlock));
+	bzero(ip, sizeof (k_siginfo_t));
+	ip->si_signo = ltos_signo[dat->l_signal];
+	ip->si_code = pp->p_wcode;
+	ip->si_pid = pp->p_pid;
+	ip->si_ctid = PRCTID(pp);
+	ip->si_zoneid = pp->p_zone->zone_id;
+	ip->si_status = pp->p_wdata;
+	ip->si_stime = pp->p_stime;
+	ip->si_utime = pp->p_utime;
+}
+
+static void
+lx_post_exit_sig(proc_t *cp, sigqueue_t *sqp, struct lx_proc_data *dat)
+{
+	proc_t *pp = cp->p_parent;
+
+	ASSERT(MUTEX_HELD(&pidlock));
+	mutex_enter(&pp->p_lock);
+	/*
+	 * Since Linux doesn't queue SIGCHLD, or any other non RT
+	 * signals, we just blindly deliver whatever signal we can.
+	 */
+	ASSERT(sqp != NULL);
+	lx_winfo(cp, &sqp->sq_info, dat);
+	sigaddqa(pp, NULL, sqp);
+	sqp = NULL;
+	mutex_exit(&pp->p_lock);
+}
+
+
+/*
+ * Brand specific code for exiting and sending a signal to the parent, as
+ * opposed to sigcld().
+ */
+void
+lx_exit_with_sig(proc_t *cp, sigqueue_t *sqp)
+{
+	proc_t *pp = cp->p_parent;
+	lx_proc_data_t *lx_brand_data = ptolxproc(cp);
+	ASSERT(MUTEX_HELD(&pidlock));
+
+	switch (cp->p_wcode) {
+	case CLD_EXITED:
+	case CLD_DUMPED:
+	case CLD_KILLED:
+			ASSERT(cp->p_stat == SZOMB);
+			/*
+			 * The broadcast on p_srwchan_cv is a kludge to
+			 * wakeup a possible thread in uadmin(A_SHUTDOWN).
+			 */
+			cv_broadcast(&cp->p_srwchan_cv);
+
+			/*
+			 * Add to newstate list of the parent
+			 */
+			add_ns(pp, cp);
+
+			cv_broadcast(&pp->p_cv);
+			if ((pp->p_flag & SNOWAIT) ||
+			    PTOU(pp)->u_signal[SIGCLD - 1] == SIG_IGN) {
+				if (!(cp->p_pidflag & CLDWAITPID))
+					freeproc(cp);
+			} else if (!(cp->p_pidflag & CLDNOSIGCHLD) &&
+			    lx_brand_data->l_signal != 0) {
+				lx_post_exit_sig(cp, sqp, lx_brand_data);
+				sqp = NULL;
+			}
+			break;
+
+	case CLD_STOPPED:
+	case CLD_CONTINUED:
+	case CLD_TRAPPED:
+			panic("Should not be called in this case");
+	}
+
+	if (sqp)
+		siginfofree(sqp);
+}
+
+/*
+ * Filters based on arguments that have been passed in by a separate syscall
+ * using the B_STORE_ARGS mechanism. if the __WALL flag is set, no filter is
+ * applied, otherwise we look at the difference between a clone and non-clone
+ * process.
+ * The definition of a clone process in Linux is a thread that does not deliver
+ * SIGCHLD to its parent. The option __WCLONE indicates to wait only on clone
+ * processes. Without that option, a process should only wait on normal
+ * children. The following table shows the cases.
+ *
+ *                   default    __WCLONE
+ *   no SIGCHLD      -           X
+ *   SIGCHLD         X           -
+ *
+ * This is an XOR of __WCLONE being set, and SIGCHLD being the signal sent on
+ * process exit.
+ *
+ * More information on wait in lx brands can be found at
+ * usr/src/lib/brand/lx/lx_brand/common/wait.c.
+ */
+/* ARGSUSED */
+boolean_t
+lx_wait_filter(proc_t *pp, proc_t *cp)
+{
+	lx_lwp_data_t *lwpd = ttolxlwp(curthread);
+	int flags = lwpd->br_waitid_flags;
+	boolean_t ret;
+
+	if (!lwpd->br_waitid_emulate) {
+		return (B_TRUE);
+	}
+
+	mutex_enter(&cp->p_lock);
+	if (flags & LX_WALL) {
+		ret = B_TRUE;
+	} else {
+		lx_proc_data_t *pd = ptolxproc(cp);
+		boolean_t is_sigchld = B_TRUE;
+		boolean_t match_wclone = B_FALSE;
+
+		/*
+		 * When calling clone, an alternate signal can be chosen to
+		 * deliver to the parent when the child exits.
+		 */
+		if (pd != NULL && pd->l_signal != stol_signo[SIGCHLD]) {
+			is_sigchld = B_FALSE;
+		}
+		if ((flags & LX_WCLONE) != 0) {
+			match_wclone = B_TRUE;
+		}
+
+		ret = (match_wclone ^ is_sigchld) ? B_TRUE : B_FALSE;
+	}
+	mutex_exit(&cp->p_lock);
+
+	return (ret);
+}
+
+void
+lx_ifname_convert(char *ifname, lx_if_action_t act)
+{
+	if (act == LX_IF_TONATIVE) {
+		if (strncmp(ifname, "lo", IFNAMSIZ) == 0)
+			(void) strlcpy(ifname, "lo0", IFNAMSIZ);
+	} else {
+		if (strncmp(ifname, "lo0", IFNAMSIZ) == 0)
+			(void) strlcpy(ifname, "lo", IFNAMSIZ);
+	}
+}
+
+void
+lx_ifflags_convert(uint64_t *flags, lx_if_action_t act)
+{
+	uint64_t buf;
+
+	buf = *flags & (IFF_UP | IFF_BROADCAST | IFF_DEBUG |
+	    IFF_LOOPBACK | IFF_POINTOPOINT | IFF_NOTRAILERS |
+	    IFF_RUNNING | IFF_NOARP | IFF_PROMISC | IFF_ALLMULTI);
+
+	/* Linux has different shift for multicast flag */
+	if (act == LX_IF_TONATIVE) {
+		if (*flags & 0x1000)
+			buf |= IFF_MULTICAST;
+	} else {
+		if (*flags & IFF_MULTICAST)
+			buf |= 0x1000;
+	}
+	*flags = buf;
+}
+
+/*
+ * Convert an IPv6 address into the numbers used by /proc/net/if_inet6
+ */
+unsigned int
+lx_ipv6_scope_convert(const in6_addr_t *addr)
+{
+	if (IN6_IS_ADDR_V4COMPAT(addr)) {
+		return (LX_IPV6_ADDR_COMPATv4);
+	} else if (IN6_ARE_ADDR_EQUAL(addr, &ipv6_loopback)) {
+		return (LX_IPV6_ADDR_LOOPBACK);
+	} else if (IN6_IS_ADDR_LINKLOCAL(addr)) {
+		return (LX_IPV6_ADDR_LINKLOCAL);
+	} else if (IN6_IS_ADDR_SITELOCAL(addr)) {
+		return (LX_IPV6_ADDR_SITELOCAL);
+	} else {
+		return (0x0000U);
+	}
+}
+
+
+void
+lx_stol_hwaddr(const struct sockaddr_dl *src, struct sockaddr *dst, int *size)
+{
+	int copy_size = MIN(src->sdl_alen, sizeof (dst->sa_data));
+
+	switch (src->sdl_type) {
+	case DL_ETHER:
+		dst->sa_family = LX_ARPHRD_ETHER;
+		break;
+	case DL_LOOP:
+		dst->sa_family = LX_ARPHRD_LOOPBACK;
+		break;
+	default:
+		dst->sa_family = LX_ARPHRD_VOID;
+	}
+
+	bcopy(LLADDR(src), dst->sa_data, copy_size);
+	*size = copy_size;
+}
+
+/*
+ * Brand hook to convert native kernel siginfo signal number, errno, code, pid
+ * and si_status to Linux values. Similar to the stol_ksiginfo function but
+ * this one converts in-place, converts the pid, and does not copyout.
+ */
+void
+lx_sigfd_translate(k_siginfo_t *infop)
+{
+	infop->si_signo = lx_stol_signo(infop->si_signo, LX_SIGKILL);
+
+	infop->si_status = lx_stol_status(infop->si_status, LX_SIGKILL);
+
+	infop->si_code = lx_stol_sigcode(infop->si_code);
+
+	infop->si_errno = lx_errno(infop->si_errno, EINVAL);
+
+	if (infop->si_pid == curproc->p_zone->zone_proc_initpid) {
+		infop->si_pid = 1;
+	} else if (infop->si_pid == curproc->p_zone->zone_zsched->p_pid) {
+		infop->si_pid = 0;
+	}
+}
+
+int
+stol_ksiginfo_copyout(k_siginfo_t *sip, void *ulxsip)
+{
+	lx_siginfo_t lsi;
+
+	bzero(&lsi, sizeof (lsi));
+	lsi.lsi_signo = lx_stol_signo(sip->si_signo, SIGCLD);
+	lsi.lsi_code = lx_stol_sigcode(sip->si_code);
+	lsi.lsi_errno = lx_errno(sip->si_errno, EINVAL);
+
+	switch (lsi.lsi_signo) {
+	case LX_SIGPOLL:
+		lsi.lsi_band = sip->si_band;
+		lsi.lsi_fd = sip->si_fd;
+		break;
+
+	case LX_SIGCHLD:
+		lsi.lsi_pid = sip->si_pid;
+		if (sip->si_code <= 0 || sip->si_code == CLD_EXITED) {
+			lsi.lsi_status = sip->si_status;
+		} else {
+			lsi.lsi_status = lx_stol_status(sip->si_status,
+			    SIGKILL);
+		}
+		lsi.lsi_utime = sip->si_utime;
+		lsi.lsi_stime = sip->si_stime;
+		break;
+
+	case LX_SIGILL:
+	case LX_SIGBUS:
+	case LX_SIGFPE:
+	case LX_SIGSEGV:
+		lsi.lsi_addr = sip->si_addr;
+		break;
+
+	default:
+		lsi.lsi_pid = sip->si_pid;
+		lsi.lsi_uid = LX_UID32_TO_UID16(sip->si_uid);
+	}
+
+	if (copyout(&lsi, ulxsip, sizeof (lsi)) != 0) {
+		return (set_errno(EFAULT));
+	}
+
+	return (0);
+}
+
+#if defined(_SYSCALL32_IMPL)
+int
+stol_ksiginfo32_copyout(k_siginfo_t *sip, void *ulxsip)
+{
+	lx_siginfo32_t lsi;
+
+	bzero(&lsi, sizeof (lsi));
+	lsi.lsi_signo = lx_stol_signo(sip->si_signo, SIGCLD);
+	lsi.lsi_code = lx_stol_sigcode(sip->si_code);
+	lsi.lsi_errno = lx_errno(sip->si_errno, EINVAL);
+
+	switch (lsi.lsi_signo) {
+	case LX_SIGPOLL:
+		lsi.lsi_band = sip->si_band;
+		lsi.lsi_fd = sip->si_fd;
+		break;
+
+	case LX_SIGCHLD:
+		lsi.lsi_pid = sip->si_pid;
+		if (sip->si_code <= 0 || sip->si_code == CLD_EXITED) {
+			lsi.lsi_status = sip->si_status;
+		} else {
+			lsi.lsi_status = lx_stol_status(sip->si_status,
+			    SIGKILL);
+		}
+		lsi.lsi_utime = sip->si_utime;
+		lsi.lsi_stime = sip->si_stime;
+		break;
+
+	case LX_SIGILL:
+	case LX_SIGBUS:
+	case LX_SIGFPE:
+	case LX_SIGSEGV:
+		lsi.lsi_addr = (caddr32_t)(uintptr_t)sip->si_addr;
+		break;
+
+	default:
+		lsi.lsi_pid = sip->si_pid;
+		lsi.lsi_uid = LX_UID32_TO_UID16(sip->si_uid);
+	}
+
+	if (copyout(&lsi, ulxsip, sizeof (lsi)) != 0) {
+		return (set_errno(EFAULT));
+	}
+
+	return (0);
+}
+#endif
+
+/*
+ * Linux uses the original bounds of the argv array when determining the
+ * contents of /proc/<pid/cmdline.  We mimic those bounds using argv[0] and
+ * envp[0] as the beginning and end, respectively.
+ */
+void
+lx_read_argv_bounds(proc_t *p)
+{
+	user_t *up = PTOU(p);
+	lx_proc_data_t *pd = ptolxproc(p);
+	uintptr_t addr_arg = up->u_argv;
+	uintptr_t addr_env = up->u_envp;
+	uintptr_t arg_start = 0, env_start = 0, env_end = 0;
+	int i = 0;
+
+	VERIFY(pd != NULL);
+	VERIFY(MUTEX_HELD(&p->p_lock));
+
+	/*
+	 * Use AT_SUN_PLATFORM in the aux vector to find the end of the envp
+	 * strings.
+	 */
+	for (i = 0; i < __KERN_NAUXV_IMPL; i++) {
+		if (up->u_auxv[i].a_type == AT_SUN_PLATFORM) {
+			env_end = (uintptr_t)up->u_auxv[i].a_un.a_val;
+		}
+	}
+
+	mutex_exit(&p->p_lock);
+#if defined(_LP64)
+	if (p->p_model != DATAMODEL_NATIVE) {
+		uint32_t buf32;
+		if (copyin((void *)addr_arg, &buf32, sizeof (buf32)) == 0) {
+			arg_start = (uintptr_t)buf32;
+		}
+		if (copyin((void *)addr_env, &buf32, sizeof (buf32)) == 0) {
+			env_start = (uintptr_t)buf32;
+		}
+	} else
+#endif /* defined(_LP64) */
+	{
+		uintptr_t buf;
+		if (copyin((void *)addr_arg, &buf, sizeof (buf)) == 0) {
+			arg_start = buf;
+		}
+		if (copyin((void *)addr_env, &buf, sizeof (buf)) == 0) {
+			env_start = buf;
+		}
+	}
+	mutex_enter(&p->p_lock);
+	pd->l_args_start = arg_start;
+	pd->l_envs_start = env_start;
+	pd->l_envs_end = env_end;
+}
+
+/* Given an LX LWP, determine where user register state is stored. */
+lx_regs_location_t
+lx_regs_location(lx_lwp_data_t *lwpd, void **ucp, boolean_t for_write)
+{
+	switch (lwpd->br_stack_mode) {
+	case LX_STACK_MODE_BRAND:
+		/*
+		 * The LWP was stopped with the brand stack and register state
+		 * loaded, e.g. during a syscall emulated within the kernel.
+		 */
+		return (LX_REG_LOC_LWP);
+
+	case LX_STACK_MODE_PREINIT:
+		if (for_write) {
+			/* setting registers not allowed in this state */
+			break;
+		}
+		if (lwpd->br_ptrace_whatstop == LX_PR_SIGNALLED ||
+		    lwpd->br_ptrace_whatstop == LX_PR_SYSEXIT) {
+			/* The LWP was stopped by tracing on exec. */
+			return (LX_REG_LOC_LWP);
+		}
+		break;
+
+	case LX_STACK_MODE_NATIVE:
+		if (for_write) {
+			/* setting registers not allowed in this state */
+			break;
+		}
+		if (lwpd->br_ptrace_whystop == PR_BRAND) {
+			/* Called while ptrace-event-stopped by lx_exec. */
+			if (lwpd->br_ptrace_whatstop == LX_PR_EVENT) {
+				return (LX_REG_LOC_LWP);
+			}
+
+			/* Called while ptrace-event-stopped after clone. */
+			if (lwpd->br_ptrace_whatstop == LX_PR_SIGNALLED &&
+			    lwpd->br_ptrace_stopsig == LX_SIGSTOP &&
+			    (lwpd->br_ptrace_flags & LX_PTF_STOPPED)) {
+				return (LX_REG_LOC_LWP);
+			}
+
+			/*
+			 * Called to obtain syscall exit for other cases
+			 * (e.g. pseudo return from rt_sigreturn).
+			 */
+			if (lwpd->br_ptrace_whatstop == LX_PR_SYSEXIT &&
+			    (lwpd->br_ptrace_flags & LX_PTF_STOPPED)) {
+				return (LX_REG_LOC_LWP);
+			}
+		}
+		break;
+	default:
+		break;
+	}
+
+	if (lwpd->br_ptrace_stopucp != NULL) {
+		/*
+		 * The LWP was stopped in the usermode emulation library
+		 * but a ucontext_t for the preserved brand stack and
+		 * register state was provided.  Return the register state
+		 * from that ucontext_t.
+		 */
+		VERIFY(ucp != NULL);
+		*ucp = (void *)lwpd->br_ptrace_stopucp;
+		return (LX_REG_LOC_UCP);
+	}
+
+	return (LX_REG_LOC_UNAVAIL);
+}
--- /dev/null	Wed Oct  5 00:32:05 2016
+++ new/usr/src/uts/common/brand/lx/os/lx_pid.c	Wed Oct  5 00:32:05 2016
@@ -0,0 +1,395 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2015, Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysmacros.h>
+#include <sys/bitmap.h>
+#include <sys/var.h>
+#include <sys/thread.h>
+#include <sys/proc.h>
+#include <sys/brand.h>
+#include <sys/zone.h>
+#include <sys/lx_brand.h>
+
+#define	LINUX_PROC_FACTOR	8	/* factor down the hash table by this */
+static int hash_len = 4;		/* desired average hash chain length */
+static int hash_size;			/* no of buckets in the hash table */
+
+static struct lx_pid **stol_pid_hash;
+static struct lx_pid **ltos_pid_hash;
+
+#define	LTOS_HASH(pid)		((pid) & (hash_size - 1))
+#define	STOL_HASH(pid, tid)	(((pid) + (tid)) & (hash_size - 1))
+
+static kmutex_t hash_lock;
+
+static void
+lx_pid_insert_hash(struct lx_pid *lpidp)
+{
+	int shash = STOL_HASH(lpidp->s_pid, lpidp->s_tid);
+	int lhash = LTOS_HASH(lpidp->l_pid);
+
+	ASSERT(MUTEX_HELD(&hash_lock));
+
+	lpidp->stol_next = stol_pid_hash[shash];
+	stol_pid_hash[shash] = lpidp;
+
+	lpidp->ltos_next = ltos_pid_hash[lhash];
+	ltos_pid_hash[lhash] = lpidp;
+}
+
+static struct lx_pid *
+lx_pid_remove_hash(pid_t pid, id_t tid)
+{
+	struct lx_pid **hpp;
+	struct lx_pid *lpidp = NULL;
+
+	ASSERT(MUTEX_HELD(&hash_lock));
+
+	hpp = &stol_pid_hash[STOL_HASH(pid, tid)];
+	while (*hpp) {
+		if ((*hpp)->s_pid == pid && (*hpp)->s_tid == tid) {
+			lpidp = *hpp;
+			*hpp = (*hpp)->stol_next;
+			break;
+		}
+		hpp = &(*hpp)->stol_next;
+	}
+
+	/*
+	 * when called during error recovery the pid may already
+	 * be released
+	 */
+	if (lpidp == NULL)
+		return (NULL);
+
+	hpp = &ltos_pid_hash[LTOS_HASH(lpidp->l_pid)];
+	while (*hpp) {
+		if (*hpp == lpidp) {
+			*hpp = lpidp->ltos_next;
+			break;
+		}
+		hpp = &(*hpp)->ltos_next;
+	}
+
+	return (lpidp);
+}
+
+/*
+ * given a solaris pid/tid pair, create a linux pid
+ */
+void
+lx_pid_assign(kthread_t *t, struct lx_pid *lpidp)
+{
+	proc_t *p = ttoproc(t);
+	lx_lwp_data_t *lwpd = ttolxlwp(t);
+	pid_t s_pid = p->p_pid;
+	id_t s_tid = t->t_tid;
+
+	/*
+	 * When lx_initlwp is called from lx_setbrand, p_lwpcnt will already be
+	 * equal to 1. Since lx_initlwp is being called against an lwp that
+	 * already exists, an additional pid allocation is not necessary.
+	 *
+	 * We check for this by testing br_ppid == 0.
+	 */
+	if (p->p_lwpcnt > 0 && lwpd->br_ppid != 0) {
+		/*
+		 * Assign allocated pid to any thread other than the first.
+		 * The l_pid and l_pidp fields should be populated.
+		 */
+		VERIFY(lpidp->l_pidp != NULL);
+		VERIFY(lpidp->l_pid != 0);
+	} else {
+		/*
+		 * There are cases where a pid is speculatively allocated but
+		 * is not needed.  We are obligated to free it here.
+		 */
+		if (lpidp->l_pidp != NULL) {
+			(void) pid_rele(lpidp->l_pidp);
+		}
+		lpidp->l_pidp = NULL;
+		lpidp->l_pid = s_pid;
+	}
+
+	lpidp->s_pid = s_pid;
+	lpidp->s_tid = s_tid;
+	lpidp->l_start = t->t_start;
+
+	/*
+	 * now put the pid into the linux-solaris and solaris-linux
+	 * conversion hash tables
+	 */
+	mutex_enter(&hash_lock);
+	lx_pid_insert_hash(lpidp);
+	mutex_exit(&hash_lock);
+
+	lwpd->br_pid = lpidp->l_pid;
+}
+
+/*
+ * If we are exec()ing the process, this thread's tid is about to be reset
+ * to 1.  Make sure the Linux PID bookkeeping reflects that change.
+ */
+void
+lx_pid_reassign(kthread_t *t)
+{
+	proc_t *p = ttoproc(t);
+	struct pid *old_pidp;
+	struct lx_pid *lpidp;
+
+	ASSERT(p->p_lwpcnt == 1);
+
+	mutex_enter(&hash_lock);
+
+	/*
+	 * Clean up all the traces of this thread's 'fake' Linux PID.
+	 */
+	lpidp = lx_pid_remove_hash(p->p_pid, t->t_tid);
+	ASSERT(lpidp != NULL);
+	old_pidp = lpidp->l_pidp;
+	lpidp->l_pidp = NULL;
+
+	/*
+	 * Now register this thread as (pid, 1).
+	 */
+	lpidp->l_pid = p->p_pid;
+	lpidp->s_pid = p->p_pid;
+	lpidp->s_tid = 1;
+	lx_pid_insert_hash(lpidp);
+
+	mutex_exit(&hash_lock);
+
+	if (old_pidp)
+		(void) pid_rele(old_pidp);
+}
+
+/*
+ * release a solaris pid/tid pair
+ */
+void
+lx_pid_rele(pid_t pid, id_t tid)
+{
+	struct lx_pid *lpidp;
+
+	mutex_enter(&hash_lock);
+	lpidp = lx_pid_remove_hash(pid, tid);
+	mutex_exit(&hash_lock);
+
+	if (lpidp) {
+		if (lpidp->l_pidp)
+			(void) pid_rele(lpidp->l_pidp);
+
+		kmem_free(lpidp, sizeof (*lpidp));
+	}
+}
+
+/*
+ * given a linux pid, return the solaris pid/tid pair
+ */
+int
+lx_lpid_to_spair(pid_t l_pid, pid_t *s_pid, id_t *s_tid)
+{
+	struct lx_pid *hp;
+
+	if (l_pid == 1) {
+		pid_t initpid;
+
+		/*
+		 * We are trying to look up the Linux init process for the
+		 * current zone, which we pretend has pid 1.
+		 */
+		if ((initpid = curzone->zone_proc_initpid) == -1) {
+			/*
+			 * We could not find the init process for this zone.
+			 */
+			return (-1);
+		}
+
+		if (s_pid != NULL)
+			*s_pid = initpid;
+		if (s_tid != NULL)
+			*s_tid = 1;
+
+		return (0);
+	}
+
+	mutex_enter(&hash_lock);
+	for (hp = ltos_pid_hash[LTOS_HASH(l_pid)]; hp; hp = hp->ltos_next) {
+		if (l_pid == hp->l_pid) {
+			if (s_pid)
+				*s_pid = hp->s_pid;
+			if (s_tid)
+				*s_tid = hp->s_tid;
+			break;
+		}
+	}
+	mutex_exit(&hash_lock);
+	if (hp != NULL)
+		return (0);
+
+	/*
+	 * We didn't find this pid in our translation table.
+	 * But this still could be the pid of a native process
+	 * running in the current zone so check for that here.
+	 *
+	 * Note that prfind() only searches for processes in the current zone.
+	 */
+	mutex_enter(&pidlock);
+	if (prfind(l_pid) != NULL) {
+		mutex_exit(&pidlock);
+		if (s_pid)
+			*s_pid = l_pid;
+		if (s_tid)
+			*s_tid = 0;
+		return (0);
+	}
+	mutex_exit(&pidlock);
+
+	return (-1);
+}
+
+/*
+ * Given an lwp, return the Linux pid of its parent.  If the caller
+ * wants them, we return the Solaris (pid, tid) as well.
+ */
+pid_t
+lx_lwp_ppid(klwp_t *lwp, pid_t *ppidp, id_t *ptidp)
+{
+	lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+	proc_t *p = lwptoproc(lwp);
+	struct lx_pid *hp;
+	pid_t zoneinit = curproc->p_zone->zone_proc_initpid;
+	pid_t lppid, ppid;
+
+	/*
+	 * Be sure not to return a parent pid that should be invisible
+	 * within this zone.
+	 */
+	ppid = ((p->p_flag & SZONETOP)
+	    ? curproc->p_zone->zone_zsched->p_pid : p->p_ppid);
+
+	/*
+	 * If the parent process's pid is the zone's init process, force it
+	 * to the Linux init pid value of 1.
+	 */
+	if (ppid == zoneinit)
+		ppid = 1;
+
+	/*
+	 * There are two cases in which the Linux definition of a 'parent'
+	 * matches that of Solaris:
+	 *
+	 * - if our tgid is the same as our PID, then we are either the
+	 *   first thread in the process or a CLONE_THREAD thread.
+	 *
+	 * - if the brand lwp value for ppid is 0, then we are either the
+	 *   child of a differently-branded process or a CLONE_PARENT thread.
+	 */
+	if (p->p_pid == lwpd->br_tgid || lwpd->br_ppid == 0) {
+		if (ppidp != NULL)
+			*ppidp = ppid;
+		if (ptidp != NULL)
+			*ptidp = -1;
+		return (ppid);
+	}
+
+	/*
+	 * Set the default Linux parent pid to be the pid of the zone's init
+	 * process; this will get converted back to the Linux default of 1
+	 * later.
+	 */
+	lppid = zoneinit;
+
+	/*
+	 * If the process's parent isn't init, try and look up the Linux "pid"
+	 * corresponding to the process's parent.
+	 */
+	if (ppid != 1) {
+		/*
+		 * In all other cases, we are looking for the parent of this
+		 * specific thread, which in Linux refers to the thread that
+		 * clone()d it.   We stashed that thread's PID away when this
+		 * thread was created.
+		 */
+		mutex_enter(&hash_lock);
+		for (hp = ltos_pid_hash[LTOS_HASH(lwpd->br_ppid)]; hp;
+		    hp = hp->ltos_next) {
+			if (lwpd->br_ppid == hp->l_pid) {
+				/*
+				 * We found the PID we were looking for, but
+				 * since we cached its value in this LWP's brand
+				 * structure, it has exited and been reused by
+				 * another process.
+				 */
+				if (hp->l_start > lwptot(lwp)->t_start)
+					break;
+
+				lppid = lwpd->br_ppid;
+				if (ppidp != NULL)
+					*ppidp = hp->s_pid;
+				if (ptidp != NULL)
+					*ptidp = hp->s_tid;
+
+				break;
+			}
+		}
+		mutex_exit(&hash_lock);
+	}
+
+	if (lppid == zoneinit) {
+		lppid = 1;
+
+		if (ppidp != NULL)
+			*ppidp = lppid;
+		if (ptidp != NULL)
+			*ptidp = -1;
+	}
+
+	return (lppid);
+}
+
+void
+lx_pid_init(void)
+{
+	hash_size = 1 << highbit(v.v_proc / (hash_len * LINUX_PROC_FACTOR));
+
+	stol_pid_hash = kmem_zalloc(sizeof (struct lx_pid *) * hash_size,
+	    KM_SLEEP);
+	ltos_pid_hash = kmem_zalloc(sizeof (struct lx_pid *) * hash_size,
+	    KM_SLEEP);
+
+	mutex_init(&hash_lock, NULL, MUTEX_DEFAULT, NULL);
+}
+
+void
+lx_pid_fini(void)
+{
+	kmem_free(stol_pid_hash, sizeof (struct lx_pid *) * hash_size);
+	kmem_free(ltos_pid_hash, sizeof (struct lx_pid *) * hash_size);
+}
--- /dev/null	Wed Oct  5 00:32:05 2016
+++ new/usr/src/uts/common/brand/lx/os/lx_ptrace.c	Wed Oct  5 00:32:05 2016
@@ -0,0 +1,2638 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+/*
+ * Emulation of the Linux ptrace(2) interface.
+ *
+ * OVERVIEW
+ *
+ * The Linux process model is somewhat different from the illumos native
+ * model.  One critical difference is that each Linux thread has a unique
+ * identifier in the pid namespace.  The lx brand assigns a pid to each LWP
+ * within the emulated process, giving the pid of the process itself to the
+ * first LWP.
+ *
+ * The Linux ptrace(2) interface allows for any LWP in a branded process to
+ * exert control over any other LWP within the same zone.  Control is exerted
+ * by the use of the ptrace(2) system call itself, which accepts a number of
+ * request codes.  Feedback on traced events is primarily received by the
+ * tracer through SIGCLD and the emulated waitpid(2) and waitid(2) system
+ * calls.  Many of the possible ptrace(2) requests will only succeed if the
+ * target LWP is in a "ptrace-stop" condition.
+ *
+ * HISTORY
+ *
+ * The brand support for ptrace(2) was originally built on top of the rich
+ * support for debugging and tracing provided through the illumos /proc
+ * interfaces, mounted at /native/proc within the zone.  The native legacy
+ * ptrace(3C) functionality was used as a starting point, but was generally
+ * insufficient for complete and precise emulation.  The extant legacy
+ * interface, and indeed our native SIGCLD and waitid(2) facilities, are
+ * focused on _process_ level concerns -- the Linux interface has been
+ * extended to be aware of LWPs as well.
+ *
+ * In order to allow us to focus on providing more complete and accurate
+ * emulation without extensive and undesirable changes to the native
+ * facilities, this second generation ptrace(2) emulation is mostly separate
+ * from any other tracing or debugging framework in the system.
+ *
+ * ATTACHING TRACERS TO TRACEES
+ *
+ * There are several ways that a child LWP may becomed traced by a tracer.
+ * To determine which attach method caused a tracee to become attached, one
+ * may inspect the "br_ptrace_attach" member of the LWP-specific brand data
+ * with the debugger.
+ *
+ * The first attach methods to consider are the attaching ptrace(2) requests:
+ *
+ *   PTRACE_TRACEME
+ *
+ *   If an LWP makes a PTRACE_TRACEME call, it will be attached as a tracee
+ *   to its parent LWP (br_ppid).  Using PTRACE_TRACEME does _not_ cause the
+ *   tracee to be held in a stop condition.  It is common practice for
+ *   consumers to raise(SIGSTOP) immediately afterward.
+ *
+ *   PTRACE_ATTACH
+ *
+ *   An LWP may attempt to trace any other LWP in this, or another, process.
+ *   We currently allow any attach where the process containing the tracer
+ *   LWP has permission to write to /proc for the process containing the
+ *   intended tracer.  This action also sends a SIGSTOP to the newly attached
+ *   tracee.
+ *
+ * The second class of attach methods are the clone(2)/fork(2) inheritance
+ * options that may be set on a tracee with PTRACE_SETOPTIONS:
+ *
+ *   PTRACE_O_TRACEFORK, PTRACE_O_TRACEVFORK and PTRACE_O_TRACECLONE
+ *
+ *   If these options have been set on a tracee, then a fork(2), vfork(2) or
+ *   clone(2) respectively will cause the newly created LWP to be traced by
+ *   the same tracer.  The same set of ptrace(2) options will also be set on
+ *   the new child.
+ *
+ * The third class of attach method is the PTRACE_CLONE flag to clone(2).
+ * This flag induces the same inheritance as PTRACE_O_TRACECLONE, but is
+ * passed by the tracee as an argument to clone(2).
+ *
+ * DETACHING TRACEES
+ *
+ * Tracees can be detached by the tracer with the PTRACE_DETACH request.
+ * This request is only valid when the tracee is in a ptrace(2) stop
+ * condition, and is itself a restarting action.
+ *
+ * If the tracer exits without detaching all of its tracees, then all of the
+ * tracees are automatically detached and restarted.  If a tracee was in
+ * "signal-delivery-stop" at the time the tracer exited, the signal will be
+ * released to the child unless it is a SIGSTOP.  We drop this instance of
+ * SIGSTOP in order to prevent the child from becoming stopped by job
+ * control.
+ *
+ * ACCORD ALLOCATION AND MANAGEMENT
+ *
+ * The "lx_ptrace_accord_t" object tracks the agreement between a tracer LWP
+ * and zero or more tracee LWPs.  It is explicitly illegal for a tracee to
+ * trace its tracer, and we block this in PTRACE_ATTACH/PTRACE_TRACEME.
+ *
+ * An LWP starts out without an accord.  If a child of that LWP calls
+ * ptrace(2) with the PTRACE_TRACEME subcommand, or if the LWP itself uses
+ * PTRACE_ATTACH, an accord will be allocated and stored on that LWP.  The
+ * accord structure is not released from that LWP until it arrives in
+ * lx_exitlwp(), as called by lwp_exit().  A new accord will not be
+ * allocated, even if one does not exist, once an LWP arrives in lx_exitlwp()
+ * and sets the LX_PTF_EXITING flag.  An LWP will have at most one accord
+ * structure throughout its entire lifecycle; once it has one, it has the
+ * same one until death.
+ *
+ * The accord is reference counted (lxpa_refcnt), starting at a count of one
+ * at creation to represent the link from the tracer LWP to its accord.  The
+ * accord is not freed until the reference count falls to zero.
+ *
+ * To make mutual exclusion between a detaching tracer and various notifying
+ * tracees simpler, the tracer will hold "pidlock" while it clears the
+ * accord members that point back to the tracer LWP and CV.
+ *
+ * SIGNALS AND JOB CONTROL
+ *
+ * Various actions, either directly ptrace(2) related or commonly associated
+ * with tracing, cause process- or thread-directed SIGSTOP signals to be sent
+ * to tracees (a "signal-delivery-stop"). These signals, and indeed any signal
+ * other than SIGKILL, can be suppressed by the tracer when using a restarting
+ * request (including PTRACE_DETACH) on a child.  The signal may also be
+ * substituted for a different signal.
+ *
+ * If a SIGSTOP (or other stopping signal) is not suppressed by the tracer,
+ * it will induce the regular illumos native job control stop of the entire
+ * traced process.  This is at least passingly similar to the Linux "group
+ * stop" ptrace(2) condition.
+ *
+ * SYSTEM CALL TRACING
+ *
+ * The ptrace(2) interface enables the tracer to hold the tracee on entry and
+ * exit from system calls.  When a stopped tracee is restarted through the
+ * PTRACE_SYSCALL request, the LX_PTF_SYSCALL flag is set until the next
+ * system call boundary.  Whether this is a "syscall-entry-stop" or
+ * "syscall-exit-stop", the tracee is held and the tracer is notified via
+ * SIGCLD/waitpid(2) in the usual way.  The flag LX_PTF_SYSCALL flag is
+ * cleared after each stop; for ongoing system call tracing the tracee must
+ * be continuously restarted with PTRACE_SYSCALL.
+ *
+ * SPECIAL CASES FOR STOP EVENTS
+ *
+ * The strace command is one of the primary consumers of ptrace. In order for
+ * strace to properly understand what is actually happening when it receives a
+ * signal associated with a stop event, these signals must match Linux behavior
+ * exactly or the strace consumer will get out of sync and report incorrect
+ * state. There are a couple of special cases we have to handle to provide
+ * proper interaction of the syscall-entry-stop, syscall-exit-stop, and
+ * signal-delivery-stop events:
+ * 1) The child process of a clone/fork does not emit a syscall-exit-stop event.
+ * 2) A signal that arrives between syscall-enter-stop & syscall-exit-stop must
+ *    not immediately emit signal-delivery-stop. This event must be emitted
+ *    after the syscall is interrupted and syscall-exit-stop has been emitted.
+ *
+ * EVENT STOPS
+ *
+ * Various events (particularly FORK, VFORK, CLONE, EXEC and EXIT) are
+ * enabled by the tracer through PTRACE_SETOPTIONS.  Once enabled, the tracee
+ * will be stopped at the nominated points of interest and the tracer
+ * notified.  The tracer may request additional information about the event,
+ * such as the pid of new LWPs and processes, via PTRACE_GETEVENTMSG.
+ *
+ * LOCK ORDERING RULES
+ *
+ * It is not safe, in general, to hold p_lock for two different processes at
+ * the same time.  This constraint is the primary reason for the existence
+ * (and complexity) of the ptrace(2) accord mechanism.
+ *
+ * In order to facilitate looking up accords by the "pid" of a tracer LWP,
+ * p_lock for the tracer process may be held while entering the accord mutex
+ * (lxpa_lock).  This mutex protects the accord flags and reference count.
+ * The reference count is manipulated through lx_ptrace_accord_hold() and
+ * lx_ptrace_accord_rele().
+ *
+ * DO NOT interact with the accord mutex (lxpa_lock) directly.  The
+ * lx_ptrace_accord_enter() and lx_ptrace_accord_exit() functions do various
+ * book-keeping and lock ordering enforcement and MUST be used.
+ *
+ * It is NOT legal to take ANY p_lock while holding the accord mutex
+ * (lxpa_lock).  If the lxpa_tracees_lock is to be held concurrently with
+ * lxpa_lock, lxpa_lock MUST be taken first and dropped before taking p_lock
+ * of any processes from the tracee list.
+ *
+ * It is NOT legal to take a tracee p_lock and then attempt to enter the
+ * accord mutex (or tracee list mutex) of its tracer.  When running as the
+ * tracee LWP, the tracee's hold will prevent the accord from being freed.
+ * Use of the LX_PTF_STOPPING or LX_PTF_CLONING flag in the LWP-specific brand
+ * data prevents an exiting tracer from altering the tracee until the tracee
+ * has come to an orderly stop, without requiring the tracee to hold its own
+ * p_lock the entire time it is stopping.
+ *
+ * It is not safe, in general, to enter "pidlock" while holding the p_lock of
+ * any process.  It is similarly illegal to hold any accord locks (lxpa_lock
+ * or lxpa_sublock) while attempting to enter "pidlock".  As "pidlock" is a
+ * global mutex, it should be held for the shortest possible time.
+ */
+
+#include <sys/types.h>
+#include <sys/kmem.h>
+#include <sys/ksynch.h>
+#include <sys/sysmacros.h>
+#include <sys/procfs.h>
+#include <sys/cmn_err.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/wait.h>
+#include <sys/prsystm.h>
+#include <sys/note.h>
+
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_impl.h>
+#include <sys/lx_misc.h>
+#include <lx_syscall.h>
+#include <lx_signum.h>
+
+
+typedef enum lx_ptrace_cont_flags_t {
+	LX_PTC_NONE = 0x00,
+	LX_PTC_SYSCALL = 0x01,
+	LX_PTC_SINGLESTEP = 0x02
+} lx_ptrace_cont_flags_t;
+
+
+extern int lx_user_regs_copyin(lx_lwp_data_t *, void *);
+extern int lx_user_regs_copyout(lx_lwp_data_t *, void *);
+extern int lx_ptrace_peekuser(lx_lwp_data_t *, uintptr_t, void *);
+extern int lx_ptrace_pokeuser(lx_lwp_data_t *, uintptr_t, void *);
+extern int lx_user_fpregs_copyin(lx_lwp_data_t *, void *);
+extern int lx_user_fpregs_copyout(lx_lwp_data_t *, void *);
+extern int lx_user_fpxregs_copyin(lx_lwp_data_t *, void *);
+extern int lx_user_fpxregs_copyout(lx_lwp_data_t *, void *);
+
+/*
+ * Macros for checking the state of an LWP via "br_ptrace_flags":
+ */
+#define	LX_PTRACE_BUSY \
+	(LX_PTF_EXITING | LX_PTF_STOPPING | LX_PTF_CLONING)
+
+#define	VISIBLE(a)	(((a)->br_ptrace_flags & LX_PTF_EXITING) == 0)
+#define	TRACEE_BUSY(a)	(((a)->br_ptrace_flags & LX_PTRACE_BUSY) != 0)
+
+#define	ACCORD_HELD(a)	MUTEX_HELD(&(a)->lxpa_lock)
+
+#define	LX_PID_TO_INIT(x)	((x) == curproc->p_zone->zone_proc_initpid ? \
+	1 : (x))
+#define	LX_INIT_TO_PID(x)	((x) == 1 ? \
+	curproc->p_zone->zone_proc_initpid : (x))
+
+static kcondvar_t lx_ptrace_busy_cv;
+static kmem_cache_t *lx_ptrace_accord_cache;
+
+/*
+ * Enter the accord mutex.
+ */
+static void
+lx_ptrace_accord_enter(lx_ptrace_accord_t *accord)
+{
+	VERIFY(MUTEX_NOT_HELD(&accord->lxpa_tracees_lock));
+
+	mutex_enter(&accord->lxpa_lock);
+}
+
+/*
+ * Exit the accord mutex.  If the reference count has dropped to zero,
+ * free the accord.
+ */
+static void
+lx_ptrace_accord_exit(lx_ptrace_accord_t *accord)
+{
+	VERIFY(ACCORD_HELD(accord));
+
+	if (accord->lxpa_refcnt > 0) {
+		mutex_exit(&accord->lxpa_lock);
+		return;
+	}
+
+	/*
+	 * When the reference count drops to zero we must free the accord.
+	 */
+	VERIFY(accord->lxpa_tracer == NULL);
+	VERIFY(MUTEX_NOT_HELD(&accord->lxpa_tracees_lock));
+	VERIFY(list_is_empty(&accord->lxpa_tracees));
+	VERIFY(accord->lxpa_flags & LX_ACC_TOMBSTONE);
+
+	mutex_destroy(&accord->lxpa_lock);
+	mutex_destroy(&accord->lxpa_tracees_lock);
+
+	kmem_cache_free(lx_ptrace_accord_cache, accord);
+}
+
+/*
+ * Drop our reference to this accord.  If this drops the reference count
+ * to zero, the next lx_ptrace_accord_exit() will free the accord.
+ */
+static void
+lx_ptrace_accord_rele(lx_ptrace_accord_t *accord)
+{
+	VERIFY(ACCORD_HELD(accord));
+
+	VERIFY(accord->lxpa_refcnt > 0);
+	accord->lxpa_refcnt--;
+}
+
+/*
+ * Place an additional hold on an accord.
+ */
+static void
+lx_ptrace_accord_hold(lx_ptrace_accord_t *accord)
+{
+	VERIFY(ACCORD_HELD(accord));
+
+	accord->lxpa_refcnt++;
+}
+
+/*
+ * Fetch the accord for this LWP.  If one has not yet been created, and the
+ * process is not exiting, allocate it now.  Must be called with p_lock held
+ * for the process containing the target LWP.
+ *
+ * If successful, we return holding the accord lock (lxpa_lock).
+ */
+static int
+lx_ptrace_accord_get_locked(klwp_t *lwp, lx_ptrace_accord_t **accordp,
+    boolean_t allocate_one)
+{
+	lx_ptrace_accord_t *lxpa;
+	lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+	proc_t *p = lwptoproc(lwp);
+
+	VERIFY(MUTEX_HELD(&p->p_lock));
+
+	/*
+	 * If this LWP does not have an accord, we wish to allocate
+	 * and install one.
+	 */
+	if ((lxpa = lwpd->br_ptrace_accord) == NULL) {
+		if (!allocate_one || !VISIBLE(lwpd)) {
+			/*
+			 * Either we do not wish to allocate an accord, or this
+			 * LWP has already begun exiting from a ptrace
+			 * perspective.
+			 */
+			*accordp = NULL;
+			return (ESRCH);
+		}
+
+		lxpa = kmem_cache_alloc(lx_ptrace_accord_cache, KM_SLEEP);
+		bzero(lxpa, sizeof (*lxpa));
+
+		/*
+		 * The initial reference count is 1 because we are referencing
+		 * it in from the soon-to-be tracer LWP.
+		 */
+		lxpa->lxpa_refcnt = 1;
+		mutex_init(&lxpa->lxpa_lock, NULL, MUTEX_DEFAULT, NULL);
+		mutex_init(&lxpa->lxpa_tracees_lock, NULL, MUTEX_DEFAULT, NULL);
+		list_create(&lxpa->lxpa_tracees, sizeof (lx_lwp_data_t),
+		    offsetof(lx_lwp_data_t, br_ptrace_linkage));
+		lxpa->lxpa_cvp = &p->p_cv;
+
+		lxpa->lxpa_tracer = lwpd;
+		lwpd->br_ptrace_accord = lxpa;
+	}
+
+	/*
+	 * Lock the accord before returning it to the caller.
+	 */
+	lx_ptrace_accord_enter(lxpa);
+
+	/*
+	 * There should be at least one active reference to this accord,
+	 * otherwise it should have been freed.
+	 */
+	VERIFY(lxpa->lxpa_refcnt > 0);
+
+	*accordp = lxpa;
+	return (0);
+}
+
+/*
+ * Accords belong to the tracer LWP.  Get the accord for this tracer or return
+ * an error if it was not possible.  To prevent deadlocks, the caller MUST NOT
+ * hold p_lock on its own or any other process.
+ *
+ * If successful, we return holding the accord lock (lxpa_lock).
+ */
+static int
+lx_ptrace_accord_get_by_pid(pid_t lxpid, lx_ptrace_accord_t **accordp)
+{
+	int ret = ESRCH;
+	pid_t apid;
+	id_t atid;
+	proc_t *aproc;
+	kthread_t *athr;
+	klwp_t *alwp;
+	lx_lwp_data_t *alwpd;
+
+	VERIFY(MUTEX_NOT_HELD(&curproc->p_lock));
+
+	/*
+	 * Locate the process containing the tracer LWP based on its Linux pid
+	 * and lock it.
+	 */
+	if (lx_lpid_to_spair(lxpid, &apid, &atid) != 0 ||
+	    (aproc = sprlock(apid)) == NULL) {
+		return (ESRCH);
+	}
+
+	/*
+	 * Locate the tracer LWP itself and ensure that it is visible to
+	 * ptrace(2).
+	 */
+	if ((athr = idtot(aproc, atid)) == NULL ||
+	    (alwp = ttolwp(athr)) == NULL ||
+	    (alwpd = lwptolxlwp(alwp)) == NULL ||
+	    !VISIBLE(alwpd)) {
+		sprunlock(aproc);
+		return (ESRCH);
+	}
+
+	/*
+	 * We should not fetch our own accord this way.
+	 */
+	if (athr == curthread) {
+		sprunlock(aproc);
+		return (EPERM);
+	}
+
+	/*
+	 * Fetch (or allocate) the accord owned by this tracer LWP:
+	 */
+	ret = lx_ptrace_accord_get_locked(alwp, accordp, B_TRUE);
+
+	/*
+	 * Unlock the process and return.
+	 */
+	sprunlock(aproc);
+	return (ret);
+}
+
+/*
+ * Get (or allocate) the ptrace(2) accord for the current LWP, acting as a
+ * tracer.  The caller MUST NOT currently hold p_lock on the process containing
+ * this LWP.
+ *
+ * If successful, we return holding the accord lock (lxpa_lock).
+ */
+static int
+lx_ptrace_accord_get(lx_ptrace_accord_t **accordp, boolean_t allocate_one)
+{
+	klwp_t *lwp = ttolwp(curthread);
+	proc_t *p = lwptoproc(lwp);
+	int ret;
+
+	VERIFY(MUTEX_NOT_HELD(&p->p_lock));
+
+	/*
+	 * Lock the tracer (this LWP).
+	 */
+	mutex_enter(&p->p_lock);
+
+	/*
+	 * Fetch (or allocate) the accord for this LWP:
+	 */
+	ret = lx_ptrace_accord_get_locked(lwp, accordp, allocate_one);
+
+	mutex_exit(&p->p_lock);
+
+	return (ret);
+}
+
+/*
+ * Restart an LWP if it is in "ptrace-stop".  This function may induce sleep,
+ * so the caller MUST NOT hold any mutexes other than p_lock for the process
+ * containing the LWP.
+ */
+static void
+lx_ptrace_restart_lwp(klwp_t *lwp)
+{
+	kthread_t *rt = lwptot(lwp);
+	proc_t *rproc = lwptoproc(lwp);
+	lx_lwp_data_t *rlwpd = lwptolxlwp(lwp);
+
+	VERIFY(rt != curthread);
+	VERIFY(MUTEX_HELD(&rproc->p_lock));
+
+	/*
+	 * Exclude potential meddling from procfs.
+	 */
+	prbarrier(rproc);
+
+	/*
+	 * Check that the LWP is still in "ptrace-stop" and, if so, restart it.
+	 */
+	thread_lock(rt);
+	if (BSTOPPED(rt) && rt->t_whystop == PR_BRAND) {
+		rt->t_schedflag |= TS_BSTART;
+		setrun_locked(rt);
+
+		/*
+		 * Clear stop reason.
+		 */
+		rlwpd->br_ptrace_whystop = 0;
+		rlwpd->br_ptrace_whatstop = 0;
+		rlwpd->br_ptrace_flags &= ~(LX_PTF_CLDPEND | LX_PTF_WAITPEND);
+	}
+	thread_unlock(rt);
+}
+
+static void
+lx_ptrace_winfo(lx_lwp_data_t *remote, k_siginfo_t *ip, boolean_t waitflag,
+    pid_t *event_ppid, pid_t *event_pid)
+{
+	int signo;
+
+	/*
+	 * Populate our k_siginfo_t with data about this "ptrace-stop"
+	 * condition:
+	 */
+	bzero(ip, sizeof (*ip));
+	ip->si_signo = SIGCLD;
+	ip->si_pid = LX_PID_TO_INIT(remote->br_pid);
+	ip->si_code = CLD_TRAPPED;
+
+	switch (remote->br_ptrace_whatstop) {
+	case LX_PR_SYSENTRY:
+	case LX_PR_SYSEXIT:
+		ip->si_status = SIGTRAP;
+		if (remote->br_ptrace_options & LX_PTRACE_O_TRACESYSGOOD) {
+			ip->si_status |= 0x80;
+		}
+		break;
+
+	case LX_PR_SIGNALLED:
+		signo = remote->br_ptrace_stopsig;
+		if (signo < 1 || signo >= LX_NSIG) {
+			/*
+			 * If this signal number is not valid, pretend it
+			 * was a SIGTRAP.
+			 */
+			ip->si_status = SIGTRAP;
+		} else {
+			ip->si_status = ltos_signo[signo];
+		}
+		break;
+
+	case LX_PR_EVENT:
+		ip->si_status = SIGTRAP | remote->br_ptrace_event;
+		/*
+		 * Record the Linux pid of both this LWP and the create
+		 * event we are dispatching.  We will use this information
+		 * to unblock any subsequent ptrace(2) events that depend
+		 * on this one.
+		 */
+		if (event_ppid != NULL)
+			*event_ppid = remote->br_pid;
+		if (event_pid != NULL)
+			*event_pid = (pid_t)remote->br_ptrace_eventmsg;
+		break;
+
+	default:
+		cmn_err(CE_PANIC, "unxpected stop subreason: %d",
+		    remote->br_ptrace_whatstop);
+	}
+
+	/*
+	 * If WNOWAIT was specified, do not mark the event as posted
+	 * so that it may be re-fetched on another call to waitid().
+	 */
+	if (waitflag)
+		remote->br_ptrace_flags &= ~(LX_PTF_CLDPEND | LX_PTF_WAITPEND);
+}
+
+/*
+ * Receive notification from stop() of a PR_BRAND stop.
+ */
+void
+lx_stop_notify(proc_t *p, klwp_t *lwp, ushort_t why, ushort_t what)
+{
+	lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+	lx_ptrace_accord_t *accord;
+	klwp_t *plwp = NULL;
+	proc_t *pp = NULL;
+	lx_lwp_data_t *parent;
+	boolean_t cldpend = B_TRUE;
+	boolean_t cldpost = B_FALSE;
+	sigqueue_t *sqp = NULL;
+
+	/*
+	 * We currently only care about LX-specific stop reasons.
+	 */
+	if (why != PR_BRAND)
+		return;
+
+	switch (what) {
+	case LX_PR_SYSENTRY:
+	case LX_PR_SYSEXIT:
+	case LX_PR_SIGNALLED:
+	case LX_PR_EVENT:
+		break;
+	default:
+		cmn_err(CE_PANIC, "unexpected subreason for PR_BRAND"
+		    " stop: %d", (int)what);
+	}
+
+	/*
+	 * We should be holding the lock on our containing process.  The
+	 * STOPPING flag should have been set by lx_ptrace_stop() for all
+	 * PR_BRAND stops.
+	 */
+	VERIFY(MUTEX_HELD(&p->p_lock));
+	VERIFY(lwpd->br_ptrace_flags & LX_PTF_STOPPING);
+	VERIFY((accord = lwpd->br_ptrace_tracer) != NULL);
+
+	/*
+	 * We must drop our process lock to take "pidlock".  The
+	 * LX_PTF_STOPPING flag protects us from an exiting tracer.
+	 */
+	mutex_exit(&p->p_lock);
+
+	/*
+	 * Allocate before we enter any mutexes.
+	 */
+	sqp = kmem_zalloc(sizeof (*sqp), KM_SLEEP);
+
+	/*
+	 * We take pidlock now, which excludes all callers of waitid() and
+	 * prevents a detaching tracer from clearing critical accord members.
+	 */
+	mutex_enter(&pidlock);
+	mutex_enter(&p->p_lock);
+
+	/*
+	 * Get the ptrace(2) "parent" process, to which we may send
+	 * a SIGCLD signal later.
+	 */
+	if ((parent = accord->lxpa_tracer) != NULL &&
+	    (plwp = parent->br_lwp) != NULL) {
+		pp = lwptoproc(plwp);
+	}
+
+	/*
+	 * Our tracer should not have been modified in our absence; the
+	 * LX_PTF_STOPPING flag prevents it.
+	 */
+	VERIFY(lwpd->br_ptrace_tracer == accord);
+
+	/*
+	 * Stash data for this stop condition in the LWP data while we hold
+	 * both pidlock and our p_lock.
+	 */
+	lwpd->br_ptrace_whystop = why;
+	lwpd->br_ptrace_whatstop = what;
+	lwpd->br_ptrace_flags |= LX_PTF_WAITPEND;
+
+	/*
+	 * If this event does not depend on an event from the parent LWP,
+	 * populate the siginfo_t for the event pending on this tracee LWP.
+	 */
+	if (!(lwpd->br_ptrace_flags & LX_PTF_PARENT_WAIT) && pp != NULL) {
+		cldpost = B_TRUE;
+		lx_ptrace_winfo(lwpd, &sqp->sq_info, B_FALSE, NULL, NULL);
+	}
+
+	/*
+	 * Drop our p_lock so that we may lock the tracer.
+	 */
+	mutex_exit(&p->p_lock);
+	if (cldpost && pp != NULL) {
+		/*
+		 * Post the SIGCLD to the tracer.
+		 */
+		mutex_enter(&pp->p_lock);
+		if (!sigismember(&pp->p_sig, SIGCLD)) {
+			sigaddqa(pp, plwp->lwp_thread, sqp);
+			cldpend = B_FALSE;
+			sqp = NULL;
+		}
+		mutex_exit(&pp->p_lock);
+	}
+
+	/*
+	 * We re-take our process lock now.  The lock will be held until
+	 * the thread is actually marked stopped, so we will not race with
+	 * lx_ptrace_lock_if_stopped() or lx_waitid_helper().
+	 */
+	mutex_enter(&p->p_lock);
+
+	/*
+	 * We clear the STOPPING flag; stop() continues to hold our p_lock
+	 * until our thread stop state is visible.
+	 */
+	lwpd->br_ptrace_flags &= ~LX_PTF_STOPPING;
+	lwpd->br_ptrace_flags |= LX_PTF_STOPPED;
+	if (cldpend) {
+		/*
+		 * We sent the SIGCLD for this new wait condition already.
+		 */
+		lwpd->br_ptrace_flags |= LX_PTF_CLDPEND;
+	}
+
+	/*
+	 * If lx_ptrace_exit_tracer() is trying to detach our tracer, it will
+	 * be sleeping on this CV until LX_PTF_STOPPING is clear.  Wake it
+	 * now.
+	 */
+	cv_broadcast(&lx_ptrace_busy_cv);
+
+	/*
+	 * While still holding pidlock, we attempt to wake our tracer from a
+	 * potential waitid() slumber.
+	 */
+	if (accord->lxpa_cvp != NULL) {
+		cv_broadcast(accord->lxpa_cvp);
+	}
+
+	/*
+	 * We release pidlock and return as we were called: with our p_lock
+	 * held.
+	 */
+	mutex_exit(&pidlock);
+
+	if (sqp != NULL) {
+		kmem_free(sqp, sizeof (*sqp));
+	}
+}
+
+/*
+ * For any restarting action (e.g. PTRACE_CONT, PTRACE_SYSCALL or
+ * PTRACE_DETACH) to be allowed, the tracee LWP must be in "ptrace-stop".  This
+ * check must ONLY be run on tracees of the current LWP.  If the check is
+ * successful, we return with the tracee p_lock held.
+ *
+ * In the case of PTRACE_DETACH, we can return with the tracee locked even if
+ * it is not in "ptrace-stop". This can happen for various reasons, such as if
+ * the remote process is already job-stopped in the kernel. We must still be
+ * able to detach from this process. We return ENOENT in this case.
+ */
+static int
+lx_ptrace_lock_if_stopped(lx_ptrace_accord_t *accord, lx_lwp_data_t *remote,
+    boolean_t detaching)
+{
+	klwp_t *rlwp = remote->br_lwp;
+	proc_t *rproc = lwptoproc(rlwp);
+	kthread_t *rt = lwptot(rlwp);
+
+	/*
+	 * We must never check that we, ourselves, are stopped.  We must also
+	 * have the accord tracee list locked while we lock our tracees.
+	 */
+	VERIFY(curthread != rt);
+	VERIFY(MUTEX_HELD(&accord->lxpa_tracees_lock));
+	VERIFY(accord->lxpa_tracer == ttolxlwp(curthread));
+
+	/*
+	 * Lock the process containing the tracee LWP.
+	 */
+	mutex_enter(&rproc->p_lock);
+	if (!VISIBLE(remote)) {
+		/*
+		 * The tracee LWP is currently detaching itself as it exits.
+		 * It is no longer visible to ptrace(2).
+		 */
+		mutex_exit(&rproc->p_lock);
+		return (ESRCH);
+	}
+
+	/*
+	 * We must only check whether tracees of the current LWP are stopped.
+	 * We check this condition after confirming visibility as an exiting
+	 * tracee may no longer be completely consistent.
+	 */
+	VERIFY(remote->br_ptrace_tracer == accord);
+
+	if (!(remote->br_ptrace_flags & LX_PTF_STOPPED)) {
+		if (detaching) {
+			/*
+			 * The tracee is not in "ptrace-stop", but we still
+			 * return with the locked process. This is indicated
+			 * by ENOENT.
+			 */
+			return (ENOENT);
+		}
+
+		/*
+		 * The tracee is not in "ptrace-stop", so we release the
+		 * process.
+		 */
+		mutex_exit(&rproc->p_lock);
+		return (ESRCH);
+	}
+
+	/*
+	 * The tracee is stopped.  We return holding its process lock so that
+	 * the caller may manipulate it.
+	 */
+	return (0);
+}
+
+static int
+lx_ptrace_setoptions(lx_lwp_data_t *remote, uintptr_t options)
+{
+	/*
+	 * Check for valid options.
+	 */
+	if ((options & ~LX_PTRACE_O_ALL) != 0) {
+		return (EINVAL);
+	}
+
+	/*
+	 * Set ptrace options on the target LWP.
+	 */
+	remote->br_ptrace_options = (lx_ptrace_options_t)options;
+
+	return (0);
+}
+
+static int
+lx_ptrace_geteventmsg(lx_lwp_data_t *remote, void *umsgp)
+{
+	int error;
+
+#if defined(_SYSCALL32_IMPL)
+	if (get_udatamodel() != DATAMODEL_NATIVE) {
+		uint32_t tmp = remote->br_ptrace_eventmsg;
+
+		error = copyout(&tmp, umsgp, sizeof (uint32_t));
+	} else
+#endif
+	{
+		error = copyout(&remote->br_ptrace_eventmsg, umsgp,
+		    sizeof (ulong_t));
+	}
+
+	return (error);
+}
+
+static int
+lx_ptrace_getsiginfo(lx_lwp_data_t *remote, void *usiginfo)
+{
+	klwp_t *lwp = remote->br_lwp;
+	int lx_sig;
+
+	lx_sig = lx_stol_signo(lwp->lwp_cursig, 0);
+	if (lx_sig < 1 || lwp->lwp_curinfo == NULL) {
+		return (EINVAL);
+	}
+
+#if defined(_SYSCALL32_IMPL)
+	if (get_udatamodel() != DATAMODEL_NATIVE) {
+		if (stol_ksiginfo32_copyout(&lwp->lwp_curinfo->sq_info,
+		    usiginfo) != 0) {
+			return (EFAULT);
+		}
+	} else
+#endif
+	{
+		if (stol_ksiginfo_copyout(&lwp->lwp_curinfo->sq_info,
+		    usiginfo) != 0) {
+			return (EFAULT);
+		}
+	}
+
+	return (0);
+}
+
+
+/*
+ * Implements the PTRACE_CONT subcommand of the Linux ptrace(2) interface.
+ */
+static int
+lx_ptrace_cont(lx_lwp_data_t *remote, lx_ptrace_cont_flags_t flags, int signo)
+{
+	klwp_t *lwp = remote->br_lwp;
+
+	if (flags & LX_PTC_SINGLESTEP) {
+		/*
+		 * We do not currently support single-stepping.
+		 */
+		lx_unsupported("PTRACE_SINGLESTEP not currently implemented");
+		return (EINVAL);
+	}
+
+	/*
+	 * The tracer may choose to suppress the delivery of a signal, or
+	 * select an alternative signal for delivery.  If this is an
+	 * appropriate ptrace(2) "signal-delivery-stop", br_ptrace_stopsig
+	 * will be used as the new signal number.
+	 *
+	 * As with so many other aspects of the Linux ptrace(2) interface, this
+	 * may fail silently if the state machine is not aligned correctly.
+	 */
+	remote->br_ptrace_stopsig = signo;
+	remote->br_ptrace_donesig = 0;
+
+	/*
+	 * Handle the syscall-stop flag if this is a PTRACE_SYSCALL restart:
+	 */
+	if (flags & LX_PTC_SYSCALL) {
+		remote->br_ptrace_flags |= LX_PTF_SYSCALL;
+	} else {
+		remote->br_ptrace_flags &= ~LX_PTF_SYSCALL;
+	}
+
+	lx_ptrace_restart_lwp(lwp);
+
+	return (0);
+}
+
+/*
+ * Implements the PTRACE_DETACH subcommand of the Linux ptrace(2) interface.
+ *
+ * The LWP identified by the Linux pid "lx_pid" will, if it as a tracee of the
+ * current LWP, be detached and (optionally) set runnable.
+ */
+static void
+lx_ptrace_detach(lx_ptrace_accord_t *accord, lx_lwp_data_t *remote, int signo,
+    boolean_t restart)
+{
+	klwp_t *rlwp = remote->br_lwp;
+
+	/*
+	 * The tracee LWP was in "ptrace-stop" and we now hold its p_lock.
+	 * Detach the LWP from the accord and set it running.
+	 */
+	VERIFY(!TRACEE_BUSY(remote));
+	VERIFY(MUTEX_HELD(&accord->lxpa_tracees_lock));
+	remote->br_ptrace_flags &= ~(LX_PTF_SYSCALL | LX_PTF_INHERIT);
+	VERIFY(list_link_active(&remote->br_ptrace_linkage));
+	list_remove(&accord->lxpa_tracees, remote);
+
+	remote->br_ptrace_attach = LX_PTA_NONE;
+	remote->br_ptrace_tracer = NULL;
+	remote->br_ptrace_flags = 0;
+
+	/*
+	 * Decrement traced-lwp count for the process.
+	 */
+	ASSERT(MUTEX_HELD(&rlwp->lwp_procp->p_lock));
+	VERIFY(ptolxproc(rlwp->lwp_procp)->l_ptrace-- >= 1);
+
+	/*
+	 * The tracer may, as described in lx_ptrace_cont(), choose to suppress
+	 * or modify the delivered signal.
+	 */
+	remote->br_ptrace_stopsig = signo;
+	remote->br_ptrace_donesig = 0;
+
+	if (restart) {
+		lx_ptrace_restart_lwp(rlwp);
+	}
+}
+
+/*
+ * This routine implements the PTRACE_ATTACH operation of the Linux ptrace(2)
+ * interface.
+ *
+ * This LWP is requesting to be attached as a tracer to another LWP -- the
+ * tracee.  If a ptrace accord to track the list of tracees has not yet been
+ * allocated, one will be allocated and attached to this LWP now.
+ *
+ * The "br_ptrace_tracer" on the tracee LWP is set to this accord, and the
+ * tracee LWP is then added to the "lxpa_tracees" list in the accord.  We drop
+ * locks between these two phases; the only consumer of trace events from this
+ * accord is this LWP, which obviously cannot be running waitpid(2) at the same
+ * time as this call to ptrace(2).
+ */
+static int
+lx_ptrace_attach(pid_t lx_pid)
+{
+	int error = ESRCH;
+	/*
+	 * Our (Tracer) LWP:
+	 */
+	lx_ptrace_accord_t *accord;
+	lx_lwp_data_t *lwpd = ttolxlwp(curthread);
+	/*
+	 * Remote (Tracee) LWP:
+	 */
+	pid_t rpid;
+	id_t rtid;
+	proc_t *rproc;
+	kthread_t *rthr;
+	klwp_t *rlwp;
+	lx_lwp_data_t *rlwpd;
+
+	if (lwpd->br_pid == lx_pid) {
+		/*
+		 * We cannot trace ourselves.
+		 */
+		return (EPERM);
+	}
+
+	/*
+	 * Ensure that we have an accord and obtain a lock on it.  This
+	 * routine should not fail because the LWP cannot make ptrace(2) system
+	 * calls after it has begun exiting.
+	 */
+	VERIFY0(lwpd->br_ptrace_flags & LX_PTF_EXITING);
+	VERIFY(lx_ptrace_accord_get(&accord, B_TRUE) == 0);
+
+	/*
+	 * Place speculative hold in case the attach is successful.
+	 */
+	lx_ptrace_accord_hold(accord);
+	lx_ptrace_accord_exit(accord);
+
+	/*
+	 * Locate the process containing the tracee LWP based on its Linux pid
+	 * and lock it.
+	 */
+	if (lx_lpid_to_spair(lx_pid, &rpid, &rtid) != 0 ||
+	    (rproc = sprlock(rpid)) == NULL) {
+		/*
+		 * We could not find the target process.
+		 */
+		goto errout;
+	}
+
+	/*
+	 * Locate the tracee LWP.
+	 */
+	if ((rthr = idtot(rproc, rtid)) == NULL ||
+	    (rlwp = ttolwp(rthr)) == NULL ||
+	    (rlwpd = lwptolxlwp(rlwp)) == NULL ||
+	    !VISIBLE(rlwpd)) {
+		/*
+		 * The LWP could not be found, was not branded, or is not
+		 * visible to ptrace(2) at this time.
+		 */
+		goto unlock_errout;
+	}
+
+	/*
+	 * We now hold the lock on the tracee.  Attempt to install ourselves
+	 * as the tracer.
+	 */
+	if (curproc != rproc && priv_proc_cred_perm(curproc->p_cred, rproc,
+	    NULL, VWRITE) != 0) {
+		/*
+		 * This process does not have permission to trace the remote
+		 * process.
+		 */
+		error = EPERM;
+	} else if (rlwpd->br_ptrace_tracer != NULL) {
+		/*
+		 * This LWP is already being traced.
+		 */
+		VERIFY(list_link_active(&rlwpd->br_ptrace_linkage));
+		VERIFY(rlwpd->br_ptrace_attach != LX_PTA_NONE);
+		error = EPERM;
+	} else {
+		lx_proc_data_t *rprocd = ptolxproc(rproc);
+
+		/*
+		 * Bond the tracee to the accord.
+		 */
+		VERIFY0(rlwpd->br_ptrace_flags & LX_PTF_EXITING);
+		VERIFY(rlwpd->br_ptrace_attach == LX_PTA_NONE);
+		rlwpd->br_ptrace_attach = LX_PTA_ATTACH;
+		rlwpd->br_ptrace_tracer = accord;
+
+		/* Don't emit ptrace syscall-stop-exit event on kernel exit. */
+		rlwpd->br_ptrace_flags |= LX_PTF_NOSTOP;
+
+		/*
+		 * We had no tracer, and are thus not in the tracees list.
+		 * It is safe to take the tracee list lock while we insert
+		 * ourselves.
+		 */
+		mutex_enter(&accord->lxpa_tracees_lock);
+		VERIFY(!list_link_active(&rlwpd->br_ptrace_linkage));
+		list_insert_tail(&accord->lxpa_tracees, rlwpd);
+		/*
+		 * Bump traced-lwp count for the remote process.
+		 */
+		rprocd->l_ptrace++;
+		mutex_exit(&accord->lxpa_tracees_lock);
+
+		/*
+		 * Send a thread-directed SIGSTOP.
+		 */
+		sigtoproc(rproc, rthr, SIGSTOP);
+
+
+		error = 0;
+	}
+
+unlock_errout:
+	/*
+	 * Unlock the process containing the tracee LWP and the accord.
+	 */
+	sprunlock(rproc);
+
+errout:
+	if (error != 0) {
+		/*
+		 * The attach was not successful.  Remove our speculative
+		 * hold.
+		 */
+		lx_ptrace_accord_enter(accord);
+		lx_ptrace_accord_rele(accord);
+		lx_ptrace_accord_exit(accord);
+	}
+
+	return (error);
+}
+
+int
+lx_ptrace_set_clone_inherit(int option, boolean_t inherit_flag)
+{
+	klwp_t *lwp = ttolwp(curthread);
+	proc_t *p = lwptoproc(lwp);
+	lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+
+	mutex_enter(&p->p_lock);
+
+	switch (option) {
+	case LX_PTRACE_O_TRACEFORK:
+	case LX_PTRACE_O_TRACEVFORK:
+	case LX_PTRACE_O_TRACECLONE:
+		lwpd->br_ptrace_clone_option = option;
+		break;
+
+	default:
+		return (EINVAL);
+	}
+
+	if (inherit_flag) {
+		lwpd->br_ptrace_flags |= LX_PTF_INHERIT;
+	} else {
+		lwpd->br_ptrace_flags &= ~LX_PTF_INHERIT;
+	}
+
+	mutex_exit(&p->p_lock);
+	return (0);
+}
+
+/*
+ * If the parent LWP is being traced, we want to attach ourselves to the
+ * same accord.
+ */
+void
+lx_ptrace_inherit_tracer(lx_lwp_data_t *src, lx_lwp_data_t *dst)
+{
+	proc_t *srcp = lwptoproc(src->br_lwp);
+	proc_t *dstp = lwptoproc(dst->br_lwp);
+	lx_ptrace_accord_t *accord;
+	boolean_t is_fork = B_FALSE;
+
+	VERIFY(MUTEX_HELD(&dstp->p_lock));
+	if (srcp != dstp) {
+		/*
+		 * In the case of being called via forklwp, some lock shuffling
+		 * is required.  The destination p_lock must be dropped to
+		 * avoid deadlocks when locking the source and manipulating
+		 * ptrace accord resources.
+		 */
+		is_fork = B_TRUE;
+		sprlock_proc(dstp);
+		mutex_exit(&dstp->p_lock);
+		mutex_enter(&srcp->p_lock);
+	}
+
+	if ((accord = src->br_ptrace_tracer) == NULL) {
+		/*
+		 * The source LWP does not have a tracer to inherit.
+		 */
+		goto out;
+	}
+
+	/*
+	 * There are two conditions to check when determining if the new
+	 * child should inherit the same tracer (and tracing options) as its
+	 * parent.  Either condition is sufficient to trigger inheritance.
+	 */
+	dst->br_ptrace_attach = LX_PTA_NONE;
+	if ((src->br_ptrace_options & src->br_ptrace_clone_option) != 0) {
+		/*
+		 * Condition 1:
+		 * The clone(2), fork(2) and vfork(2) emulated system calls
+		 * populate "br_ptrace_clone_option" with the specific
+		 * ptrace(2) SETOPTIONS option that applies to this
+		 * operation.  If the relevant option has been enabled by the
+		 * tracer then we inherit.
+		 */
+		dst->br_ptrace_attach |= LX_PTA_INHERIT_OPTIONS;
+
+	} else if ((src->br_ptrace_flags & LX_PTF_INHERIT) != 0) {
+		/*
+		 * Condition 2:
+		 * If the caller opted in to inheritance with the
+		 * PTRACE_CLONE flag to clone(2), the LX_PTF_INHERIT flag
+		 * will be set and we inherit.
+		 */
+		dst->br_ptrace_attach |= LX_PTA_INHERIT_CLONE;
+	}
+
+	/*
+	 * These values only apply for the duration of a single clone(2), et
+	 * al, system call.
+	 */
+	src->br_ptrace_flags &= ~LX_PTF_INHERIT;
+	src->br_ptrace_clone_option = 0;
+
+	if (dst->br_ptrace_attach == LX_PTA_NONE) {
+		/*
+		 * No condition triggered inheritance.
+		 */
+		goto out;
+	}
+
+	/*
+	 * Set the LX_PTF_CLONING flag to prevent us from being detached
+	 * while our p_lock is dropped.
+	 */
+	src->br_ptrace_flags |= LX_PTF_CLONING;
+	mutex_exit(&srcp->p_lock);
+
+	/*
+	 * Hold the accord for the new LWP.
+	 */
+	lx_ptrace_accord_enter(accord);
+	lx_ptrace_accord_hold(accord);
+	lx_ptrace_accord_exit(accord);
+
+	/*
+	 * Install the tracer and copy the current PTRACE_SETOPTIONS options.
+	 */
+	dst->br_ptrace_tracer = accord;
+	dst->br_ptrace_options = src->br_ptrace_options;
+
+	/*
+	 * This flag prevents waitid() from seeing events for the new child
+	 * until the parent is able to post the relevant ptrace event to
+	 * the tracer.
+	 */
+	dst->br_ptrace_flags |= LX_PTF_PARENT_WAIT;
+
+	mutex_enter(&accord->lxpa_tracees_lock);
+	VERIFY(list_link_active(&src->br_ptrace_linkage));
+	VERIFY(!list_link_active(&dst->br_ptrace_linkage));
+	list_insert_tail(&accord->lxpa_tracees, dst);
+	mutex_exit(&accord->lxpa_tracees_lock);
+
+	/*
+	 * Relock our process and clear our busy flag.
+	 */
+	mutex_enter(&srcp->p_lock);
+	src->br_ptrace_flags &= ~LX_PTF_CLONING;
+
+	/*
+	 * Bump traced-lwp count for the process.
+	 */
+	ptolxproc(dstp)->l_ptrace++;
+
+	/*
+	 * If lx_ptrace_exit_tracer() is trying to detach our tracer, it will
+	 * be sleeping on this CV until LX_PTF_CLONING is clear.  Wake it
+	 * now.
+	 */
+	cv_broadcast(&lx_ptrace_busy_cv);
+
+out:
+	if (is_fork) {
+		mutex_exit(&srcp->p_lock);
+		mutex_enter(&dstp->p_lock);
+		sprunprlock(dstp);
+	}
+}
+
+static int
+lx_ptrace_traceme(void)
+{
+	int error;
+	boolean_t did_attach = B_FALSE;
+	/*
+	 * Our (Tracee) LWP:
+	 */
+	klwp_t *lwp = ttolwp(curthread);
+	proc_t *p = lwptoproc(lwp);
+	lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+	/*
+	 * Remote (Tracer) LWP:
+	 */
+	lx_ptrace_accord_t *accord;
+
+	/*
+	 * We are intending to be the tracee.  Fetch (or allocate) the accord
+	 * for our parent LWP.
+	 */
+	if ((error = lx_ptrace_accord_get_by_pid(lx_lwp_ppid(lwp, NULL,
+	    NULL), &accord)) != 0) {
+		/*
+		 * Could not determine the Linux pid of the parent LWP, or
+		 * could not get the accord for that LWP.
+		 */
+		return (error);
+	}
+
+	/*
+	 * We now hold the accord lock.
+	 */
+	if (accord->lxpa_flags & LX_ACC_TOMBSTONE) {
+		/*
+		 * The accord is marked for death; give up now.
+		 */
+		lx_ptrace_accord_exit(accord);
+		return (ESRCH);
+	}
+
+	/*
+	 * Bump the reference count so that the accord is not freed.  We need
+	 * to drop the accord lock before we take our own p_lock.
+	 */
+	lx_ptrace_accord_hold(accord);
+	lx_ptrace_accord_exit(accord);
+
+	/*
+	 * We now lock _our_ process and determine if we can install our parent
+	 * as our tracer.
+	 */
+	mutex_enter(&p->p_lock);
+	if (lwpd->br_ptrace_tracer != NULL) {
+		/*
+		 * This LWP is already being traced.
+		 */
+		VERIFY(lwpd->br_ptrace_attach != LX_PTA_NONE);
+		error = EPERM;
+	} else {
+		/*
+		 * Bond ourselves to the accord.  We already bumped the accord
+		 * reference count.
+		 */
+		VERIFY(lwpd->br_ptrace_attach == LX_PTA_NONE);
+		lwpd->br_ptrace_attach = LX_PTA_TRACEME;
+		lwpd->br_ptrace_tracer = accord;
+		did_attach = B_TRUE;
+		error = 0;
+
+		/*
+		 * Speculatively bump l_ptrace now before dropping p_lock.
+		 * It will be reverted if the tracee attachment fails.
+		 */
+		ptolxproc(p)->l_ptrace++;
+	}
+	mutex_exit(&p->p_lock);
+
+	/*
+	 * Lock the accord tracee list and add this LWP.  Once we are in the
+	 * tracee list, it is the responsibility of the tracer to detach us.
+	 */
+	if (error == 0) {
+		lx_ptrace_accord_enter(accord);
+		mutex_enter(&accord->lxpa_tracees_lock);
+
+		if (!(accord->lxpa_flags & LX_ACC_TOMBSTONE)) {
+			/*
+			 * Put ourselves in the tracee list for this accord.
+			 */
+			VERIFY(!list_link_active(&lwpd->br_ptrace_linkage));
+			list_insert_tail(&accord->lxpa_tracees, lwpd);
+			mutex_exit(&accord->lxpa_tracees_lock);
+			lx_ptrace_accord_exit(accord);
+
+			return (0);
+		}
+		mutex_exit(&accord->lxpa_tracees_lock);
+
+		/*
+		 * The accord has been marked for death.  We must
+		 * untrace ourselves.
+		 */
+		error = ESRCH;
+		lx_ptrace_accord_exit(accord);
+
+		/*
+		 * Undo speculative increment of ptracer count.
+		 */
+		mutex_enter(&p->p_lock);
+		ptolxproc(p)->l_ptrace--;
+		mutex_exit(&p->p_lock);
+	}
+
+	/*
+	 * Our optimism was unjustified: We were unable to attach.  We need to
+	 * lock the process containing this LWP again in order to remove the
+	 * tracer.
+	 */
+	VERIFY(error != 0);
+	mutex_enter(&p->p_lock);
+	if (did_attach) {
+		/*
+		 * Verify that things were as we left them:
+		 */
+		VERIFY(!list_link_active(&lwpd->br_ptrace_linkage));
+		VERIFY(lwpd->br_ptrace_tracer == accord);
+
+		lwpd->br_ptrace_attach = LX_PTA_NONE;
+		lwpd->br_ptrace_tracer = NULL;
+	}
+	mutex_exit(&p->p_lock);
+
+	/*
+	 * Remove our speculative hold on the accord, possibly causing it to be
+	 * freed in the process.
+	 */
+	lx_ptrace_accord_enter(accord);
+	lx_ptrace_accord_rele(accord);
+	lx_ptrace_accord_exit(accord);
+
+	return (error);
+}
+
+static boolean_t
+lx_ptrace_stop_common(proc_t *p, lx_lwp_data_t *lwpd, ushort_t what)
+{
+	boolean_t reset_nostop = B_FALSE;
+
+	VERIFY(MUTEX_HELD(&p->p_lock));
+
+	/*
+	 * Mark this LWP as stopping and call stop() to enter "ptrace-stop".
+	 */
+	VERIFY0(lwpd->br_ptrace_flags & LX_PTF_STOPPING);
+	lwpd->br_ptrace_flags |= LX_PTF_STOPPING;
+
+	if (lwpd->br_lwp->lwp_nostop == 1 &&
+	    lwpd->br_ptrace_event == LX_PTRACE_EVENT_EXEC) {
+		/* We need to clear this to get the signal delivered. */
+		lwpd->br_lwp->lwp_nostop = 0;
+		reset_nostop = B_TRUE;
+	}
+
+	stop(PR_BRAND, what);
+
+	if (reset_nostop) {
+		VERIFY(lwpd->br_lwp->lwp_nostop == 0);
+		lwpd->br_lwp->lwp_nostop = 1;
+	}
+
+	/*
+	 * We are back from "ptrace-stop" with our process lock held.
+	 */
+	lwpd->br_ptrace_flags &= ~(LX_PTF_STOPPING | LX_PTF_STOPPED |
+	    LX_PTF_CLDPEND);
+	lwpd->br_ptrace_stopucp = NULL;
+	cv_broadcast(&lx_ptrace_busy_cv);
+	mutex_exit(&p->p_lock);
+
+	return (B_TRUE);
+}
+
+int
+lx_ptrace_stop_for_option(int option, boolean_t child, ulong_t msg,
+    uintptr_t ucp)
+{
+	kthread_t *t = curthread;
+	klwp_t *lwp = ttolwp(t);
+	proc_t *p = lwptoproc(lwp);
+	lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+
+	mutex_enter(&p->p_lock);
+	if (lwpd->br_ptrace_tracer == NULL) {
+		mutex_exit(&p->p_lock);
+		return (ESRCH);
+	}
+
+	if (!child) {
+		/*
+		 * Only the first event posted by a new process is to be held
+		 * until the matching parent event is dispatched, and only if
+		 * it is a "child" event.  This is not a child event, so we
+		 * clear the wait flag.
+		 */
+		lwpd->br_ptrace_flags &= ~LX_PTF_PARENT_WAIT;
+
+	} else if (option == LX_PTRACE_O_TRACEVFORK) {
+		/*
+		 * For a child, we have to handle vfork as a special case. In
+		 * lx_ptrace_inherit_tracer() we set LX_PTF_PARENT_WAIT to
+		 * force events to be delayed until the parent posts its event.
+		 * This flag is cleared in lx_waitid_helper() to enforce a
+		 * "happens after" relationship. However, this obviously cannot
+		 * work for the vfork case. Thus, we clear our flag now so that
+		 * we can deliver the signal in lx_stop_notify(), if necessary.
+		 */
+		lwpd->br_ptrace_flags &= ~LX_PTF_PARENT_WAIT;
+	}
+
+	if (!(lwpd->br_ptrace_options & option)) {
+		if (option == LX_PTRACE_O_TRACEEXEC) {
+			/*
+			 * Without PTRACE_O_TRACEEXEC, the Linux kernel will
+			 * send SIGTRAP to the process.
+			 */
+			sigtoproc(p, t, SIGTRAP);
+			mutex_exit(&p->p_lock);
+			return (0);
+		}
+
+		/*
+		 * The flag for this trace event is not enabled, so we will not
+		 * stop.
+		 */
+		mutex_exit(&p->p_lock);
+		return (ESRCH);
+	}
+
+	if (child) {
+		switch (option) {
+		case LX_PTRACE_O_TRACECLONE:
+		case LX_PTRACE_O_TRACEFORK:
+		case LX_PTRACE_O_TRACEVFORK:
+			/*
+			 * Send the child LWP a directed SIGSTOP.
+			 */
+			sigtoproc(p, t, SIGSTOP);
+			mutex_exit(&p->p_lock);
+			return (0);
+		default:
+			goto nostop;
+		}
+	}
+
+	lwpd->br_ptrace_eventmsg = msg;
+
+	switch (option) {
+	case LX_PTRACE_O_TRACECLONE:
+		lwpd->br_ptrace_event = LX_PTRACE_EVENT_CLONE;
+		break;
+	case LX_PTRACE_O_TRACEEXEC:
+		lwpd->br_ptrace_event = LX_PTRACE_EVENT_EXEC;
+		lwpd->br_ptrace_eventmsg = 0;
+		break;
+	case LX_PTRACE_O_TRACEEXIT:
+		lwpd->br_ptrace_event = LX_PTRACE_EVENT_EXIT;
+		break;
+	case LX_PTRACE_O_TRACEFORK:
+		lwpd->br_ptrace_event = LX_PTRACE_EVENT_FORK;
+		break;
+	case LX_PTRACE_O_TRACEVFORK:
+		lwpd->br_ptrace_event = LX_PTRACE_EVENT_VFORK;
+		break;
+	case LX_PTRACE_O_TRACEVFORKDONE:
+		lwpd->br_ptrace_event = LX_PTRACE_EVENT_VFORK_DONE;
+		lwpd->br_ptrace_eventmsg = 0;
+		break;
+	default:
+		goto nostop;
+	}
+
+	/*
+	 * Userland may have passed in a ucontext_t pointer for
+	 * PTRACE_GETREGS/PTRACE_SETREGS usage while stopped.
+	 */
+	lwpd->br_ptrace_stopucp = ucp;
+
+	/*
+	 * p_lock for the process containing the tracee will be dropped by
+	 * lx_ptrace_stop_common().
+	 */
+	return (lx_ptrace_stop_common(p, lwpd, LX_PR_EVENT) ? 0 : ESRCH);
+
+nostop:
+	lwpd->br_ptrace_event = 0;
+	lwpd->br_ptrace_eventmsg = 0;
+	mutex_exit(&p->p_lock);
+	return (ESRCH);
+}
+
+boolean_t
+lx_ptrace_stop(ushort_t what)
+{
+	klwp_t *lwp = ttolwp(curthread);
+	proc_t *p = lwptoproc(lwp);
+	lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+
+	VERIFY(what == LX_PR_SYSENTRY || what == LX_PR_SYSEXIT ||
+	    what == LX_PR_SIGNALLED);
+
+	/*
+	 * If we do not have an accord, bail out early.
+	 */
+	if (lwpd->br_ptrace_tracer == NULL)
+		return (B_FALSE);
+
+	/*
+	 * Lock this process and re-check the condition.
+	 */
+	mutex_enter(&p->p_lock);
+
+	/*
+	 * The child after a fork/clone doesn't emit syscall-exit-stop event.
+	 */
+	if (what == LX_PR_SYSEXIT && (lwpd->br_ptrace_flags & LX_PTF_NOSTOP)) {
+		lwpd->br_ptrace_flags &= ~LX_PTF_NOSTOP;
+		mutex_exit(&p->p_lock);
+		return (B_FALSE);
+	}
+
+	if (lwpd->br_ptrace_tracer == NULL) {
+		VERIFY0(lwpd->br_ptrace_flags & LX_PTF_SYSCALL);
+		mutex_exit(&p->p_lock);
+		return (B_FALSE);
+	}
+
+	if (what == LX_PR_SYSENTRY || what == LX_PR_SYSEXIT) {
+		if (what == LX_PR_SYSENTRY) {
+			lwpd->br_ptrace_flags |= LX_PTF_INSYSCALL;
+		} else {
+			lwpd->br_ptrace_flags &= ~LX_PTF_INSYSCALL;
+		}
+
+		/*
+		 * This is a syscall-entry-stop or syscall-exit-stop point.
+		 */
+		if (!(lwpd->br_ptrace_flags & LX_PTF_SYSCALL)) {
+			/*
+			 * A system call stop has not been requested.
+			 */
+			mutex_exit(&p->p_lock);
+			return (B_FALSE);
+		}
+
+		/*
+		 * The PTRACE_SYSCALL restart command applies only to the next
+		 * system call entry or exit.  The tracer must restart us with
+		 * PTRACE_SYSCALL while we are in ptrace-stop for us to fire
+		 * again at the next system call boundary.
+		 */
+		lwpd->br_ptrace_flags &= ~LX_PTF_SYSCALL;
+	}
+
+	/*
+	 * p_lock for the process containing the tracee will be dropped by
+	 * lx_ptrace_stop_common().
+	 */
+	return (lx_ptrace_stop_common(p, lwpd, what));
+}
+
+int
+lx_ptrace_issig_stop(proc_t *p, klwp_t *lwp)
+{
+	lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+	int lx_sig;
+
+	VERIFY(MUTEX_HELD(&p->p_lock));
+
+	/*
+	 * In very rare circumstances, a process which is almost completely
+	 * through proc_exit() may incur issig checks in the current thread via
+	 * clean-up actions.  The process will still be branded, but the thread
+	 * will have already been stripped of any LX-specific data on its way
+	 * to the grave.  Bail early if the brand data is missing.
+	 */
+	if (lwpd == NULL) {
+		return (0);
+	}
+
+	/*
+	 * If we do not have an accord, bail out now.  Additionally, if there
+	 * is no valid signal then we have no reason to stop.
+	 */
+	if (lwpd->br_ptrace_tracer == NULL || lwp->lwp_cursig == SIGKILL ||
+	    (lwp->lwp_cursig == 0 || lwp->lwp_cursig > NSIG) ||
+	    (lx_sig = stol_signo[lwp->lwp_cursig]) < 1) {
+		if (lwp->lwp_cursig == 0) {
+			/*
+			 * If this lwp has no current signal, it means that any
+			 * signal ignorance enabled by br_ptrace_donesig has
+			 * already taken place (the signal was consumed).
+			 * By clearing donesig, we declare desire to ignore no
+			 * signals for accurate ptracing.
+			 */
+			lwpd->br_ptrace_donesig = 0;
+		}
+		return (0);
+	}
+
+	/*
+	 * We can't deliver the signal-delivery-stop condition while we're
+	 * between the syscall-enter-stop and syscall-exit-stop conditions.
+	 * We must first let the signal interrupt the in-progress syscall, let
+	 * it emit syscall-exit-stop with the interrupted result, then we'll
+	 * come back here to emit signal-delivery-stop.
+	 */
+	if (lwpd->br_ptrace_flags & LX_PTF_INSYSCALL) {
+		return (0);
+	}
+
+	/*
+	 * We stash the signal on the LWP where our waitid_helper will find it
+	 * and enter the ptrace "signal-delivery-stop" condition.
+	 */
+	lwpd->br_ptrace_stopsig = lx_sig;
+	lwpd->br_ptrace_donesig = 0;
+	(void) lx_ptrace_stop_common(p, lwpd, LX_PR_SIGNALLED);
+	mutex_enter(&p->p_lock);
+
+	/*
+	 * When we return, the signal may have been altered or suppressed.
+	 */
+	if (lwpd->br_ptrace_stopsig != lx_sig) {
+		int native_sig;
+		lx_sig = lwpd->br_ptrace_stopsig;
+
+		if (lx_sig >= LX_NSIG) {
+			lx_sig = 0;
+		}
+
+		/*
+		 * Translate signal from Linux signal number back to
+		 * an illumos native signal.
+		 */
+		if (lx_sig >= LX_NSIG || lx_sig < 0 || (native_sig =
+		    ltos_signo[lx_sig]) < 1) {
+			/*
+			 * The signal is not deliverable.
+			 */
+			lwp->lwp_cursig = 0;
+			lwp->lwp_extsig = 0;
+			if (lwp->lwp_curinfo) {
+				siginfofree(lwp->lwp_curinfo);
+				lwp->lwp_curinfo = NULL;
+			}
+		} else {
+			/*
+			 * Alter the currently dispatching signal.
+			 */
+			if (native_sig == SIGKILL) {
+				/*
+				 * We mark ourselves the victim and request
+				 * a restart of signal processing.
+				 */
+				p->p_flag |= SKILLED;
+				p->p_flag &= ~SEXTKILLED;
+				return (-1);
+			}
+			lwp->lwp_cursig = native_sig;
+			lwp->lwp_extsig = 0;
+			if (lwp->lwp_curinfo != NULL) {
+				lwp->lwp_curinfo->sq_info.si_signo = native_sig;
+			}
+		}
+	}
+
+	lwpd->br_ptrace_donesig = lwp->lwp_cursig;
+	lwpd->br_ptrace_stopsig = 0;
+	return (0);
+}
+
+boolean_t
+lx_ptrace_sig_ignorable(proc_t *p, klwp_t *lwp, int sig)
+{
+	lx_proc_data_t *lxpd = ptolxproc(p);
+
+	/*
+	 * Ignored signals and ptrace:
+	 *
+	 * When a process is being ptraced by another, special care is needed
+	 * while handling signals.  Since the tracer is interested in all
+	 * signals sent to the tracee, an effort must be made to initially
+	 * bypass signal ignorance logic.  This allows the signal to be placed
+	 * in the tracee's sigqueue to be inspected and potentially altered by
+	 * the tracer.
+	 *
+	 * A critical detail in this procedure is how a signal is handled after
+	 * tracer has completed processing for the event.  If the signal would
+	 * have been ignored, were it not for the initial ptrace override, then
+	 * lx_ptrace_sig_ignorable must report B_TRUE when the tracee is
+	 * restarted and resumes signal processing.  This is done by recording
+	 * the most recent tracee signal consumed by ptrace.
+	 */
+
+	if (lxpd->l_ptrace != 0 && lx_stol_signo(sig, 0) != 0) {
+		/*
+		 * This process is being ptraced.  Bypass signal ignorance for
+		 * anything that maps to a valid Linux signal...
+		 */
+		if (lwp != NULL && lwptolxlwp(lwp)->br_ptrace_donesig == sig) {
+			/*
+			 * ...Unless it is a signal which has already been
+			 * processed by the tracer.
+			 */
+			return (B_TRUE);
+		}
+		return (B_FALSE);
+	}
+	return (B_TRUE);
+}
+
+static void
+lx_ptrace_exit_tracer(proc_t *p, lx_lwp_data_t *lwpd,
+    lx_ptrace_accord_t *accord)
+{
+	VERIFY(MUTEX_NOT_HELD(&p->p_lock));
+
+	lx_ptrace_accord_enter(accord);
+	/*
+	 * Mark this accord for death.  This means no new tracees can be
+	 * attached to this accord.
+	 */
+	VERIFY0(accord->lxpa_flags & LX_ACC_TOMBSTONE);
+	accord->lxpa_flags |= LX_ACC_TOMBSTONE;
+	lx_ptrace_accord_exit(accord);
+
+	/*
+	 * Walk the list of tracees, detaching them and setting them runnable
+	 * if they are stopped.
+	 */
+	for (;;) {
+		klwp_t *rlwp;
+		proc_t *rproc;
+		lx_lwp_data_t *remote;
+		kmutex_t *rmp;
+
+		mutex_enter(&accord->lxpa_tracees_lock);
+		if (list_is_empty(&accord->lxpa_tracees)) {
+			mutex_exit(&accord->lxpa_tracees_lock);
+			break;
+		}
+
+		/*
+		 * Fetch the first tracee LWP in the list and lock the process
+		 * which contains it.
+		 */
+		remote = list_head(&accord->lxpa_tracees);
+		rlwp = remote->br_lwp;
+		rproc = lwptoproc(rlwp);
+		/*
+		 * The p_lock mutex persists beyond the life of the process
+		 * itself.  We save the address, here, to prevent the need to
+		 * dereference the proc_t after awaking from sleep.
+		 */
+		rmp = &rproc->p_lock;
+		mutex_enter(rmp);
+
+		if (TRACEE_BUSY(remote)) {
+			/*
+			 * This LWP is currently detaching itself on exit, or
+			 * mid-way through stop().  We must wait for this
+			 * action to be completed.  While we wait on the CV, we
+			 * must drop the accord tracee list lock.
+			 */
+			mutex_exit(&accord->lxpa_tracees_lock);
+			cv_wait(&lx_ptrace_busy_cv, rmp);
+
+			/*
+			 * While we were waiting, some state may have changed.
+			 * Restart the walk to be sure we don't miss anything.
+			 */
+			mutex_exit(rmp);
+			continue;
+		}
+
+		/*
+		 * We now hold p_lock on the process.  Remove the tracee from
+		 * the list.
+		 */
+		VERIFY(list_link_active(&remote->br_ptrace_linkage));
+		list_remove(&accord->lxpa_tracees, remote);
+
+		/*
+		 * Unlink the accord and clear our trace flags.
+		 */
+		remote->br_ptrace_attach = LX_PTA_NONE;
+		remote->br_ptrace_tracer = NULL;
+		remote->br_ptrace_flags = 0;
+
+		/*
+		 * Let go of the list lock before we restart the LWP.  We must
+		 * not hold any locks other than the process p_lock when
+		 * we call lx_ptrace_restart_lwp() as it will thread_lock
+		 * the tracee.
+		 */
+		mutex_exit(&accord->lxpa_tracees_lock);
+
+		/*
+		 * Decrement traced-lwp count for the remote process.
+		 */
+		VERIFY(ptolxproc(rproc)->l_ptrace-- >= 1);
+
+		/*
+		 * Ensure that the LWP is not stopped on our account.
+		 */
+		lx_ptrace_restart_lwp(rlwp);
+
+		/*
+		 * Unlock the former tracee.
+		 */
+		mutex_exit(rmp);
+
+		/*
+		 * Drop the hold this tracee had on the accord.
+		 */
+		lx_ptrace_accord_enter(accord);
+		lx_ptrace_accord_rele(accord);
+		lx_ptrace_accord_exit(accord);
+	}
+
+	mutex_enter(&p->p_lock);
+	lwpd->br_ptrace_accord = NULL;
+	mutex_exit(&p->p_lock);
+
+	/*
+	 * Clean up and release our hold on the accord If we completely
+	 * detached all tracee LWPs, this will free the accord.  Otherwise, it
+	 * will be freed when they complete their cleanup.
+	 *
+	 * We hold "pidlock" while clearing these members for easy exclusion of
+	 * waitid(), etc.
+	 */
+	mutex_enter(&pidlock);
+	lx_ptrace_accord_enter(accord);
+	accord->lxpa_cvp = NULL;
+	accord->lxpa_tracer = NULL;
+	mutex_exit(&pidlock);
+	lx_ptrace_accord_rele(accord);
+	lx_ptrace_accord_exit(accord);
+}
+
+static void
+lx_ptrace_exit_tracee(proc_t *p, lx_lwp_data_t *lwpd,
+    lx_ptrace_accord_t *accord)
+{
+	VERIFY(MUTEX_NOT_HELD(&p->p_lock));
+
+	/*
+	 * We are the tracee LWP.  Lock the accord tracee list and then our
+	 * containing process.
+	 */
+	mutex_enter(&accord->lxpa_tracees_lock);
+	mutex_enter(&p->p_lock);
+
+	/*
+	 * Remove our reference to the accord.  We will release our hold
+	 * later.
+	 */
+	VERIFY(lwpd->br_ptrace_tracer == accord);
+	lwpd->br_ptrace_attach = LX_PTA_NONE;
+	lwpd->br_ptrace_tracer = NULL;
+
+	/*
+	 * Remove this LWP from the accord tracee list:
+	 */
+	VERIFY(list_link_active(&lwpd->br_ptrace_linkage));
+	list_remove(&accord->lxpa_tracees, lwpd);
+
+	/*
+	 * Wake up any tracers waiting for us to detach from the accord.
+	 */
+	cv_broadcast(&lx_ptrace_busy_cv);
+
+	/*
+	 * Decrement traced-lwp count for the process.
+	 */
+	VERIFY(ptolxproc(p)->l_ptrace-- >= 1);
+
+	mutex_exit(&p->p_lock);
+	mutex_exit(&accord->lxpa_tracees_lock);
+
+	/*
+	 * Grab "pidlock" and wake the tracer if it is blocked in waitid().
+	 */
+	mutex_enter(&pidlock);
+	if (accord->lxpa_cvp != NULL) {
+		cv_broadcast(accord->lxpa_cvp);
+	}
+	mutex_exit(&pidlock);
+
+	/*
+	 * Release our hold on the accord.
+	 */
+	lx_ptrace_accord_enter(accord);
+	lx_ptrace_accord_rele(accord);
+	lx_ptrace_accord_exit(accord);
+}
+
+/*
+ * This routine is called from lx_exitlwp() when an LWP is ready to exit.  If
+ * this LWP is being traced, it will be detached from the tracer's accord.  The
+ * routine will also detach any LWPs being traced by this LWP.
+ */
+void
+lx_ptrace_exit(proc_t *p, klwp_t *lwp)
+{
+	lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+	lx_ptrace_accord_t *accord;
+
+	VERIFY(MUTEX_HELD(&p->p_lock));
+
+	/*
+	 * Mark our LWP as exiting from a ptrace perspective.  This will
+	 * prevent a new accord from being allocated if one does not exist
+	 * already, and will make us invisible to PTRACE_ATTACH/PTRACE_TRACEME.
+	 */
+	VERIFY0(lwpd->br_ptrace_flags & LX_PTF_EXITING);
+	lwpd->br_ptrace_flags |= LX_PTF_EXITING;
+
+	if ((accord = lwpd->br_ptrace_tracer) != NULL) {
+		/*
+		 * We are traced by another LWP and must detach ourselves.
+		 */
+		mutex_exit(&p->p_lock);
+		lx_ptrace_exit_tracee(p, lwpd, accord);
+		mutex_enter(&p->p_lock);
+	}
+
+	if ((accord = lwpd->br_ptrace_accord) != NULL) {
+		/*
+		 * We have been tracing other LWPs, and must detach from
+		 * them and clean up our accord.
+		 */
+		mutex_exit(&p->p_lock);
+		lx_ptrace_exit_tracer(p, lwpd, accord);
+		mutex_enter(&p->p_lock);
+	}
+}
+
+/*
+ * Called when a SIGCLD signal is dispatched so that we may enqueue another.
+ * Return 0 if we enqueued a signal, or -1 if not.
+ */
+int
+lx_sigcld_repost(proc_t *pp, sigqueue_t *sqp)
+{
+	klwp_t *lwp = ttolwp(curthread);
+	lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+	lx_ptrace_accord_t *accord;
+	lx_lwp_data_t *remote;
+	klwp_t *rlwp;
+	proc_t *rproc;
+	boolean_t found = B_FALSE;
+
+	VERIFY(MUTEX_HELD(&pidlock));
+	VERIFY(MUTEX_NOT_HELD(&pp->p_lock));
+	VERIFY(lwptoproc(lwp) == pp);
+
+	mutex_enter(&pp->p_lock);
+	if ((accord = lwpd->br_ptrace_accord) == NULL) {
+		/*
+		 * This LWP is not a tracer LWP, so there will be no
+		 * SIGCLD.
+		 */
+		mutex_exit(&pp->p_lock);
+		return (-1);
+	}
+	mutex_exit(&pp->p_lock);
+
+	mutex_enter(&accord->lxpa_tracees_lock);
+	for (remote = list_head(&accord->lxpa_tracees); remote != NULL;
+	    remote = list_next(&accord->lxpa_tracees, remote)) {
+		rlwp = remote->br_lwp;
+		rproc = lwptoproc(rlwp);
+
+		/*
+		 * Check if this LWP is in "ptrace-stop".  If in the correct
+		 * stop condition, lock the process containing the tracee LWP.
+		 */
+		if (lx_ptrace_lock_if_stopped(accord, remote, B_FALSE) != 0) {
+			continue;
+		}
+
+		if (remote->br_ptrace_flags & LX_PTF_PARENT_WAIT) {
+			/*
+			 * This event depends on waitid() clearing out the
+			 * event of another LWP.  Skip it for now.
+			 */
+			mutex_exit(&rproc->p_lock);
+			continue;
+		}
+
+		if (!(remote->br_ptrace_flags & LX_PTF_CLDPEND)) {
+			/*
+			 * No SIGCLD is required for this LWP.
+			 */
+			mutex_exit(&rproc->p_lock);
+			continue;
+		}
+
+		if (!(remote->br_ptrace_flags & LX_PTF_WAITPEND) ||
+		    remote->br_ptrace_whystop == 0 ||
+		    remote->br_ptrace_whatstop == 0) {
+			/*
+			 * No (new) stop reason to post for this LWP.
+			 */
+			mutex_exit(&rproc->p_lock);
+			continue;
+		}
+
+		/*
+		 * We found a process of interest.  Leave the process
+		 * containing the tracee LWP locked and break out of the loop.
+		 */
+		found = B_TRUE;
+		break;
+	}
+	mutex_exit(&accord->lxpa_tracees_lock);
+
+	if (!found) {
+		return (-1);
+	}
+
+	/*
+	 * Generate siginfo for this tracee LWP.
+	 */
+	lx_ptrace_winfo(remote, &sqp->sq_info, B_FALSE, NULL, NULL);
+	remote->br_ptrace_flags &= ~LX_PTF_CLDPEND;
+	mutex_exit(&rproc->p_lock);
+
+	mutex_enter(&pp->p_lock);
+	if (sigismember(&pp->p_sig, SIGCLD)) {
+		mutex_exit(&pp->p_lock);
+
+		mutex_enter(&rproc->p_lock);
+		remote->br_ptrace_flags |= LX_PTF_CLDPEND;
+		mutex_exit(&rproc->p_lock);
+
+		return (-1);
+	}
+	sigaddqa(pp, curthread, sqp);
+	mutex_exit(&pp->p_lock);
+
+	return (0);
+}
+
+/*
+ * Consume the next available ptrace(2) event queued against the accord for
+ * this LWP.  The event will be emitted as if through waitid(), and converted
+ * by lx_waitpid() and friends before the return to usermode.
+ */
+int
+lx_waitid_helper(idtype_t idtype, id_t id, k_siginfo_t *ip, int options,
+    boolean_t *brand_wants_wait, int *rval)
+{
+	lx_ptrace_accord_t *accord;
+	klwp_t *lwp = ttolwp(curthread);
+	proc_t *p = lwptoproc(lwp);
+	lx_lwp_data_t *local = lwptolxlwp(lwp);
+	lx_lwp_data_t *remote;
+	boolean_t found = B_FALSE;
+	klwp_t *rlwp = NULL;
+	proc_t *rproc = NULL;
+	pid_t event_pid = 0, event_ppid = 0;
+	boolean_t waitflag = !(options & WNOWAIT);
+	boolean_t target_found = B_FALSE;
+
+	VERIFY(MUTEX_HELD(&pidlock));
+	VERIFY(MUTEX_NOT_HELD(&p->p_lock));
+
+	/*
+	 * By default, we do not expect waitid() to block on our account.
+	 */
+	*brand_wants_wait = B_FALSE;
+
+	if (!local->br_waitid_emulate) {
+		/*
+		 * This waitid() call is not expecting emulated results.
+		 */
+		return (-1);
+	}
+
+	switch (idtype) {
+	case P_ALL:
+	case P_PID:
+	case P_PGID:
+		break;
+	default:
+		/*
+		 * This idtype has no power here.
+		 */
+		return (-1);
+	}
+
+	if (lx_ptrace_accord_get(&accord, B_FALSE) != 0) {
+		/*
+		 * This LWP does not have an accord; it cannot be tracing.
+		 */
+		return (-1);
+	}
+
+	/*
+	 * We do not need an additional hold on the accord as it belongs to
+	 * the running, tracer, LWP.
+	 */
+	lx_ptrace_accord_exit(accord);
+
+	mutex_enter(&accord->lxpa_tracees_lock);
+	if (list_is_empty(&accord->lxpa_tracees)) {
+		/*
+		 * Though it has an accord, there are currently no tracees in
+		 * the list for this LWP.
+		 */
+		mutex_exit(&accord->lxpa_tracees_lock);
+		return (-1);
+	}
+
+	/*
+	 * Walk the list of tracees and determine if any of them have events to
+	 * report.
+	 */
+	for (remote = list_head(&accord->lxpa_tracees); remote != NULL;
+	    remote = list_next(&accord->lxpa_tracees, remote)) {
+		rlwp = remote->br_lwp;
+		rproc = lwptoproc(rlwp);
+
+		/*
+		 * We check to see if this LWP matches an id we are waiting for.
+		 */
+		switch (idtype) {
+		case P_ALL:
+			break;
+		case P_PID:
+			if (remote->br_pid != id)
+				continue;
+			break;
+		case P_PGID:
+			if (rproc->p_pgrp != id)
+				continue;
+			break;
+		default:
+			cmn_err(CE_PANIC, "unexpected idtype: %d", idtype);
+		}
+
+		/* This tracee matches provided idtype and id */
+		target_found = B_TRUE;
+
+		/*
+		 * Check if this LWP is in "ptrace-stop".  If in the correct
+		 * stop condition, lock the process containing the tracee LWP.
+		 */
+		if (lx_ptrace_lock_if_stopped(accord, remote, B_FALSE) != 0) {
+			continue;
+		}
+
+		if (remote->br_ptrace_flags & LX_PTF_PARENT_WAIT) {
+			/*
+			 * This event depends on waitid() clearing out the
+			 * event of another LWP.  Skip it for now.
+			 */
+			mutex_exit(&rproc->p_lock);
+			continue;
+		}
+
+		if (!(remote->br_ptrace_flags & LX_PTF_WAITPEND) ||
+		    remote->br_ptrace_whystop == 0 ||
+		    remote->br_ptrace_whatstop == 0) {
+			/*
+			 * No (new) stop reason to post for this LWP.
+			 */
+			mutex_exit(&rproc->p_lock);
+			continue;
+		}
+
+		/*
+		 * We found a process of interest.  Leave the process
+		 * containing the tracee LWP locked and break out of the loop.
+		 */
+		found = B_TRUE;
+		break;
+	}
+	mutex_exit(&accord->lxpa_tracees_lock);
+
+	if (!found) {
+		/*
+		 * There were no events of interest, but we have tracees.
+		 * If any of the tracees matched the spcified criteria, signal
+		 * to waitid() that it should block if the provided flags allow
+		 * for it.
+		 */
+		if (target_found) {
+			*brand_wants_wait = B_TRUE;
+		}
+
+		return (-1);
+	}
+
+	/*
+	 * Populate the signal information.
+	 */
+	lx_ptrace_winfo(remote, ip, waitflag, &event_ppid, &event_pid);
+
+	/*
+	 * Unlock the tracee.
+	 */
+	mutex_exit(&rproc->p_lock);
+
+	if (event_pid != 0 && event_ppid != 0) {
+		/*
+		 * We need to do another pass around the tracee list and
+		 * unblock any events that have a "happens after" relationship
+		 * with this event.
+		 */
+		mutex_enter(&accord->lxpa_tracees_lock);
+		for (remote = list_head(&accord->lxpa_tracees); remote != NULL;
+		    remote = list_next(&accord->lxpa_tracees, remote)) {
+			rlwp = remote->br_lwp;
+			rproc = lwptoproc(rlwp);
+
+			mutex_enter(&rproc->p_lock);
+
+			if (remote->br_pid != event_pid ||
+			    remote->br_ppid != event_ppid) {
+				mutex_exit(&rproc->p_lock);
+				continue;
+			}
+
+			remote->br_ptrace_flags &= ~LX_PTF_PARENT_WAIT;
+
+			mutex_exit(&rproc->p_lock);
+		}
+		mutex_exit(&accord->lxpa_tracees_lock);
+	}
+
+	/*
+	 * If we are consuming this wait state, we remove the SIGCLD from
+	 * the queue and post another.
+	 */
+	if (waitflag) {
+		mutex_exit(&pidlock);
+		sigcld_delete(ip);
+		sigcld_repost();
+		mutex_enter(&pidlock);
+	}
+
+	*rval = 0;
+	return (0);
+}
+
+static int
+lx_ptrace_peek(lx_lwp_data_t *lwpd, uintptr_t addr, void *data)
+{
+	proc_t *p = lwptoproc(lwpd->br_lwp);
+	long buf;
+	int error = 0, size = sizeof (buf);
+
+#if defined(_LP64)
+	if (get_udatamodel() != DATAMODEL_NATIVE) {
+		size = sizeof (uint32_t);
+	}
+#endif
+	if ((addr & (size - 1)) != 0) {
+		/* unaligned access */
+		return (EINVAL);
+	}
+
+	mutex_exit(&p->p_lock);
+	error = uread(p, &buf, size, addr);
+	mutex_enter(&p->p_lock);
+
+	if (error != 0) {
+		return (EIO);
+	}
+	if (copyout(&buf, data, size) != 0) {
+		return (EFAULT);
+	}
+
+	return (0);
+}
+
+static int
+lx_ptrace_poke(lx_lwp_data_t *lwpd, uintptr_t addr, uintptr_t data)
+{
+	proc_t *p = lwptoproc(lwpd->br_lwp);
+	int error = 0, size = sizeof (data);
+
+#if defined(_LP64)
+	if (get_udatamodel() != DATAMODEL_NATIVE) {
+		size = sizeof (uint32_t);
+	}
+#endif
+	if ((addr & (size - 1)) != 0) {
+		/* unaligned access */
+		return (EINVAL);
+	}
+
+	mutex_exit(&p->p_lock);
+	error = uwrite(p, &data, size, addr);
+	mutex_enter(&p->p_lock);
+
+	if (error != 0) {
+		return (EIO);
+	}
+	return (0);
+}
+
+static int
+lx_ptrace_kill(lx_lwp_data_t *lwpd)
+{
+	sigtoproc(lwptoproc(lwpd->br_lwp), NULL, SIGKILL);
+
+	return (0);
+}
+
+static int
+lx_ptrace_kernel(int ptrace_op, pid_t lxpid, uintptr_t addr, uintptr_t data)
+{
+	lx_lwp_data_t *local = ttolxlwp(curthread);
+	lx_ptrace_accord_t *accord;
+	lx_lwp_data_t *remote;
+	klwp_t *rlwp;
+	proc_t *rproc;
+	int error;
+	boolean_t found = B_FALSE, restart = B_TRUE;
+
+	/*
+	 * PTRACE_TRACEME and PTRACE_ATTACH operations induce the tracing of
+	 * one LWP by another.  The target LWP must not be traced already.
+	 */
+	switch (ptrace_op) {
+	case LX_PTRACE_TRACEME:
+		return (lx_ptrace_traceme());
+
+	case LX_PTRACE_ATTACH:
+		return (lx_ptrace_attach(lxpid));
+	}
+
+	/*
+	 * Ensure that we have an accord and obtain a lock on it.  This routine
+	 * should not fail because the LWP cannot make ptrace(2) system calls
+	 * after it has begun exiting.
+	 */
+	VERIFY0(local->br_ptrace_flags & LX_PTF_EXITING);
+	VERIFY(lx_ptrace_accord_get(&accord, B_TRUE) == 0);
+
+	/*
+	 * The accord belongs to this (the tracer) LWP, and we have a hold on
+	 * it.  We drop the lock so that we can take other locks.
+	 */
+	lx_ptrace_accord_exit(accord);
+
+	/*
+	 * Does the tracee list contain the pid in question?
+	 */
+	mutex_enter(&accord->lxpa_tracees_lock);
+	for (remote = list_head(&accord->lxpa_tracees); remote != NULL;
+	    remote = list_next(&accord->lxpa_tracees, remote)) {
+		if (remote->br_pid == lxpid) {
+			found = B_TRUE;
+			break;
+		}
+	}
+	if (!found) {
+		/*
+		 * The requested pid does not appear in the tracee list.
+		 */
+		mutex_exit(&accord->lxpa_tracees_lock);
+		return (ESRCH);
+	}
+
+	if (ptrace_op == LX_PTRACE_DETACH) {
+		/*
+		 * We're detaching, make sure in-syscall flag is off so that
+		 * signal will stop the process directly.
+		 */
+		remote->br_ptrace_flags &= ~LX_PTF_INSYSCALL;
+	}
+
+	/*
+	 * Attempt to lock the target LWP.
+	 */
+	if ((error = lx_ptrace_lock_if_stopped(accord, remote,
+	    (ptrace_op == LX_PTRACE_DETACH))) != 0) {
+		/*
+		 * The LWP was not in "ptrace-stop". For detach, ENOENT
+		 * indicates that the LWP was not in "ptrace-stop", but is
+		 * still locked.
+		 */
+		if (ptrace_op == LX_PTRACE_DETACH && error == ENOENT) {
+			/*
+			 * We're detaching, but the process was not in
+			 * ptrace_stop, so we don't want to try to restart it.
+			 */
+			restart = B_FALSE;
+		} else {
+			mutex_exit(&accord->lxpa_tracees_lock);
+			return (error);
+		}
+	}
+
+	/*
+	 * The target LWP is in "ptrace-stop".  We have the containing process
+	 * locked.
+	 */
+	rlwp = remote->br_lwp;
+	rproc = lwptoproc(rlwp);
+
+
+	if (ptrace_op == LX_PTRACE_DETACH) {
+		lx_ptrace_detach(accord, remote, (int)data, restart);
+		/*
+		 * Drop the lock on both the tracee process and the tracee list.
+		 */
+		mutex_exit(&rproc->p_lock);
+		mutex_exit(&accord->lxpa_tracees_lock);
+
+		/*
+		 * Release a hold from the accord.
+		 */
+		lx_ptrace_accord_enter(accord);
+		lx_ptrace_accord_rele(accord);
+		lx_ptrace_accord_exit(accord);
+
+		return (0);
+	}
+
+	/*
+	 * The tracees lock is not needed for any of the other operations.
+	 * Drop it so further actions can avoid deadlock.
+	 */
+	mutex_exit(&accord->lxpa_tracees_lock);
+
+	/*
+	 * Process the ptrace(2) request:
+	 */
+	switch (ptrace_op) {
+	case LX_PTRACE_CONT:
+		error = lx_ptrace_cont(remote, LX_PTC_NONE, (int)data);
+		break;
+
+	case LX_PTRACE_SYSCALL:
+		error = lx_ptrace_cont(remote, LX_PTC_SYSCALL, (int)data);
+		break;
+
+	case LX_PTRACE_SINGLESTEP:
+		error = lx_ptrace_cont(remote, LX_PTC_SINGLESTEP, (int)data);
+		break;
+
+	case LX_PTRACE_SETOPTIONS:
+		error = lx_ptrace_setoptions(remote, data);
+		break;
+
+	case LX_PTRACE_GETEVENTMSG:
+		error = lx_ptrace_geteventmsg(remote, (void *)data);
+		break;
+
+	case LX_PTRACE_GETREGS:
+		error = lx_user_regs_copyout(remote, (void *)data);
+		break;
+
+	case LX_PTRACE_SETREGS:
+		error = lx_user_regs_copyin(remote, (void *)data);
+		break;
+
+	case LX_PTRACE_GETSIGINFO:
+		error = lx_ptrace_getsiginfo(remote, (void *)data);
+		break;
+
+	case LX_PTRACE_PEEKTEXT:
+	case LX_PTRACE_PEEKDATA:
+		error = lx_ptrace_peek(remote, addr, (void *)data);
+		break;
+
+	case LX_PTRACE_POKETEXT:
+	case LX_PTRACE_POKEDATA:
+		error = lx_ptrace_poke(remote, addr, data);
+		break;
+
+	case LX_PTRACE_PEEKUSER:
+		error = lx_ptrace_peekuser(remote, addr, (void *)data);
+		break;
+
+	case LX_PTRACE_POKEUSER:
+		error = lx_ptrace_pokeuser(remote, addr, (void *)data);
+		break;
+
+	case LX_PTRACE_GETFPREGS:
+		error = lx_user_fpregs_copyout(remote, (void *)data);
+		break;
+
+	case LX_PTRACE_SETFPREGS:
+		error = lx_user_fpregs_copyin(remote, (void *)data);
+		break;
+
+	case LX_PTRACE_GETFPXREGS:
+		error = lx_user_fpxregs_copyout(remote, (void *)data);
+		break;
+
+	case LX_PTRACE_SETFPXREGS:
+		error = lx_user_fpxregs_copyin(remote, (void *)data);
+		break;
+
+	case LX_PTRACE_KILL:
+		error = lx_ptrace_kill(remote);
+		break;
+
+	default:
+		error = EINVAL;
+	}
+
+	/*
+	 * Drop the lock on both the tracee process and the tracee list.
+	 */
+	mutex_exit(&rproc->p_lock);
+
+	return (error);
+}
+
+int
+lx_ptrace(int ptrace_op, pid_t lxpid, uintptr_t addr, uintptr_t data)
+{
+	int error;
+
+	error = lx_ptrace_kernel(ptrace_op, LX_INIT_TO_PID(lxpid), addr, data);
+	if (error != 0) {
+		return (set_errno(error));
+	}
+	return (0);
+}
+
+void
+lx_ptrace_init(void)
+{
+	cv_init(&lx_ptrace_busy_cv, NULL, CV_DEFAULT, NULL);
+
+	lx_ptrace_accord_cache = kmem_cache_create("lx_ptrace_accord",
+	    sizeof (lx_ptrace_accord_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
+}
+
+void
+lx_ptrace_fini(void)
+{
+	cv_destroy(&lx_ptrace_busy_cv);
+
+	kmem_cache_destroy(lx_ptrace_accord_cache);
+}
--- /dev/null	Wed Oct  5 00:32:05 2016
+++ new/usr/src/uts/common/brand/lx/os/lx_signal.c	Wed Oct  5 00:32:05 2016
@@ -0,0 +1,50 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/signal.h>
+#include <sys/sunddi.h>
+#include <lx_signum.h>
+
+void
+lx_ltos_sigset(lx_sigset_t *lsigp, k_sigset_t *ssigp)
+{
+	int lx_sig, sig;
+
+	sigemptyset(ssigp);
+	for (lx_sig = 1; lx_sig <= LX_NSIG; lx_sig++) {
+		if (lx_sigismember(lsigp, lx_sig) &&
+		    ((sig = ltos_signo[lx_sig]) > 0))
+			sigaddset(ssigp, sig);
+	}
+
+	/* Emulate sigutok() restrictions */
+	ssigp->__sigbits[0] &= (FILLSET0 & ~CANTMASK0);
+	ssigp->__sigbits[1] &= (FILLSET1 & ~CANTMASK1);
+	ssigp->__sigbits[2] &= (FILLSET2 & ~CANTMASK2);
+}
+
+void
+lx_stol_sigset(k_sigset_t *ssigp, lx_sigset_t *lsigp)
+{
+	int sig, lx_sig;
+
+	bzero(lsigp, sizeof (lx_sigset_t));
+	for (sig = 1; sig < NSIG; sig++) {
+		if (sigismember(ssigp, sig) &&
+		    ((lx_sig = stol_signo[sig]) > 0))
+			lx_sigaddset(lsigp, lx_sig);
+	}
+}
--- /dev/null	Wed Oct  5 00:32:05 2016
+++ new/usr/src/uts/common/brand/lx/os/lx_syscall.c	Wed Oct  5 00:32:05 2016
@@ -0,0 +1,1221 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#include <sys/kmem.h>
+#include <sys/errno.h>
+#include <sys/thread.h>
+#include <sys/systm.h>
+#include <sys/syscall.h>
+#include <sys/proc.h>
+#include <sys/modctl.h>
+#include <sys/cmn_err.h>
+#include <sys/model.h>
+#include <sys/privregs.h>
+#include <sys/brand.h>
+#include <sys/machbrand.h>
+#include <sys/sdt.h>
+#include <sys/lx_syscalls.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_impl.h>
+#include <sys/lx_misc.h>
+#include <lx_errno.h>
+
+
+/*
+ * Flags for sysent entries:
+ */
+#define	LX_SYS_NOSYS_REASON	0x07
+#define	LX_SYS_EBPARG6		0x08
+
+/*
+ * Flags that denote the specific reason we do not have a particular system
+ * call.  These reasons are only valid if the function is NULL.
+ */
+#define	NOSYS_USERMODE		0
+#define	NOSYS_NULL		1
+#define	NOSYS_NONE		2
+#define	NOSYS_NO_EQUIV		3
+#define	NOSYS_KERNEL		4
+#define	NOSYS_UNDOC		5
+#define	NOSYS_OBSOLETE		6
+#define	NOSYS_MAX		NOSYS_OBSOLETE
+
+#if NOSYS_MAX > LX_SYS_NOSYS_REASON
+#error NOSYS reason codes must fit in LX_SYS_NOSYS_REASON
+#endif
+
+/*
+ * Strings describing the reason we do not emulate a particular system call
+ * in the kernel.
+ */
+static char *nosys_reasons[] = {
+	NULL, /* NOSYS_USERMODE means this call is emulated in usermode */
+	"Not done yet",
+	"No such Linux system call",
+	"No equivalent illumos functionality",
+	"Reads/modifies Linux kernel state",
+	"Undocumented and/or rarely used system call",
+	"Unsupported, obsolete system call"
+};
+
+
+#if defined(_LP64)
+/*
+ * System call handler table and entry count for Linux x86_64 (amd64):
+ */
+lx_sysent_t lx_sysent64[LX_NSYSCALLS + 1];
+int lx_nsysent64;
+#endif
+/*
+ * System call handler table and entry count for Linux x86 (i386):
+ */
+lx_sysent_t lx_sysent32[LX_NSYSCALLS + 1];
+int lx_nsysent32;
+
+#if defined(_LP64)
+struct lx_vsyscall
+{
+	uintptr_t lv_addr;
+	uintptr_t lv_scnum;
+} lx_vsyscalls[] = {
+	{ LX_VSYS_gettimeofday, LX_SYS_gettimeofday },
+	{ LX_VSYS_time, LX_SYS_time },
+	{ LX_VSYS_getcpu, LX_SYS_getcpu },
+	{ NULL, NULL }
+};
+#endif
+
+#if defined(__amd64)
+static int
+lx_emulate_args(klwp_t *lwp, const lx_sysent_t *s, uintptr_t *args)
+{
+	struct regs *rp = lwptoregs(lwp);
+
+	if (get_udatamodel() == DATAMODEL_NATIVE) {
+		/*
+		 * Note: Syscall argument passing is different from function
+		 * call argument passing on amd64.  For function calls, the
+		 * fourth arg is passed via %rcx, but for system calls the 4th
+		 * arg is passed via %r10.  This is because in amd64, the
+		 * syscall instruction puts the lower 32 bits of %rflags in
+		 * %r11 and puts the %rip value to %rcx.
+		 *
+		 * Appendix A of the amd64 ABI (Linux conventions) states that
+		 * syscalls are limited to 6 args and no arg is passed on the
+		 * stack.
+		 */
+		args[0] = rp->r_rdi;
+		args[1] = rp->r_rsi;
+		args[2] = rp->r_rdx;
+		args[3] = rp->r_r10;
+		args[4] = rp->r_r8;
+		args[5] = rp->r_r9;
+	} else {
+		/*
+		 * If the system call takes 6 args, then libc has stashed them
+		 * in memory at the address contained in %ebx. Except for some
+		 * syscalls which store the 6th argument in %ebp.
+		 */
+		if (s->sy_narg == 6 && !(s->sy_flags & LX_SYS_EBPARG6)) {
+			uint32_t args32[6];
+
+			if (copyin((void *)rp->r_rbx, &args32,
+			    sizeof (args32)) != 0) {
+				/*
+				 * Clear the argument vector so that the
+				 * trace probe does not expose kernel
+				 * memory.
+				 */
+				bzero(args, 6 * sizeof (uintptr_t));
+				return (set_errno(EFAULT));
+			}
+
+			args[0] = args32[0];
+			args[1] = args32[1];
+			args[2] = args32[2];
+			args[3] = args32[3];
+			args[4] = args32[4];
+			args[5] = args32[5];
+		} else {
+			args[0] = rp->r_rbx;
+			args[1] = rp->r_rcx;
+			args[2] = rp->r_rdx;
+			args[3] = rp->r_rsi;
+			args[4] = rp->r_rdi;
+			args[5] = rp->r_rbp;
+		}
+	}
+
+	return (0);
+}
+
+#else	/* !__amd64 */
+
+static int
+lx_emulate_args(klwp_t *lwp, const lx_sysent_t *s, uintptr_t *args)
+{
+	struct regs *rp = lwptoregs(lwp);
+
+	/*
+	 * If the system call takes 6 args, then libc has stashed them
+	 * in memory at the address contained in %ebx. Except for some
+	 * syscalls which store the 6th argument in %ebp.
+	 */
+	if (s->sy_narg == 6 && !(s->sy_flags & LX_SYS_EBPARG6)) {
+		if (copyin((void *)rp->r_ebx, args, 6 * sizeof (uintptr_t)) !=
+		    0) {
+			/*
+			 * Clear the argument vector so that the trace probe
+			 * does not expose kernel memory.
+			 */
+			bzero(args, 6 * sizeof (uintptr_t));
+			return (set_errno(EFAULT));
+		}
+	} else {
+		args[0] = rp->r_ebx;
+		args[1] = rp->r_ecx;
+		args[2] = rp->r_edx;
+		args[3] = rp->r_esi;
+		args[4] = rp->r_edi;
+		args[5] = rp->r_ebp;
+	}
+
+	return (0);
+}
+#endif
+
+void
+lx_syscall_return(klwp_t *lwp, int syscall_num, long ret)
+{
+	lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+	struct regs *rp = lwptoregs(lwp);
+	int error = lwp->lwp_errno;
+
+	if (error != EINTR) {
+		/*
+		 * If this system call was not interrupted, clear the system
+		 * call restart flag before lx_setcontext() can pass it to
+		 * usermode.
+		 */
+		lwpd->br_syscall_restart = B_FALSE;
+	}
+
+	if (error != 0) {
+		/*
+		 * Convert from illumos to Linux errno:
+		 */
+		ret = -lx_errno(error, EINVAL);
+	}
+
+	/*
+	 * 32-bit Linux system calls return via %eax; 64-bit calls return via
+	 * %rax.
+	 */
+	rp->r_r0 = ret;
+
+	/*
+	 * Hold for the ptrace(2) "syscall-exit-stop" condition if required by
+	 * PTRACE_SYSCALL.  Note that the register state may be modified by
+	 * tracer.
+	 */
+	(void) lx_ptrace_stop(LX_PR_SYSEXIT);
+
+	/*
+	 * Fire the DTrace "lx-syscall:::return" probe:
+	 */
+	lx_trace_sysreturn(syscall_num, ret);
+
+	/*
+	 * Clear errno for next time.  We do not clear "br_syscall_restart" or
+	 * "br_syscall_num" as they are potentially used by "lx_savecontext()"
+	 * in the signal delivery path.
+	 */
+	lwp->lwp_errno = 0;
+
+	lx_check_strict_failure(lwpd);
+
+	/*
+	 * We want complete control of the registers on return from this
+	 * emulated Linux system call:
+	 */
+	lwp->lwp_eosys = JUSTRETURN;
+	curthread->t_post_sys = 1;
+	aston(curthread);
+}
+
+static void
+lx_syscall_unsup_msg(lx_sysent_t *s, int syscall_num, int unsup_reason)
+{
+	char buf[100];
+
+	if (s == NULL) {
+		(void) snprintf(buf, sizeof (buf), "NOSYS (%d): out of bounds",
+		    syscall_num);
+	} else {
+		VERIFY(unsup_reason < (sizeof (nosys_reasons) /
+		    sizeof (*nosys_reasons)));
+
+		if (s->sy_name == NULL) {
+			(void) snprintf(buf, sizeof (buf), "NOSYS (%d): %s",
+			    syscall_num, nosys_reasons[unsup_reason]);
+		} else {
+			(void) snprintf(buf, sizeof (buf), "NOSYS (%s): %s",
+			    s->sy_name, nosys_reasons[unsup_reason]);
+		}
+	}
+
+	lx_unsupported(buf);
+}
+
+/*
+ * This function is used to override the processing of arguments and
+ * invocation of a handler for emulated system calls, installed on each
+ * branded LWP as "lwp_brand_syscall".  If this system call should use the
+ * native path, we return 1.  If we handled this system call (and have made
+ * arrangements with respect to post-return usermode register state) we
+ * return 0.
+ */
+int
+lx_syscall_enter(void)
+{
+	klwp_t *lwp = ttolwp(curthread);
+	lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+	struct regs *rp = lwptoregs(lwp);
+	int syscall_num;
+	int error;
+	long ret = 0;
+	lx_sysent_t *s;
+	uintptr_t args[6];
+	unsigned int unsup_reason;
+
+	/*
+	 * If we got here, we should have an LWP-specific brand data
+	 * structure.
+	 */
+	VERIFY(lwpd != NULL);
+
+	if (lwpd->br_stack_mode != LX_STACK_MODE_BRAND) {
+		/*
+		 * The lwp is not in in BRAND execution mode, so we return
+		 * to the regular native system call path.
+		 */
+		DTRACE_PROBE(brand__lx__syscall__hook__skip);
+		return (1);
+	}
+
+	/*
+	 * Clear the restartable system call flag.  This flag will be set
+	 * on in the system call handler if the call is a candidate for
+	 * a restart.  It will be saved by lx_setcontext() in the event
+	 * that we take a signal, and used in the signal handling path
+	 * to restart the system call iff SA_RESTART was set for this
+	 * signal.  Save the system call number so that we can store it
+	 * in the saved context if required.
+	 */
+	lwpd->br_syscall_restart = B_FALSE;
+	lwpd->br_syscall_num = (int)rp->r_r0;
+
+	/*
+	 * Hold for the ptrace(2) "syscall-entry-stop" condition if traced by
+	 * PTRACE_SYSCALL.  The system call number and arguments may be
+	 * modified by the tracer.
+	 */
+	(void) lx_ptrace_stop(LX_PR_SYSENTRY);
+
+	/*
+	 * Check that the system call number is within the bounds we expect.
+	 */
+	syscall_num = lwpd->br_syscall_num;
+	if (syscall_num < 0 || syscall_num > LX_MAX_SYSCALL(lwp)) {
+		lx_syscall_unsup_msg(NULL, syscall_num, 0);
+
+		(void) set_errno(ENOTSUP);
+		lx_syscall_return(lwp, syscall_num, -1);
+		return (0);
+	}
+
+#if defined(_LP64)
+	if (lwp_getdatamodel(lwp) == DATAMODEL_NATIVE) {
+		s = &lx_sysent64[syscall_num];
+	} else
+#endif
+	{
+		s = &lx_sysent32[syscall_num];
+	}
+
+	/*
+	 * Process the arguments for this system call and fire the DTrace
+	 * "lx-syscall:::entry" probe:
+	 */
+	error = lx_emulate_args(lwp, s, args);
+	lx_trace_sysenter(syscall_num, args);
+	if (error != 0) {
+		/*
+		 * Could not read and process the arguments.  Return the error
+		 * to the process.
+		 */
+		(void) set_errno(error);
+		lx_syscall_return(lwp, syscall_num, -1);
+		return (0);
+	}
+
+	if (s->sy_callc != NULL) {
+		/*
+		 * Call the in-kernel handler for this Linux system call:
+		 */
+		lwpd->br_eosys = NORMALRETURN;
+		ret = s->sy_callc(args[0], args[1], args[2], args[3], args[4],
+		    args[5]);
+		if (lwpd->br_eosys == NORMALRETURN) {
+			lx_syscall_return(lwp, syscall_num, ret);
+		}
+		return (0);
+	}
+
+	/*
+	 * There is no in-kernel handler.
+	 */
+	switch (unsup_reason = (s->sy_flags & LX_SYS_NOSYS_REASON)) {
+	case NOSYS_USERMODE:
+		/*
+		 * Pass to the usermode emulation routine.
+		 */
+#if defined(_LP64)
+		if (get_udatamodel() != DATAMODEL_NATIVE) {
+			lx_emulate_user32(lwp, syscall_num, args);
+		} else
+#endif
+		{
+			lx_emulate_user(lwp, syscall_num, args);
+		}
+		return (0);
+
+	default:
+		/*
+		 * We are not emulating this system call at all.
+		 */
+		lx_syscall_unsup_msg(s, syscall_num, unsup_reason);
+
+		(void) set_errno(ENOTSUP);
+		lx_syscall_return(lwp, syscall_num, -1);
+		return (0);
+	}
+}
+
+#if defined(_LP64)
+/*
+ * Emulate vsyscall support.
+ *
+ * Linux magically maps a single page into the address space of each process,
+ * allowing them to make 'vsyscalls'.  Originally designed to counteract the
+ * perceived overhead of regular system calls, vsyscalls were implemented as
+ * code residing in userspace which could be called directly.  The userspace
+ * implementations of these vsyscalls which have now been replaced by
+ * instructions which vector into the normal syscall path.
+ *
+ * Implementing vsyscalls on Illumos is complicated by the fact that the
+ * required static address region resides inside the kernel address space.
+ * Rather than mapping a user-accessible page into the KAS, a different
+ * approach is taken.  The vsyscall gate is emulated by interposing on
+ * pagefaults in trap().  An attempt to execute a known vsyscall address will
+ * result in emulating the appropriate system call rather than inducing a
+ * SIGSEGV.
+ */
+void
+lx_vsyscall_enter(proc_t *p, klwp_t *lwp, int scnum)
+{
+	struct regs *rp = lwptoregs(lwp);
+	uintptr_t raddr;
+
+	/*
+	 * Fetch the return address from the process stack.
+	 */
+	VERIFY(MUTEX_NOT_HELD(&p->p_lock));
+	if (copyin((void *)rp->r_rsp, &raddr, sizeof (raddr)) != 0) {
+#if DEBUG
+		printf("lx_vsyscall_call: bad brand stack at vsyscall "
+		    "cmd=%s, pid=%d, sp=0x%p\n", PTOU(p)->u_comm,
+		    p->p_pid, (void *)rp->r_rsp);
+#endif
+
+		/*
+		 * The process jumped to the vsyscall address without a
+		 * correctly configured stack.  Terminate the process.
+		 */
+		exit(CLD_KILLED, SIGSEGV);
+		return;
+	}
+
+	DTRACE_PROBE1(brand__lx__vsyscall, int, scnum);
+
+	/* Simulate vectoring into the syscall */
+	rp->r_rax = scnum;
+	rp->r_rip = raddr;
+	rp->r_rsp += sizeof (uintptr_t);
+
+	(void) lx_syscall_enter();
+}
+
+boolean_t
+lx_vsyscall_iscall(klwp_t *lwp, uintptr_t addr, int *scnum)
+{
+	lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+	int i;
+
+	if (lwpd->br_stack_mode != LX_STACK_MODE_BRAND) {
+		/*
+		 * We only handle vsyscalls when running Linux code.
+		 */
+		return (B_FALSE);
+	}
+
+	if (addr < LX_VSYSCALL_ADDR ||
+	    addr >= (LX_VSYSCALL_ADDR + LX_VSYSCALL_SIZE)) {
+		/*
+		 * Ignore faults outside the vsyscall page.
+		 */
+		return (B_FALSE);
+	}
+
+	for (i = 0; lx_vsyscalls[i].lv_addr != NULL; i++) {
+		if (addr == lx_vsyscalls[i].lv_addr) {
+			/*
+			 * This is a valid vsyscall address.
+			 */
+			*scnum = lx_vsyscalls[i].lv_scnum;
+			return (B_TRUE);
+		}
+	}
+
+	lx_unsupported("bad vsyscall access");
+	return (B_FALSE);
+}
+#endif
+
+/*
+ * Linux defines system call numbers for 32-bit x86 in the file:
+ *   arch/x86/syscalls/syscall_32.tbl
+ */
+lx_sysent_t lx_sysent32[] = {
+	{"nosys",	NULL,			NOSYS_NONE,	0}, /*  0 */
+	{"exit",	NULL,			0,		1}, /*  1 */
+	{"fork",	NULL,			0,		0}, /*  2 */
+	{"read",	lx_read,		0,		3}, /*  3 */
+	{"write",	lx_write,		0,		3}, /*  4 */
+	{"open",	lx_open,		0,		3}, /*  5 */
+	{"close",	lx_close,		0,		1}, /*  6 */
+	{"waitpid",	lx_waitpid,		0,		3}, /*  7 */
+	{"creat",	lx_creat,		0,		2}, /*  8 */
+	{"link",	lx_link,		0,		2}, /*  9 */
+	{"unlink",	lx_unlink,		0,		1}, /* 10 */
+	{"execve",	NULL,			0,		3}, /* 11 */
+	{"chdir",	lx_chdir,		0,		1}, /* 12 */
+	{"time",	lx_time,		0,		1}, /* 13 */
+	{"mknod",	NULL,			0,		3}, /* 14 */
+	{"chmod",	lx_chmod,		0,		2}, /* 15 */
+	{"lchown16",	lx_lchown16,		0,		3}, /* 16 */
+	{"break",	NULL,			NOSYS_OBSOLETE,	0}, /* 17 */
+	{"stat",	NULL,			NOSYS_OBSOLETE,	0}, /* 18 */
+	{"lseek",	lx_lseek32,		0,		3}, /* 19 */
+	{"getpid",	lx_getpid,		0,		0}, /* 20 */
+	{"mount",	lx_mount,		0,		5}, /* 21 */
+	{"umount",	lx_umount,		0,		1}, /* 22 */
+	{"setuid16",	lx_setuid16,		0,		1}, /* 23 */
+	{"getuid16",	lx_getuid16,		0,		0}, /* 24 */
+	{"stime",	lx_stime,		0,		1}, /* 25 */
+	{"ptrace",	lx_ptrace,		0,		4}, /* 26 */
+	{"alarm",	lx_alarm,		0,		1}, /* 27 */
+	{"fstat",	NULL,			NOSYS_OBSOLETE,	0}, /* 28 */
+	{"pause",	lx_pause,		0,		0}, /* 29 */
+	{"utime",	NULL,			0,		2}, /* 30 */
+	{"stty",	NULL,			NOSYS_OBSOLETE,	0}, /* 31 */
+	{"gtty",	NULL,			NOSYS_OBSOLETE,	0}, /* 32 */
+	{"access",	lx_access,		0,		2}, /* 33 */
+	{"nice",	lx_nice,		0,		1}, /* 34 */
+	{"ftime",	NULL,			NOSYS_OBSOLETE,	0}, /* 35 */
+	{"sync",	lx_sync,		0, 		0}, /* 36 */
+	{"kill",	lx_kill,		0,		2}, /* 37 */
+	{"rename",	lx_rename,		0,		2}, /* 38 */
+	{"mkdir",	lx_mkdir,		0,		2}, /* 39 */
+	{"rmdir",	NULL,			0,		1}, /* 40 */
+	{"dup",		lx_dup,			0,		1}, /* 41 */
+	{"pipe",	lx_pipe,		0,		1}, /* 42 */
+	{"times",	NULL,			0,		1}, /* 43 */
+	{"prof",	NULL,			NOSYS_OBSOLETE,	0}, /* 44 */
+	{"brk",		lx_brk,			0,		1}, /* 45 */
+	{"setgid16",	lx_setgid16,		0,		1}, /* 46 */
+	{"getgid16",	lx_getgid16,		0,		0}, /* 47 */
+	{"signal",	NULL,			0,		2}, /* 48 */
+	{"geteuid16",	lx_geteuid16,		0,		0}, /* 49 */
+	{"getegid16",	lx_getegid16,		0,		0}, /* 50 */
+	{"acct",	NULL,			NOSYS_NO_EQUIV,	0}, /* 51 */
+	{"umount2",	lx_umount2,		0,		2}, /* 52 */
+	{"lock",	NULL,			NOSYS_OBSOLETE,	0}, /* 53 */
+	{"ioctl",	lx_ioctl,		0,		3}, /* 54 */
+	{"fcntl",	lx_fcntl,		0,		3}, /* 55 */
+	{"mpx",		NULL,			NOSYS_OBSOLETE,	0}, /* 56 */
+	{"setpgid",	lx_setpgid,		0,		2}, /* 57 */
+	{"ulimit",	NULL,			NOSYS_OBSOLETE,	0}, /* 58 */
+	{"olduname",	NULL,			NOSYS_OBSOLETE,	0}, /* 59 */
+	{"umask",	lx_umask,		0,		1}, /* 60 */
+	{"chroot",	lx_chroot,		0,		1}, /* 61 */
+	{"ustat",	NULL,			NOSYS_OBSOLETE,	2}, /* 62 */
+	{"dup2",	lx_dup2,		0,		2}, /* 63 */
+	{"getppid",	lx_getppid,		0,		0}, /* 64 */
+	{"getpgrp",	lx_getpgrp,		0,		0}, /* 65 */
+	{"setsid",	lx_setsid,		0,		0}, /* 66 */
+	{"sigaction",	NULL,			0,		3}, /* 67 */
+	{"sgetmask",	NULL,			NOSYS_OBSOLETE,	0}, /* 68 */
+	{"ssetmask",	NULL,			NOSYS_OBSOLETE,	0}, /* 69 */
+	{"setreuid16",	lx_setreuid16,		0,		2}, /* 70 */
+	{"setregid16",	lx_setregid16,		0,		2}, /* 71 */
+	{"sigsuspend",	NULL,			0,		1}, /* 72 */
+	{"sigpending",	NULL,			0,		1}, /* 73 */
+	{"sethostname",	lx_sethostname,		0,		2}, /* 74 */
+	{"setrlimit",	lx_setrlimit,		0,		2}, /* 75 */
+	{"getrlimit",	lx_oldgetrlimit,	0,		2}, /* 76 */
+	{"getrusage",	lx_getrusage,		0,		2}, /* 77 */
+	{"gettimeofday", lx_gettimeofday,	0,		2}, /* 78 */
+	{"settimeofday", NULL, 			0,		2}, /* 79 */
+	{"getgroups16",	NULL,			0,		2}, /* 80 */
+	{"setgroups16",	NULL,			0,		2}, /* 81 */
+	{"select",	NULL,			NOSYS_OBSOLETE,	0}, /* 82 */
+	{"symlink",	lx_symlink,		0,		2}, /* 83 */
+	{"oldlstat",	NULL,			NOSYS_OBSOLETE,	0}, /* 84 */
+	{"readlink",	lx_readlink,		0,		3}, /* 85 */
+	{"uselib",	NULL,			NOSYS_KERNEL,	0}, /* 86 */
+	{"swapon",	NULL,			NOSYS_KERNEL,	0}, /* 87 */
+	{"reboot",	lx_reboot,		0,		4}, /* 88 */
+	{"readdir",	NULL,			0,		3}, /* 89 */
+	{"mmap",	NULL,			0,		6}, /* 90 */
+	{"munmap",	lx_munmap,		0,		2}, /* 91 */
+	{"truncate",	NULL,			0,		2}, /* 92 */
+	{"ftruncate",	NULL,			0,		2}, /* 93 */
+	{"fchmod",	lx_fchmod,		0,		2}, /* 94 */
+	{"fchown16",	lx_fchown16,		0,		3}, /* 95 */
+	{"getpriority",	NULL,			0,		2}, /* 96 */
+	{"setpriority",	NULL,			0,		3}, /* 97 */
+	{"profil",	NULL,			NOSYS_NO_EQUIV,	0}, /* 98 */
+	{"statfs",	NULL,			0,		2}, /* 99 */
+	{"fstatfs",	NULL,			0,		2}, /* 100 */
+	{"ioperm",	NULL,			NOSYS_NO_EQUIV,	0}, /* 101 */
+	{"socketcall",	lx_socketcall,		0,		2}, /* 102 */
+	{"syslog",	lx_syslog,		0,		3}, /* 103 */
+	{"setitimer",	NULL,			0,		3}, /* 104 */
+	{"getitimer",	lx_getitimer,		0,		2}, /* 105 */
+	{"stat",	lx_stat32,		0,		2}, /* 106 */
+	{"lstat",	lx_lstat32,		0,		2}, /* 107 */
+	{"fstat",	lx_fstat32,		0,		2}, /* 108 */
+	{"uname",	NULL,			NOSYS_OBSOLETE,	0}, /* 109 */
+	{"oldiopl",	NULL,			NOSYS_NO_EQUIV,	0}, /* 110 */
+	{"vhangup",	lx_vhangup,		0,		0}, /* 111 */
+	{"idle",	NULL,			NOSYS_NO_EQUIV,	0}, /* 112 */
+	{"vm86old",	NULL,			NOSYS_OBSOLETE,	0}, /* 113 */
+	{"wait4",	lx_wait4,		0,		4}, /* 114 */
+	{"swapoff",	NULL,			NOSYS_KERNEL,	0}, /* 115 */
+	{"sysinfo",	lx_sysinfo32,		0,		1}, /* 116 */
+	{"ipc",		NULL,			0,		5}, /* 117 */
+	{"fsync",	NULL,			0,		1}, /* 118 */
+	{"sigreturn",	NULL,			0,		1}, /* 119 */
+	{"clone",	NULL,			0,		5}, /* 120 */
+	{"setdomainname", lx_setdomainname,	0,		2}, /* 121 */
+	{"uname",	lx_uname,		0,		1}, /* 122 */
+	{"modify_ldt",	lx_modify_ldt,		0,		3}, /* 123 */
+	{"adjtimex",	NULL,			0,		1}, /* 124 */
+	{"mprotect",	NULL,			0,		3}, /* 125 */
+	{"sigprocmask",	NULL,			0,		3}, /* 126 */
+	{"create_module", NULL,			NOSYS_KERNEL,	0}, /* 127 */
+	{"init_module",	NULL,			NOSYS_KERNEL,	0}, /* 128 */
+	{"delete_module", NULL,			NOSYS_KERNEL,	0}, /* 129 */
+	{"get_kernel_syms", NULL,		NOSYS_KERNEL,	0}, /* 130 */
+	{"quotactl",	NULL,			NOSYS_KERNEL,	0}, /* 131 */
+	{"getpgid",	lx_getpgid,		0,		1}, /* 132 */
+	{"fchdir",	lx_fchdir,		0,		1}, /* 133 */
+	{"bdflush",	NULL,			NOSYS_KERNEL,	0}, /* 134 */
+	{"sysfs",	NULL,			0,		3}, /* 135 */
+	{"personality",	lx_personality,		0,		1}, /* 136 */
+	{"afs_syscall",	NULL,			NOSYS_KERNEL,	0}, /* 137 */
+	{"setfsuid16",	lx_setfsuid16,		0,		1}, /* 138 */
+	{"setfsgid16",	lx_setfsgid16,		0,		1}, /* 139 */
+	{"llseek",	lx_llseek,		0,		5}, /* 140 */
+	{"getdents",	lx_getdents_32,		0,		3}, /* 141 */
+	{"select",	lx_select,		0,		5}, /* 142 */
+	{"flock",	NULL,			0,		2}, /* 143 */
+	{"msync",	NULL,			0,		3}, /* 144 */
+	{"readv",	lx_readv,		0,		3}, /* 145 */
+	{"writev",	lx_writev,		0,		3}, /* 146 */
+	{"getsid",	lx_getsid,		0,		1}, /* 147 */
+	{"fdatasync",	NULL,			0,		1}, /* 148 */
+	{"sysctl",	NULL,			0,		1}, /* 149 */
+	{"mlock",	NULL,			0,		2}, /* 150 */
+	{"munlock",	NULL,			0,		2}, /* 151 */
+	{"mlockall",	NULL,			0,		1}, /* 152 */
+	{"munlockall",	NULL,			0,		0}, /* 153 */
+	{"sched_setparam", NULL,		0,		2}, /* 154 */
+	{"sched_getparam", NULL,		0,		2}, /* 155 */
+	{"sched_setscheduler", NULL,		0,		3}, /* 156 */
+	{"sched_getscheduler", NULL,		0,		1}, /* 157 */
+	{"sched_yield",	lx_sched_yield,		0,		0}, /* 158 */
+	{"sched_get_priority_max", NULL, 	0,		1}, /* 159 */
+	{"sched_get_priority_min", NULL, 	0,		1}, /* 160 */
+	{"sched_rr_get_interval", NULL, 	0,		2}, /* 161 */
+	{"nanosleep",	lx_nanosleep,		0,		2}, /* 162 */
+	{"mremap",	NULL,			0,		5}, /* 163 */
+	{"setresuid16",	lx_setresuid16,		0,		3}, /* 164 */
+	{"getresuid16",	lx_getresuid16,		0,		3}, /* 165 */
+	{"vm86",	NULL,			NOSYS_NO_EQUIV,	0}, /* 166 */
+	{"query_module", NULL,			0,		5}, /* 167 */
+	{"poll",	lx_poll,		0,		3}, /* 168 */
+	{"nfsservctl",	NULL,			NOSYS_KERNEL,	0}, /* 169 */
+	{"setresgid16",	lx_setresgid16,		0,		3}, /* 170 */
+	{"getresgid16",	lx_getresgid16,		0,		3}, /* 171 */
+	{"prctl",	lx_prctl,		0,		5}, /* 172 */
+	{"rt_sigreturn", NULL,			0,		0}, /* 173 */
+	{"rt_sigaction", NULL,			0,		4}, /* 174 */
+	{"rt_sigprocmask", NULL,		0,		4}, /* 175 */
+	{"rt_sigpending", NULL,			0,		2}, /* 176 */
+	{"rt_sigtimedwait", NULL,		0,		4}, /* 177 */
+	{"rt_sigqueueinfo", NULL,		0,		3}, /* 178 */
+	{"rt_sigsuspend", NULL,			0,		2}, /* 179 */
+	{"pread64",	lx_pread32,		0,		5}, /* 180 */
+	{"pwrite64",	lx_pwrite32,		0,		5}, /* 181 */
+	{"chown16",	lx_chown16,		0,		3}, /* 182 */
+	{"getcwd",	lx_getcwd,		0,		2}, /* 183 */
+	{"capget",	NULL,			0,		2}, /* 184 */
+	{"capset",	NULL,			0,		2}, /* 185 */
+	{"sigaltstack",	NULL,			0,		2}, /* 186 */
+	{"sendfile",	NULL,			0,		4}, /* 187 */
+	{"getpmsg",	NULL,			NOSYS_OBSOLETE,	0}, /* 188 */
+	{"putpmsg",	NULL,			NOSYS_OBSOLETE,	0}, /* 189 */
+	{"vfork",	NULL,			0,		0}, /* 190 */
+	{"getrlimit",	lx_getrlimit,		0,		2}, /* 191 */
+	{"mmap2",	NULL,			LX_SYS_EBPARG6,	6}, /* 192 */
+	{"truncate64",	NULL,			0,		3}, /* 193 */
+	{"ftruncate64",	NULL,			0,		3}, /* 194 */
+	{"stat64",	lx_stat64,		0,		2}, /* 195 */
+	{"lstat64",	lx_lstat64,		0,		2}, /* 196 */
+	{"fstat64",	lx_fstat64,		0,		2}, /* 197 */
+	{"lchown",	lx_lchown,		0,		3}, /* 198 */
+	{"getuid",	lx_getuid,		0,		0}, /* 199 */
+	{"getgid",	lx_getgid,		0,		0}, /* 200 */
+	{"geteuid",	lx_geteuid,		0,		0}, /* 201 */
+	{"getegid",	lx_getegid,		0,		0}, /* 202 */
+	{"setreuid",	lx_setreuid,		0,		0}, /* 203 */
+	{"setregid",	lx_setregid,		0,		0}, /* 204 */
+	{"getgroups",	NULL,			0,		2}, /* 205 */
+	{"setgroups",	NULL,			0,		2}, /* 206 */
+	{"fchown",	lx_fchown,		0,		3}, /* 207 */
+	{"setresuid",	lx_setresuid,		0,		3}, /* 208 */
+	{"getresuid",	lx_getresuid,		0,		3}, /* 209 */
+	{"setresgid",	lx_setresgid,		0,		3}, /* 210 */
+	{"getresgid",	lx_getresgid,		0,		3}, /* 211 */
+	{"chown",	lx_chown,		0,		3}, /* 212 */
+	{"setuid",	lx_setuid,		0,		1}, /* 213 */
+	{"setgid",	lx_setgid,		0,		1}, /* 214 */
+	{"setfsuid",	lx_setfsuid,		0,		1}, /* 215 */
+	{"setfsgid",	lx_setfsgid,		0,		1}, /* 216 */
+	{"pivot_root",	NULL,			NOSYS_KERNEL,	0}, /* 217 */
+	{"mincore",	lx_mincore,		0,		3}, /* 218 */
+	{"madvise",	NULL,			0,		3}, /* 219 */
+	{"getdents64",	lx_getdents64,		0,		3}, /* 220 */
+	{"fcntl64",	lx_fcntl64,		0,		3}, /* 221 */
+	{"tux",		NULL,			NOSYS_NO_EQUIV,	0}, /* 222 */
+	{"security",	NULL,			NOSYS_NO_EQUIV,	0}, /* 223 */
+	{"gettid",	lx_gettid,		0,		0}, /* 224 */
+	{"readahead",	NULL,			NOSYS_NO_EQUIV,	0}, /* 225 */
+	{"setxattr",	lx_setxattr,		0,		5}, /* 226 */
+	{"lsetxattr",	lx_lsetxattr,		0,		5}, /* 227 */
+	{"fsetxattr",	lx_fsetxattr,		0,		5}, /* 228 */
+	{"getxattr",	lx_getxattr,		0,		4}, /* 229 */
+	{"lgetxattr",	lx_lgetxattr,		0,		4}, /* 230 */
+	{"fgetxattr",	lx_fgetxattr,		0,		4}, /* 231 */
+	{"listxattr",	lx_listxattr,		0,		3}, /* 232 */
+	{"llistxattr",	lx_llistxattr,		0,		3}, /* 233 */
+	{"flistxattr",	lx_flistxattr,		0,		3}, /* 234 */
+	{"removexattr",	lx_removexattr,		0,		2}, /* 235 */
+	{"lremovexattr", lx_lremovexattr,	0,		2}, /* 236 */
+	{"fremovexattr", lx_fremovexattr,	0,		2}, /* 237 */
+	{"tkill",	lx_tkill,		0,		2}, /* 238 */
+	{"sendfile64",	NULL,			0,		4}, /* 239 */
+	{"futex",	lx_futex,		LX_SYS_EBPARG6,	6}, /* 240 */
+	{"sched_setaffinity", NULL, 		0,		3}, /* 241 */
+	{"sched_getaffinity", NULL, 		0,		3}, /* 242 */
+	{"set_thread_area", lx_set_thread_area,	0,		1}, /* 243 */
+	{"get_thread_area", lx_get_thread_area,	0,		1}, /* 244 */
+	{"io_setup",	lx_io_setup,		0,		2}, /* 245 */
+	{"io_destroy",	NULL,			0,		1}, /* 246 */
+	{"io_getevents", NULL,			0,		5}, /* 247 */
+	{"io_submit",	NULL,			0,		3}, /* 248 */
+	{"io_cancel",	NULL,			0,		3}, /* 249 */
+	{"fadvise64",	lx_fadvise64_32,	0,		5}, /* 250 */
+	{"nosys",	NULL,			0,		0}, /* 251 */
+	{"group_exit",	NULL,			0,		1}, /* 252 */
+	{"lookup_dcookie", NULL,		NOSYS_NO_EQUIV,	0}, /* 253 */
+	{"epoll_create", lx_epoll_create,	0,		1}, /* 254 */
+	{"epoll_ctl",	lx_epoll_ctl,		0,		4}, /* 255 */
+	{"epoll_wait",	lx_epoll_wait,		0,		4}, /* 256 */
+	{"remap_file_pages", NULL,		NOSYS_NO_EQUIV,	0}, /* 257 */
+	{"set_tid_address", lx_set_tid_address,	0,		1}, /* 258 */
+	{"timer_create", NULL,			0,		3}, /* 259 */
+	{"timer_settime", NULL,			0,		4}, /* 260 */
+	{"timer_gettime", NULL,			0,		2}, /* 261 */
+	{"timer_getoverrun", NULL,		0,		1}, /* 262 */
+	{"timer_delete", NULL,			0,		1}, /* 263 */
+	{"clock_settime", lx_clock_settime,	0,		2}, /* 264 */
+	{"clock_gettime", lx_clock_gettime,	0,		2}, /* 265 */
+	{"clock_getres", lx_clock_getres,	0,		2}, /* 266 */
+	{"clock_nanosleep", NULL,		0,		4}, /* 267 */
+	{"statfs64",	NULL,			0,		2}, /* 268 */
+	{"fstatfs64",	NULL,			0,		2}, /* 269 */
+	{"tgkill",	lx_tgkill,		0,		3}, /* 270 */
+
+/*
+ * The following system calls only exist in kernel 2.6 and greater:
+ */
+	{"utimes",	NULL,			0,		2}, /* 271 */
+	{"fadvise64_64", lx_fadvise64_64,	LX_SYS_EBPARG6,	6}, /* 272 */
+	{"vserver",	NULL,			NOSYS_NULL,	0}, /* 273 */
+	{"mbind",	NULL,			NOSYS_NULL,	0}, /* 274 */
+	{"get_mempolicy", NULL,			NOSYS_NULL,	0}, /* 275 */
+	{"set_mempolicy", NULL,			NOSYS_NULL,	0}, /* 276 */
+	{"mq_open",	NULL,			NOSYS_NULL,	0}, /* 277 */
+	{"mq_unlink",	NULL,			NOSYS_NULL,	0}, /* 278 */
+	{"mq_timedsend", NULL,			NOSYS_NULL,	0}, /* 279 */
+	{"mq_timedreceive", NULL,		NOSYS_NULL,	0}, /* 280 */
+	{"mq_notify",	NULL,			NOSYS_NULL,	0}, /* 281 */
+	{"mq_getsetattr", NULL,			NOSYS_NULL,	0}, /* 282 */
+	{"kexec_load",	NULL,			NOSYS_NULL,	0}, /* 283 */
+	{"waitid",	lx_waitid,		0,		4}, /* 284 */
+	{"sys_setaltroot", NULL,		NOSYS_NULL,	0}, /* 285 */
+	{"add_key",	NULL,			NOSYS_NULL,	0}, /* 286 */
+	{"request_key",	NULL,			NOSYS_NULL,	0}, /* 287 */
+	{"keyctl",	NULL,			NOSYS_NULL,	0}, /* 288 */
+	{"ioprio_set",	lx_ioprio_set,		0,		3}, /* 289 */
+	{"ioprio_get",	lx_ioprio_get,		0,		2}, /* 290 */
+	{"inotify_init", NULL,			0,		0}, /* 291 */
+	{"inotify_add_watch", NULL,		0,		3}, /* 292 */
+	{"inotify_rm_watch", NULL,		0,		2}, /* 293 */
+	{"migrate_pages", NULL,			NOSYS_NULL,	0}, /* 294 */
+	{"openat",	lx_openat,		0,		4}, /* 295 */
+	{"mkdirat",	lx_mkdirat,		0,		3}, /* 296 */
+	{"mknodat",	NULL,			0,		4}, /* 297 */
+	{"fchownat",	lx_fchownat,		0,		5}, /* 298 */
+	{"futimesat",	NULL,			0,		3}, /* 299 */
+	{"fstatat64",	lx_fstatat64,		0,		4}, /* 300 */
+	{"unlinkat",	lx_unlinkat,		0,		3}, /* 301 */
+	{"renameat",	lx_renameat,		0,		4}, /* 302 */
+	{"linkat",	lx_linkat,		0,		5}, /* 303 */
+	{"symlinkat",	lx_symlinkat,		0,		3}, /* 304 */
+	{"readlinkat",	lx_readlinkat,		0,		4}, /* 305 */
+	{"fchmodat",	lx_fchmodat,		0,		3}, /* 306 */
+	{"faccessat",	lx_faccessat,		0,		4}, /* 307 */
+	{"pselect6",	lx_pselect,		LX_SYS_EBPARG6,	6}, /* 308 */
+	{"ppoll",	lx_ppoll,		0,		5}, /* 309 */
+	{"unshare",	NULL,			NOSYS_NULL,	0}, /* 310 */
+	{"set_robust_list", lx_set_robust_list,	0,		2}, /* 311 */
+	{"get_robust_list", lx_get_robust_list,	0,		3}, /* 312 */
+	{"splice",	NULL,			NOSYS_NULL,	0}, /* 313 */
+	{"sync_file_range", lx_sync_file_range,	0,		4}, /* 314 */
+	{"tee",		NULL,			NOSYS_NULL,	0}, /* 315 */
+	{"vmsplice",	NULL,			NOSYS_NULL,	0}, /* 316 */
+	{"move_pages",	NULL,			NOSYS_NULL,	0}, /* 317 */
+	{"getcpu",	lx_getcpu,		0,		3}, /* 318 */
+	{"epoll_pwait",	lx_epoll_pwait,		0,		5}, /* 319 */
+	{"utimensat",	NULL,			0,		4}, /* 320 */
+	{"signalfd",	NULL,			0,		3}, /* 321 */
+	{"timerfd_create", NULL,		0,		2}, /* 322 */
+	{"eventfd",	NULL,			0,		1}, /* 323 */
+	{"fallocate",	lx_fallocate32,		LX_SYS_EBPARG6,	6}, /* 324 */
+	{"timerfd_settime", NULL,		0,		4}, /* 325 */
+	{"timerfd_gettime", NULL,		0,		2}, /* 326 */
+	{"signalfd4",	NULL,			0,		4}, /* 327 */
+	{"eventfd2",	NULL,			0,		2}, /* 328 */
+	{"epoll_create1", lx_epoll_create1,	0,		1}, /* 329 */
+	{"dup3",	lx_dup3,		0,		3}, /* 330 */
+	{"pipe2",	lx_pipe2,		0,		2}, /* 331 */
+	{"inotify_init1", NULL,			0,		1}, /* 332 */
+	{"preadv",	lx_preadv32,		0,		5}, /* 333 */
+	{"pwritev",	lx_pwritev32,		0,		5}, /* 334 */
+	{"rt_tgsigqueueinfo", NULL,		0,		4}, /* 335 */
+	{"perf_event_open", NULL,		NOSYS_NULL,	0}, /* 336 */
+	{"recvmmsg",	NULL,			NOSYS_NULL,	0}, /* 337 */
+	{"fanotify_init", NULL,			NOSYS_NULL,	0}, /* 338 */
+	{"fanotify_mark", NULL,			NOSYS_NULL,	0}, /* 339 */
+	{"prlimit64",	lx_prlimit64,		0,		4}, /* 340 */
+	{"name_to_handle_at", NULL,		NOSYS_NULL,	0}, /* 341 */
+	{"open_by_handle_at", NULL,		NOSYS_NULL,	0}, /* 342 */
+	{"clock_adjtime", NULL,			NOSYS_NULL,	0}, /* 343 */
+	{"syncfs",	lx_syncfs,		0,		1}, /* 344 */
+	{"sendmmsg",	NULL,			NOSYS_NULL,	0}, /* 345 */
+	{"setns",	NULL,			NOSYS_NULL,	0}, /* 346 */
+	{"process_vm_readv", NULL,		NOSYS_NULL,	0}, /* 347 */
+	{"process_vm_writev", NULL,		NOSYS_NULL,	0}, /* 348 */
+	{"kcmp",	NULL,			NOSYS_NULL,	0}, /* 349 */
+	{"finit_module", NULL,			NOSYS_NULL,	0}, /* 350 */
+	{"sched_setattr", NULL,			NOSYS_NULL,	0}, /* 351 */
+	{"sched_getattr", NULL,			NOSYS_NULL,	0}, /* 352 */
+	{"renameat2",	NULL,			NOSYS_NULL,	0}, /* 353 */
+	{"seccomp",	NULL,			NOSYS_NULL,	0}, /* 354 */
+	{"getrandom",	lx_getrandom,		0,		3}, /* 355 */
+	{"memfd_create", NULL,			NOSYS_NULL,	0}, /* 356 */
+	{"bpf",		NULL,			NOSYS_NULL,	0}, /* 357 */
+	{"execveat",	NULL,			NOSYS_NULL,	0}, /* 358 */
+};
+
+#if defined(_LP64)
+/*
+ * Linux defines system call numbers for 64-bit x86 in the file:
+ *   arch/x86/syscalls/syscall_64.tbl
+ */
+lx_sysent_t lx_sysent64[] = {
+	{"read",	lx_read,		0,		3}, /* 0 */
+	{"write",	lx_write,		0,		3}, /* 1 */
+	{"open",	lx_open,		0,		3}, /* 2 */
+	{"close",	lx_close,		0,		1}, /* 3 */
+	{"stat",	lx_stat64,		0,		2}, /* 4 */
+	{"fstat",	lx_fstat64,		0,		2}, /* 5 */
+	{"lstat",	lx_lstat64,		0,		2}, /* 6 */
+	{"poll",	lx_poll,		0,		3}, /* 7 */
+	{"lseek",	lx_lseek64,		0,		3}, /* 8 */
+	{"mmap",	NULL,			0,		6}, /* 9 */
+	{"mprotect",	NULL,			0,		3}, /* 10 */
+	{"munmap",	lx_munmap,		0,		2}, /* 11 */
+	{"brk",		lx_brk,			0,		1}, /* 12 */
+	{"rt_sigaction", NULL,			0,		4}, /* 13 */
+	{"rt_sigprocmask", NULL,		0,		4}, /* 14 */
+	{"rt_sigreturn", NULL,			0,		0}, /* 15 */
+	{"ioctl",	lx_ioctl,		0,		3}, /* 16 */
+	{"pread64",	lx_pread,		0,		4}, /* 17 */
+	{"pwrite64",	lx_pwrite,		0,		4}, /* 18 */
+	{"readv",	lx_readv,		0,		3}, /* 19 */
+	{"writev",	lx_writev,		0,		3}, /* 20 */
+	{"access",	lx_access,		0,		2}, /* 21 */
+	{"pipe",	lx_pipe,		0,		1}, /* 22 */
+	{"select",	lx_select,		0,		5}, /* 23 */
+	{"sched_yield",	lx_sched_yield,		0,		0}, /* 24 */
+	{"mremap",	NULL,			0,		5}, /* 25 */
+	{"msync",	NULL,			0,		3}, /* 26 */
+	{"mincore",	lx_mincore,		0,		3}, /* 27 */
+	{"madvise",	NULL,			0,		3}, /* 28 */
+	{"shmget",	NULL,			0,		3}, /* 29 */
+	{"shmat",	NULL,			0,		4}, /* 30 */
+	{"shmctl",	NULL,			0,		3}, /* 31 */
+	{"dup",		lx_dup,			0,		1}, /* 32 */
+	{"dup2",	lx_dup2,		0,		2}, /* 33 */
+	{"pause",	lx_pause,		0,		0}, /* 34 */
+	{"nanosleep",	lx_nanosleep,		0,		2}, /* 35 */
+	{"getitimer",	lx_getitimer,		0,		2}, /* 36 */
+	{"alarm",	lx_alarm,		0,		1}, /* 37 */
+	{"setitimer",	NULL,			0,		3}, /* 38 */
+	{"getpid",	lx_getpid,		0,		0}, /* 39 */
+	{"sendfile",	NULL,			0,		4}, /* 40 */
+	{"socket",	lx_socket,		0,		3}, /* 41 */
+	{"connect",	lx_connect,		0,		3}, /* 42 */
+	{"accept",	lx_accept,		0,		3}, /* 43 */
+	{"sendto",	lx_sendto,		0,		6}, /* 44 */
+	{"recvfrom",	lx_recvfrom,		0,		6}, /* 45 */
+	{"sendmsg",	lx_sendmsg,		0,		3}, /* 46 */
+	{"recvmsg",	lx_recvmsg,		0,		3}, /* 47 */
+	{"shutdown",	lx_shutdown,		0,		2}, /* 48 */
+	{"bind",	lx_bind,		0,		3}, /* 49 */
+	{"listen",	lx_listen,		0,		2}, /* 50 */
+	{"getsockname",	lx_getsockname,		0,		3}, /* 51 */
+	{"getpeername",	lx_getpeername,		0,		3}, /* 52 */
+	{"socketpair",	lx_socketpair,		0,		4}, /* 53 */
+	{"setsockopt",	lx_setsockopt,		0,		5}, /* 54 */
+	{"getsockopt",	lx_getsockopt,		0,		5}, /* 55 */
+	{"clone",	NULL,			0,		5}, /* 56 */
+	{"fork",	NULL,			0,		0}, /* 57 */
+	{"vfork",	NULL,			0,		0}, /* 58 */
+	{"execve",	NULL,			0,		3}, /* 59 */
+	{"exit",	NULL,			0,		1}, /* 60 */
+	{"wait4",	lx_wait4,		0,		4}, /* 61 */
+	{"kill",	lx_kill,		0,		2}, /* 62 */
+	{"uname",	lx_uname,		0,		1}, /* 63 */
+	{"semget",	NULL,			0,		3}, /* 64 */
+	{"semop",	NULL,			0,		3}, /* 65 */
+	{"semctl",	NULL,			0,		4}, /* 66 */
+	{"shmdt",	NULL,			0,		1}, /* 67 */
+	{"msgget",	NULL,			0,		2}, /* 68 */
+	{"msgsnd",	NULL,			0,		4}, /* 69 */
+	{"msgrcv",	NULL,			0,		5}, /* 70 */
+	{"msgctl",	NULL,			0,		3}, /* 71 */
+	{"fcntl",	lx_fcntl64,		0,		3}, /* 72 */
+	{"flock",	NULL,			0,		2}, /* 73 */
+	{"fsync",	NULL,			0,		1}, /* 74 */
+	{"fdatasync",	NULL,			0,		1}, /* 75 */
+	{"truncate",	NULL,			0,		2}, /* 76 */
+	{"ftruncate",	NULL,			0,		2}, /* 77 */
+	{"getdents",	lx_getdents_64,		0,		3}, /* 78 */
+	{"getcwd",	lx_getcwd,		0,		2}, /* 79 */
+	{"chdir",	lx_chdir,		0,		1}, /* 80 */
+	{"fchdir",	lx_fchdir,		0,		1}, /* 81 */
+	{"rename",	lx_rename,		0,		2}, /* 82 */
+	{"mkdir",	lx_mkdir,		0,		2}, /* 83 */
+	{"rmdir",	NULL,			0,		1}, /* 84 */
+	{"creat",	lx_creat,		0,		2}, /* 85 */
+	{"link",	lx_link,		0,		2}, /* 86 */
+	{"unlink",	lx_unlink,		0,		1}, /* 87 */
+	{"symlink",	lx_symlink,		0,		2}, /* 88 */
+	{"readlink",	lx_readlink,		0,		3}, /* 89 */
+	{"chmod",	lx_chmod,		0,		2}, /* 90 */
+	{"fchmod",	lx_fchmod,		0,		2}, /* 91 */
+	{"chown",	lx_chown,		0,		3}, /* 92 */
+	{"fchown",	lx_fchown,		0,		3}, /* 93 */
+	{"lchown",	lx_lchown,		0,		3}, /* 94 */
+	{"umask",	lx_umask,		0,		1}, /* 95 */
+	{"gettimeofday", lx_gettimeofday,	0,		2}, /* 96 */
+	{"getrlimit",	lx_getrlimit,		0,		2}, /* 97 */
+	{"getrusage",	lx_getrusage,		0,		2}, /* 98 */
+	{"sysinfo",	lx_sysinfo64,		0,		1}, /* 99 */
+	{"times",	NULL,			0,		1}, /* 100 */
+	{"ptrace",	lx_ptrace,		0,		4}, /* 101 */
+	{"getuid",	lx_getuid,		0,		0}, /* 102 */
+	{"syslog",	lx_syslog,		0,		3}, /* 103 */
+	{"getgid",	lx_getgid,		0,		0}, /* 104 */
+	{"setuid",	lx_setuid,		0,		1}, /* 105 */
+	{"setgid",	lx_setgid,		0,		1}, /* 106 */
+	{"geteuid",	lx_geteuid,		0,		0}, /* 107 */
+	{"getegid",	lx_getegid,		0,		0}, /* 108 */
+	{"setpgid",	lx_setpgid,		0,		2}, /* 109 */
+	{"getppid",	lx_getppid,		0,		0}, /* 110 */
+	{"getpgrp",	lx_getpgrp,		0,		0}, /* 111 */
+	{"setsid",	lx_setsid,		0,		0}, /* 112 */
+	{"setreuid",	lx_setreuid,		0,		0}, /* 113 */
+	{"setregid",	lx_setregid,		0,		0}, /* 114 */
+	{"getgroups",	NULL,			0,		2}, /* 115 */
+	{"setgroups",	NULL,			0,		2}, /* 116 */
+	{"setresuid",	lx_setresuid,		0,		3}, /* 117 */
+	{"getresuid",	lx_getresuid,		0,		3}, /* 118 */
+	{"setresgid",	lx_setresgid,		0,		3}, /* 119 */
+	{"getresgid",	lx_getresgid,		0,		3}, /* 120 */
+	{"getpgid",	lx_getpgid,		0,		1}, /* 121 */
+	{"setfsuid",	lx_setfsuid,		0,		1}, /* 122 */
+	{"setfsgid",	lx_setfsgid,		0,		1}, /* 123 */
+	{"getsid",	lx_getsid,		0,		1}, /* 124 */
+	{"capget",	NULL,			0,		2}, /* 125 */
+	{"capset",	NULL,			0,		2}, /* 126 */
+	{"rt_sigpending", NULL,			0,		2}, /* 127 */
+	{"rt_sigtimedwait", NULL,		0,		4}, /* 128 */
+	{"rt_sigqueueinfo", NULL,		0,		3}, /* 129 */
+	{"rt_sigsuspend", NULL,			0,		2}, /* 130 */
+	{"sigaltstack",	NULL,			0,		2}, /* 131 */
+	{"utime",	NULL,			0,		2}, /* 132 */
+	{"mknod",	NULL,			0,		3}, /* 133 */
+	{"uselib",	NULL,			NOSYS_KERNEL,	0}, /* 134 */
+	{"personality",	lx_personality,		0,		1}, /* 135 */
+	{"ustat",	NULL,			NOSYS_OBSOLETE,	2}, /* 136 */
+	{"statfs",	NULL,			0,		2}, /* 137 */
+	{"fstatfs",	NULL,			0,		2}, /* 138 */
+	{"sysfs",	NULL,			0,		3}, /* 139 */
+	{"getpriority",	NULL,			0,		2}, /* 140 */
+	{"setpriority",	NULL,			0,		3}, /* 141 */
+	{"sched_setparam", NULL,		0,		2}, /* 142 */
+	{"sched_getparam", NULL,		0,		2}, /* 143 */
+	{"sched_setscheduler", NULL,		0,		3}, /* 144 */
+	{"sched_getscheduler", NULL,		0,		1}, /* 145 */
+	{"sched_get_priority_max", NULL,	0,		1}, /* 146 */
+	{"sched_get_priority_min", NULL,	0,		1}, /* 147 */
+	{"sched_rr_get_interval", NULL,		0,		2}, /* 148 */
+	{"mlock",	NULL,			0,		2}, /* 149 */
+	{"munlock",	NULL,			0,		2}, /* 150 */
+	{"mlockall",	NULL,			0,		1}, /* 151 */
+	{"munlockall",	NULL,			0,		0}, /* 152 */
+	{"vhangup",	lx_vhangup,		0,		0}, /* 153 */
+	{"modify_ldt",	lx_modify_ldt,		0,		3}, /* 154 */
+	{"pivot_root",	NULL,			NOSYS_KERNEL,	0}, /* 155 */
+	{"sysctl",	NULL,			0,		1}, /* 156 */
+	{"prctl",	lx_prctl,		0,		5}, /* 157 */
+	{"arch_prctl",	lx_arch_prctl,		0,		2}, /* 158 */
+	{"adjtimex",	NULL,			0,		1}, /* 159 */
+	{"setrlimit",	lx_setrlimit,		0,		2}, /* 160 */
+	{"chroot",	lx_chroot,		0,		1}, /* 161 */
+	{"sync",	lx_sync,		0,		0}, /* 162 */
+	{"acct",	NULL,			NOSYS_NO_EQUIV,	0}, /* 163 */
+	{"settimeofday", NULL,			0,		2}, /* 164 */
+	{"mount",	lx_mount,		0,		5}, /* 165 */
+	{"umount2",	lx_umount2,		0,		2}, /* 166 */
+	{"swapon",	NULL,			NOSYS_KERNEL,	0}, /* 167 */
+	{"swapoff",	NULL,			NOSYS_KERNEL,	0}, /* 168 */
+	{"reboot",	lx_reboot,		0,		4}, /* 169 */
+	{"sethostname",	lx_sethostname,		0,		2}, /* 170 */
+	{"setdomainname", lx_setdomainname,	0,		2}, /* 171 */
+	{"iopl",	NULL,			NOSYS_NO_EQUIV,	0}, /* 172 */
+	{"ioperm",	NULL,			NOSYS_NO_EQUIV,	0}, /* 173 */
+	{"create_module", NULL,			NOSYS_KERNEL,	0}, /* 174 */
+	{"init_module",	NULL,			NOSYS_KERNEL,	0}, /* 175 */
+	{"delete_module", NULL,			NOSYS_KERNEL,	0}, /* 176 */
+	{"get_kernel_syms", NULL,		NOSYS_KERNEL,	0}, /* 177 */
+	{"query_module", NULL,			0,		5}, /* 178 */
+	{"quotactl",	NULL,			NOSYS_KERNEL,	0}, /* 179 */
+	{"nfsservctl",	NULL,			NOSYS_KERNEL,	0}, /* 180 */
+	{"getpmsg",	NULL,			NOSYS_OBSOLETE,	0}, /* 181 */
+	{"putpmsg",	NULL,			NOSYS_OBSOLETE,	0}, /* 182 */
+	{"afs_syscall",	NULL,			NOSYS_KERNEL,	0}, /* 183 */
+	{"tux",		NULL,			NOSYS_NO_EQUIV,	0}, /* 184 */
+	{"security",	NULL,			NOSYS_NO_EQUIV,	0}, /* 185 */
+	{"gettid",	lx_gettid,		0,		0}, /* 186 */
+	{"readahead",	NULL,			NOSYS_NO_EQUIV,	0}, /* 187 */
+	{"setxattr",	lx_setxattr,		0,		5}, /* 188 */
+	{"lsetxattr",	lx_lsetxattr,		0,		5}, /* 189 */
+	{"fsetxattr",	lx_fsetxattr,		0,		5}, /* 190 */
+	{"getxattr",	lx_getxattr,		0,		4}, /* 191 */
+	{"lgetxattr",	lx_lgetxattr,		0,		4}, /* 192 */
+	{"fgetxattr",	lx_fgetxattr,		0,		4}, /* 193 */
+	{"listxattr",	lx_listxattr,		0,		3}, /* 194 */
+	{"llistxattr",	lx_llistxattr,		0,		3}, /* 195 */
+	{"flistxattr",	lx_flistxattr,		0,		3}, /* 196 */
+	{"removexattr",	lx_removexattr,		0,		2}, /* 197 */
+	{"lremovexattr", lx_lremovexattr,	0,		2}, /* 198 */
+	{"fremovexattr", lx_fremovexattr,	0,		2}, /* 199 */
+	{"tkill",	lx_tkill,		0,		2}, /* 200 */
+	{"time",	lx_time,		0,		1}, /* 201 */
+	{"futex",	lx_futex,		0,		6}, /* 202 */
+	{"sched_setaffinity", NULL,		0,		3}, /* 203 */
+	{"sched_getaffinity", NULL,		0,		3}, /* 204 */
+	{"set_thread_area", lx_set_thread_area, 0,		1}, /* 205 */
+	{"io_setup",	lx_io_setup,		0,		2}, /* 206 */
+	{"io_destroy",	NULL,			0,		1}, /* 207 */
+	{"io_getevents", NULL,			0,		5}, /* 208 */
+	{"io_submit",	NULL,			0,		3}, /* 209 */
+	{"io_cancel",	NULL,			0,		3}, /* 210 */
+	{"get_thread_area", lx_get_thread_area,	0,		1}, /* 211 */
+	{"lookup_dcookie", NULL,		NOSYS_NO_EQUIV,	0}, /* 212 */
+	{"epoll_create", lx_epoll_create,	0,		1}, /* 213 */
+	{"epoll_ctl_old", NULL,			NOSYS_NULL,	0}, /* 214 */
+	{"epoll_wait_old", NULL,		NOSYS_NULL,	0}, /* 215 */
+	{"remap_file_pages", NULL,		NOSYS_NO_EQUIV,	0}, /* 216 */
+	{"getdents64",	lx_getdents64,		0,		3}, /* 217 */
+	{"set_tid_address", lx_set_tid_address, 0,		1}, /* 218 */
+	{"restart_syscall", NULL,		NOSYS_NULL,	0}, /* 219 */
+	{"semtimedop",	NULL,			0,		4}, /* 220 */
+	{"fadvise64",	lx_fadvise64,		0,		4}, /* 221 */
+	{"timer_create", NULL,			0,		3}, /* 222 */
+	{"timer_settime", NULL,			0,		4}, /* 223 */
+	{"timer_gettime", NULL,			0,		2}, /* 224 */
+	{"timer_getoverrun", NULL,		0,		1}, /* 225 */
+	{"timer_delete", NULL,			0,		1}, /* 226 */
+	{"clock_settime", lx_clock_settime,	0,		2}, /* 227 */
+	{"clock_gettime", lx_clock_gettime,	0,		2}, /* 228 */
+	{"clock_getres", lx_clock_getres,	0,		2}, /* 229 */
+	{"clock_nanosleep", NULL,		0,		4}, /* 230 */
+	{"exit_group",	NULL,			0,		1}, /* 231 */
+	{"epoll_wait",	lx_epoll_wait,		0,		4}, /* 232 */
+	{"epoll_ctl",	lx_epoll_ctl,		0,		4}, /* 233 */
+	{"tgkill",	lx_tgkill,		0,		3}, /* 234 */
+	{"utimes",	NULL,			0,		2}, /* 235 */
+	{"vserver",	NULL,			NOSYS_NULL,	0}, /* 236 */
+	{"mbind",	NULL,			NOSYS_NULL,	0}, /* 237 */
+	{"set_mempolicy", NULL,			NOSYS_NULL,	0}, /* 238 */
+	{"get_mempolicy", NULL,			NOSYS_NULL,	0}, /* 239 */
+	{"mq_open",	NULL,			NOSYS_NULL,	0}, /* 240 */
+	{"mq_unlink",	NULL,			NOSYS_NULL,	0}, /* 241 */
+	{"mq_timedsend", NULL,			NOSYS_NULL,	0}, /* 242 */
+	{"mq_timedreceive", NULL,		NOSYS_NULL,	0}, /* 243 */
+	{"mq_notify",	NULL,			NOSYS_NULL,	0}, /* 244 */
+	{"mq_getsetattr", NULL,			NOSYS_NULL,	0}, /* 245 */
+	{"kexec_load",	NULL,			NOSYS_NULL,	0}, /* 246 */
+	{"waitid",	lx_waitid,		0,		4}, /* 247 */
+	{"add_key",	NULL,			NOSYS_NULL,	0}, /* 248 */
+	{"request_key",	NULL,			NOSYS_NULL,	0}, /* 249 */
+	{"keyctl",	NULL,			NOSYS_NULL,	0}, /* 250 */
+	{"ioprio_set",	lx_ioprio_set,		0,		3}, /* 251 */
+	{"ioprio_get",	lx_ioprio_get,		0,		2}, /* 252 */
+	{"inotify_init", NULL,			0,		0}, /* 253 */
+	{"inotify_add_watch", NULL,		0,		3}, /* 254 */
+	{"inotify_rm_watch", NULL,		0,		2}, /* 255 */
+	{"migrate_pages", NULL,			NOSYS_NULL,	0}, /* 256 */
+	{"openat",	lx_openat,		0,		4}, /* 257 */
+	{"mkdirat",	lx_mkdirat,		0,		3}, /* 258 */
+	{"mknodat",	NULL,			0,		4}, /* 259 */
+	{"fchownat",	lx_fchownat,		0,		5}, /* 260 */
+	{"futimesat",	NULL,			0,		3}, /* 261 */
+	{"fstatat64",	lx_fstatat64,		0,		4}, /* 262 */
+	{"unlinkat",	lx_unlinkat,		0,		3}, /* 263 */
+	{"renameat",	lx_renameat,		0,		4}, /* 264 */
+	{"linkat",	lx_linkat,		0,		5}, /* 265 */
+	{"symlinkat",	lx_symlinkat,		0,		3}, /* 266 */
+	{"readlinkat",	lx_readlinkat,		0,		4}, /* 267 */
+	{"fchmodat",	lx_fchmodat,		0,		3}, /* 268 */
+	{"faccessat",	lx_faccessat,		0,		4}, /* 269 */
+	{"pselect6",	lx_pselect,		0,		6}, /* 270 */
+	{"ppoll",	lx_ppoll,		0,		5}, /* 271 */
+	{"unshare",	NULL,			NOSYS_NULL,	0}, /* 272 */
+	{"set_robust_list", lx_set_robust_list,	0,		2}, /* 273 */
+	{"get_robust_list", lx_get_robust_list,	0,		3}, /* 274 */
+	{"splice",	NULL,			NOSYS_NULL,	0}, /* 275 */
+	{"tee",		NULL,			NOSYS_NULL,	0}, /* 276 */
+	{"sync_file_range", lx_sync_file_range,	0,		4}, /* 277 */
+	{"vmsplice",	NULL,			NOSYS_NULL,	0}, /* 278 */
+	{"move_pages",	NULL,			NOSYS_NULL,	0}, /* 279 */
+	{"utimensat",	NULL,			0,		4}, /* 280 */
+	{"epoll_pwait",	lx_epoll_pwait,		0,		5}, /* 281 */
+	{"signalfd",	NULL,			0,		3}, /* 282 */
+	{"timerfd_create", NULL,		0,		2}, /* 283 */
+	{"eventfd",	NULL,			0,		1}, /* 284 */
+	{"fallocate",	lx_fallocate,		0,		4}, /* 285 */
+	{"timerfd_settime", NULL,		0,		4}, /* 286 */
+	{"timerfd_gettime", NULL,		0,		2}, /* 287 */
+	{"accept4",	lx_accept4,		0,		4}, /* 288 */
+	{"signalfd4",	NULL,			0,		4}, /* 289 */
+	{"eventfd2",	NULL,			0,		2}, /* 290 */
+	{"epoll_create1", lx_epoll_create1,	0,		1}, /* 291 */
+	{"dup3",	lx_dup3,		0,		3}, /* 292 */
+	{"pipe2",	lx_pipe2,		0,		2}, /* 293 */
+	{"inotify_init1", NULL,			0,		1}, /* 294 */
+	{"preadv",	lx_preadv,		0,		4}, /* 295 */
+	{"pwritev",	lx_pwritev,		0,		4}, /* 296 */
+	{"rt_tgsigqueueinfo", NULL, 		0,		4}, /* 297 */
+	{"perf_event_open", NULL,		NOSYS_NULL,	0}, /* 298 */
+	{"recvmmsg",	NULL,			NOSYS_NULL,	0}, /* 299 */
+	{"fanotify_init", NULL,			NOSYS_NULL,	0}, /* 300 */
+	{"fanotify_mark", NULL,			NOSYS_NULL,	0}, /* 301 */
+	{"prlimit64",	lx_prlimit64,		0,		4}, /* 302 */
+	{"name_to_handle_at", NULL,		NOSYS_NULL,	0}, /* 303 */
+	{"open_by_handle_at", NULL,		NOSYS_NULL,	0}, /* 304 */
+	{"clock_adjtime", NULL,			NOSYS_NULL,	0}, /* 305 */
+	{"syncfs",	lx_syncfs,		0,		1}, /* 306 */
+	{"sendmmsg",	NULL,			NOSYS_NULL,	0}, /* 307 */
+	{"setns",	NULL,			NOSYS_NULL,	0}, /* 309 */
+	{"getcpu",	lx_getcpu,		0,		3}, /* 309 */
+	{"process_vm_readv", NULL,		NOSYS_NULL,	0}, /* 310 */
+	{"process_vm_writev", NULL,		NOSYS_NULL,	0}, /* 311 */
+	{"kcmp",	NULL,			NOSYS_NULL,	0}, /* 312 */
+	{"finit_module", NULL,			NOSYS_NULL,	0}, /* 313 */
+	{"sched_setattr", NULL,			NOSYS_NULL,	0}, /* 314 */
+	{"sched_getattr", NULL,			NOSYS_NULL,	0}, /* 315 */
+	{"renameat2", NULL,			NOSYS_NULL,	0}, /* 316 */
+	{"seccomp",	NULL,			NOSYS_NULL,	0}, /* 317 */
+	{"getrandom",	lx_getrandom,		0,		3}, /* 318 */
+	{"memfd_create", NULL,			NOSYS_NULL,	0}, /* 319 */
+	{"kexec_file_load", NULL,		NOSYS_NULL,	0}, /* 320 */
+	{"bpf",		NULL,			NOSYS_NULL,	0}, /* 321 */
+	{"execveat",	NULL,			NOSYS_NULL,	0}, /* 322 */
+
+	/* XXX TBD gap then x32 syscalls from 512 - 544 */
+};
+#endif
--- /dev/null	Wed Oct  5 00:32:06 2016
+++ new/usr/src/uts/common/brand/lx/procfs/lx_proc.h	Wed Oct  5 00:32:06 2016
@@ -0,0 +1,351 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#ifndef	_LX_PROC_H
+#define	_LX_PROC_H
+
+#ifdef _LXPROC_NATIVE_H
+#error Attempted to include branded lx_proc.h after native lxproc.h
+#endif
+
+#define	_LXPROC_BRANDED_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/*
+ * lxproc.h: declarations, data structures and macros for lxprocfs
+ */
+
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/policy.h>
+#include <sys/debug.h>
+#include <sys/dirent.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/kmem.h>
+#include <sys/pathname.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/var.h>
+#include <sys/user.h>
+#include <sys/t_lock.h>
+#include <sys/sysmacros.h>
+#include <sys/cred.h>
+#include <sys/priv.h>
+#include <sys/vnode.h>
+#include <sys/vfs.h>
+#include <sys/statvfs.h>
+#include <sys/cmn_err.h>
+#include <sys/zone.h>
+#include <sys/uio.h>
+#include <sys/utsname.h>
+#include <sys/dnlc.h>
+#include <sys/atomic.h>
+#include <sys/sunddi.h>
+#include <sys/sunldi.h>
+#include <sys/zfs_ioctl.h>
+#include <sys/nvpair.h>
+#include <vm/as.h>
+#include <vm/anon.h>
+
+/*
+ * Convert a vnode into an lxpr_mnt_t
+ */
+#define	VTOLXPM(vp)	((lxpr_mnt_t *)(vp)->v_vfsp->vfs_data)
+
+/*
+ * convert a vnode into an lxpr_node
+ */
+#define	VTOLXP(vp)	((lxpr_node_t *)(vp)->v_data)
+
+/*
+ * convert a lxprnode into a vnode
+ */
+#define	LXPTOV(lxpnp)	((lxpnp)->lxpr_vnode)
+
+/*
+ * convert a lxpr_node into zone for fs
+ */
+#define	LXPTOZ(lxpnp) \
+	(((lxpr_mnt_t *)(lxpnp)->lxpr_vnode->v_vfsp->vfs_data)->lxprm_zone)
+
+#define	LXPNSIZ		256	/* max size of lx /proc file name entries */
+
+/*
+ * Pretend that a directory entry takes 16 bytes
+ */
+#define	LXPR_SDSIZE	16
+
+/*
+ * Node/file types for lx /proc files
+ * (directories and files contained therein).
+ */
+typedef enum lxpr_nodetype {
+	LXPR_INVALID,		/* nodes start at 1	*/
+	LXPR_PROCDIR,		/* /proc		*/
+	LXPR_PIDDIR,		/* /proc/<pid>		*/
+	LXPR_PID_AUXV,		/* /proc/<pid>/auxv	*/
+	LXPR_PID_CGROUP,	/* /proc/<pid>/cgroup	*/
+	LXPR_PID_CMDLINE,	/* /proc/<pid>/cmdline	*/
+	LXPR_PID_COMM,		/* /proc/<pid>/comm	*/
+	LXPR_PID_CPU,		/* /proc/<pid>/cpu	*/
+	LXPR_PID_CURDIR,	/* /proc/<pid>/cwd	*/
+	LXPR_PID_ENV,		/* /proc/<pid>/environ	*/
+	LXPR_PID_EXE,		/* /proc/<pid>/exe	*/
+	LXPR_PID_LIMITS,	/* /proc/<pid>/limits	*/
+	LXPR_PID_LOGINUID,	/* /proc/<pid>/loginuid	*/
+	LXPR_PID_MAPS,		/* /proc/<pid>/maps	*/
+	LXPR_PID_MEM,		/* /proc/<pid>/mem	*/
+	LXPR_PID_MOUNTINFO,	/* /proc/<pid>/mountinfo */
+	LXPR_PID_MOUNTS,	/* /proc/<pid>/mounts	*/
+	LXPR_PID_OOM_SCR_ADJ,	/* /proc/<pid>/oom_score_adj	*/
+	LXPR_PID_PERSONALITY,	/* /proc/<pid>/personality	*/
+	LXPR_PID_ROOTDIR,	/* /proc/<pid>/root	*/
+	LXPR_PID_STAT,		/* /proc/<pid>/stat	*/
+	LXPR_PID_STATM,		/* /proc/<pid>/statm	*/
+	LXPR_PID_STATUS,	/* /proc/<pid>/status	*/
+	LXPR_PID_TASKDIR,	/* /proc/<pid>/task	*/
+	LXPR_PID_TASK_IDDIR,	/* /proc/<pid>/task/<tid>		*/
+	LXPR_PID_FDDIR,		/* /proc/<pid>/fd	*/
+	LXPR_PID_FD_FD,		/* /proc/<pid>/fd/nn	*/
+	LXPR_PID_TID_AUXV,	/* /proc/<pid>/task/<tid>/auxv		*/
+	LXPR_PID_TID_CGROUP,	/* /proc/<pid>/task/<tid>/cgroup	*/
+	LXPR_PID_TID_CMDLINE,	/* /proc/<pid>/task/<tid>/cmdline	*/
+	LXPR_PID_TID_COMM,	/* /proc/<pid>/task/<tid>/comm		*/
+	LXPR_PID_TID_CPU,	/* /proc/<pid>/task/<tid>/cpu		*/
+	LXPR_PID_TID_CURDIR,	/* /proc/<pid>/task/<tid>/cwd		*/
+	LXPR_PID_TID_ENV,	/* /proc/<pid>/task/<tid>/environ	*/
+	LXPR_PID_TID_EXE,	/* /proc/<pid>/task/<tid>/exe		*/
+	LXPR_PID_TID_LIMITS,	/* /proc/<pid>/task/<tid>/limits	*/
+	LXPR_PID_TID_LOGINUID,	/* /proc/<pid>/task/<tid>/loginuid	*/
+	LXPR_PID_TID_MAPS,	/* /proc/<pid>/task/<tid>/maps		*/
+	LXPR_PID_TID_MEM,	/* /proc/<pid>/task/<tid>/mem		*/
+	LXPR_PID_TID_MOUNTINFO,	/* /proc/<pid>/task/<tid>/mountinfo	*/
+	LXPR_PID_TID_OOM_SCR_ADJ, /* /proc/<pid>/task/<tid>/oom_score_adj */
+	LXPR_PID_TID_PERSONALITY, /* /proc/<pid>/task/<tid>/personality */
+	LXPR_PID_TID_ROOTDIR,	/* /proc/<pid>/task/<tid>/root		*/
+	LXPR_PID_TID_STAT,	/* /proc/<pid>/task/<tid>/stat		*/
+	LXPR_PID_TID_STATM,	/* /proc/<pid>/task/<tid>/statm		*/
+	LXPR_PID_TID_STATUS,	/* /proc/<pid>/task/<tid>/status	*/
+	LXPR_PID_TID_FDDIR,	/* /proc/<pid>/task/<tid>/fd		*/
+	LXPR_PID_TID_FD_FD,	/* /proc/<pid>/task/<tid>/fd/nn		*/
+	LXPR_CGROUPS,		/* /proc/cgroups	*/
+	LXPR_CMDLINE,		/* /proc/cmdline	*/
+	LXPR_CPUINFO,		/* /proc/cpuinfo	*/
+	LXPR_DEVICES,		/* /proc/devices	*/
+	LXPR_DISKSTATS,		/* /proc/diskstats	*/
+	LXPR_DMA,		/* /proc/dma		*/
+	LXPR_FILESYSTEMS,	/* /proc/filesystems	*/
+	LXPR_INTERRUPTS,	/* /proc/interrupts	*/
+	LXPR_IOPORTS,		/* /proc/ioports	*/
+	LXPR_KCORE,		/* /proc/kcore		*/
+	LXPR_KMSG,		/* /proc/kmsg		*/
+	LXPR_LOADAVG,		/* /proc/loadavg	*/
+	LXPR_MEMINFO,		/* /proc/meminfo	*/
+	LXPR_MODULES,		/* /proc/modules	*/
+	LXPR_MOUNTS,		/* /proc/mounts		*/
+	LXPR_NETDIR,		/* /proc/net		*/
+	LXPR_NET_ARP,		/* /proc/net/arp	*/
+	LXPR_NET_DEV,		/* /proc/net/dev	*/
+	LXPR_NET_DEV_MCAST,	/* /proc/net/dev_mcast	*/
+	LXPR_NET_IF_INET6,	/* /proc/net/if_inet6	*/
+	LXPR_NET_IGMP,		/* /proc/net/igmp	*/
+	LXPR_NET_IP_MR_CACHE,	/* /proc/net/ip_mr_cache */
+	LXPR_NET_IP_MR_VIF,	/* /proc/net/ip_mr_vif	*/
+	LXPR_NET_IPV6_ROUTE,	/* /proc/net/ipv6_route	*/
+	LXPR_NET_MCFILTER,	/* /proc/net/mcfilter	*/
+	LXPR_NET_NETSTAT,	/* /proc/net/netstat	*/
+	LXPR_NET_RAW,		/* /proc/net/raw	*/
+	LXPR_NET_ROUTE,		/* /proc/net/route	*/
+	LXPR_NET_RPC,		/* /proc/net/rpc	*/
+	LXPR_NET_RT_CACHE,	/* /proc/net/rt_cache	*/
+	LXPR_NET_SOCKSTAT,	/* /proc/net/sockstat	*/
+	LXPR_NET_SNMP,		/* /proc/net/snmp	*/
+	LXPR_NET_STAT,		/* /proc/net/stat	*/
+	LXPR_NET_TCP,		/* /proc/net/tcp	*/
+	LXPR_NET_TCP6,		/* /proc/net/tcp6	*/
+	LXPR_NET_UDP,		/* /proc/net/udp	*/
+	LXPR_NET_UDP6,		/* /proc/net/udp6	*/
+	LXPR_NET_UNIX,		/* /proc/net/unix	*/
+	LXPR_PARTITIONS,	/* /proc/partitions	*/
+	LXPR_SELF,		/* /proc/self		*/
+	LXPR_STAT,		/* /proc/stat		*/
+	LXPR_SWAPS,		/* /proc/swaps		*/
+	LXPR_SYSDIR,		/* /proc/sys/		*/
+	LXPR_SYS_FSDIR,		/* /proc/sys/fs/	*/
+	LXPR_SYS_FS_INOTIFYDIR,	/* /proc/sys/fs/inotify	*/
+	LXPR_SYS_FS_INOTIFY_MAX_QUEUED_EVENTS,	/* inotify/max_queued_events */
+	LXPR_SYS_FS_INOTIFY_MAX_USER_INSTANCES,	/* inotify/max_user_instances */
+	LXPR_SYS_FS_INOTIFY_MAX_USER_WATCHES,	/* inotify/max_user_watches */
+	LXPR_SYS_KERNELDIR,	/* /proc/sys/kernel/	*/
+	LXPR_SYS_KERNEL_CAPLCAP,	/* /proc/sys/kernel/cap_last_cap */
+	LXPR_SYS_KERNEL_COREPATT,	/* /proc/sys/kernel/core_pattern */
+	LXPR_SYS_KERNEL_HOSTNAME,	/* /proc/sys/kernel/hostname */
+	LXPR_SYS_KERNEL_MSGMNI,	/* /proc/sys/kernel/msgmni */
+	LXPR_SYS_KERNEL_NGROUPS_MAX,	/* /proc/sys/kernel/ngroups_max */
+	LXPR_SYS_KERNEL_OSREL,	/* /proc/sys/kernel/osrelease */
+	LXPR_SYS_KERNEL_PID_MAX,	/* /proc/sys/kernel/pid_max */
+	LXPR_SYS_KERNEL_RANDDIR,	/* /proc/sys/kernel/random */
+	LXPR_SYS_KERNEL_RAND_BOOTID, /* /proc/sys/kernel/random/boot_id */
+	LXPR_SYS_KERNEL_SEM,		/* /proc/sys/kernel/sem		*/
+	LXPR_SYS_KERNEL_SHMALL,		/* /proc/sys/kernel/shmall	*/
+	LXPR_SYS_KERNEL_SHMMAX,		/* /proc/sys/kernel/shmmax	*/
+	LXPR_SYS_KERNEL_SHMMNI,		/* /proc/sys/kernel/shmmni	*/
+	LXPR_SYS_KERNEL_THREADS_MAX,	/* /proc/sys/kernel/threads-max */
+	LXPR_SYS_NETDIR,		/* /proc/sys/net		*/
+	LXPR_SYS_NET_COREDIR,		/* /proc/sys/net/core		*/
+	LXPR_SYS_NET_CORE_SOMAXCON,	/* /proc/sys/net/core/somaxconn	*/
+	LXPR_SYS_NET_IPV4DIR,		/* /proc/sys/net/ipv4		*/
+	LXPR_SYS_NET_IPV4_IP_LPORT_RANGE, /* .../net/ipv4/ip_local_port_range */
+	LXPR_SYS_NET_IPV4_TCP_FIN_TO,	/* /proc/sys/net/ipv4/tcp_fin_timeout */
+	LXPR_SYS_NET_IPV4_TCP_KA_INT,	/* .../net/ipv4/tcp_keepalive_intvl */
+	LXPR_SYS_NET_IPV4_TCP_KA_TIM,	/* .../net/ipv4/tcp_keepalive_time */
+	LXPR_SYS_NET_IPV4_TCP_SACK,	/* /proc/sys/net/ipv4/tcp_sack */
+	LXPR_SYS_NET_IPV4_TCP_WINSCALE,	/* .../net/ipv4/tcp_window_scaling */
+	LXPR_SYS_VMDIR,			/* /proc/sys/vm			*/
+	LXPR_SYS_VM_MAX_MAP_CNT,	/* /proc/sys/vm/max_map_count	*/
+	LXPR_SYS_VM_MINFR_KB,		/* /proc/sys/vm/min_free_kbytes	*/
+	LXPR_SYS_VM_NHUGEP,		/* /proc/sys/vm/nr_hugepages	*/
+	LXPR_SYS_VM_OVERCOMMIT_MEM,	/* /proc/sys/vm/overcommit_memory */
+	LXPR_SYS_VM_SWAPPINESS,		/* /proc/sys/vm/swappiness	*/
+	LXPR_UPTIME,		/* /proc/uptime		*/
+	LXPR_VERSION,		/* /proc/version	*/
+	LXPR_VMSTAT,		/* /proc/vmstat		*/
+	LXPR_NFILES		/* number of lx /proc file types */
+} lxpr_nodetype_t;
+
+
+/*
+ * Number of fds allowed for in the inode number calculation
+ * per process (if a process has more fds then inode numbers
+ * may be duplicated)
+ */
+#define	LXPR_FD_PERPROC 2000
+
+/*
+ * Linux sector size for /proc/diskstats
+ */
+#define	LXPR_SECTOR_SIZE	512
+
+/*
+ * external dirent characteristics
+ */
+typedef struct {
+	lxpr_nodetype_t	d_type;
+	char		*d_name;
+} lxpr_dirent_t;
+
+/*
+ * This is the lxprocfs private data object
+ * which is attached to v_data in the vnode structure
+ */
+typedef struct lxpr_node {
+	lxpr_nodetype_t	lxpr_type;	/* type of this node 		*/
+	vnode_t		*lxpr_vnode;	/* vnode for the node		*/
+	vnode_t		*lxpr_parent;	/* parent directory		*/
+	vnode_t		*lxpr_realvp;	/* real vnode, file in dirs	*/
+	timestruc_t	lxpr_time;	/* creation etc time for file	*/
+	mode_t		lxpr_mode;	/* file mode bits		*/
+	uid_t		lxpr_uid;	/* file owner			*/
+	gid_t		lxpr_gid;	/* file group owner		*/
+	pid_t		lxpr_pid;	/* pid of proc referred to	*/
+	uint_t		lxpr_desc;	/* addl. descriptor (fd or tid)	*/
+	ino_t		lxpr_ino;	/* node id 			*/
+} lxpr_node_t;
+
+struct zone;    /* forward declaration */
+
+/*
+ * This is the lxprocfs private data object
+ * which is attached to vfs_data in the vfs structure
+ */
+typedef struct lxpr_mnt {
+	lxpr_node_t	*lxprm_node;	/* node at root of proc mount */
+	struct zone	*lxprm_zone;	/* zone for this mount */
+	ldi_ident_t	lxprm_li;	/* ident for ldi */
+} lxpr_mnt_t;
+
+extern vnodeops_t	*lxpr_vnodeops;
+extern int		nproc_highbit;	/* highbit(v.v_nproc)		*/
+
+typedef struct mounta	mounta_t;
+
+extern void lxpr_initnodecache();
+extern void lxpr_fininodecache();
+extern void lxpr_initrootnode(lxpr_node_t **, vfs_t *);
+extern ino_t lxpr_inode(lxpr_nodetype_t, pid_t, int);
+extern ino_t lxpr_parentinode(lxpr_node_t *);
+extern boolean_t lxpr_is_writable(lxpr_nodetype_t);
+extern lxpr_node_t *lxpr_getnode(vnode_t *, lxpr_nodetype_t, proc_t *, int);
+extern void lxpr_freenode(lxpr_node_t *);
+extern vnode_t *lxpr_lookup_fdnode(vnode_t *, const char *);
+extern int lxpr_readlink_fdnode(lxpr_node_t *, char *, size_t);
+
+typedef struct lxpr_uiobuf {
+	uio_t *uiop;
+	char *buffer;
+	uint32_t buffsize;
+	char *pos;
+	size_t beg;
+	int error;
+} lxpr_uiobuf_t;
+
+extern lxpr_uiobuf_t *lxpr_uiobuf_new(uio_t *);
+extern void lxpr_uiobuf_free(lxpr_uiobuf_t *);
+extern int lxpr_uiobuf_flush(lxpr_uiobuf_t *);
+extern void lxpr_uiobuf_seek(lxpr_uiobuf_t *, offset_t);
+extern boolean_t lxpr_uiobuf_nonblock(lxpr_uiobuf_t *);
+extern void lxpr_uiobuf_write(lxpr_uiobuf_t *, const char *, size_t);
+extern void lxpr_uiobuf_printf(lxpr_uiobuf_t *, const char *, ...);
+extern void lxpr_uiobuf_seterr(lxpr_uiobuf_t *, int);
+
+extern int lxpr_core_path_l2s(const char *, char *, size_t);
+extern int lxpr_core_path_s2l(const char *, char *, size_t);
+
+typedef enum lxpr_zombok {
+	NO_ZOMB = 0,
+	ZOMB_OK
+} zombok_t;
+
+proc_t *lxpr_lock(pid_t, zombok_t);
+void lxpr_unlock(proc_t *);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#ifndef islower
+#define	islower(x)	(((unsigned)(x) >= 'a') && ((unsigned)(x) <= 'z'))
+#endif
+#ifndef toupper
+#define	toupper(x)	(islower(x) ? (x) - 'a' + 'A' : (x))
+#endif
+
+#endif /* _LX_PROC_H */
--- /dev/null	Wed Oct  5 00:32:06 2016
+++ new/usr/src/uts/common/brand/lx/procfs/lx_prsubr.c	Wed Oct  5 00:32:06 2016
@@ -0,0 +1,852 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.  All rights reserved.
+ */
+
+/*
+ * lxprsubr.c: Various functions for the /lxproc vnodeops.
+ */
+
+#include <sys/varargs.h>
+
+#include <sys/cpuvar.h>
+#include <sys/mman.h>
+#include <sys/vmsystm.h>
+#include <sys/prsystm.h>
+#include <sys/zfs_ioctl.h>
+
+#include "lx_proc.h"
+
+#define	LXPRCACHE_NAME "lxbpr_cache"
+
+static int lxpr_node_constructor(void *, void *, int);
+static void lxpr_node_destructor(void *, void *);
+
+static kmem_cache_t *lxpr_node_cache;
+
+int lx_pr_bufsize = 4000;
+
+struct lxpr_zfs_ds {
+	list_node_t	ds_link;
+	char		ds_name[MAXPATHLEN];
+	uint64_t	ds_cookie;
+};
+
+struct lxpr_uiobuf *
+lxpr_uiobuf_new(uio_t *uiop)
+{
+	/* Allocate memory for both lxpr_uiobuf and output buffer */
+	int bufsize = lx_pr_bufsize;
+	struct lxpr_uiobuf *uiobuf =
+	    kmem_alloc(sizeof (struct lxpr_uiobuf) + bufsize, KM_SLEEP);
+
+	uiobuf->uiop = uiop;
+	uiobuf->buffer = (char *)&uiobuf[1];
+	uiobuf->buffsize = bufsize;
+	uiobuf->pos = uiobuf->buffer;
+	uiobuf->beg = 0;
+	uiobuf->error = 0;
+
+	return (uiobuf);
+}
+
+void
+lxpr_uiobuf_free(struct lxpr_uiobuf *uiobuf)
+{
+	ASSERT(uiobuf != NULL);
+	ASSERT(uiobuf->pos == uiobuf->buffer);
+
+	kmem_free(uiobuf, sizeof (struct lxpr_uiobuf) + uiobuf->buffsize);
+}
+
+void
+lxpr_uiobuf_seek(struct lxpr_uiobuf *uiobuf, offset_t offset)
+{
+	uiobuf->uiop->uio_offset = (off_t)offset;
+}
+
+boolean_t
+lxpr_uiobuf_nonblock(struct lxpr_uiobuf *uiobuf)
+{
+	if ((uiobuf->uiop->uio_fmode & FNONBLOCK) != 0)
+		return (B_TRUE);
+	return (B_FALSE);
+}
+
+void
+lxpr_uiobuf_seterr(struct lxpr_uiobuf *uiobuf, int err)
+{
+	ASSERT(uiobuf->error == 0);
+
+	uiobuf->error = err;
+}
+
+int
+lxpr_uiobuf_flush(struct lxpr_uiobuf *uiobuf)
+{
+	off_t off = uiobuf->uiop->uio_offset;
+	caddr_t uaddr = uiobuf->buffer;
+	size_t beg = uiobuf->beg;
+	size_t size = (uintptr_t)uiobuf->pos - (uintptr_t)uaddr;
+
+	if (uiobuf->error == 0 && uiobuf->uiop->uio_resid != 0) {
+		ASSERT(off >= beg);
+
+		if (beg + size > off && off >= 0)
+			uiobuf->error =
+			    uiomove(uaddr + (off - beg), size - (off - beg),
+			    UIO_READ, uiobuf->uiop);
+
+		uiobuf->beg += size;
+	}
+
+	uiobuf->pos = uaddr;
+
+	return (uiobuf->error);
+}
+
+void
+lxpr_uiobuf_write(struct lxpr_uiobuf *uiobuf, const char *buf, size_t size)
+{
+	/* While we can still carry on */
+	while (uiobuf->error == 0 && uiobuf->uiop->uio_resid != 0) {
+		uintptr_t remain = (uintptr_t)uiobuf->buffsize -
+		    ((uintptr_t)uiobuf->pos - (uintptr_t)uiobuf->buffer);
+
+		/* Enough space in buffer? */
+		if (remain >= size) {
+			bcopy(buf, uiobuf->pos, size);
+			uiobuf->pos += size;
+			return;
+		}
+
+		/* Not enough space, so copy all we can and try again */
+		bcopy(buf, uiobuf->pos, remain);
+		uiobuf->pos += remain;
+		(void) lxpr_uiobuf_flush(uiobuf);
+		buf += remain;
+		size -= remain;
+	}
+}
+
+#define	TYPBUFFSIZE 256
+
+void
+lxpr_uiobuf_printf(struct lxpr_uiobuf *uiobuf, const char *fmt, ...)
+{
+	va_list args;
+	char buff[TYPBUFFSIZE];
+	int len;
+	char *buffer;
+
+	/* Can we still do any output */
+	if (uiobuf->error != 0 || uiobuf->uiop->uio_resid == 0)
+		return;
+
+	va_start(args, fmt);
+
+	/* Try using stack allocated buffer */
+	len = vsnprintf(buff, TYPBUFFSIZE, fmt, args);
+	if (len < TYPBUFFSIZE) {
+		va_end(args);
+		lxpr_uiobuf_write(uiobuf, buff, len);
+		return;
+	}
+
+	/* Not enough space in pre-allocated buffer */
+	buffer = kmem_alloc(len + 1, KM_SLEEP);
+
+	/*
+	 * We know we allocated the correct amount of space
+	 * so no check on the return value
+	 */
+	(void) vsnprintf(buffer, len+1, fmt, args);
+	lxpr_uiobuf_write(uiobuf, buffer, len);
+	va_end(args);
+	kmem_free(buffer, len+1);
+}
+
+/*
+ * lxpr_lock():
+ *
+ * Lookup process from pid and return with p_plock and P_PR_LOCK held.
+ */
+proc_t *
+lxpr_lock(pid_t pid, zombok_t zombie_ok)
+{
+	proc_t *p;
+	kmutex_t *mp;
+	pid_t find_pid;
+	uint_t flags;
+
+	ASSERT(!MUTEX_HELD(&pidlock));
+
+	for (;;) {
+		mutex_enter(&pidlock);
+
+		/*
+		 * If the pid is 1, we really want the zone's init process;
+		 * if 0 we want zsched.
+		 */
+		if (pid == 1) {
+			find_pid = curproc->p_zone->zone_proc_initpid;
+		} else if (pid == 0) {
+			find_pid = curproc->p_zone->zone_zsched->p_pid;
+		} else {
+			find_pid = pid;
+		}
+		p = prfind(find_pid);
+
+		if (p == NULL || p->p_stat == SIDL) {
+			mutex_exit(&pidlock);
+			return (NULL);
+		}
+
+		/*
+		 * p_lock is persistent, but p itself is not -- it could
+		 * vanish during cv_wait().  Load p->p_lock now so we can
+		 * drop it after cv_wait() without referencing p.
+		 */
+		mp = &p->p_lock;
+		mutex_enter(mp);
+
+		mutex_exit(&pidlock);
+
+		/*
+		 * Filter out exiting or zombie processes, if requested.
+		 */
+		if (zombie_ok == NO_ZOMB &&
+		    ((p->p_flag & SEXITING) || p->p_stat == SZOMB)) {
+			mutex_exit(mp);
+			return (NULL);
+		}
+
+		flags = p->p_proc_flag & (P_PR_LOCK | P_PR_EXEC);
+		if (flags == 0) {
+			break;
+		} else if (flags == P_PR_EXEC && p == curproc) {
+			/*
+			 * Forward progress with (only) the PR_EXEC flag is
+			 * safe if a process is accessing resources in its own
+			 * piddir.  Executing its own /proc/<pid>/exe symlink
+			 * is one potential example.
+			 *
+			 * For all other processes, it is necessary to wait
+			 * until the exec is completed.
+			 */
+			break;
+		}
+
+		cv_wait(&pr_pid_cv[p->p_slot], mp);
+		mutex_exit(mp);
+	}
+
+	p->p_proc_flag |= P_PR_LOCK;
+	THREAD_KPRI_REQUEST();
+	return (p);
+}
+
+/*
+ * lxpr_unlock()
+ *
+ * Unlock locked process
+ */
+void
+lxpr_unlock(proc_t *p)
+{
+	ASSERT(p->p_proc_flag & P_PR_LOCK);
+	ASSERT(MUTEX_HELD(&p->p_lock));
+	ASSERT(!MUTEX_HELD(&pidlock));
+
+	cv_signal(&pr_pid_cv[p->p_slot]);
+	p->p_proc_flag &= ~P_PR_LOCK;
+	mutex_exit(&p->p_lock);
+	THREAD_KPRI_RELEASE();
+}
+
+void
+lxpr_initnodecache()
+{
+	lxpr_node_cache = kmem_cache_create(LXPRCACHE_NAME,
+	    sizeof (lxpr_node_t), 0,
+	    lxpr_node_constructor, lxpr_node_destructor, NULL, NULL, NULL, 0);
+}
+
+void
+lxpr_fininodecache()
+{
+	kmem_cache_destroy(lxpr_node_cache);
+}
+
+/* ARGSUSED */
+static int
+lxpr_node_constructor(void *buf, void *un, int kmflags)
+{
+	lxpr_node_t	*lxpnp = buf;
+	vnode_t		*vp;
+
+	vp = lxpnp->lxpr_vnode = vn_alloc(kmflags);
+	if (vp == NULL)
+		return (-1);
+
+	(void) vn_setops(vp, lxpr_vnodeops);
+	vp->v_data = lxpnp;
+
+	return (0);
+}
+
+/* ARGSUSED */
+static void
+lxpr_node_destructor(void *buf, void *un)
+{
+	lxpr_node_t	*lxpnp = buf;
+
+	vn_free(LXPTOV(lxpnp));
+}
+
+/*
+ * Calculate an inode number
+ *
+ * This takes various bits of info and munges them
+ * to give the inode number for an lxproc node
+ */
+ino_t
+lxpr_inode(lxpr_nodetype_t type, pid_t pid, int desc)
+{
+	if (pid == 1) {
+		pid = curproc->p_zone->zone_proc_initpid;
+	} else if (pid == 0) {
+		pid = curproc->p_zone->zone_zsched->p_pid;
+	}
+
+	switch (type) {
+	case LXPR_PIDDIR:
+		return (maxpid + pid + 1);
+	case LXPR_PID_TASK_IDDIR:
+		return (maxpid + (desc * 10));
+	case LXPR_PROCDIR:
+		return (maxpid + 2);
+	case LXPR_PID_FD_FD:
+		return (maxpid + 2 +
+		    (pid * (LXPR_FD_PERPROC + LXPR_NFILES)) +
+		    LXPR_NFILES + desc);
+	default:
+		return (maxpid + 2 +
+		    (pid * (LXPR_FD_PERPROC + LXPR_NFILES)) +
+		    type);
+	}
+}
+
+/*
+ * Return inode number of parent (directory)
+ */
+ino_t
+lxpr_parentinode(lxpr_node_t *lxpnp)
+{
+	/*
+	 * If the input node is the root then the parent inode
+	 * is the mounted on inode so just return our inode number
+	 */
+	if (lxpnp->lxpr_type != LXPR_PROCDIR)
+		return (VTOLXP(lxpnp->lxpr_parent)->lxpr_ino);
+	else
+		return (lxpnp->lxpr_ino);
+}
+
+/*
+ * Allocate a new lxproc node
+ *
+ * This also allocates the vnode associated with it
+ */
+lxpr_node_t *
+lxpr_getnode(vnode_t *dp, lxpr_nodetype_t type, proc_t *p, int desc)
+{
+	lxpr_node_t *lxpnp;
+	vnode_t *vp;
+	user_t *up;
+	timestruc_t now;
+
+	/*
+	 * Allocate a new node. It is deallocated in vop_innactive
+	 */
+	lxpnp = kmem_cache_alloc(lxpr_node_cache, KM_SLEEP);
+
+	/*
+	 * Set defaults (may be overridden below)
+	 */
+	gethrestime(&now);
+	lxpnp->lxpr_type = type;
+	lxpnp->lxpr_realvp = NULL;
+	lxpnp->lxpr_parent = dp;
+	lxpnp->lxpr_desc = desc;
+	VN_HOLD(dp);
+	if (p != NULL) {
+		if (p->p_pid == curproc->p_zone->zone_proc_initpid) {
+			lxpnp->lxpr_pid = 1;
+		} else if (p->p_pid == curproc->p_zone->zone_zsched->p_pid) {
+			lxpnp->lxpr_pid = 0;
+		} else {
+			lxpnp->lxpr_pid = p->p_pid;
+		}
+
+		lxpnp->lxpr_time = PTOU(p)->u_start;
+		lxpnp->lxpr_uid = crgetruid(p->p_cred);
+		lxpnp->lxpr_gid = crgetrgid(p->p_cred);
+		lxpnp->lxpr_ino = lxpr_inode(type, p->p_pid, desc);
+	} else {
+		/* Pretend files without a proc belong to sched */
+		lxpnp->lxpr_pid = 0;
+		lxpnp->lxpr_time = now;
+		lxpnp->lxpr_uid = lxpnp->lxpr_gid = 0;
+		lxpnp->lxpr_ino = lxpr_inode(type, 0, 0);
+	}
+
+	/* initialize the vnode data */
+	vp = lxpnp->lxpr_vnode;
+	vn_reinit(vp);
+	vp->v_flag = VNOCACHE|VNOMAP|VNOSWAP|VNOMOUNT;
+	vp->v_vfsp = dp->v_vfsp;
+
+	/*
+	 * Do node specific stuff
+	 */
+	if (lxpr_is_writable(type)) {
+		/* These two have different modes; handled later. */
+		if (type != LXPR_PID_FD_FD && type != LXPR_PID_TID_FD_FD) {
+			vp->v_type = VREG;
+			lxpnp->lxpr_mode = 0644;
+			return (lxpnp);
+		}
+	}
+
+	switch (type) {
+	case LXPR_PROCDIR:
+		vp->v_flag |= VROOT;
+		vp->v_type = VDIR;
+		lxpnp->lxpr_mode = 0555;	/* read-search by everyone */
+		break;
+
+	case LXPR_PID_CURDIR:
+		ASSERT(p != NULL);
+
+		/*
+		 * Zombie check.  p_stat is officially protected by pidlock,
+		 * but we can't grab pidlock here because we already hold
+		 * p_lock.  Luckily if we look at the process exit code
+		 * we see that p_stat only transisions from SRUN to SZOMB
+		 * while p_lock is held.  Aside from this, the only other
+		 * p_stat transition that we need to be aware about is
+		 * SIDL to SRUN, but that's not a problem since lxpr_lock()
+		 * ignores nodes in the SIDL state so we'll never get a node
+		 * that isn't already in the SRUN state.
+		 */
+		if (p->p_stat == SZOMB) {
+			lxpnp->lxpr_realvp = NULL;
+		} else {
+			up = PTOU(p);
+			lxpnp->lxpr_realvp = up->u_cdir;
+			ASSERT(lxpnp->lxpr_realvp != NULL);
+			VN_HOLD(lxpnp->lxpr_realvp);
+		}
+		vp->v_type = VLNK;
+		lxpnp->lxpr_mode = 0777;	/* anyone does anything ! */
+		break;
+
+	case LXPR_PID_ROOTDIR:
+		ASSERT(p != NULL);
+		/* Zombie check.  see locking comment above */
+		if (p->p_stat == SZOMB) {
+			lxpnp->lxpr_realvp = NULL;
+		} else {
+			up = PTOU(p);
+			lxpnp->lxpr_realvp =
+			    up->u_rdir != NULL ? up->u_rdir : rootdir;
+			ASSERT(lxpnp->lxpr_realvp != NULL);
+			VN_HOLD(lxpnp->lxpr_realvp);
+		}
+		vp->v_type = VLNK;
+		lxpnp->lxpr_mode = 0777;	/* anyone does anything ! */
+		break;
+
+	case LXPR_PID_EXE:
+		ASSERT(p != NULL);
+		lxpnp->lxpr_realvp = p->p_exec;
+		if (lxpnp->lxpr_realvp != NULL) {
+			VN_HOLD(lxpnp->lxpr_realvp);
+		}
+		vp->v_type = VLNK;
+		lxpnp->lxpr_mode = 0777;
+		break;
+
+	case LXPR_SELF:
+		vp->v_type = VLNK;
+		lxpnp->lxpr_mode = 0777;	/* anyone does anything ! */
+		break;
+
+	case LXPR_PID_TASKDIR:
+		ASSERT(p != NULL);
+		vp->v_type = VDIR;
+		lxpnp->lxpr_mode = 0555;	/* read-search by everyone */
+		break;
+
+	case LXPR_PID_TASK_IDDIR:
+		ASSERT(p != NULL);
+		vp->v_type = VDIR;
+		lxpnp->lxpr_mode = 0555;	/* read-search by everyone */
+		break;
+
+	case LXPR_PID_FD_FD:
+	case LXPR_PID_TID_FD_FD:
+		ASSERT(p != NULL);
+		/* lxpr_realvp is set after we return */
+		lxpnp->lxpr_mode = 0700;	/* read-write-exe owner only */
+		vp->v_type = VLNK;
+		break;
+
+	case LXPR_PID_FDDIR:
+	case LXPR_PID_TID_FDDIR:
+		ASSERT(p != NULL);
+		vp->v_type = VDIR;
+		lxpnp->lxpr_mode = 0500;	/* read-search by owner only */
+		break;
+
+	case LXPR_PIDDIR:
+		ASSERT(p != NULL);
+		vp->v_type = VDIR;
+		lxpnp->lxpr_mode = 0511;
+		break;
+
+	case LXPR_NETDIR:
+	case LXPR_SYSDIR:
+	case LXPR_SYS_FSDIR:
+	case LXPR_SYS_FS_INOTIFYDIR:
+	case LXPR_SYS_KERNELDIR:
+	case LXPR_SYS_KERNEL_RANDDIR:
+	case LXPR_SYS_NETDIR:
+	case LXPR_SYS_NET_COREDIR:
+	case LXPR_SYS_NET_IPV4DIR:
+	case LXPR_SYS_VMDIR:
+		vp->v_type = VDIR;
+		lxpnp->lxpr_mode = 0555;	/* read-search by all */
+		break;
+
+	case LXPR_PID_ENV:
+	case LXPR_PID_MEM:
+		ASSERT(p != NULL);
+		/*FALLTHRU*/
+	case LXPR_KCORE:
+		vp->v_type = VREG;
+		lxpnp->lxpr_mode = 0400;	/* read-only by owner only */
+		break;
+
+	default:
+		vp->v_type = VREG;
+		lxpnp->lxpr_mode = 0444;	/* read-only by all */
+		break;
+	}
+
+	return (lxpnp);
+}
+
+
+/*
+ * Free the storage obtained from lxpr_getnode().
+ */
+void
+lxpr_freenode(lxpr_node_t *lxpnp)
+{
+	ASSERT(lxpnp != NULL);
+	ASSERT(LXPTOV(lxpnp) != NULL);
+
+	/*
+	 * delete any association with realvp
+	 */
+	if (lxpnp->lxpr_realvp != NULL)
+		VN_RELE(lxpnp->lxpr_realvp);
+
+	/*
+	 * delete any association with parent vp
+	 */
+	if (lxpnp->lxpr_parent != NULL)
+		VN_RELE(lxpnp->lxpr_parent);
+
+	/*
+	 * Release the lxprnode.
+	 */
+	kmem_cache_free(lxpr_node_cache, lxpnp);
+}
+
+/*
+ * Attempt to locate vnode for /proc/<pid>/fd/<#>.
+ */
+vnode_t *
+lxpr_lookup_fdnode(vnode_t *dvp, const char *name)
+{
+	lxpr_node_t *lxdp = VTOLXP(dvp);
+	lxpr_node_t *lxfp;
+	char *endptr = NULL;
+	long num;
+	int fd;
+	proc_t *p;
+	vnode_t *vp = NULL;
+	file_t *fp;
+	uf_entry_t *ufp;
+	uf_info_t *fip;
+
+	ASSERT(lxdp->lxpr_type == LXPR_PID_FDDIR ||
+	    lxdp->lxpr_type == LXPR_PID_TID_FDDIR);
+
+	if (ddi_strtol(name, &endptr, 10, &num) != 0) {
+		return (NULL);
+	} else if (name[0] < '0' || name[0] > '9' || *endptr != '\0') {
+		/*
+		 * ddi_strtol allows leading spaces and trailing garbage
+		 * We do not tolerate such foolishness.
+		 */
+		return (NULL);
+	} else if ((fd = (int)num) < 0) {
+		return (NULL);
+	}
+
+	/* Lock the owner process */
+	p = lxpr_lock(lxdp->lxpr_pid, NO_ZOMB);
+	if ((p == NULL))
+		return (NULL);
+
+	/* Not applicable to processes which are system-owned. */
+	if ((p->p_flag & SSYS) || (p->p_as == &kas)) {
+		lxpr_unlock(p);
+		return (NULL);
+	}
+
+	lxfp = lxpr_getnode(dvp, LXPR_PID_FD_FD, p, fd);
+
+	/*
+	 * Drop p_lock, but keep the process P_PR_LOCK'd to prevent it from
+	 * going away while we dereference into fi_list.
+	 */
+	fip = P_FINFO(p);
+	mutex_exit(&p->p_lock);
+	mutex_enter(&fip->fi_lock);
+	if (fd < fip->fi_nfiles) {
+		UF_ENTER(ufp, fip, fd);
+		if ((fp = ufp->uf_file) != NULL) {
+			vp = fp->f_vnode;
+			VN_HOLD(vp);
+		}
+		UF_EXIT(ufp);
+	}
+	mutex_exit(&fip->fi_lock);
+
+	if (vp == NULL) {
+		mutex_enter(&p->p_lock);
+		lxpr_unlock(p);
+		lxpr_freenode(lxfp);
+		return (NULL);
+	} else {
+		/*
+		 * Fill in the lxpr_node so future references will be able to
+		 * find the underlying vnode. The vnode is held on the realvp.
+		 */
+		lxfp->lxpr_realvp = vp;
+
+		/*
+		 * For certain entries (sockets, pipes, etc), Linux expects a
+		 * bogus-named symlink.  If that's the case, report the type as
+		 * VNON to bypass link-following elsewhere in the vfs system.
+		 *
+		 * See lxpr_readlink for more details.
+		 */
+		if (lxpr_readlink_fdnode(lxfp, NULL, 0) == 0)
+			LXPTOV(lxfp)->v_type = VNON;
+	}
+
+	mutex_enter(&p->p_lock);
+	lxpr_unlock(p);
+	ASSERT(LXPTOV(lxfp) != NULL);
+	return (LXPTOV(lxfp));
+}
+
+/*
+ * Attempt to create Linux-proc-style fake symlinks contents for supported
+ * /proc/<pid>/fd/<#> entries.
+ */
+int
+lxpr_readlink_fdnode(lxpr_node_t *lxpnp, char *bp, size_t len)
+{
+	const char *format;
+	vnode_t *rvp = lxpnp->lxpr_realvp;
+	vattr_t attr;
+
+	switch (rvp->v_type) {
+	case VSOCK:
+		format = "socket:[%lu]";
+		break;
+	case VFIFO:
+		format = "pipe:[%lu]";
+		break;
+	default:
+		return (-1);
+	}
+
+	/* Fetch the inode of the underlying vnode */
+	if (VOP_GETATTR(rvp, &attr, 0, CRED(), NULL) != 0)
+		return (-1);
+
+	if (bp != NULL)
+		(void) snprintf(bp, len, format, (ino_t)attr.va_nodeid);
+	return (0);
+}
+
+/*
+ * Translate a Linux core_pattern path to a native Illumos one, by replacing
+ * the appropriate % escape sequences.
+ *
+ * Any % escape sequences that are not recognised are double-escaped so that
+ * they will be inserted literally into the path (to mimic Linux).
+ */
+int
+lxpr_core_path_l2s(const char *inp, char *outp, size_t outsz)
+{
+	int i = 0, j = 0;
+	char x;
+
+	while (j < outsz - 1) {
+		x = inp[i++];
+		if (x == '\0')
+			break;
+		if (x != '%') {
+			outp[j++] = x;
+			continue;
+		}
+
+		x = inp[i++];
+		if (x == '\0')
+			break;
+
+		/* Make sure we have enough space in the output buffer. */
+		if (j + 2 >= outsz - 1)
+			return (EINVAL);
+
+		switch (x) {
+		case 'E':
+			if (j + 4 >= outsz - 1)
+				return (EINVAL);
+			outp[j++] = '%';
+			outp[j++] = 'd';
+			outp[j++] = '%';
+			outp[j++] = 'f';
+			break;
+		case 'e':
+			outp[j++] = '%';
+			outp[j++] = 'f';
+			break;
+		case 'p':
+		case 'g':
+		case 'u':
+		case 't':
+		case '%':
+			outp[j++] = '%';
+			outp[j++] = x;
+			break;
+		case 'h':
+			outp[j++] = '%';
+			outp[j++] = 'n';
+			break;
+		default:
+			/* No translation, make it literal. */
+			if (j + 3 >= outsz - 1)
+				return (EINVAL);
+			outp[j++] = '%';
+			outp[j++] = '%';
+			outp[j++] = x;
+			break;
+		}
+	}
+
+	outp[j] = '\0';
+	return (0);
+}
+
+/*
+ * Translate an Illumos core pattern path back to Linux format.
+ */
+int
+lxpr_core_path_s2l(const char *inp, char *outp, size_t outsz)
+{
+	int i = 0, j = 0;
+	char x;
+
+	while (j < outsz - 1) {
+		x = inp[i++];
+		if (x == '\0')
+			break;
+		if (x != '%') {
+			outp[j++] = x;
+			continue;
+		}
+
+		x = inp[i++];
+		if (x == '\0')
+			break;
+
+		/* Make sure we have enough space in the output buffer. */
+		if (j + 2 >= outsz - 1)
+			return (EINVAL);
+
+		switch (x) {
+		case 'd':
+			/* No Linux equivalent unless it's %d%f. */
+			if (inp[i] == '%' && inp[i + 1] == 'f') {
+				i += 2;
+				outp[j++] = '%';
+				outp[j++] = 'E';
+			}
+			break;
+		case 'f':
+			outp[j++] = '%';
+			outp[j++] = 'e';
+			break;
+		case 'p':
+		case 'P':
+		case 'g':
+		case 'u':
+		case 't':
+		case '%':
+			outp[j++] = '%';
+			outp[j++] = (x == 'P' ? 'p' : x);
+			break;
+		case 'n':
+			outp[j++] = '%';
+			outp[j++] = 'h';
+			break;
+		default:
+			/* No translation. */
+			break;
+		}
+	}
+
+	outp[j] = '\0';
+	return (0);
+}
--- /dev/null	Wed Oct  5 00:32:06 2016
+++ new/usr/src/uts/common/brand/lx/procfs/lx_prvfsops.c	Wed Oct  5 00:32:06 2016
@@ -0,0 +1,377 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+/*
+ * lxprvfsops.c: vfs operations for /lxprocfs.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/cmn_err.h>
+#include <sys/cred.h>
+#include <sys/debug.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/sysmacros.h>
+#include <sys/systm.h>
+#include <sys/var.h>
+#include <sys/vfs.h>
+#include <sys/vfs_opreg.h>
+#include <sys/vnode.h>
+#include <sys/mode.h>
+#include <sys/signal.h>
+#include <sys/user.h>
+#include <sys/mount.h>
+#include <sys/bitmap.h>
+#include <sys/kmem.h>
+#include <sys/policy.h>
+#include <sys/modctl.h>
+#include <sys/sunddi.h>
+#include <sys/sunldi.h>
+#include <sys/lx_impl.h>
+#include <sys/lx_brand.h>
+
+#include "lx_proc.h"
+
+/* Module level parameters */
+static int	lxprocfstype;
+static dev_t	lxprocdev;
+static kmutex_t	lxpr_mount_lock;
+
+int nproc_highbit;	/* highbit(v.v_nproc) */
+
+static int lxpr_mount(vfs_t *, vnode_t *, mounta_t *, cred_t *);
+static int lxpr_unmount(vfs_t *, int, cred_t *);
+static int lxpr_root(vfs_t *, vnode_t **);
+static int lxpr_statvfs(vfs_t *, statvfs64_t *);
+static int lxpr_init(int, char *);
+
+static vfsdef_t vfw = {
+	VFSDEF_VERSION,
+	"lx_proc",
+	lxpr_init,
+	VSW_ZMOUNT,
+	NULL
+};
+
+/*
+ * Module linkage information for the kernel.
+ */
+extern struct mod_ops mod_fsops;
+
+static struct modlfs modlfs = {
+	&mod_fsops, "lx brand procfs", &vfw
+};
+
+static struct modlinkage modlinkage = {
+	MODREV_1, (void *)&modlfs, NULL
+};
+
+int
+_init(void)
+{
+	return (mod_install(&modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+	int retval;
+
+	/*
+	 * attempt to unload the module
+	 */
+	if ((retval = mod_remove(&modlinkage)) != 0)
+		goto done;
+
+	/*
+	 * destroy lxpr_node cache
+	 */
+	lxpr_fininodecache();
+
+	/*
+	 * clean out the vfsops and vnodeops
+	 */
+	(void) vfs_freevfsops_by_type(lxprocfstype);
+	vn_freevnodeops(lxpr_vnodeops);
+
+	mutex_destroy(&lxpr_mount_lock);
+done:
+	return (retval);
+}
+
+static int
+lxpr_init(int fstype, char *name)
+{
+	static const fs_operation_def_t lxpr_vfsops_template[] = {
+		VFSNAME_MOUNT,		{ .vfs_mount = lxpr_mount },
+		VFSNAME_UNMOUNT,	{ .vfs_unmount = lxpr_unmount },
+		VFSNAME_ROOT,		{ .vfs_root = lxpr_root },
+		VFSNAME_STATVFS,	{ .vfs_statvfs = lxpr_statvfs },
+		NULL,			NULL
+	};
+	extern const fs_operation_def_t lxpr_vnodeops_template[];
+	int error;
+	major_t dev;
+
+	nproc_highbit = highbit(v.v_proc);
+	lxprocfstype = fstype;
+	ASSERT(lxprocfstype != 0);
+
+	mutex_init(&lxpr_mount_lock, NULL, MUTEX_DEFAULT, NULL);
+
+	/*
+	 * Associate VFS ops vector with this fstype.
+	 */
+	error = vfs_setfsops(fstype, lxpr_vfsops_template, NULL);
+	if (error != 0) {
+		cmn_err(CE_WARN, "lxpr_init: bad vfs ops template");
+		return (error);
+	}
+
+	/*
+	 * Set up vnode ops vector too.
+	 */
+	error = vn_make_ops(name, lxpr_vnodeops_template, &lxpr_vnodeops);
+	if (error != 0) {
+		(void) vfs_freevfsops_by_type(fstype);
+		cmn_err(CE_WARN, "lxpr_init: bad vnode ops template");
+		return (error);
+	}
+
+	/*
+	 * Assign a unique "device" number (used by stat(2)).
+	 */
+	if ((dev = getudev()) == (major_t)-1) {
+		cmn_err(CE_WARN, "lxpr_init: can't get unique device number");
+		dev = 0;
+	}
+
+	/*
+	 * Make the pseudo device
+	 */
+	lxprocdev = makedevice(dev, 0);
+
+	/*
+	 * Initialise cache for lxpr_nodes
+	 */
+	lxpr_initnodecache();
+
+	return (0);
+}
+
+static int
+lxpr_mount(vfs_t *vfsp, vnode_t *mvp, mounta_t *uap, cred_t *cr)
+{
+	lxpr_mnt_t *lxpr_mnt;
+	zone_t *zone = curproc->p_zone;
+	ldi_ident_t li;
+	int err;
+
+	/*
+	 * must be root to mount
+	 */
+	if (secpolicy_fs_mount(cr, mvp, vfsp) != 0)
+		return (EPERM);
+
+	/*
+	 * mount point must be a directory
+	 */
+	if (mvp->v_type != VDIR)
+		return (ENOTDIR);
+
+	/*
+	 * Mounting lx_proc is not allowed outside an LX zone.
+	 */
+	if (zone->zone_brand != &lx_brand) {
+		return (ENOTSUP);
+	}
+
+	/*
+	 * Having the resource be anything but "lxproc" doesn't make sense
+	 */
+	vfs_setresource(vfsp, "lxproc", 0);
+
+	lxpr_mnt = kmem_alloc(sizeof (*lxpr_mnt), KM_SLEEP);
+
+	if ((err = ldi_ident_from_mod(&modlinkage, &li)) != 0) {
+		kmem_free(lxpr_mnt, sizeof (*lxpr_mnt));
+		return (err);
+	}
+	lxpr_mnt->lxprm_li = li;
+
+	mutex_enter(&lxpr_mount_lock);
+
+	/*
+	 * Ensure we don't allow overlaying mounts
+	 */
+	mutex_enter(&mvp->v_lock);
+	if ((uap->flags & MS_OVERLAY) == 0 &&
+	    (mvp->v_count > 1 || (mvp->v_flag & VROOT))) {
+		mutex_exit(&mvp->v_lock);
+		mutex_exit(&lxpr_mount_lock);
+		kmem_free(lxpr_mnt, sizeof ((*lxpr_mnt)));
+		return (EBUSY);
+	}
+	mutex_exit(&mvp->v_lock);
+
+	/*
+	 * Hold a zone reference for access to the lxzd structure.
+	 */
+	zone_hold(lxpr_mnt->lxprm_zone = zone);
+
+	/*
+	 * Allocate the first vnode and arbitrarily set the parent vnode to the
+	 * mounted over directory
+	 */
+	lxpr_mnt->lxprm_node = lxpr_getnode(mvp, LXPR_PROCDIR, NULL, 0);
+
+	/* Correctly set the fs for the root node */
+	lxpr_mnt->lxprm_node->lxpr_vnode->v_vfsp = vfsp;
+
+	vfs_make_fsid(&vfsp->vfs_fsid, lxprocdev, lxprocfstype);
+	vfsp->vfs_bsize = DEV_BSIZE;
+	vfsp->vfs_fstype = lxprocfstype;
+	vfsp->vfs_data = (caddr_t)lxpr_mnt;
+	vfsp->vfs_dev = lxprocdev;
+
+	mutex_exit(&lxpr_mount_lock);
+
+	return (0);
+}
+
+static int
+lxpr_unmount(vfs_t *vfsp, int flag, cred_t *cr)
+{
+	lxpr_mnt_t *lxpr_mnt = (lxpr_mnt_t *)vfsp->vfs_data;
+	vnode_t *vp;
+	int count;
+
+	ASSERT(lxpr_mnt != NULL);
+	vp = LXPTOV(lxpr_mnt->lxprm_node);
+
+	mutex_enter(&lxpr_mount_lock);
+
+	/*
+	 * must be root to unmount
+	 */
+	if (secpolicy_fs_unmount(cr, vfsp) != 0) {
+		mutex_exit(&lxpr_mount_lock);
+		return (EPERM);
+	}
+
+	/*
+	 * forced unmount is not supported by this file system
+	 */
+	if (flag & MS_FORCE) {
+		mutex_exit(&lxpr_mount_lock);
+		return (ENOTSUP);
+	}
+
+	/*
+	 * Ensure that no vnodes are in use on this mount point.
+	 */
+	mutex_enter(&vp->v_lock);
+	count = vp->v_count;
+	mutex_exit(&vp->v_lock);
+	if (count > 1) {
+		mutex_exit(&lxpr_mount_lock);
+		return (EBUSY);
+	}
+
+
+	/*
+	 * purge the dnlc cache for vnode entries
+	 * associated with this file system
+	 */
+	count = dnlc_purge_vfsp(vfsp, 0);
+
+	/*
+	 * free up the lxprnode
+	 */
+	lxpr_freenode(lxpr_mnt->lxprm_node);
+	zone_rele(lxpr_mnt->lxprm_zone);
+
+	ldi_ident_release(lxpr_mnt->lxprm_li);
+
+	kmem_free(lxpr_mnt, sizeof (*lxpr_mnt));
+
+	mutex_exit(&lxpr_mount_lock);
+
+	return (0);
+}
+
+static int
+lxpr_root(vfs_t *vfsp, vnode_t **vpp)
+{
+	lxpr_node_t *lxpnp = ((lxpr_mnt_t *)vfsp->vfs_data)->lxprm_node;
+	vnode_t *vp = LXPTOV(lxpnp);
+
+	VN_HOLD(vp);
+	*vpp = vp;
+	return (0);
+}
+
+static int
+lxpr_statvfs(vfs_t *vfsp, statvfs64_t *sp)
+{
+	int n;
+	dev32_t d32;
+	extern uint_t nproc;
+
+	n = v.v_proc - nproc;
+
+	bzero((caddr_t)sp, sizeof (*sp));
+	sp->f_bsize	= DEV_BSIZE;
+	sp->f_frsize	= DEV_BSIZE;
+	sp->f_blocks	= (fsblkcnt64_t)0;
+	sp->f_bfree	= (fsblkcnt64_t)0;
+	sp->f_bavail	= (fsblkcnt64_t)0;
+	sp->f_files	= (fsfilcnt64_t)v.v_proc + 2;
+	sp->f_ffree	= (fsfilcnt64_t)n;
+	sp->f_favail	= (fsfilcnt64_t)n;
+	(void) cmpldev(&d32, vfsp->vfs_dev);
+	sp->f_fsid	= d32;
+	/* It is guaranteed that vsw_name will fit in f_basetype */
+	(void) strcpy(sp->f_basetype, vfssw[lxprocfstype].vsw_name);
+	sp->f_flag = vf_to_stf(vfsp->vfs_flag);
+	sp->f_namemax = 64;		/* quite arbitrary */
+	bzero(sp->f_fstr, sizeof (sp->f_fstr));
+
+	/* We know f_fstr is 32 chars */
+	(void) strcpy(sp->f_fstr, "/proc");
+	(void) strcpy(&sp->f_fstr[6], "/proc");
+
+	return (0);
+}
--- /dev/null	Wed Oct  5 00:32:06 2016
+++ new/usr/src/uts/common/brand/lx/procfs/lx_prvnops.c	Wed Oct  5 00:32:06 2016
@@ -0,0 +1,7344 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+/*
+ * lx_proc -- a Linux-compatible /proc for the LX brand
+ *
+ * We have -- confusingly -- two implementations of Linux /proc.  One is to
+ * support native (but Linux-borne) programs that wish to view the native
+ * system through the Linux /proc model; the other -- this one -- is to
+ * support Linux binaries via the LX brand.  These two implementations differ
+ * greatly in their aspirations (and their willingness to bend the truth
+ * of the system to accommodate those aspirations); they should not be unified.
+ */
+
+#include <sys/cpupart.h>
+#include <sys/cpuvar.h>
+#include <sys/session.h>
+#include <sys/vmparam.h>
+#include <sys/mman.h>
+#include <vm/rm.h>
+#include <vm/seg_vn.h>
+#include <sys/sdt.h>
+#include <lx_signum.h>
+#include <sys/strlog.h>
+#include <sys/stropts.h>
+#include <sys/cmn_err.h>
+#include <sys/lx_brand.h>
+#include <lx_auxv.h>
+#include <sys/x86_archext.h>
+#include <sys/archsystm.h>
+#include <sys/fp.h>
+#include <sys/pool_pset.h>
+#include <sys/pset.h>
+#include <sys/zone.h>
+#include <sys/fcntl.h>
+#include <sys/pghw.h>
+#include <sys/vfs_opreg.h>
+#include <sys/param.h>
+#include <sys/utsname.h>
+#include <sys/rctl.h>
+#include <sys/kstat.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_types.h>
+#include <sys/brand.h>
+#include <sys/cred_impl.h>
+#include <sys/tihdr.h>
+#include <sys/corectl.h>
+#include <inet/ip.h>
+#include <inet/ip_ire.h>
+#include <inet/ip6.h>
+#include <inet/ip_if.h>
+#include <inet/tcp.h>
+#include <inet/tcp_impl.h>
+#include <inet/udp_impl.h>
+#include <inet/ipclassifier.h>
+#include <sys/socketvar.h>
+#include <fs/sockfs/socktpi.h>
+
+/* Dependent on procfs */
+extern kthread_t *prchoose(proc_t *);
+extern int prreadargv(proc_t *, char *, size_t, size_t *);
+extern int prreadenvv(proc_t *, char *, size_t, size_t *);
+extern int prreadbuf(proc_t *, uintptr_t, uint8_t *, size_t, size_t *);
+
+#include "lx_proc.h"
+
+extern pgcnt_t swapfs_minfree;
+extern time_t boot_time;
+
+/*
+ * Pointer to the vnode ops vector for this fs.
+ * This is instantiated in lxprinit() in lxpr_vfsops.c
+ */
+vnodeops_t *lxpr_vnodeops;
+
+static int lxpr_open(vnode_t **, int, cred_t *, caller_context_t *);
+static int lxpr_close(vnode_t *, int, int, offset_t, cred_t *,
+    caller_context_t *);
+static int lxpr_create(struct vnode *, char *, struct vattr *, enum vcexcl,
+    int, struct vnode **, struct cred *, int, caller_context_t *, vsecattr_t *);
+static int lxpr_read(vnode_t *, uio_t *, int, cred_t *, caller_context_t *);
+static int lxpr_write(vnode_t *, uio_t *, int, cred_t *, caller_context_t *);
+static int lxpr_space(vnode_t *, int, flock64_t *, int, offset_t, cred_t *,
+    caller_context_t *);
+static int lxpr_setattr(vnode_t *, vattr_t *, int, cred_t *,
+    caller_context_t *);
+static int lxpr_getattr(vnode_t *, vattr_t *, int, cred_t *,
+    caller_context_t *);
+static int lxpr_access(vnode_t *, int, int, cred_t *, caller_context_t *);
+static int lxpr_lookup(vnode_t *, char *, vnode_t **,
+    pathname_t *, int, vnode_t *, cred_t *, caller_context_t *, int *,
+    pathname_t *);
+static int lxpr_readdir(vnode_t *, uio_t *, cred_t *, int *,
+    caller_context_t *, int);
+static int lxpr_readlink(vnode_t *, uio_t *, cred_t *, caller_context_t *);
+static int lxpr_cmp(vnode_t *, vnode_t *, caller_context_t *);
+static int lxpr_realvp(vnode_t *, vnode_t **, caller_context_t *);
+static int lxpr_sync(void);
+static void lxpr_inactive(vnode_t *, cred_t *, caller_context_t *);
+
+static vnode_t *lxpr_lookup_procdir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_piddir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_not_a_dir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_fddir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_netdir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_sysdir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_sys_fsdir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_sys_fs_inotifydir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_sys_kerneldir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_sys_kdir_randdir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_sys_netdir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_sys_net_coredir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_sys_net_ipv4dir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_sys_vmdir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_taskdir(vnode_t *, char *);
+static vnode_t *lxpr_lookup_task_tid_dir(vnode_t *, char *);
+
+static int lxpr_readdir_procdir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_piddir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_not_a_dir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_fddir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_netdir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_sysdir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_sys_fsdir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_sys_fs_inotifydir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_sys_kerneldir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_sys_kdir_randdir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_sys_netdir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_sys_net_coredir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_sys_net_ipv4dir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_sys_vmdir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_taskdir(lxpr_node_t *, uio_t *, int *);
+static int lxpr_readdir_task_tid_dir(lxpr_node_t *, uio_t *, int *);
+
+static void lxpr_read_invalid(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_empty(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_cgroups(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_cmdline(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_cpuinfo(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_devices(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_diskstats(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_isdir(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_fd(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_filesystems(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_kmsg(lxpr_node_t *, lxpr_uiobuf_t *, ldi_handle_t);
+static void lxpr_read_loadavg(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_meminfo(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_mounts(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_partitions(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_stat(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_swaps(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_uptime(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_version(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_vmstat(lxpr_node_t *, lxpr_uiobuf_t *);
+
+static void lxpr_read_pid_auxv(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_cgroup(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_cmdline(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_comm(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_env(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_limits(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_loginuid(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_maps(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_mountinfo(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_oom_scr_adj(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_personality(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_stat(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_statm(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_status(lxpr_node_t *, lxpr_uiobuf_t *);
+
+static void lxpr_read_pid_tid_stat(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_pid_tid_status(lxpr_node_t *, lxpr_uiobuf_t *);
+
+static void lxpr_read_net_arp(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_dev(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_dev_mcast(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_if_inet6(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_igmp(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_ip_mr_cache(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_ip_mr_vif(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_ipv6_route(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_mcfilter(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_netstat(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_raw(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_route(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_rpc(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_rt_cache(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_sockstat(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_snmp(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_stat(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_tcp(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_tcp6(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_udp(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_udp6(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_net_unix(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_fs_inotify_max_queued_events(lxpr_node_t *,
+    lxpr_uiobuf_t *);
+static void lxpr_read_sys_fs_inotify_max_user_instances(lxpr_node_t *,
+    lxpr_uiobuf_t *);
+static void lxpr_read_sys_fs_inotify_max_user_watches(lxpr_node_t *,
+    lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_caplcap(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_corepatt(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_hostname(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_msgmni(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_ngroups_max(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_osrel(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_pid_max(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_rand_bootid(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_sem(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_shmall(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_shmmax(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_shmmni(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_kernel_threads_max(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_net_core_somaxc(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_net_ipv4_ip_lport_range(lxpr_node_t *,
+    lxpr_uiobuf_t *);
+static void lxpr_read_sys_net_ipv4_tcp_fin_to(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_net_ipv4_tcp_ka_int(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_net_ipv4_tcp_ka_tim(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_net_ipv4_tcp_sack(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_net_ipv4_tcp_winscale(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_vm_max_map_cnt(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_vm_minfr_kb(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_vm_nhpages(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_vm_overcommit_mem(lxpr_node_t *, lxpr_uiobuf_t *);
+static void lxpr_read_sys_vm_swappiness(lxpr_node_t *, lxpr_uiobuf_t *);
+
+static int lxpr_write_pid_loginuid(lxpr_node_t *, uio_t *, cred_t *,
+    caller_context_t *);
+static int lxpr_write_sys_net_core_somaxc(lxpr_node_t *, uio_t *, cred_t *,
+    caller_context_t *);
+static int lxpr_write_sys_net_ipv4_ip_lport_range(lxpr_node_t *, uio_t *,
+    cred_t *, caller_context_t *);
+static int lxpr_write_sys_net_ipv4_tcp_fin_to(lxpr_node_t *, uio_t *, cred_t *,
+    caller_context_t *);
+static int lxpr_write_sys_net_ipv4_tcp_ka_int(lxpr_node_t *, uio_t *,
+    cred_t *, caller_context_t *);
+static int lxpr_write_sys_net_ipv4_tcp_ka_tim(lxpr_node_t *, uio_t *,
+    cred_t *, caller_context_t *);
+static int lxpr_write_sys_net_ipv4_tcp_sack(lxpr_node_t *, uio_t *,
+    cred_t *, caller_context_t *);
+static int lxpr_write_sys_net_ipv4_tcp_winscale(lxpr_node_t *, uio_t *,
+    cred_t *, caller_context_t *);
+static int lxpr_write_sys_kernel_corepatt(lxpr_node_t *, uio_t *, cred_t *,
+    caller_context_t *);
+
+/*
+ * Simple conversion
+ */
+#define	btok(x)	((x) >> 10)			/* bytes to kbytes */
+#define	ptok(x)	((x) << (PAGESHIFT - 10))	/* pages to kbytes */
+
+#define	ttolxlwp(t)	((struct lx_lwp_data *)ttolwpbrand(t))
+
+extern rctl_hndl_t rc_process_semmsl;
+extern rctl_hndl_t rc_process_semopm;
+extern rctl_hndl_t rc_zone_semmni;
+
+extern rctl_hndl_t rc_zone_msgmni;
+extern rctl_hndl_t rc_zone_shmmax;
+extern rctl_hndl_t rc_zone_shmmni;
+#define	FOURGB	4294967295ULL
+
+/*
+ * The maximum length of the concatenation of argument vector strings we
+ * will return to the user via the branded procfs. Likewise for the env vector.
+ */
+int lxpr_maxargvlen = 4096;
+int lxpr_maxenvvlen = 4096;
+
+/*
+ * The lx /proc vnode operations vector
+ */
+const fs_operation_def_t lxpr_vnodeops_template[] = {
+	VOPNAME_OPEN,		{ .vop_open = lxpr_open },
+	VOPNAME_CLOSE,		{ .vop_close = lxpr_close },
+	VOPNAME_READ,		{ .vop_read = lxpr_read },
+	VOPNAME_WRITE,		{ .vop_read = lxpr_write },
+	VOPNAME_GETATTR,	{ .vop_getattr = lxpr_getattr },
+	VOPNAME_ACCESS,		{ .vop_access = lxpr_access },
+	VOPNAME_LOOKUP,		{ .vop_lookup = lxpr_lookup },
+	VOPNAME_CREATE,		{ .vop_create = lxpr_create },
+	VOPNAME_READDIR,	{ .vop_readdir = lxpr_readdir },
+	VOPNAME_READLINK,	{ .vop_readlink = lxpr_readlink },
+	VOPNAME_SPACE,		{ .vop_space = lxpr_space },
+	VOPNAME_SETATTR,	{ .vop_setattr = lxpr_setattr },
+	VOPNAME_FSYNC,		{ .error = lxpr_sync },
+	VOPNAME_SEEK,		{ .error = lxpr_sync },
+	VOPNAME_INACTIVE,	{ .vop_inactive = lxpr_inactive },
+	VOPNAME_CMP,		{ .vop_cmp = lxpr_cmp },
+	VOPNAME_REALVP,		{ .vop_realvp = lxpr_realvp },
+	NULL,			NULL
+};
+
+
+/*
+ * file contents of an lx /proc directory.
+ */
+static lxpr_dirent_t lx_procdir[] = {
+	{ LXPR_CGROUPS,		"cgroups" },
+	{ LXPR_CMDLINE,		"cmdline" },
+	{ LXPR_CPUINFO,		"cpuinfo" },
+	{ LXPR_DEVICES,		"devices" },
+	{ LXPR_DISKSTATS,	"diskstats" },
+	{ LXPR_DMA,		"dma" },
+	{ LXPR_FILESYSTEMS,	"filesystems" },
+	{ LXPR_INTERRUPTS,	"interrupts" },
+	{ LXPR_IOPORTS,		"ioports" },
+	{ LXPR_KCORE,		"kcore" },
+	{ LXPR_KMSG,		"kmsg" },
+	{ LXPR_LOADAVG,		"loadavg" },
+	{ LXPR_MEMINFO,		"meminfo" },
+	{ LXPR_MODULES,		"modules" },
+	{ LXPR_MOUNTS,		"mounts" },
+	{ LXPR_NETDIR,		"net" },
+	{ LXPR_PARTITIONS,	"partitions" },
+	{ LXPR_SELF,		"self" },
+	{ LXPR_STAT,		"stat" },
+	{ LXPR_SWAPS,		"swaps" },
+	{ LXPR_SYSDIR,		"sys" },
+	{ LXPR_UPTIME,		"uptime" },
+	{ LXPR_VERSION,		"version" },
+	{ LXPR_VMSTAT,		"vmstat" }
+};
+
+#define	PROCDIRFILES	(sizeof (lx_procdir) / sizeof (lx_procdir[0]))
+
+/*
+ * Contents of an lx /proc/<pid> directory.
+ */
+static lxpr_dirent_t piddir[] = {
+	{ LXPR_PID_AUXV,	"auxv" },
+	{ LXPR_PID_CGROUP,	"cgroup" },
+	{ LXPR_PID_CMDLINE,	"cmdline" },
+	{ LXPR_PID_COMM,	"comm" },
+	{ LXPR_PID_CPU,		"cpu" },
+	{ LXPR_PID_CURDIR,	"cwd" },
+	{ LXPR_PID_ENV,		"environ" },
+	{ LXPR_PID_EXE,		"exe" },
+	{ LXPR_PID_LIMITS,	"limits" },
+	{ LXPR_PID_LOGINUID,	"loginuid" },
+	{ LXPR_PID_MAPS,	"maps" },
+	{ LXPR_PID_MEM,		"mem" },
+	{ LXPR_PID_MOUNTINFO,	"mountinfo" },
+	{ LXPR_PID_MOUNTS,	"mounts" },
+	{ LXPR_PID_OOM_SCR_ADJ,	"oom_score_adj" },
+	{ LXPR_PID_PERSONALITY,	"personality" },
+	{ LXPR_PID_ROOTDIR,	"root" },
+	{ LXPR_PID_STAT,	"stat" },
+	{ LXPR_PID_STATM,	"statm" },
+	{ LXPR_PID_STATUS,	"status" },
+	{ LXPR_PID_TASKDIR,	"task" },
+	{ LXPR_PID_FDDIR,	"fd" }
+};
+
+#define	PIDDIRFILES	(sizeof (piddir) / sizeof (piddir[0]))
+
+/*
+ * Contents of an lx /proc/<pid>/task/<tid> directory.
+ */
+static lxpr_dirent_t tiddir[] = {
+	{ LXPR_PID_TID_AUXV,	"auxv" },
+	{ LXPR_PID_CGROUP,	"cgroup" },
+	{ LXPR_PID_CMDLINE,	"cmdline" },
+	{ LXPR_PID_TID_COMM,	"comm" },
+	{ LXPR_PID_CPU,		"cpu" },
+	{ LXPR_PID_CURDIR,	"cwd" },
+	{ LXPR_PID_ENV,		"environ" },
+	{ LXPR_PID_EXE,		"exe" },
+	{ LXPR_PID_LIMITS,	"limits" },
+	{ LXPR_PID_LOGINUID,	"loginuid" },
+	{ LXPR_PID_MAPS,	"maps" },
+	{ LXPR_PID_MEM,		"mem" },
+	{ LXPR_PID_MOUNTINFO,	"mountinfo" },
+	{ LXPR_PID_TID_OOM_SCR_ADJ,	"oom_score_adj" },
+	{ LXPR_PID_PERSONALITY,	"personality" },
+	{ LXPR_PID_ROOTDIR,	"root" },
+	{ LXPR_PID_TID_STAT,	"stat" },
+	{ LXPR_PID_STATM,	"statm" },
+	{ LXPR_PID_TID_STATUS,	"status" },
+	{ LXPR_PID_FDDIR,	"fd" }
+};
+
+#define	TIDDIRFILES	(sizeof (tiddir) / sizeof (tiddir[0]))
+
+#define	LX_RLIM_INFINITY	0xFFFFFFFFFFFFFFFF
+
+#define	RCTL_INFINITE(x) \
+	((x.rcv_flagaction & RCTL_LOCAL_MAXIMAL) && \
+	(x.rcv_flagaction & RCTL_GLOBAL_INFINITE))
+
+typedef struct lxpr_rlimtab {
+	char	*rlim_name;	/* limit name */
+	char	*rlim_unit;	/* limit unit */
+	char	*rlim_rctl;	/* rctl source */
+} lxpr_rlimtab_t;
+
+static lxpr_rlimtab_t lxpr_rlimtab[] = {
+	{ "Max cpu time",	"seconds",	"process.max-cpu-time" },
+	{ "Max file size",	"bytes",	"process.max-file-size" },
+	{ "Max data size",	"bytes",	"process.max-data-size" },
+	{ "Max stack size",	"bytes",	"process.max-stack-size" },
+	{ "Max core file size",	"bytes",	"process.max-core-size" },
+	{ "Max resident set",	"bytes",	"zone.max-physical-memory" },
+	{ "Max processes",	"processes",	"zone.max-lwps" },
+	{ "Max open files",	"files",	"process.max-file-descriptor" },
+	{ "Max locked memory",	"bytes",	"zone.max-locked-memory" },
+	{ "Max address space",	"bytes",	"process.max-address-space" },
+	{ "Max file locks",	"locks",	NULL },
+	{ "Max pending signals",	"signals",
+		"process.max-sigqueue-size" },
+	{ "Max msgqueue size",	"bytes",	"process.max-msg-messages" }
+};
+
+#define	LX_RLIM_TAB_LEN	(sizeof (lxpr_rlimtab) / sizeof (lxpr_rlimtab[0]))
+
+
+/*
+ * contents of lx /proc/net directory
+ */
+static lxpr_dirent_t netdir[] = {
+	{ LXPR_NET_ARP,		"arp" },
+	{ LXPR_NET_DEV,		"dev" },
+	{ LXPR_NET_DEV_MCAST,	"dev_mcast" },
+	{ LXPR_NET_IF_INET6,	"if_inet6" },
+	{ LXPR_NET_IGMP,	"igmp" },
+	{ LXPR_NET_IP_MR_CACHE,	"ip_mr_cache" },
+	{ LXPR_NET_IP_MR_VIF,	"ip_mr_vif" },
+	{ LXPR_NET_IPV6_ROUTE,	"ipv6_route" },
+	{ LXPR_NET_MCFILTER,	"mcfilter" },
+	{ LXPR_NET_NETSTAT,	"netstat" },
+	{ LXPR_NET_RAW,		"raw" },
+	{ LXPR_NET_ROUTE,	"route" },
+	{ LXPR_NET_RPC,		"rpc" },
+	{ LXPR_NET_RT_CACHE,	"rt_cache" },
+	{ LXPR_NET_SOCKSTAT,	"sockstat" },
+	{ LXPR_NET_SNMP,	"snmp" },
+	{ LXPR_NET_STAT,	"stat" },
+	{ LXPR_NET_TCP,		"tcp" },
+	{ LXPR_NET_TCP6,	"tcp6" },
+	{ LXPR_NET_UDP,		"udp" },
+	{ LXPR_NET_UDP6,	"udp6" },
+	{ LXPR_NET_UNIX,	"unix" }
+};
+
+#define	NETDIRFILES	(sizeof (netdir) / sizeof (netdir[0]))
+
+/*
+ * contents of /proc/sys directory
+ */
+static lxpr_dirent_t sysdir[] = {
+	{ LXPR_SYS_FSDIR,	"fs" },
+	{ LXPR_SYS_KERNELDIR,	"kernel" },
+	{ LXPR_SYS_NETDIR,	"net" },
+	{ LXPR_SYS_VMDIR,	"vm" },
+};
+
+#define	SYSDIRFILES	(sizeof (sysdir) / sizeof (sysdir[0]))
+
+/*
+ * contents of /proc/sys/fs directory
+ */
+static lxpr_dirent_t sys_fsdir[] = {
+	{ LXPR_SYS_FS_INOTIFYDIR,	"inotify" },
+};
+
+#define	SYS_FSDIRFILES (sizeof (sys_fsdir) / sizeof (sys_fsdir[0]))
+
+/*
+ * contents of /proc/sys/fs/inotify directory
+ */
+static lxpr_dirent_t sys_fs_inotifydir[] = {
+	{ LXPR_SYS_FS_INOTIFY_MAX_QUEUED_EVENTS,	"max_queued_events" },
+	{ LXPR_SYS_FS_INOTIFY_MAX_USER_INSTANCES,	"max_user_instances" },
+	{ LXPR_SYS_FS_INOTIFY_MAX_USER_WATCHES,		"max_user_watches" },
+};
+
+#define	SYS_FS_INOTIFYDIRFILES \
+	(sizeof (sys_fs_inotifydir) / sizeof (sys_fs_inotifydir[0]))
+
+/*
+ * contents of /proc/sys/kernel directory
+ */
+static lxpr_dirent_t sys_kerneldir[] = {
+	{ LXPR_SYS_KERNEL_CAPLCAP,	"cap_last_cap" },
+	{ LXPR_SYS_KERNEL_COREPATT,	"core_pattern" },
+	{ LXPR_SYS_KERNEL_HOSTNAME,	"hostname" },
+	{ LXPR_SYS_KERNEL_MSGMNI,	"msgmni" },
+	{ LXPR_SYS_KERNEL_NGROUPS_MAX,	"ngroups_max" },
+	{ LXPR_SYS_KERNEL_OSREL,	"osrelease" },
+	{ LXPR_SYS_KERNEL_PID_MAX,	"pid_max" },
+	{ LXPR_SYS_KERNEL_RANDDIR,	"random" },
+	{ LXPR_SYS_KERNEL_SEM,		"sem" },
+	{ LXPR_SYS_KERNEL_SHMALL,	"shmall" },
+	{ LXPR_SYS_KERNEL_SHMMAX,	"shmmax" },
+	{ LXPR_SYS_KERNEL_SHMMNI,	"shmmni" },
+	{ LXPR_SYS_KERNEL_THREADS_MAX,	"threads-max" },
+};
+
+#define	SYS_KERNELDIRFILES (sizeof (sys_kerneldir) / sizeof (sys_kerneldir[0]))
+
+/*
+ * contents of /proc/sys/kernel/random directory
+ */
+static lxpr_dirent_t sys_randdir[] = {
+	{ LXPR_SYS_KERNEL_RAND_BOOTID,	"boot_id" },
+};
+
+#define	SYS_RANDDIRFILES (sizeof (sys_randdir) / sizeof (sys_randdir[0]))
+
+/*
+ * contents of /proc/sys/net directory
+ */
+static lxpr_dirent_t sys_netdir[] = {
+	{ LXPR_SYS_NET_COREDIR,		"core" },
+	{ LXPR_SYS_NET_IPV4DIR,		"ipv4" },
+};
+
+#define	SYS_NETDIRFILES (sizeof (sys_netdir) / sizeof (sys_netdir[0]))
+
+/*
+ * contents of /proc/sys/net/core directory
+ */
+static lxpr_dirent_t sys_net_coredir[] = {
+	{ LXPR_SYS_NET_CORE_SOMAXCON,	"somaxconn" },
+};
+
+#define	SYS_NET_COREDIRFILES \
+	(sizeof (sys_net_coredir) / sizeof (sys_net_coredir[0]))
+
+/*
+ * contents of /proc/sys/net/ipv4 directory
+ * See the Linux ip(7) & tcp(7) man pages for descriptions and the illumos
+ * ip(7p) & tcp(7p) man pages for the native descriptions.
+ */
+static lxpr_dirent_t sys_net_ipv4dir[] = {
+	{ LXPR_SYS_NET_IPV4_IP_LPORT_RANGE, "ip_local_port_range" },
+	{ LXPR_SYS_NET_IPV4_TCP_FIN_TO,	"tcp_fin_timeout" },
+	{ LXPR_SYS_NET_IPV4_TCP_KA_INT,	"tcp_keepalive_intvl" },
+	{ LXPR_SYS_NET_IPV4_TCP_KA_TIM,	"tcp_keepalive_time" },
+	{ LXPR_SYS_NET_IPV4_TCP_SACK,	"tcp_sack" },
+	{ LXPR_SYS_NET_IPV4_TCP_WINSCALE, "tcp_window_scaling" },
+};
+
+#define	SYS_NET_IPV4DIRFILES \
+	(sizeof (sys_net_ipv4dir) / sizeof (sys_net_ipv4dir[0]))
+
+/*
+ * contents of /proc/sys/vm directory
+ */
+static lxpr_dirent_t sys_vmdir[] = {
+	{ LXPR_SYS_VM_MAX_MAP_CNT,	"max_map_count" },
+	{ LXPR_SYS_VM_MINFR_KB,		"min_free_kbytes" },
+	{ LXPR_SYS_VM_NHUGEP,		"nr_hugepages" },
+	{ LXPR_SYS_VM_OVERCOMMIT_MEM,	"overcommit_memory" },
+	{ LXPR_SYS_VM_SWAPPINESS,	"swappiness" },
+};
+
+#define	SYS_VMDIRFILES (sizeof (sys_vmdir) / sizeof (sys_vmdir[0]))
+
+/*
+ * Table for standard writable files. Non-standard writable files not in this
+ * table can be handled explicitly as special cases.
+ * This table drives lxpr_is_writable, lxpr_write, and lxpr_create.
+ * Note that the entries LXPR_PID_FD_FD and LXPR_PID_TID_FD_FD exist in the
+ * table both to verify writability and to satisfy opening with O_CREATE.
+ */
+typedef struct wftab {
+	lxpr_nodetype_t	wft_type;	/* file entry type */
+	int		(*wft_wrf)(lxpr_node_t *, struct uio *, cred_t *,
+			    caller_context_t *); /* write function */
+} wftab_t;
+
+static wftab_t wr_tab[] = {
+	{LXPR_PID_FD_FD, NULL},
+	{LXPR_PID_LOGINUID, lxpr_write_pid_loginuid},
+	{LXPR_PID_OOM_SCR_ADJ, NULL},
+	{LXPR_PID_TID_FD_FD, NULL},
+	{LXPR_PID_TID_OOM_SCR_ADJ, NULL},
+	{LXPR_SYS_KERNEL_COREPATT, lxpr_write_sys_kernel_corepatt},
+	{LXPR_SYS_KERNEL_SHMALL, NULL},
+	{LXPR_SYS_KERNEL_SHMMAX, NULL},
+	{LXPR_SYS_NET_CORE_SOMAXCON, lxpr_write_sys_net_core_somaxc},
+	{LXPR_SYS_NET_IPV4_IP_LPORT_RANGE,
+	    lxpr_write_sys_net_ipv4_ip_lport_range},
+	{LXPR_SYS_NET_IPV4_TCP_FIN_TO, lxpr_write_sys_net_ipv4_tcp_fin_to},
+	{LXPR_SYS_NET_IPV4_TCP_KA_INT, lxpr_write_sys_net_ipv4_tcp_ka_int},
+	{LXPR_SYS_NET_IPV4_TCP_KA_TIM, lxpr_write_sys_net_ipv4_tcp_ka_tim},
+	{LXPR_SYS_NET_IPV4_TCP_SACK, lxpr_write_sys_net_ipv4_tcp_sack},
+	{LXPR_SYS_NET_IPV4_TCP_WINSCALE, lxpr_write_sys_net_ipv4_tcp_winscale},
+	{LXPR_SYS_VM_OVERCOMMIT_MEM, NULL},
+	{LXPR_SYS_VM_SWAPPINESS, NULL},
+	{LXPR_INVALID, NULL}
+};
+
+/*
+ * Centralized test for the standard writable proc files. Other non-standard
+ * writable files might be handled separately.
+ */
+boolean_t
+lxpr_is_writable(lxpr_nodetype_t type)
+{
+	int i;
+
+	for (i = 0; wr_tab[i].wft_type != LXPR_INVALID; i++) {
+		if (wr_tab[i].wft_type == type)
+			return (B_TRUE);
+	}
+	return (B_FALSE);
+}
+
+/*
+ * lxpr_open(): Vnode operation for VOP_OPEN()
+ */
+static int
+lxpr_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
+{
+	vnode_t		*vp = *vpp;
+	lxpr_node_t	*lxpnp = VTOLXP(vp);
+	lxpr_nodetype_t	type = lxpnp->lxpr_type;
+	vnode_t		*rvp;
+	int		error = 0;
+
+	/* Restrict writes to certain files */
+	if ((flag & FWRITE) && !lxpr_is_writable(type)) {
+		return (EPERM);
+	}
+
+	/*
+	 * If we are opening an underlying file only allow regular files,
+	 * fifos or sockets; reject the open for anything else.
+	 * Just do it if we are opening the current or root directory.
+	 */
+	if (lxpnp->lxpr_realvp != NULL) {
+		rvp = lxpnp->lxpr_realvp;
+
+		if (type == LXPR_PID_FD_FD && rvp->v_type != VREG &&
+		    rvp->v_type != VFIFO && rvp->v_type != VSOCK) {
+			error = EACCES;
+		} else {
+			if (type == LXPR_PID_FD_FD && rvp->v_type == VFIFO) {
+				/*
+				 * This flag lets the fifo open know that
+				 * we're using proc/fd to open a fd which we
+				 * already have open. Otherwise, the fifo might
+				 * reject an open if the other end has closed.
+				 */
+				flag |= FKLYR;
+			}
+			/*
+			 * Need to hold rvp since VOP_OPEN() may release it.
+			 */
+			VN_HOLD(rvp);
+			error = VOP_OPEN(&rvp, flag, cr, ct);
+			if (error) {
+				VN_RELE(rvp);
+			} else {
+				*vpp = rvp;
+				VN_RELE(vp);
+			}
+		}
+	}
+
+	return (error);
+}
+
+
+/*
+ * lxpr_close(): Vnode operation for VOP_CLOSE()
+ */
+/* ARGSUSED */
+static int
+lxpr_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr,
+    caller_context_t *ct)
+{
+#ifdef DEBUG
+	lxpr_node_t	*lxpr = VTOLXP(vp);
+	lxpr_nodetype_t	type = lxpr->lxpr_type;
+
+	/*
+	 * we should never get here because the close is done on the realvp
+	 * for these nodes
+	 */
+	ASSERT(type != LXPR_PID_FD_FD &&
+	    type != LXPR_PID_CURDIR &&
+	    type != LXPR_PID_ROOTDIR &&
+	    type != LXPR_PID_EXE);
+#endif /* DEBUG */
+
+	return (0);
+}
+
+static void (*lxpr_read_function[LXPR_NFILES])() = {
+	NULL,				/* invalid		*/
+	lxpr_read_isdir,		/* /proc		*/
+	lxpr_read_isdir,		/* /proc/<pid>		*/
+	lxpr_read_pid_auxv,		/* /proc/<pid>/auxv	*/
+	lxpr_read_pid_cgroup,		/* /proc/<pid>/cgroup	*/
+	lxpr_read_pid_cmdline,		/* /proc/<pid>/cmdline	*/
+	lxpr_read_pid_comm,		/* /proc/<pid>/comm	*/
+	lxpr_read_empty,		/* /proc/<pid>/cpu	*/
+	lxpr_read_invalid,		/* /proc/<pid>/cwd	*/
+	lxpr_read_pid_env,		/* /proc/<pid>/environ	*/
+	lxpr_read_invalid,		/* /proc/<pid>/exe	*/
+	lxpr_read_pid_limits,		/* /proc/<pid>/limits	*/
+	lxpr_read_pid_loginuid,		/* /proc/<pid>/loginuid	*/
+	lxpr_read_pid_maps,		/* /proc/<pid>/maps	*/
+	lxpr_read_empty,		/* /proc/<pid>/mem	*/
+	lxpr_read_pid_mountinfo,	/* /proc/<pid>/mountinfo */
+	lxpr_read_mounts,		/* /proc/<pid>/mounts	*/
+	lxpr_read_pid_oom_scr_adj,	/* /proc/<pid>/oom_score_adj */
+	lxpr_read_pid_personality,	/* /proc/<pid>/personality */
+	lxpr_read_invalid,		/* /proc/<pid>/root	*/
+	lxpr_read_pid_stat,		/* /proc/<pid>/stat	*/
+	lxpr_read_pid_statm,		/* /proc/<pid>/statm	*/
+	lxpr_read_pid_status,		/* /proc/<pid>/status	*/
+	lxpr_read_isdir,		/* /proc/<pid>/task	*/
+	lxpr_read_isdir,		/* /proc/<pid>/task/nn	*/
+	lxpr_read_isdir,		/* /proc/<pid>/fd	*/
+	lxpr_read_fd,			/* /proc/<pid>/fd/nn	*/
+	lxpr_read_pid_auxv,		/* /proc/<pid>/task/<tid>/auxv	*/
+	lxpr_read_pid_cgroup,		/* /proc/<pid>/task/<tid>/cgroup */
+	lxpr_read_pid_cmdline,		/* /proc/<pid>/task/<tid>/cmdline */
+	lxpr_read_pid_comm,		/* /proc/<pid>/task/<tid>/comm	*/
+	lxpr_read_empty,		/* /proc/<pid>/task/<tid>/cpu	*/
+	lxpr_read_invalid,		/* /proc/<pid>/task/<tid>/cwd	*/
+	lxpr_read_pid_env,		/* /proc/<pid>/task/<tid>/environ */
+	lxpr_read_invalid,		/* /proc/<pid>/task/<tid>/exe	*/
+	lxpr_read_pid_limits,		/* /proc/<pid>/task/<tid>/limits */
+	lxpr_read_pid_loginuid,		/* /proc/<pid>/task/<tid>/loginuid */
+	lxpr_read_pid_maps,		/* /proc/<pid>/task/<tid>/maps	*/
+	lxpr_read_empty,		/* /proc/<pid>/task/<tid>/mem	*/
+	lxpr_read_pid_mountinfo,	/* /proc/<pid>/task/<tid>/mountinfo */
+	lxpr_read_pid_oom_scr_adj,	/* /proc/<pid>/task/<tid>/oom_scr_adj */
+	lxpr_read_pid_personality,	/* /proc/<pid>/task/<tid>/personality */
+	lxpr_read_invalid,		/* /proc/<pid>/task/<tid>/root	*/
+	lxpr_read_pid_tid_stat,		/* /proc/<pid>/task/<tid>/stat	*/
+	lxpr_read_pid_statm,		/* /proc/<pid>/task/<tid>/statm	*/
+	lxpr_read_pid_tid_status,	/* /proc/<pid>/task/<tid>/status */
+	lxpr_read_isdir,		/* /proc/<pid>/task/<tid>/fd	*/
+	lxpr_read_fd,			/* /proc/<pid>/task/<tid>/fd/nn	*/
+	lxpr_read_cgroups,		/* /proc/cgroups	*/
+	lxpr_read_cmdline,		/* /proc/cmdline	*/
+	lxpr_read_cpuinfo,		/* /proc/cpuinfo	*/
+	lxpr_read_devices,		/* /proc/devices	*/
+	lxpr_read_diskstats,		/* /proc/diskstats	*/
+	lxpr_read_empty,		/* /proc/dma		*/
+	lxpr_read_filesystems,		/* /proc/filesystems	*/
+	lxpr_read_empty,		/* /proc/interrupts	*/
+	lxpr_read_empty,		/* /proc/ioports	*/
+	lxpr_read_empty,		/* /proc/kcore		*/
+	lxpr_read_invalid,		/* /proc/kmsg -- see lxpr_read() */
+	lxpr_read_loadavg,		/* /proc/loadavg	*/
+	lxpr_read_meminfo,		/* /proc/meminfo	*/
+	lxpr_read_empty,		/* /proc/modules	*/
+	lxpr_read_mounts,		/* /proc/mounts		*/
+	lxpr_read_isdir,		/* /proc/net		*/
+	lxpr_read_net_arp,		/* /proc/net/arp	*/
+	lxpr_read_net_dev,		/* /proc/net/dev	*/
+	lxpr_read_net_dev_mcast,	/* /proc/net/dev_mcast	*/
+	lxpr_read_net_if_inet6,		/* /proc/net/if_inet6	*/
+	lxpr_read_net_igmp,		/* /proc/net/igmp	*/
+	lxpr_read_net_ip_mr_cache,	/* /proc/net/ip_mr_cache */
+	lxpr_read_net_ip_mr_vif,	/* /proc/net/ip_mr_vif	*/
+	lxpr_read_net_ipv6_route,	/* /proc/net/ipv6_route	*/
+	lxpr_read_net_mcfilter,		/* /proc/net/mcfilter	*/
+	lxpr_read_net_netstat,		/* /proc/net/netstat	*/
+	lxpr_read_net_raw,		/* /proc/net/raw	*/
+	lxpr_read_net_route,		/* /proc/net/route	*/
+	lxpr_read_net_rpc,		/* /proc/net/rpc	*/
+	lxpr_read_net_rt_cache,		/* /proc/net/rt_cache	*/
+	lxpr_read_net_sockstat,		/* /proc/net/sockstat	*/
+	lxpr_read_net_snmp,		/* /proc/net/snmp	*/
+	lxpr_read_net_stat,		/* /proc/net/stat	*/
+	lxpr_read_net_tcp,		/* /proc/net/tcp	*/
+	lxpr_read_net_tcp6,		/* /proc/net/tcp6	*/
+	lxpr_read_net_udp,		/* /proc/net/udp	*/
+	lxpr_read_net_udp6,		/* /proc/net/udp6	*/
+	lxpr_read_net_unix,		/* /proc/net/unix	*/
+	lxpr_read_partitions,		/* /proc/partitions	*/
+	lxpr_read_invalid,		/* /proc/self		*/
+	lxpr_read_stat,			/* /proc/stat		*/
+	lxpr_read_swaps,		/* /proc/swaps		*/
+	lxpr_read_invalid,		/* /proc/sys		*/
+	lxpr_read_invalid,		/* /proc/sys/fs		*/
+	lxpr_read_invalid,		/* /proc/sys/fs/inotify	*/
+	lxpr_read_sys_fs_inotify_max_queued_events, /* max_queued_events */
+	lxpr_read_sys_fs_inotify_max_user_instances, /* max_user_instances */
+	lxpr_read_sys_fs_inotify_max_user_watches, /* max_user_watches */
+	lxpr_read_invalid,		/* /proc/sys/kernel	*/
+	lxpr_read_sys_kernel_caplcap,	/* /proc/sys/kernel/cap_last_cap */
+	lxpr_read_sys_kernel_corepatt,	/* /proc/sys/kernel/core_pattern */
+	lxpr_read_sys_kernel_hostname,	/* /proc/sys/kernel/hostname */
+	lxpr_read_sys_kernel_msgmni,	/* /proc/sys/kernel/msgmni */
+	lxpr_read_sys_kernel_ngroups_max, /* /proc/sys/kernel/ngroups_max */
+	lxpr_read_sys_kernel_osrel,	/* /proc/sys/kernel/osrelease */
+	lxpr_read_sys_kernel_pid_max,	/* /proc/sys/kernel/pid_max */
+	lxpr_read_invalid,		/* /proc/sys/kernel/random */
+	lxpr_read_sys_kernel_rand_bootid, /* /proc/sys/kernel/random/boot_id */
+	lxpr_read_sys_kernel_sem,	/* /proc/sys/kernel/sem */
+	lxpr_read_sys_kernel_shmall,	/* /proc/sys/kernel/shmall */
+	lxpr_read_sys_kernel_shmmax,	/* /proc/sys/kernel/shmmax */
+	lxpr_read_sys_kernel_shmmni,	/* /proc/sys/kernel/shmmni */
+	lxpr_read_sys_kernel_threads_max, /* /proc/sys/kernel/threads-max */
+	lxpr_read_invalid,		/* /proc/sys/net	*/
+	lxpr_read_invalid,		/* /proc/sys/net/core	*/
+	lxpr_read_sys_net_core_somaxc,	/* /proc/sys/net/core/somaxconn	*/
+	lxpr_read_invalid,		/* /proc/sys/net/ipv4	*/
+	lxpr_read_sys_net_ipv4_ip_lport_range, /* ../ipv4/ip_local_port_range */
+	lxpr_read_sys_net_ipv4_tcp_fin_to, /* .../ipv4/tcp_fin_timeout */
+	lxpr_read_sys_net_ipv4_tcp_ka_int, /* .../ipv4/tcp_keepalive_intvl */
+	lxpr_read_sys_net_ipv4_tcp_ka_tim, /* .../ipv4/tcp_keepalive_time */
+	lxpr_read_sys_net_ipv4_tcp_sack, /* .../ipv4/tcp_sack */
+	lxpr_read_sys_net_ipv4_tcp_winscale, /* .../ipv4/tcp_window_scaling */
+	lxpr_read_invalid,		/* /proc/sys/vm	*/
+	lxpr_read_sys_vm_max_map_cnt,	/* /proc/sys/vm/max_map_count */
+	lxpr_read_sys_vm_minfr_kb,	/* /proc/sys/vm/min_free_kbytes */
+	lxpr_read_sys_vm_nhpages,	/* /proc/sys/vm/nr_hugepages */
+	lxpr_read_sys_vm_overcommit_mem, /* /proc/sys/vm/overcommit_memory */
+	lxpr_read_sys_vm_swappiness,	/* /proc/sys/vm/swappiness */
+	lxpr_read_uptime,		/* /proc/uptime		*/
+	lxpr_read_version,		/* /proc/version	*/
+	lxpr_read_vmstat,		/* /proc/vmstat		*/
+};
+
+/*
+ * Array of lookup functions, indexed by lx /proc file type.
+ */
+static vnode_t *(*lxpr_lookup_function[LXPR_NFILES])() = {
+	NULL,				/* invalid		*/
+	lxpr_lookup_procdir,		/* /proc		*/
+	lxpr_lookup_piddir,		/* /proc/<pid>		*/
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/auxv	*/
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/cgroup	*/
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/cmdline	*/
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/comm	*/
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/cpu	*/
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/cwd	*/
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/environ	*/
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/exe	*/
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/limits	*/
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/loginuid	*/
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/maps	*/
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/mem	*/
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/mountinfo */
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/mounts	*/
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/oom_score_adj */
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/personality */
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/root	*/
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/stat	*/
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/statm	*/
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/status	*/
+	lxpr_lookup_taskdir,		/* /proc/<pid>/task	*/
+	lxpr_lookup_task_tid_dir,	/* /proc/<pid>/task/nn	*/
+	lxpr_lookup_fddir,		/* /proc/<pid>/fd	*/
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/fd/nn	*/
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/task/<tid>/auxv	*/
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/task/<tid>/cgroup */
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/task/<tid>/cmdline */
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/task/<tid>/comm	*/
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/task/<tid>/cpu	*/
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/task/<tid>/cwd	*/
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/task/<tid>/environ */
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/task/<tid>/exe	*/
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/task/<tid>/limits */
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/task/<tid>/loginuid */
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/task/<tid>/maps	*/
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/task/<tid>/mem	*/
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/task/<tid>/mountinfo */
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/task/<tid>/oom_scr_adj */
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/task/<tid>/personality */
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/task/<tid>/root	*/
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/task/<tid>/stat	*/
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/task/<tid>/statm	*/
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/task/<tid>/status */
+	lxpr_lookup_fddir,		/* /proc/<pid>/task/<tid>/fd	*/
+	lxpr_lookup_not_a_dir,		/* /proc/<pid>/task/<tid>/fd/nn	*/
+	lxpr_lookup_not_a_dir,		/* /proc/cgroups	*/
+	lxpr_lookup_not_a_dir,		/* /proc/cmdline	*/
+	lxpr_lookup_not_a_dir,		/* /proc/cpuinfo	*/
+	lxpr_lookup_not_a_dir,		/* /proc/devices	*/
+	lxpr_lookup_not_a_dir,		/* /proc/diskstats	*/
+	lxpr_lookup_not_a_dir,		/* /proc/dma		*/
+	lxpr_lookup_not_a_dir,		/* /proc/filesystems	*/
+	lxpr_lookup_not_a_dir,		/* /proc/interrupts	*/
+	lxpr_lookup_not_a_dir,		/* /proc/ioports	*/
+	lxpr_lookup_not_a_dir,		/* /proc/kcore		*/
+	lxpr_lookup_not_a_dir,		/* /proc/kmsg		*/
+	lxpr_lookup_not_a_dir,		/* /proc/loadavg	*/
+	lxpr_lookup_not_a_dir,		/* /proc/meminfo	*/
+	lxpr_lookup_not_a_dir,		/* /proc/modules	*/
+	lxpr_lookup_not_a_dir,		/* /proc/mounts		*/
+	lxpr_lookup_netdir,		/* /proc/net		*/
+	lxpr_lookup_not_a_dir,		/* /proc/net/arp	*/
+	lxpr_lookup_not_a_dir,		/* /proc/net/dev	*/
+	lxpr_lookup_not_a_dir,		/* /proc/net/dev_mcast	*/
+	lxpr_lookup_not_a_dir,		/* /proc/net/if_inet6	*/
+	lxpr_lookup_not_a_dir,		/* /proc/net/igmp	*/
+	lxpr_lookup_not_a_dir,		/* /proc/net/ip_mr_cache */
+	lxpr_lookup_not_a_dir,		/* /proc/net/ip_mr_vif	*/
+	lxpr_lookup_not_a_dir,		/* /proc/net/ipv6_route	*/
+	lxpr_lookup_not_a_dir,		/* /proc/net/mcfilter	*/
+	lxpr_lookup_not_a_dir,		/* /proc/net/netstat	*/
+	lxpr_lookup_not_a_dir,		/* /proc/net/raw	*/
+	lxpr_lookup_not_a_dir,		/* /proc/net/route	*/
+	lxpr_lookup_not_a_dir,		/* /proc/net/rpc	*/
+	lxpr_lookup_not_a_dir,		/* /proc/net/rt_cache	*/
+	lxpr_lookup_not_a_dir,		/* /proc/net/sockstat	*/
+	lxpr_lookup_not_a_dir,		/* /proc/net/snmp	*/
+	lxpr_lookup_not_a_dir,		/* /proc/net/stat	*/
+	lxpr_lookup_not_a_dir,		/* /proc/net/tcp	*/
+	lxpr_lookup_not_a_dir,		/* /proc/net/tcp6	*/
+	lxpr_lookup_not_a_dir,		/* /proc/net/udp	*/
+	lxpr_lookup_not_a_dir,		/* /proc/net/udp6	*/
+	lxpr_lookup_not_a_dir,		/* /proc/net/unix	*/
+	lxpr_lookup_not_a_dir,		/* /proc/partitions	*/
+	lxpr_lookup_not_a_dir,		/* /proc/self		*/
+	lxpr_lookup_not_a_dir,		/* /proc/stat		*/
+	lxpr_lookup_not_a_dir,		/* /proc/swaps		*/
+	lxpr_lookup_sysdir,		/* /proc/sys		*/
+	lxpr_lookup_sys_fsdir,		/* /proc/sys/fs		*/
+	lxpr_lookup_sys_fs_inotifydir,	/* /proc/sys/fs/inotify	*/
+	lxpr_lookup_not_a_dir,		/* .../inotify/max_queued_events */
+	lxpr_lookup_not_a_dir,		/* .../inotify/max_user_instances */
+	lxpr_lookup_not_a_dir,		/* .../inotify/max_user_watches */
+	lxpr_lookup_sys_kerneldir,	/* /proc/sys/kernel	*/
+	lxpr_lookup_not_a_dir,		/* /proc/sys/kernel/cap_last_cap */
+	lxpr_lookup_not_a_dir,		/* /proc/sys/kernel/core_pattern */
+	lxpr_lookup_not_a_dir,		/* /proc/sys/kernel/hostname */
+	lxpr_lookup_not_a_dir,		/* /proc/sys/kernel/msgmni */
+	lxpr_lookup_not_a_dir,		/* /proc/sys/kernel/ngroups_max */
+	lxpr_lookup_not_a_dir,		/* /proc/sys/kernel/osrelease */
+	lxpr_lookup_not_a_dir,		/* /proc/sys/kernel/pid_max */
+	lxpr_lookup_sys_kdir_randdir,	/* /proc/sys/kernel/random */
+	lxpr_lookup_not_a_dir,		/* /proc/sys/kernel/random/boot_id */
+	lxpr_lookup_not_a_dir,		/* /proc/sys/kernel/sem */
+	lxpr_lookup_not_a_dir,		/* /proc/sys/kernel/shmall */
+	lxpr_lookup_not_a_dir,		/* /proc/sys/kernel/shmmax */
+	lxpr_lookup_not_a_dir,		/* /proc/sys/kernel/shmmni */
+	lxpr_lookup_not_a_dir,		/* /proc/sys/kernel/threads-max */
+	lxpr_lookup_sys_netdir,		/* /proc/sys/net */
+	lxpr_lookup_sys_net_coredir,	/* /proc/sys/net/core */
+	lxpr_lookup_not_a_dir,		/* /proc/sys/net/core/somaxconn */
+	lxpr_lookup_sys_net_ipv4dir,	/* /proc/sys/net/ipv4 */
+	lxpr_lookup_not_a_dir,		/* .../net/ipv4/ip_local_port_range */
+	lxpr_lookup_not_a_dir,		/* .../net/ipv4/tcp_fin_timeout */
+	lxpr_lookup_not_a_dir,		/* .../net/ipv4/tcp_keepalive_intvl */
+	lxpr_lookup_not_a_dir,		/* .../net/ipv4/tcp_keepalive_time */
+	lxpr_lookup_not_a_dir,		/* .../net/ipv4/tcp_sack */
+	lxpr_lookup_not_a_dir,		/* .../net/ipv4/tcp_window_scaling */
+	lxpr_lookup_sys_vmdir,		/* /proc/sys/vm */
+	lxpr_lookup_not_a_dir,		/* /proc/sys/vm/max_map_count */
+	lxpr_lookup_not_a_dir,		/* /proc/sys/vm/min_free_kbytes */
+	lxpr_lookup_not_a_dir,		/* /proc/sys/vm/nr_hugepages */
+	lxpr_lookup_not_a_dir,		/* /proc/sys/vm/overcommit_memory */
+	lxpr_lookup_not_a_dir,		/* /proc/sys/vm/swappiness */
+	lxpr_lookup_not_a_dir,		/* /proc/uptime		*/
+	lxpr_lookup_not_a_dir,		/* /proc/version	*/
+	lxpr_lookup_not_a_dir,		/* /proc/vmstat		*/
+};
+
+/*
+ * Array of readdir functions, indexed by /proc file type.
+ */
+static int (*lxpr_readdir_function[LXPR_NFILES])() = {
+	NULL,				/* invalid		*/
+	lxpr_readdir_procdir,		/* /proc		*/
+	lxpr_readdir_piddir,		/* /proc/<pid>		*/
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/auxv	*/
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/cgroup	*/
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/cmdline	*/
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/comm	*/
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/cpu	*/
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/cwd	*/
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/environ	*/
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/exe	*/
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/limits	*/
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/loginuid	*/
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/maps	*/
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/mem	*/
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/mountinfo */
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/mounts	*/
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/oom_score_adj */
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/personality */
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/root	*/
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/stat	*/
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/statm	*/
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/status	*/
+	lxpr_readdir_taskdir,		/* /proc/<pid>/task	*/
+	lxpr_readdir_task_tid_dir,	/* /proc/<pid>/task/nn	*/
+	lxpr_readdir_fddir,		/* /proc/<pid>/fd	*/
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/fd/nn	*/
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/task/<tid>/auxv	*/
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/task/<tid>/cgroup */
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/task/<tid>/cmdline */
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/task/<tid>/comm	*/
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/task/<tid>/cpu	*/
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/task/<tid>/cwd	*/
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/task/<tid>/environ */
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/task/<tid>/exe	*/
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/task/<tid>/limits */
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/task/<tid>/loginuid */
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/task/<tid>/maps	*/
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/task/<tid>/mem	*/
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/task/<tid>/mountinfo */
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/task/<tid/oom_scr_adj */
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/task/<tid/personality */
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/task/<tid>/root	*/
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/task/<tid>/stat	*/
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/task/<tid>/statm	*/
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/task/<tid>/status */
+	lxpr_readdir_fddir,		/* /proc/<pid>/task/<tid>/fd	*/
+	lxpr_readdir_not_a_dir,		/* /proc/<pid>/task/<tid>/fd/nn	*/
+	lxpr_readdir_not_a_dir,		/* /proc/cgroups	*/
+	lxpr_readdir_not_a_dir,		/* /proc/cmdline	*/
+	lxpr_readdir_not_a_dir,		/* /proc/cpuinfo	*/
+	lxpr_readdir_not_a_dir,		/* /proc/devices	*/
+	lxpr_readdir_not_a_dir,		/* /proc/diskstats	*/
+	lxpr_readdir_not_a_dir,		/* /proc/dma		*/
+	lxpr_readdir_not_a_dir,		/* /proc/filesystems	*/
+	lxpr_readdir_not_a_dir,		/* /proc/interrupts	*/
+	lxpr_readdir_not_a_dir,		/* /proc/ioports	*/
+	lxpr_readdir_not_a_dir,		/* /proc/kcore		*/
+	lxpr_readdir_not_a_dir,		/* /proc/kmsg		*/
+	lxpr_readdir_not_a_dir,		/* /proc/loadavg	*/
+	lxpr_readdir_not_a_dir,		/* /proc/meminfo	*/
+	lxpr_readdir_not_a_dir,		/* /proc/modules	*/
+	lxpr_readdir_not_a_dir,		/* /proc/mounts		*/
+	lxpr_readdir_netdir,		/* /proc/net		*/
+	lxpr_readdir_not_a_dir,		/* /proc/net/arp	*/
+	lxpr_readdir_not_a_dir,		/* /proc/net/dev	*/
+	lxpr_readdir_not_a_dir,		/* /proc/net/dev_mcast	*/
+	lxpr_readdir_not_a_dir,		/* /proc/net/if_inet6	*/
+	lxpr_readdir_not_a_dir,		/* /proc/net/igmp	*/
+	lxpr_readdir_not_a_dir,		/* /proc/net/ip_mr_cache */
+	lxpr_readdir_not_a_dir,		/* /proc/net/ip_mr_vif	*/
+	lxpr_readdir_not_a_dir,		/* /proc/net/ipv6_route	*/
+	lxpr_readdir_not_a_dir,		/* /proc/net/mcfilter	*/
+	lxpr_readdir_not_a_dir,		/* /proc/net/netstat	*/
+	lxpr_readdir_not_a_dir,		/* /proc/net/raw	*/
+	lxpr_readdir_not_a_dir,		/* /proc/net/route	*/
+	lxpr_readdir_not_a_dir,		/* /proc/net/rpc	*/
+	lxpr_readdir_not_a_dir,		/* /proc/net/rt_cache	*/
+	lxpr_readdir_not_a_dir,		/* /proc/net/sockstat	*/
+	lxpr_readdir_not_a_dir,		/* /proc/net/snmp	*/
+	lxpr_readdir_not_a_dir,		/* /proc/net/stat	*/
+	lxpr_readdir_not_a_dir,		/* /proc/net/tcp	*/
+	lxpr_readdir_not_a_dir,		/* /proc/net/tcp6	*/
+	lxpr_readdir_not_a_dir,		/* /proc/net/udp	*/
+	lxpr_readdir_not_a_dir,		/* /proc/net/udp6	*/
+	lxpr_readdir_not_a_dir,		/* /proc/net/unix	*/
+	lxpr_readdir_not_a_dir,		/* /proc/partitions	*/
+	lxpr_readdir_not_a_dir,		/* /proc/self		*/
+	lxpr_readdir_not_a_dir,		/* /proc/stat		*/
+	lxpr_readdir_not_a_dir,		/* /proc/swaps		*/
+	lxpr_readdir_sysdir,		/* /proc/sys		*/
+	lxpr_readdir_sys_fsdir,		/* /proc/sys/fs		*/
+	lxpr_readdir_sys_fs_inotifydir,	/* /proc/sys/fs/inotify	*/
+	lxpr_readdir_not_a_dir,		/* .../inotify/max_queued_events */
+	lxpr_readdir_not_a_dir,		/* .../inotify/max_user_instances */
+	lxpr_readdir_not_a_dir,		/* .../inotify/max_user_watches	*/
+	lxpr_readdir_sys_kerneldir,	/* /proc/sys/kernel	*/
+	lxpr_readdir_not_a_dir,		/* /proc/sys/kernel/cap_last_cap */
+	lxpr_readdir_not_a_dir,		/* /proc/sys/kernel/core_pattern */
+	lxpr_readdir_not_a_dir,		/* /proc/sys/kernel/hostname */
+	lxpr_readdir_not_a_dir,		/* /proc/sys/kernel/msgmni */
+	lxpr_readdir_not_a_dir,		/* /proc/sys/kernel/ngroups_max */
+	lxpr_readdir_not_a_dir,		/* /proc/sys/kernel/osrelease */
+	lxpr_readdir_not_a_dir,		/* /proc/sys/kernel/pid_max */
+	lxpr_readdir_sys_kdir_randdir,	/* /proc/sys/kernel/random */
+	lxpr_readdir_not_a_dir,		/* /proc/sys/kernel/random/boot_id */
+	lxpr_readdir_not_a_dir,		/* /proc/sys/kernel/sem */
+	lxpr_readdir_not_a_dir,		/* /proc/sys/kernel/shmall */
+	lxpr_readdir_not_a_dir,		/* /proc/sys/kernel/shmmax */
+	lxpr_readdir_not_a_dir,		/* /proc/sys/kernel/shmmni */
+	lxpr_readdir_not_a_dir,		/* /proc/sys/kernel/threads-max */
+	lxpr_readdir_sys_netdir,	/* /proc/sys/net */
+	lxpr_readdir_sys_net_coredir,	/* /proc/sys/net/core */
+	lxpr_readdir_not_a_dir,		/* /proc/sys/net/core/somaxconn */
+	lxpr_readdir_sys_net_ipv4dir,	/* /proc/sys/net/ipv4 */
+	lxpr_readdir_not_a_dir,		/* .../net/ipv4/ip_local_port_range */
+	lxpr_readdir_not_a_dir,		/* .../net/ipv4/tcp_fin_timeout */
+	lxpr_readdir_not_a_dir,		/* .../net/ipv4/tcp_keepalive_intvl */
+	lxpr_readdir_not_a_dir,		/* .../net/ipv4/tcp_keepalive_time */
+	lxpr_readdir_not_a_dir,		/* .../net/ipv4/tcp_sack */
+	lxpr_readdir_not_a_dir,		/* .../net/ipv4/tcp_window_scaling */
+	lxpr_readdir_sys_vmdir,		/* /proc/sys/vm */
+	lxpr_readdir_not_a_dir,		/* /proc/sys/vm/max_map_count */
+	lxpr_readdir_not_a_dir,		/* /proc/sys/vm/min_free_kbytes */
+	lxpr_readdir_not_a_dir,		/* /proc/sys/vm/nr_hugepages */
+	lxpr_readdir_not_a_dir,		/* /proc/sys/vm/overcommit_memory */
+	lxpr_readdir_not_a_dir,		/* /proc/sys/vm/swappiness */
+	lxpr_readdir_not_a_dir,		/* /proc/uptime		*/
+	lxpr_readdir_not_a_dir,		/* /proc/version	*/
+	lxpr_readdir_not_a_dir,		/* /proc/vmstat		*/
+};
+
+
+/*
+ * lxpr_read(): Vnode operation for VOP_READ()
+ *
+ * As the format of all the files that can be read in the lx procfs is human
+ * readable and not binary structures there do not have to be different
+ * read variants depending on whether the reading process model is 32 or 64 bits
+ * (at least in general, and certainly the difference is unlikely to be enough
+ * to justify have different routines for 32 and 64 bit reads
+ */
+/* ARGSUSED */
+static int
+lxpr_read(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
+    caller_context_t *ct)
+{
+	lxpr_node_t *lxpnp = VTOLXP(vp);
+	lxpr_nodetype_t type = lxpnp->lxpr_type;
+	lxpr_uiobuf_t *uiobuf = lxpr_uiobuf_new(uiop);
+	int error;
+
+	ASSERT(type < LXPR_NFILES);
+
+	if (type == LXPR_KMSG) {
+		ldi_ident_t	li = VTOLXPM(vp)->lxprm_li;
+		ldi_handle_t	ldih;
+		struct strioctl	str;
+		int		rv;
+
+		/*
+		 * Open the zone's console device using the layered driver
+		 * interface.
+		 */
+		if ((error =
+		    ldi_open_by_name("/dev/log", FREAD, cr, &ldih, li)) != 0)
+			return (error);
+
+		/*
+		 * Send an ioctl to the underlying console device, letting it
+		 * know we're interested in getting console messages.
+		 */
+		str.ic_cmd = I_CONSLOG;
+		str.ic_timout = 0;
+		str.ic_len = 0;
+		str.ic_dp = NULL;
+		if ((error = ldi_ioctl(ldih, I_STR,
+		    (intptr_t)&str, FKIOCTL, cr, &rv)) != 0)
+			return (error);
+
+		lxpr_read_kmsg(lxpnp, uiobuf, ldih);
+
+		if ((error = ldi_close(ldih, FREAD, cr)) != 0)
+			return (error);
+	} else {
+		lxpr_read_function[type](lxpnp, uiobuf);
+	}
+
+	error = lxpr_uiobuf_flush(uiobuf);
+	lxpr_uiobuf_free(uiobuf);
+
+	return (error);
+}
+
+/*
+ * lxpr_read_invalid(), lxpr_read_isdir(), lxpr_read_empty()
+ *
+ * Various special case reads:
+ * - trying to read a directory
+ * - invalid file (used to mean a file that should be implemented,
+ *   but isn't yet)
+ * - empty file
+ * - wait to be able to read a file that will never have anything to read
+ */
+/* ARGSUSED */
+static void
+lxpr_read_isdir(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	lxpr_uiobuf_seterr(uiobuf, EISDIR);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_invalid(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	lxpr_uiobuf_seterr(uiobuf, EINVAL);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_empty(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/*
+ * lxpr_read_pid_auxv(): read process aux vector
+ */
+static void
+lxpr_read_pid_auxv(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	proc_t *p;
+	lx_proc_data_t *pd;
+	lx_elf_data_t *edp = NULL;
+	int i, cnt;
+
+	ASSERT(lxpnp->lxpr_type == LXPR_PID_AUXV ||
+	    lxpnp->lxpr_type == LXPR_PID_TID_AUXV);
+
+	p = lxpr_lock(lxpnp->lxpr_pid, NO_ZOMB);
+
+	if (p == NULL) {
+		return;
+	}
+	if ((pd = ptolxproc(p)) == NULL) {
+		/* Emit a single AT_NULL record for non-branded processes */
+		auxv_t buf;
+
+		bzero(&buf, sizeof (buf));
+		lxpr_unlock(p);
+		lxpr_uiobuf_write(uiobuf, (char *)&buf, sizeof (buf));
+		return;
+	} else {
+		edp = &pd->l_elf_data;
+	}
+
+	if (p->p_model == DATAMODEL_NATIVE) {
+		auxv_t buf[__KERN_NAUXV_IMPL];
+
+		/*
+		 * Because a_type is only of size int (not long), the buffer
+		 * contents must be zeroed first to ensure cleanliness.
+		 */
+		bzero(buf, sizeof (buf));
+		for (i = 0, cnt = 0; i < __KERN_NAUXV_IMPL; i++) {
+			if (lx_auxv_stol(&p->p_user.u_auxv[i],
+			    &buf[cnt], edp) == 0) {
+				cnt++;
+			}
+			if (p->p_user.u_auxv[i].a_type == AT_NULL) {
+				break;
+			}
+		}
+		lxpr_unlock(p);
+		lxpr_uiobuf_write(uiobuf, (char *)buf, cnt * sizeof (buf[0]));
+	}
+#if defined(_SYSCALL32_IMPL)
+	else {
+		auxv32_t buf[__KERN_NAUXV_IMPL];
+
+		for (i = 0, cnt = 0; i < __KERN_NAUXV_IMPL; i++) {
+			auxv_t temp;
+
+			if (lx_auxv_stol(&p->p_user.u_auxv[i],
+			    &temp, edp) == 0) {
+				buf[cnt].a_type = (int)temp.a_type;
+				buf[cnt].a_un.a_val = (int)temp.a_un.a_val;
+				cnt++;
+			}
+			if (p->p_user.u_auxv[i].a_type == AT_NULL) {
+				break;
+			}
+		}
+		lxpr_unlock(p);
+		lxpr_uiobuf_write(uiobuf, (char *)buf, cnt * sizeof (buf[0]));
+	}
+#endif /* defined(_SYSCALL32_IMPL) */
+}
+
+/*
+ * lxpr_read_pid_cgroup(): read cgroups for process
+ */
+static void
+lxpr_read_pid_cgroup(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	proc_t *p;
+
+	ASSERT(lxpnp->lxpr_type == LXPR_PID_CGROUP ||
+	    lxpnp->lxpr_type == LXPR_PID_TID_CGROUP);
+
+	p = lxpr_lock(lxpnp->lxpr_pid, ZOMB_OK);
+	if (p == NULL) {
+		lxpr_uiobuf_seterr(uiobuf, EINVAL);
+		return;
+	}
+	lxpr_unlock(p);
+
+	/* basic stub, 3rd field will need to be populated */
+	lxpr_uiobuf_printf(uiobuf, "1:name=systemd:/\n");
+}
+
+static void
+lxpr_copy_cmdline(proc_t *p, lx_proc_data_t *pd, lxpr_uiobuf_t *uiobuf)
+{
+	uio_t *uiop = uiobuf->uiop;
+	char *buf = uiobuf->buffer;
+	int bsz = uiobuf->buffsize;
+	boolean_t env_overflow = B_FALSE;
+	uintptr_t pos = pd->l_args_start + uiop->uio_offset;
+	uintptr_t estart = pd->l_envs_start;
+	uintptr_t eend = pd->l_envs_end;
+	size_t chunk, copied;
+	int err = 0;
+
+	/* Do not bother with data beyond the end of the envp strings area. */
+	if (pos > eend) {
+		return;
+	}
+	mutex_exit(&p->p_lock);
+
+	/*
+	 * If the starting or ending bounds are outside the argv strings area,
+	 * check to see if the process has overwritten the terminating NULL.
+	 * If not, no data needs to be copied from oustide the argv area.
+	 */
+	if (pos >= estart || (pos + uiop->uio_resid) >= estart) {
+		uint8_t term;
+		if (uread(p, &term, sizeof (term), estart - 1) != 0) {
+			err = EFAULT;
+		} else if (term != 0) {
+			env_overflow = B_TRUE;
+		}
+	}
+
+	/* Data between astart and estart-1 can be copied freely. */
+	while (pos < estart && uiop->uio_resid > 0 && err == 0) {
+		chunk = MIN(estart - pos, uiop->uio_resid);
+		chunk = MIN(chunk, bsz);
+
+		if (prreadbuf(p, pos, (uint8_t *)buf, chunk, &copied) != 0 ||
+		    copied != chunk) {
+			err = EFAULT;
+			break;
+		}
+		err = uiomove(buf, copied, UIO_READ, uiop);
+		pos += copied;
+	}
+
+	/*
+	 * Onward from estart, data is copied as a contiguous string.  To
+	 * protect env data from potential snooping, only one buffer-sized copy
+	 * is allowed to avoid complex seek logic.
+	 */
+	if (err == 0 && env_overflow && pos == estart && uiop->uio_resid > 0) {
+		chunk = MIN(eend - pos, uiop->uio_resid);
+		chunk = MIN(chunk, bsz);
+		if (prreadbuf(p, pos, (uint8_t *)buf, chunk, &copied) == 0) {
+			int len = strnlen(buf, copied);
+			if (len > 0) {
+				err = uiomove(buf, len, UIO_READ, uiop);
+			}
+		}
+	}
+
+	uiobuf->error = err;
+	/* reset any uiobuf state */
+	uiobuf->pos = uiobuf->buffer;
+	uiobuf->beg = 0;
+
+	mutex_enter(&p->p_lock);
+}
+
+/*
+ * lxpr_read_pid_cmdline(): read argument vector from process
+ */
+static void
+lxpr_read_pid_cmdline(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	proc_t *p;
+	char *buf;
+	size_t asz = lxpr_maxargvlen, sz;
+	lx_proc_data_t *pd;
+
+	ASSERT(lxpnp->lxpr_type == LXPR_PID_CMDLINE ||
+	    lxpnp->lxpr_type == LXPR_PID_TID_CMDLINE);
+
+	buf = kmem_alloc(asz, KM_SLEEP);
+
+	p = lxpr_lock(lxpnp->lxpr_pid, NO_ZOMB);
+	if (p == NULL) {
+		kmem_free(buf, asz);
+		return;
+	}
+
+	if ((pd = ptolxproc(p)) != NULL && pd->l_args_start != 0 &&
+	    pd->l_envs_start != 0 && pd->l_envs_end != 0) {
+		/* Use Linux-style argv bounds if possible. */
+		lxpr_copy_cmdline(p, pd, uiobuf);
+		lxpr_unlock(p);
+	} else {
+		int r;
+
+		r = prreadargv(p, buf, asz, &sz);
+		lxpr_unlock(p);
+
+		if (r != 0) {
+			lxpr_uiobuf_seterr(uiobuf, EINVAL);
+		} else {
+			lxpr_uiobuf_write(uiobuf, buf, sz);
+		}
+	}
+	kmem_free(buf, asz);
+}
+
+/*
+ * lxpr_read_pid_comm(): read command from process
+ */
+static void
+lxpr_read_pid_comm(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	proc_t *p;
+	char buf[MAXCOMLEN + 1];
+
+	VERIFY(lxpnp->lxpr_type == LXPR_PID_COMM ||
+	    lxpnp->lxpr_type == LXPR_PID_TID_COMM);
+
+	/*
+	 * Because prctl(PR_SET_NAME) does not set custom names for threads
+	 * (vs processes), there is no need for special handling here.
+	 */
+	if ((p = lxpr_lock(lxpnp->lxpr_pid, ZOMB_OK)) == NULL) {
+		lxpr_uiobuf_seterr(uiobuf, EINVAL);
+		return;
+	}
+	(void) strlcpy(buf, p->p_user.u_comm, sizeof (buf));
+	lxpr_unlock(p);
+	lxpr_uiobuf_printf(uiobuf, "%s\n", buf);
+}
+
+/*
+ * lxpr_read_pid_env(): read env vector from process
+ */
+static void
+lxpr_read_pid_env(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	proc_t *p;
+	char *buf;
+	size_t asz = lxpr_maxenvvlen, sz;
+	int r;
+
+	ASSERT(lxpnp->lxpr_type == LXPR_PID_ENV);
+
+	buf = kmem_alloc(asz, KM_SLEEP);
+
+	p = lxpr_lock(lxpnp->lxpr_pid, NO_ZOMB);
+	if (p == NULL) {
+		kmem_free(buf, asz);
+		return;
+	}
+
+	r = prreadenvv(p, buf, asz, &sz);
+	lxpr_unlock(p);
+
+	if (r != 0) {
+		lxpr_uiobuf_seterr(uiobuf, EINVAL);
+	} else {
+		lxpr_uiobuf_write(uiobuf, buf, sz);
+	}
+	kmem_free(buf, asz);
+}
+
+/*
+ * lxpr_read_pid_limits(): ulimit file
+ */
+static void
+lxpr_read_pid_limits(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	proc_t *p;
+	rctl_qty_t cur[LX_RLIM_TAB_LEN], max[LX_RLIM_TAB_LEN];
+	int i;
+
+	ASSERT(lxpnp->lxpr_type == LXPR_PID_LIMITS ||
+	    lxpnp->lxpr_type == LXPR_PID_TID_LIMITS);
+
+	p = lxpr_lock(lxpnp->lxpr_pid, NO_ZOMB);
+	if (p == NULL) {
+		return;
+	}
+
+	for (i = 0; i < LX_RLIM_TAB_LEN; i++) {
+		char *kname = lxpr_rlimtab[i].rlim_rctl;
+		rctl_val_t nval, *oval = NULL;
+		rctl_hndl_t hndl;
+
+		/* default to unlimited for resources without an analog */
+		cur[i] = RLIM_INFINITY;
+		max[i] = RLIM_INFINITY;
+		if (kname == NULL || (hndl = rctl_hndl_lookup(kname)) == -1) {
+			continue;
+		}
+		while (rctl_local_get(hndl, oval, &nval, p) == 0) {
+			oval = &nval;
+			switch (nval.rcv_privilege) {
+			case RCPRIV_BASIC:
+				if (!RCTL_INFINITE(nval))
+					cur[i] = nval.rcv_value;
+				break;
+			case RCPRIV_PRIVILEGED:
+				if (!RCTL_INFINITE(nval))
+					max[i] = nval.rcv_value;
+				break;
+			}
+		}
+	}
+	lxpr_unlock(p);
+
+	lxpr_uiobuf_printf(uiobuf, "%-25s %-20s %-20s %-10s\n",
+	    "Limit", "Soft Limit", "Hard Limit", "Units");
+	for (i = 0; i < LX_RLIM_TAB_LEN; i++) {
+		lxpr_uiobuf_printf(uiobuf, "%-25s", lxpr_rlimtab[i].rlim_name);
+		if (cur[i] == RLIM_INFINITY || cur[i] == LX_RLIM_INFINITY) {
+			lxpr_uiobuf_printf(uiobuf, " %-20s", "unlimited");
+		} else {
+			lxpr_uiobuf_printf(uiobuf, " %-20lu", cur[i]);
+		}
+		if (max[i] == RLIM_INFINITY || max[i] == LX_RLIM_INFINITY) {
+			lxpr_uiobuf_printf(uiobuf, " %-20s", "unlimited");
+		} else {
+			lxpr_uiobuf_printf(uiobuf, " %-20lu", max[i]);
+		}
+		lxpr_uiobuf_printf(uiobuf, " %-10s\n",
+		    lxpr_rlimtab[i].rlim_unit);
+	}
+}
+
+/*
+ * lxpr_read_pid_loginuid(): loginuid file
+ */
+static void
+lxpr_read_pid_loginuid(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	proc_t *p;
+	lx_proc_data_t *pd;
+	uid_t lu = 0;
+
+	ASSERT(lxpnp->lxpr_type == LXPR_PID_LOGINUID ||
+	    lxpnp->lxpr_type == LXPR_PID_TID_LOGINUID);
+
+	p = lxpr_lock(lxpnp->lxpr_pid, NO_ZOMB);
+	if (p == NULL) {
+		lxpr_uiobuf_seterr(uiobuf, ENXIO);
+		return;
+	}
+
+	if ((pd = ptolxproc(p)) != NULL) {
+		lu = pd->l_loginuid;
+	}
+	lxpr_unlock(p);
+
+	lxpr_uiobuf_printf(uiobuf, "%d", lu);
+}
+
+/*
+ * lxpr_read_pid_maps(): memory map file
+ */
+static void
+lxpr_read_pid_maps(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	proc_t *p;
+	lx_proc_data_t *lxpd;
+	struct as *as;
+	struct seg *seg;
+	char *buf;
+	int buflen = MAXPATHLEN;
+	struct print_data {
+		uintptr_t saddr;
+		uintptr_t eaddr;
+		int type;
+		char prot[5];
+		uintptr_t offset;
+		vnode_t *vp;
+		char *name_override;
+		struct print_data *next;
+	} *print_head = NULL;
+	struct print_data **print_tail = &print_head;
+	struct print_data *pbuf;
+
+	ASSERT(lxpnp->lxpr_type == LXPR_PID_MAPS ||
+	    lxpnp->lxpr_type == LXPR_PID_TID_MAPS);
+
+	p = lxpr_lock(lxpnp->lxpr_pid, NO_ZOMB);
+	if (p == NULL) {
+		return;
+	}
+
+	as = p->p_as;
+	lxpd = ptolxproc(p);
+
+	if (as == &kas) {
+		lxpr_unlock(p);
+		return;
+	}
+
+	mutex_exit(&p->p_lock);
+
+	/* Iterate over all segments in the address space */
+	AS_LOCK_ENTER(as, RW_READER);
+	for (seg = AS_SEGFIRST(as); seg != NULL; seg = AS_SEGNEXT(as, seg)) {
+		vnode_t *vp;
+		uint_t protbits;
+
+		pbuf = kmem_alloc(sizeof (*pbuf), KM_SLEEP);
+
+		pbuf->saddr = (uintptr_t)seg->s_base;
+		pbuf->eaddr = pbuf->saddr + seg->s_size;
+		pbuf->type = SEGOP_GETTYPE(seg, seg->s_base);
+
+		/*
+		 * Cheat and only use the protection bits of the first page
+		 * in the segment
+		 */
+		(void) strncpy(pbuf->prot, "----", sizeof (pbuf->prot));
+		(void) SEGOP_GETPROT(seg, seg->s_base, 0, &protbits);
+
+		if (protbits & PROT_READ)	   pbuf->prot[0] = 'r';
+		if (protbits & PROT_WRITE)	   pbuf->prot[1] = 'w';
+		if (protbits & PROT_EXEC)	   pbuf->prot[2] = 'x';
+		if (pbuf->type & MAP_SHARED)	   pbuf->prot[3] = 's';
+		else if (pbuf->type & MAP_PRIVATE) pbuf->prot[3] = 'p';
+
+		if (seg->s_ops == &segvn_ops &&
+		    SEGOP_GETVP(seg, seg->s_base, &vp) == 0 &&
+		    vp != NULL && vp->v_type == VREG) {
+			VN_HOLD(vp);
+			pbuf->vp = vp;
+		} else {
+			pbuf->vp = NULL;
+		}
+
+		pbuf->offset = SEGOP_GETOFFSET(seg, (caddr_t)pbuf->saddr);
+
+		pbuf->name_override = NULL;
+		if (lxpd != NULL) {
+			if (pbuf->saddr == lxpd->l_vdso) {
+				pbuf->name_override = "[vdso]";
+			} else if (pbuf->saddr == p->p_user.u_commpagep) {
+				pbuf->name_override = "[vvar]";
+			}
+		}
+
+		pbuf->next = NULL;
+		*print_tail = pbuf;
+		print_tail = &pbuf->next;
+	}
+	AS_LOCK_EXIT(as);
+	mutex_enter(&p->p_lock);
+	lxpr_unlock(p);
+
+	buf = kmem_alloc(buflen, KM_SLEEP);
+
+	/* print the data we've extracted */
+	pbuf = print_head;
+	while (pbuf != NULL) {
+		struct print_data *pbuf_next;
+		vattr_t vattr;
+
+		int maj = 0;
+		int min = 0;
+		ino_t inode = 0;
+
+		*buf = '\0';
+		if (pbuf->name_override != NULL) {
+			(void) strncpy(buf, pbuf->name_override, buflen);
+		} else if (pbuf->vp != NULL) {
+			vattr.va_mask = AT_FSID | AT_NODEID;
+			if (VOP_GETATTR(pbuf->vp, &vattr, 0, CRED(),
+			    NULL) == 0) {
+				maj = getmajor(vattr.va_fsid);
+				min = getminor(vattr.va_fsid);
+				inode = vattr.va_nodeid;
+			}
+			(void) vnodetopath(NULL, pbuf->vp, buf, buflen, CRED());
+			VN_RELE(pbuf->vp);
+		}
+
+		if (p->p_model == DATAMODEL_LP64) {
+			lxpr_uiobuf_printf(uiobuf,
+			    "%08llx-%08llx %s %08llx %02x:%02x %llu%s%s\n",
+			    pbuf->saddr, pbuf->eaddr, pbuf->prot, pbuf->offset,
+			    maj, min, inode, *buf != '\0' ? " " : "", buf);
+		} else {
+			lxpr_uiobuf_printf(uiobuf,
+			    "%08x-%08x %s %08x %02x:%02x %llu%s%s\n",
+			    (uint32_t)pbuf->saddr, (uint32_t)pbuf->eaddr,
+			    pbuf->prot, (uint32_t)pbuf->offset, maj, min,
+			    inode, *buf != '\0' ? " " : "", buf);
+		}
+
+		pbuf_next = pbuf->next;
+		kmem_free(pbuf, sizeof (*pbuf));
+		pbuf = pbuf_next;
+	}
+
+	kmem_free(buf, buflen);
+}
+
+/*
+ * Make mount entry look more like Linux. Non-zero return to skip it.
+ */
+static int
+lxpr_clean_mntent(char **mntpt, char **fstype, char **resource)
+{
+	if (strcmp(*mntpt, "/var/ld") == 0 ||
+	    strcmp(*fstype, "objfs") == 0 ||
+	    strcmp(*fstype, "mntfs") == 0 ||
+	    strcmp(*fstype, "ctfs") == 0 ||
+	    strncmp(*mntpt, "/native/", 8) == 0) {
+		return (1);
+	}
+
+	if (strcmp(*fstype, "tmpfs") == 0) {
+		*resource = "tmpfs";
+	} else if (strcmp(*fstype, "lx_proc") == 0) {
+		*resource = *fstype = "proc";
+	} else if (strcmp(*fstype, "lx_sysfs") == 0) {
+		*resource = *fstype = "sysfs";
+	} else if (strcmp(*fstype, "lx_devfs") == 0) {
+		*resource = *fstype = "devtmpfs";
+	} else if (strcmp(*fstype, "lx_cgroup") == 0) {
+		*resource = *fstype = "cgroup";
+	} else if (strcmp(*fstype, "lxautofs") == 0) {
+		*fstype = "autofs";
+	}
+
+	return (0);
+}
+
+
+typedef struct lxpr_mount_entry {
+	list_node_t	lme_link;
+	uint_t		lme_id;
+	uint_t		lme_parent_id;
+	refstr_t	*lme_mntpt;
+	refstr_t	*lme_resource;
+	uint_t		lme_flag;
+	int		lme_fstype;
+	dev_t		lme_dev;
+	boolean_t	lme_force;
+} lxpr_mount_entry_t;
+
+static int lxpr_zfs_fstype = -1;
+
+#define	LXPR_ROOT_MOUNT_ID	15
+
+static list_t *
+lxpr_enumerate_mounts(zone_t *zone)
+{
+	vfs_t *vfsp, *rvfsp, *vfslist;
+	lx_zone_data_t *lxzd = ztolxzd(zone);
+	list_t *result;
+	lxpr_mount_entry_t *lme;
+	lx_virt_disk_t *vd;
+	uint_t root_id, mount_id;
+	char tmppath[MAXPATHLEN];
+
+	result = kmem_alloc(sizeof (list_t), KM_SLEEP);
+	list_create(result, sizeof (lxpr_mount_entry_t),
+	    offsetof(lxpr_mount_entry_t, lme_link));
+	/* use an arbitrary start value for the root mount_id */
+	root_id = 15;
+	mount_id = root_id + 1;
+
+	ASSERT(zone != global_zone);
+	ASSERT(lxzd != NULL);
+	ASSERT(lxzd->lxzd_vdisks != NULL);
+
+	vfs_list_read_lock();
+	vfsp = vfslist = zone->zone_vfslist;
+
+	/*
+	 * If the zone has a root entry, it will be the first in the list.
+	 * Conjure one up if needed.
+	 */
+	if (vfslist == NULL || strcmp(refstr_value(vfsp->vfs_mntpt),
+	    zone->zone_rootpath) != 0) {
+		rvfsp = zone->zone_rootvp->v_vfsp;
+	} else {
+		rvfsp = vfslist;
+		vfsp = vfslist->vfs_zone_next;
+	}
+
+	lme = kmem_alloc(sizeof (lxpr_mount_entry_t), KM_SLEEP);
+	lme->lme_id = root_id;
+	lme->lme_parent_id = 0;
+	lme->lme_mntpt = refstr_alloc(zone->zone_rootpath);
+	lme->lme_flag = rvfsp->vfs_flag;
+	lme->lme_fstype = rvfsp->vfs_fstype;
+	lme->lme_force = B_TRUE;
+
+	lme->lme_resource = NULL;
+	vd = list_head(lxzd->lxzd_vdisks);
+	while (vd != NULL) {
+		if (vd->lxvd_type == LXVD_ZFS_DS &&
+		    vd->lxvd_real_dev == rvfsp->vfs_dev) {
+			(void) snprintf(tmppath, sizeof (tmppath),
+			    "%sdev/%s", zone->zone_rootpath, vd->lxvd_name);
+			lme->lme_resource = refstr_alloc(tmppath);
+			lme->lme_dev = vd->lxvd_emul_dev;
+			break;
+		}
+		vd = list_next(lxzd->lxzd_vdisks, vd);
+	}
+	if (lme->lme_resource == NULL) {
+		lme->lme_resource = refstr_alloc(zone->zone_rootpath);
+		lme->lme_dev = rvfsp->vfs_dev;
+	}
+	list_insert_head(result, lme);
+
+	do {
+		if (vfsp == NULL) {
+			break;
+		}
+		/* Skip mounts we shouldn't show */
+		if ((vfsp->vfs_flag & VFS_NOMNTTAB) != 0) {
+			vfsp = vfsp->vfs_zone_next;
+			continue;
+		}
+
+		lme = kmem_alloc(sizeof (lxpr_mount_entry_t), KM_SLEEP);
+		lme->lme_id = mount_id++;
+		lme->lme_parent_id = root_id;
+		lme->lme_mntpt = vfsp->vfs_mntpt;
+		refstr_hold(vfsp->vfs_mntpt);
+		lme->lme_flag = vfsp->vfs_flag;
+		lme->lme_fstype = vfsp->vfs_fstype;
+		lme->lme_force = B_FALSE;
+
+		lme->lme_resource = NULL;
+		vd = list_head(lxzd->lxzd_vdisks);
+		while (vd != NULL) {
+			if (vd->lxvd_type == LXVD_ZFS_DS &&
+			    vd->lxvd_real_dev == vfsp->vfs_dev) {
+				char vdev[MAXPATHLEN];
+
+				(void) snprintf(vdev, sizeof (vdev),
+				    "%sdev/%s",
+				    zone->zone_rootpath, vd->lxvd_name);
+				lme->lme_resource = refstr_alloc(vdev);
+				lme->lme_dev = vd->lxvd_emul_dev;
+				break;
+			}
+			vd = list_next(lxzd->lxzd_vdisks, vd);
+		}
+		if (lme->lme_resource == NULL) {
+			lme->lme_resource = vfsp->vfs_resource;
+			refstr_hold(vfsp->vfs_resource);
+			lme->lme_dev = vfsp->vfs_dev;
+		}
+		list_insert_tail(result, lme);
+		vfsp = vfsp->vfs_zone_next;
+	} while (vfsp != vfslist);
+
+	vfs_list_unlock();
+
+	/* Add a single dummy entry for /native/usr */
+	lme = kmem_alloc(sizeof (lxpr_mount_entry_t), KM_SLEEP);
+	lme->lme_id = mount_id++;
+	lme->lme_parent_id = root_id;
+	lme->lme_flag = VFS_RDONLY;
+	lme->lme_dev = makedevice(0, 1);
+	(void) snprintf(tmppath, sizeof (tmppath),
+	    "%snative/usr", zone->zone_rootpath);
+	lme->lme_mntpt = refstr_alloc(tmppath);
+	lme->lme_resource = lme->lme_mntpt;
+	refstr_hold(lme->lme_mntpt);
+	if (lxpr_zfs_fstype == -1) {
+		vfssw_t *zfssw = vfs_getvfssw("zfs");
+		VERIFY(zfssw != NULL);
+		lxpr_zfs_fstype = ((uintptr_t)zfssw - (uintptr_t)vfssw) /
+		    sizeof (vfssw[0]);
+		VERIFY(&vfssw[lxpr_zfs_fstype] == zfssw);
+	}
+	lme->lme_fstype = lxpr_zfs_fstype;
+	lme->lme_force = B_TRUE;
+	list_insert_tail(result, lme);
+
+	return (result);
+}
+
+/*
+ * lxpr_read_pid_mountinfo(): information about process mount points.
+ */
+static void
+lxpr_read_pid_mountinfo(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	zone_t *zone = LXPTOZ(lxpnp);
+	list_t *mounts;
+	lxpr_mount_entry_t *lme;
+
+	ASSERT(lxpnp->lxpr_type == LXPR_PID_MOUNTINFO ||
+	    lxpnp->lxpr_type == LXPR_PID_TID_MOUNTINFO);
+
+	mounts = lxpr_enumerate_mounts(zone);
+
+	/*
+	 * now we can run through what we've extracted without holding
+	 * vfs_list_read_lock()
+	 */
+	lme = (lxpr_mount_entry_t *)list_remove_head(mounts);
+	while (lme != NULL) {
+		char *resource, *mntpt, *fstype, *rwflag;
+		vnode_t *vp;
+		int error;
+
+		mntpt = (char *)refstr_value(lme->lme_mntpt);
+		resource = (char *)refstr_value(lme->lme_resource);
+
+		if (mntpt == NULL || mntpt[0] == '\0') {
+			goto nextp;
+		}
+		mntpt = ZONE_PATH_TRANSLATE(mntpt, zone);
+		error = lookupname(mntpt, UIO_SYSSPACE, FOLLOW, NULLVPP, &vp);
+		if (error != 0) {
+			goto nextp;
+		} else if ((vp->v_flag & VROOT) == 0 && !lme->lme_force) {
+			VN_RELE(vp);
+			goto nextp;
+		}
+		VN_RELE(vp);
+
+		if (resource != NULL && resource[0] != '\0') {
+			if (resource[0] == '/') {
+				resource = ZONE_PATH_VISIBLE(resource, zone) ?
+				    ZONE_PATH_TRANSLATE(resource, zone) : mntpt;
+			}
+		} else {
+			resource = "none";
+		}
+
+		/*  Make things look more like Linux. */
+		fstype = vfssw[lme->lme_fstype].vsw_name;
+		if (lxpr_clean_mntent(&mntpt, &fstype, &resource) != 0 &&
+		    !lme->lme_force) {
+			goto nextp;
+		}
+		rwflag = ((lme->lme_flag & VFS_RDONLY) == 0) ? "rw" : "ro";
+
+		/*
+		 * XXX parent ID is not tracked correctly here. Currently we
+		 * always assume the parent ID is the root ID.
+		 */
+		lxpr_uiobuf_printf(uiobuf,
+		    "%d %d %d:%d / %s %s - %s %s %s\n",
+		    lme->lme_id, lme->lme_parent_id,
+		    getmajor(lme->lme_dev), getminor(lme->lme_dev),
+		    mntpt, rwflag, fstype, resource, rwflag);
+
+nextp:
+		refstr_rele(lme->lme_mntpt);
+		refstr_rele(lme->lme_resource);
+		kmem_free(lme, sizeof (lxpr_mount_entry_t));
+		lme = (lxpr_mount_entry_t *)list_remove_head(mounts);
+	}
+
+	list_destroy(mounts);
+	kmem_free(mounts, sizeof (list_t));
+}
+
+/*
+ * lxpr_read_pid_oom_scr_adj(): read oom_score_adj for process
+ */
+static void
+lxpr_read_pid_oom_scr_adj(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	proc_t *p;
+
+	ASSERT(lxpnp->lxpr_type == LXPR_PID_OOM_SCR_ADJ ||
+	    lxpnp->lxpr_type == LXPR_PID_TID_OOM_SCR_ADJ);
+
+	p = lxpr_lock(lxpnp->lxpr_pid, ZOMB_OK);
+	if (p == NULL) {
+		lxpr_uiobuf_seterr(uiobuf, EINVAL);
+		return;
+	}
+	lxpr_unlock(p);
+
+	/* always 0 */
+	lxpr_uiobuf_printf(uiobuf, "0\n");
+}
+
+/*
+ * lxpr_read_pid_personality(): read personality for process
+ */
+static void
+lxpr_read_pid_personality(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	proc_t *p;
+	lx_proc_data_t *lxpd;
+	unsigned int personality;
+
+	ASSERT(lxpnp->lxpr_type == LXPR_PID_PERSONALITY);
+
+	p = lxpr_lock(lxpnp->lxpr_pid, ZOMB_OK);
+	if (p == NULL) {
+		lxpr_uiobuf_seterr(uiobuf, EINVAL);
+		return;
+	}
+	if ((lxpd = ptolxproc(p)) != NULL) {
+		personality = lxpd->l_personality;
+	} else {
+		/* Report native processes as having the SunOS personality */
+		personality = LX_PER_SUNOS;
+	}
+	lxpr_unlock(p);
+
+	lxpr_uiobuf_printf(uiobuf, "%08x\n", personality);
+}
+
+/*
+ * lxpr_read_pid_statm(): memory status file
+ */
+static void
+lxpr_read_pid_statm(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	proc_t *p;
+	struct as *as;
+	size_t vsize, rss;
+
+	ASSERT(lxpnp->lxpr_type == LXPR_PID_STATM ||
+	    lxpnp->lxpr_type == LXPR_PID_TID_STATM);
+
+	p = lxpr_lock(lxpnp->lxpr_pid, ZOMB_OK);
+	if (p == NULL) {
+		lxpr_uiobuf_seterr(uiobuf, EINVAL);
+		return;
+	}
+
+	as = p->p_as;
+	mutex_exit(&p->p_lock);
+	if (as != &kas) {
+		AS_LOCK_ENTER(as, RW_READER);
+		vsize = btopr(as->a_resvsize);
+		rss = rm_asrss(as);
+		AS_LOCK_EXIT(as);
+	} else {
+		vsize = 0;
+		rss = 0;
+	}
+	mutex_enter(&p->p_lock);
+	lxpr_unlock(p);
+
+	lxpr_uiobuf_printf(uiobuf,
+	    "%lu %lu %lu %lu %lu %lu %lu\n",
+	    vsize, rss, 0l, rss, 0l, 0l, 0l);
+}
+
+/*
+ * Look for either the main thread (lookup_id is 0) or the specified thread.
+ * If we're looking for the main thread but the proc does not have one, we
+ * fallback to using prchoose to get any thread available.
+ */
+static kthread_t *
+lxpr_get_thread(proc_t *p, uint_t lookup_id)
+{
+	kthread_t *t;
+	uint_t emul_tid;
+	lx_lwp_data_t *lwpd;
+	pid_t pid = p->p_pid;
+	pid_t init_pid = curproc->p_zone->zone_proc_initpid;
+	boolean_t branded = (p->p_brand == &lx_brand);
+
+	/* get specified thread  */
+	if ((t = p->p_tlist) == NULL)
+		return (NULL);
+
+	do {
+		if (lookup_id == 0 && t->t_tid == 1) {
+			thread_lock(t);
+			return (t);
+		}
+
+		lwpd = ttolxlwp(t);
+		if (branded && lwpd != NULL) {
+			if (pid == init_pid && lookup_id == 1) {
+				emul_tid = t->t_tid;
+			} else {
+				emul_tid = lwpd->br_pid;
+			}
+		} else {
+			/*
+			 * Make only the first (assumed to be main) thread
+			 * visible for non-branded processes.
+			 */
+			emul_tid = p->p_pid;
+		}
+		if (emul_tid == lookup_id) {
+			thread_lock(t);
+			return (t);
+		}
+	} while ((t = t->t_forw) != p->p_tlist);
+
+	if (lookup_id == 0)
+		return (prchoose(p));
+	return (NULL);
+}
+
+/*
+ * Lookup the real pid for procs 0 or 1.
+ */
+static pid_t
+get_real_pid(pid_t p)
+{
+	pid_t find_pid;
+
+	if (p == 1) {
+		find_pid = curproc->p_zone->zone_proc_initpid;
+	} else if (p == 0) {
+		find_pid = curproc->p_zone->zone_zsched->p_pid;
+	} else {
+		find_pid = p;
+	}
+
+	return (find_pid);
+}
+
+/*
+ * pid/tid common code to read status file
+ */
+static void
+lxpr_read_status_common(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf,
+    uint_t lookup_id)
+{
+	proc_t		*p;
+	kthread_t	*t;
+	user_t		*up;
+	cred_t		*cr;
+	const gid_t	*groups;
+	struct as	*as;
+	char		*status;
+	pid_t		pid, ppid;
+	k_sigset_t	current, ignore, handle;
+	int		i, lx_sig, lwpcnt, ngroups;
+	pid_t		real_pid;
+	char		buf_comm[MAXCOMLEN + 1];
+	rlim64_t	fdlim;
+	size_t		vsize = 0, nlocked = 0, rss = 0, stksize = 0;
+	boolean_t	printsz = B_FALSE;
+
+	real_pid = get_real_pid(lxpnp->lxpr_pid);
+	p = lxpr_lock(real_pid, ZOMB_OK);
+	if (p == NULL) {
+		lxpr_uiobuf_seterr(uiobuf, EINVAL);
+		return;
+	}
+
+	pid = p->p_pid;
+
+	/*
+	 * Convert pid to the Linux default of 1 if we're the zone's init
+	 * process or if we're the zone's zsched the pid is 0.
+	 */
+	if (pid == curproc->p_zone->zone_proc_initpid) {
+		pid = 1;
+		ppid = 0;	/* parent pid for init is 0 */
+	} else if (pid == curproc->p_zone->zone_zsched->p_pid) {
+		pid = 0;	/* zsched is pid 0 */
+		ppid = 0;	/* parent pid for zsched is itself */
+	} else {
+		/*
+		 * Make sure not to reference parent PIDs that reside outside
+		 * the zone
+		 */
+		ppid = ((p->p_flag & SZONETOP)
+		    ? curproc->p_zone->zone_zsched->p_pid : p->p_ppid);
+
+		/*
+		 * Convert ppid to the Linux default of 1 if our parent is the
+		 * zone's init process
+		 */
+		if (ppid == curproc->p_zone->zone_proc_initpid)
+			ppid = 1;
+	}
+
+	t = lxpr_get_thread(p, lookup_id);
+	if (t != NULL) {
+		switch (t->t_state) {
+		case TS_SLEEP:
+			status = "S (sleeping)";
+			break;
+		case TS_RUN:
+		case TS_ONPROC:
+			status = "R (running)";
+			break;
+		case TS_ZOMB:
+			status = "Z (zombie)";
+			break;
+		case TS_STOPPED:
+			status = "T (stopped)";
+			break;
+		default:
+			status = "! (unknown)";
+			break;
+		}
+		thread_unlock(t);
+	} else {
+		if (lookup_id != 0) {
+			/* we can't find this specific thread */
+			lxpr_uiobuf_seterr(uiobuf, EINVAL);
+			lxpr_unlock(p);
+			return;
+		}
+
+		/*
+		 * there is a hole in the exit code, where a proc can have
+		 * no threads but it is yet to be flagged SZOMB. We will
+		 * assume we are about to become a zombie
+		 */
+		status = "Z (zombie)";
+	}
+
+	up = PTOU(p);
+	mutex_enter(&p->p_crlock);
+	crhold(cr = p->p_cred);
+	mutex_exit(&p->p_crlock);
+
+	(void) strlcpy(buf_comm, up->u_comm, sizeof (buf_comm));
+	fdlim = p->p_fno_ctl;
+	lwpcnt = p->p_lwpcnt;
+
+	/*
+	 * Gather memory information
+	 */
+	as = p->p_as;
+	if ((p->p_stat != SZOMB) && !(p->p_flag & (SSYS | SEXITING)) &&
+	    (as != &kas)) {
+		mutex_exit(&p->p_lock);
+		AS_LOCK_ENTER(as, RW_READER);
+		vsize = as->a_resvsize;
+		rss = rm_asrss(as);
+		AS_LOCK_EXIT(as);
+		mutex_enter(&p->p_lock);
+
+		nlocked = p->p_locked_mem;
+		stksize = p->p_stksize;
+		printsz = B_TRUE;
+	}
+
+	/*
+	 * Gather signal information
+	 */
+	sigemptyset(&current);
+	sigemptyset(&ignore);
+	sigemptyset(&handle);
+	for (i = 1; i < NSIG; i++) {
+		lx_sig = stol_signo[i];
+
+		if ((lx_sig > 0) && (lx_sig <= LX_NSIG)) {
+			if (sigismember(&p->p_sig, i))
+				sigaddset(&current, lx_sig);
+
+			if (up->u_signal[i - 1] == SIG_IGN)
+				sigaddset(&ignore, lx_sig);
+			else if (up->u_signal[i - 1] != SIG_DFL)
+				sigaddset(&handle, lx_sig);
+		}
+	}
+	lxpr_unlock(p);
+
+	lxpr_uiobuf_printf(uiobuf,
+	    "Name:\t%s\n"
+	    "State:\t%s\n"
+	    "Tgid:\t%d\n"
+	    "Pid:\t%d\n"
+	    "PPid:\t%d\n"
+	    "TracerPid:\t%d\n"
+	    "Uid:\t%u\t%u\t%u\t%u\n"
+	    "Gid:\t%u\t%u\t%u\t%u\n"
+	    "FDSize:\t%d\n"
+	    "Groups:\t",
+	    buf_comm,
+	    status,
+	    pid, /* thread group id - same as pid */
+	    (lookup_id == 0) ? pid : lxpnp->lxpr_desc,
+	    ppid,
+	    0,
+	    crgetruid(cr), crgetuid(cr), crgetsuid(cr), crgetuid(cr),
+	    crgetrgid(cr), crgetgid(cr), crgetsgid(cr), crgetgid(cr),
+	    fdlim);
+	ngroups = crgetngroups(cr);
+	groups  = crgetgroups(cr);
+	for (i = 0; i < ngroups; i++) {
+		lxpr_uiobuf_printf(uiobuf,
+		    "%u ",
+		    groups[i]);
+	}
+	crfree(cr);
+	if (printsz) {
+		lxpr_uiobuf_printf(uiobuf,
+		    "\n"
+		    "VmSize:\t%8lu kB\n"
+		    "VmLck:\t%8lu kB\n"
+		    "VmRSS:\t%8lu kB\n"
+		    "VmData:\t%8lu kB\n"
+		    "VmStk:\t%8lu kB\n"
+		    "VmExe:\t%8lu kB\n"
+		    "VmLib:\t%8lu kB",
+		    btok(vsize),
+		    btok(nlocked),
+		    ptok(rss),
+		    0l,
+		    btok(stksize),
+		    ptok(rss),
+		    0l);
+	}
+	lxpr_uiobuf_printf(uiobuf, "\nThreads:\t%u\n", lwpcnt);
+	lxpr_uiobuf_printf(uiobuf,
+	    "SigPnd:\t%08x%08x\n"
+	    "SigBlk:\t%08x%08x\n"
+	    "SigIgn:\t%08x%08x\n"
+	    "SigCgt:\t%08x%08x\n",
+	    current.__sigbits[1], current.__sigbits[0],
+	    0, 0, /* signals blocked on per thread basis */
+	    ignore.__sigbits[1], ignore.__sigbits[0],
+	    handle.__sigbits[1], handle.__sigbits[0]);
+	/* Report only the full bounding set for now */
+	lxpr_uiobuf_printf(uiobuf,
+	    "CapInh:\t%016x\n"
+	    "CapPrm:\t%016x\n"
+	    "CapEff:\t%016x\n"
+	    "CapBnd:\t%016llx\n",
+	    0, 0, 0, 0x1fffffffffLL);
+}
+
+/*
+ * lxpr_read_pid_status(): status file
+ */
+static void
+lxpr_read_pid_status(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	ASSERT(lxpnp->lxpr_type == LXPR_PID_STATUS);
+
+	lxpr_read_status_common(lxpnp, uiobuf, 0);
+}
+
+/*
+ * lxpr_read_pid_tid_status(): status file
+ */
+static void
+lxpr_read_pid_tid_status(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	ASSERT(lxpnp->lxpr_type == LXPR_PID_TID_STATUS);
+	lxpr_read_status_common(lxpnp, uiobuf, lxpnp->lxpr_desc);
+}
+
+/*
+ * Same logic as the lx devfs lxd_pts_devt_translator.
+ */
+static dev_t
+lxpr_xlate_pts_dev(dev_t dev)
+{
+	minor_t min = getminor(dev);
+	int lx_maj, lx_min;
+
+	lx_maj = LX_PTS_MAJOR_MIN + (min / LX_MAXMIN);
+	lx_min = min % LX_MAXMIN;
+
+	return (LX_MAKEDEVICE(lx_maj, lx_min));
+}
+
+/*
+ * pid/tid common code to read stat file
+ */
+static void
+lxpr_read_stat_common(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf,
+    uint_t lookup_id)
+{
+	proc_t *p;
+	kthread_t *t;
+	struct as *as;
+	char stat;
+	pid_t pid, ppid, pgpid, spid;
+	gid_t psgid;
+	dev_t psdev;
+	size_t rss, vsize;
+	int nice, pri, lwpcnt;
+	caddr_t wchan, stackbase;
+	processorid_t cpu;
+	pid_t real_pid;
+	clock_t utime, stime, cutime, cstime, ticks, boottime;
+	char buf_comm[MAXCOMLEN + 1];
+	rlim64_t vmem_ctl;
+	int exit_signal = -1;
+
+	real_pid = get_real_pid(lxpnp->lxpr_pid);
+	p = lxpr_lock(real_pid, ZOMB_OK);
+	if (p == NULL) {
+		lxpr_uiobuf_seterr(uiobuf, EINVAL);
+		return;
+	}
+
+	/*
+	 * Set Linux defaults if we're the zone's init process
+	 */
+	pid = p->p_pid;
+	if (pid == curproc->p_zone->zone_proc_initpid) {
+		pid = 1;		/* PID for init */
+		ppid = 0;		/* parent PID for init is 0 */
+		pgpid = 0;		/* process group for init is 0 */
+		psgid = (gid_t)-1;	/* credential GID for init is -1 */
+		spid = 0;		/* session id for init is 0 */
+		psdev = 0;		/* session device for init is 0 */
+	} else if (pid == curproc->p_zone->zone_zsched->p_pid) {
+		pid = 0;		/* PID for zsched */
+		ppid = 0;		/* parent PID for zsched is 0 */
+		pgpid = 0;		/* process group for zsched is 0 */
+		psgid = (gid_t)-1;	/* credential GID for zsched is -1 */
+		spid = 0;		/* session id for zsched is 0 */
+		psdev = 0;		/* session device for zsched is 0 */
+	} else {
+		/*
+		 * Make sure not to reference parent PIDs that reside outside
+		 * the zone
+		 */
+		ppid = ((p->p_flag & SZONETOP) ?
+		    curproc->p_zone->zone_zsched->p_pid : p->p_ppid);
+
+		/*
+		 * Convert ppid to the Linux default of 1 if our parent is the
+		 * zone's init process
+		 */
+		if (ppid == curproc->p_zone->zone_proc_initpid)
+			ppid = 1;
+
+		pgpid = p->p_pgrp;
+
+		mutex_enter(&p->p_splock);
+		mutex_enter(&p->p_sessp->s_lock);
+		spid = p->p_sessp->s_sid;
+		psdev = lxpr_xlate_pts_dev(p->p_sessp->s_dev);
+		if (p->p_sessp->s_cred)
+			psgid = crgetgid(p->p_sessp->s_cred);
+		else
+			psgid = crgetgid(p->p_cred);
+
+		mutex_exit(&p->p_sessp->s_lock);
+		mutex_exit(&p->p_splock);
+	}
+
+	if ((p->p_stat == SZOMB) || (p->p_flag & (SSYS | SEXITING)) ||
+	    (p->p_as == &kas)) {
+		stackbase = 0;
+	} else {
+		/* from prgetstackbase() */
+		stackbase = p->p_usrstack - p->p_stksize;
+	}
+
+	utime = stime = 0;
+	t = lxpr_get_thread(p, lookup_id);
+	if (t != NULL) {
+		klwp_t *lwp = ttolwp(t);
+		struct mstate *ms = &lwp->lwp_mstate;
+		hrtime_t utm, stm;
+
+		/*
+		 * For field 38 (the exit signal), some apps explicitly use
+		 * this field in a check to distinguish processes from threads,
+		 * and assume only processes have a valid signal in this field!
+		 */
+		if (t->t_tid == 1) {
+			lx_proc_data_t *lxpd = ptolxproc(p);
+
+			if (lxpd != NULL) {
+				exit_signal = lxpd->l_signal;
+			} else {
+				exit_signal = SIGCHLD;
+			}
+		}
+
+		switch (t->t_state) {
+		case TS_SLEEP:
+			stat = 'S';
+			break;
+		case TS_RUN:
+		case TS_ONPROC:
+			stat = 'R';
+			break;
+		case TS_ZOMB:
+			stat = 'Z';
+			break;
+		case TS_STOPPED:
+			stat = 'T';
+			break;
+		default:
+			stat = '!';
+			break;
+		}
+
+		if (CL_DONICE(t, NULL, 0, &nice) != 0)
+			nice = 0;
+
+		pri = t->t_pri;
+		wchan = t->t_wchan;
+		cpu = t->t_cpu->cpu_id;
+
+		utm = ms->ms_acct[LMS_USER];
+		stm = ms->ms_acct[LMS_SYSTEM];
+
+		thread_unlock(t);
+
+		/* convert unscaled high-res time to nanoseconds */
+		scalehrtime(&utm);
+		scalehrtime(&stm);
+
+		/* Linux /proc expects these values in ticks */
+		utime = (clock_t)NSEC_TO_TICK(utm);
+		stime = (clock_t)NSEC_TO_TICK(stm);
+	} else {
+		if (lookup_id != 0) {
+			/* we can't find this specific thread */
+			lxpr_uiobuf_seterr(uiobuf, EINVAL);
+			lxpr_unlock(p);
+			return;
+		}
+
+		/* Only zombies have no threads */
+		stat = 'Z';
+		nice = 0;
+		pri = 0;
+		wchan = 0;
+		cpu = 0;
+	}
+	as = p->p_as;
+	mutex_exit(&p->p_lock);
+	if (as != &kas) {
+		AS_LOCK_ENTER(as, RW_READER);
+		vsize = as->a_resvsize;
+		rss = rm_asrss(as);
+		AS_LOCK_EXIT(as);
+	} else {
+		vsize = 0;
+		rss = 0;
+	}
+	mutex_enter(&p->p_lock);
+
+	if (lookup_id == 0) {
+		/* process */
+		utime = p->p_utime;
+		stime = p->p_stime;
+	} else {
+		/* tid: utime & stime for the thread set in block above */
+		/* EMPTY */
+	}
+	cutime = p->p_cutime;
+	cstime = p->p_cstime;
+	lwpcnt = p->p_lwpcnt;
+	vmem_ctl = p->p_vmem_ctl;
+	(void) strlcpy(buf_comm, p->p_user.u_comm, sizeof (buf_comm));
+	ticks = p->p_user.u_ticks;	/* lbolt at process start */
+	/* adjust ticks to account for zone boot time */
+	boottime = LXPTOZ(lxpnp)->zone_zsched->p_user.u_ticks;
+	ticks -= boottime;
+	lxpr_unlock(p);
+
+	lxpr_uiobuf_printf(uiobuf,
+	    "%d "					/* 1 */
+	    "(%s) %c %d %d %d %d %d "			/* 2-8 */
+	    "%lu %lu %lu %lu %lu "			/* 9-13 */
+	    "%lu %lu %ld %ld "				/* 14-17 */
+	    "%d %d %d "					/* 18-20 */
+	    "%lu "					/* 21 */
+	    "%lu "					/* 22 */
+	    "%lu %ld %llu "				/* 23-25 */
+	    "%lu %lu %llu "				/* 26-28 */
+	    "%lu %lu "					/* 29-30 */
+	    "%lu %lu %lu %lu "				/* 31-34 */
+	    "%lu "					/* 35 */
+	    "%lu %lu "					/* 36-37 */
+	    "%d "					/* 38 */
+	    "%d"					/* 39 */
+	    "\n",
+	    (lookup_id == 0) ? pid : lxpnp->lxpr_desc,	/* 1 */
+	    buf_comm, stat, ppid, pgpid, spid, psdev, psgid, /* 2-8 */
+	    0l, 0l, 0l, 0l, 0l, /* flags, minflt, cminflt, majflt, cmajflt */
+	    utime, stime, cutime, cstime,		/* 14-17 */
+	    pri, nice, lwpcnt,				/* 18-20 */
+	    0l, /* itrealvalue (time before next SIGALRM) 21 */
+	    ticks,					/* 22 */
+	    vsize, rss, vmem_ctl,			/* 23-25 */
+	    0l, 0l, stackbase, /* startcode, endcode, startstack 26-28 */
+	    0l, 0l,				/* kstkesp, kstkeip 29-30 */
+	    0l, 0l, 0l, 0l, /* signal, blocked, sigignore, sigcatch 31-34 */
+	    wchan,					/* 35 */
+	    0l, 0l,					/* nswap,cnswap 36-37 */
+	    exit_signal,				/* exit_signal	38 */
+	    cpu						/* 39 */);
+}
+
+/*
+ * lxpr_read_pid_stat(): pid stat file
+ */
+static void
+lxpr_read_pid_stat(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	ASSERT(lxpnp->lxpr_type == LXPR_PID_STAT);
+
+	lxpr_read_stat_common(lxpnp, uiobuf, 0);
+}
+
+/*
+ * lxpr_read_pid_tid_stat(): pid stat file
+ */
+static void
+lxpr_read_pid_tid_stat(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	ASSERT(lxpnp->lxpr_type == LXPR_PID_TID_STAT);
+	lxpr_read_stat_common(lxpnp, uiobuf, lxpnp->lxpr_desc);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_arp(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+struct lxpr_ifstat {
+	uint64_t rx_bytes;
+	uint64_t rx_packets;
+	uint64_t rx_errors;
+	uint64_t rx_drop;
+	uint64_t tx_bytes;
+	uint64_t tx_packets;
+	uint64_t tx_errors;
+	uint64_t tx_drop;
+	uint64_t collisions;
+	uint64_t rx_multicast;
+};
+
+static void *
+lxpr_kstat_read(kstat_t *kn, boolean_t byname, size_t *size, int *num)
+{
+	kstat_t *kp;
+	int i, nrec = 0;
+	size_t bufsize;
+	void *buf = NULL;
+
+	if (byname == B_TRUE) {
+		kp = kstat_hold_byname(kn->ks_module, kn->ks_instance,
+		    kn->ks_name, getzoneid());
+	} else {
+		kp = kstat_hold_bykid(kn->ks_kid, getzoneid());
+	}
+	if (kp == NULL) {
+		return (NULL);
+	}
+	if (kp->ks_flags & KSTAT_FLAG_INVALID) {
+		kstat_rele(kp);
+		return (NULL);
+	}
+
+	bufsize = kp->ks_data_size + 1;
+	kstat_rele(kp);
+
+	/*
+	 * The kstat in question is released so that kmem_alloc(KM_SLEEP) is
+	 * performed without it held.  After the alloc, the kstat is reacquired
+	 * and its size is checked again. If the buffer is no longer large
+	 * enough, the alloc and check are repeated up to three times.
+	 */
+	for (i = 0; i < 2; i++) {
+		buf = kmem_alloc(bufsize, KM_SLEEP);
+
+		/* Check if bufsize still appropriate */
+		if (byname == B_TRUE) {
+			kp = kstat_hold_byname(kn->ks_module, kn->ks_instance,
+			    kn->ks_name, getzoneid());
+		} else {
+			kp = kstat_hold_bykid(kn->ks_kid, getzoneid());
+		}
+		if (kp == NULL || kp->ks_flags & KSTAT_FLAG_INVALID) {
+			if (kp != NULL) {
+				kstat_rele(kp);
+			}
+			kmem_free(buf, bufsize);
+			return (NULL);
+		}
+		KSTAT_ENTER(kp);
+		(void) KSTAT_UPDATE(kp, KSTAT_READ);
+		if (bufsize < kp->ks_data_size) {
+			kmem_free(buf, bufsize);
+			buf = NULL;
+			bufsize = kp->ks_data_size + 1;
+			KSTAT_EXIT(kp);
+			kstat_rele(kp);
+			continue;
+		} else {
+			if (KSTAT_SNAPSHOT(kp, buf, KSTAT_READ) != 0) {
+				kmem_free(buf, bufsize);
+				buf = NULL;
+			}
+			nrec = kp->ks_ndata;
+			KSTAT_EXIT(kp);
+			kstat_rele(kp);
+			break;
+		}
+	}
+
+	if (buf != NULL) {
+		*size = bufsize;
+		*num = nrec;
+	}
+	return (buf);
+}
+
+static int
+lxpr_kstat_ifstat(kstat_t *kn, struct lxpr_ifstat *ifs)
+{
+	kstat_named_t *kp;
+	int i, num;
+	size_t size;
+
+	/*
+	 * Search by name instead of by kid since there's a small window to
+	 * race against kstats being added/removed.
+	 */
+	bzero(ifs, sizeof (*ifs));
+	kp = (kstat_named_t *)lxpr_kstat_read(kn, B_TRUE, &size, &num);
+	if (kp == NULL)
+		return (-1);
+	for (i = 0; i < num; i++) {
+		if (strncmp(kp[i].name, "rbytes64", KSTAT_STRLEN) == 0)
+			ifs->rx_bytes = kp[i].value.ui64;
+		else if (strncmp(kp[i].name, "ipackets64", KSTAT_STRLEN) == 0)
+			ifs->rx_packets = kp[i].value.ui64;
+		else if (strncmp(kp[i].name, "ierrors", KSTAT_STRLEN) == 0)
+			ifs->rx_errors = kp[i].value.ui32;
+		else if (strncmp(kp[i].name, "norcvbuf", KSTAT_STRLEN) == 0)
+			ifs->rx_drop = kp[i].value.ui32;
+		else if (strncmp(kp[i].name, "multircv", KSTAT_STRLEN) == 0)
+			ifs->rx_multicast = kp[i].value.ui32;
+		else if (strncmp(kp[i].name, "obytes64", KSTAT_STRLEN) == 0)
+			ifs->tx_bytes = kp[i].value.ui64;
+		else if (strncmp(kp[i].name, "opackets64", KSTAT_STRLEN) == 0)
+			ifs->tx_packets = kp[i].value.ui64;
+		else if (strncmp(kp[i].name, "oerrors", KSTAT_STRLEN) == 0)
+			ifs->tx_errors = kp[i].value.ui32;
+		else if (strncmp(kp[i].name, "noxmtbuf", KSTAT_STRLEN) == 0)
+			ifs->tx_drop = kp[i].value.ui32;
+		else if (strncmp(kp[i].name, "collisions", KSTAT_STRLEN) == 0)
+			ifs->collisions = kp[i].value.ui32;
+	}
+	kmem_free(kp, size);
+	return (0);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_dev(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	kstat_t *ksr;
+	kstat_t ks0;
+	int i, nidx;
+	size_t sidx;
+	struct lxpr_ifstat ifs;
+
+	lxpr_uiobuf_printf(uiobuf, "Inter-|   Receive                   "
+	    "                             |  Transmit\n");
+	lxpr_uiobuf_printf(uiobuf, " face |bytes    packets errs drop fifo"
+	    " frame compressed multicast|bytes    packets errs drop fifo"
+	    " colls carrier compressed\n");
+
+	ks0.ks_kid = 0;
+	ksr = (kstat_t *)lxpr_kstat_read(&ks0, B_FALSE, &sidx, &nidx);
+	if (ksr == NULL)
+		return;
+
+	for (i = 1; i < nidx; i++) {
+		if (strncmp(ksr[i].ks_module, "link", KSTAT_STRLEN) == 0 ||
+		    strncmp(ksr[i].ks_module, "lo", KSTAT_STRLEN) == 0) {
+			if (lxpr_kstat_ifstat(&ksr[i], &ifs) != 0)
+				continue;
+
+			/* Overwriting the name is ok in the local snapshot */
+			lx_ifname_convert(ksr[i].ks_name, LX_IF_FROMNATIVE);
+			lxpr_uiobuf_printf(uiobuf, "%6s: %7llu %7llu %4lu "
+			    "%4lu %4u %5u %10u %9lu %8llu %7llu %4lu %4lu %4u "
+			    "%5lu %7u %10u\n",
+			    ksr[i].ks_name,
+			    ifs.rx_bytes, ifs.rx_packets,
+			    ifs.rx_errors, ifs.rx_drop,
+			    0, 0, 0, ifs.rx_multicast,
+			    ifs.tx_bytes, ifs.tx_packets,
+			    ifs.tx_errors, ifs.tx_drop,
+			    0, ifs.collisions, 0, 0);
+		}
+	}
+
+	kmem_free(ksr, sidx);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_dev_mcast(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+static void
+lxpr_inet6_out(const in6_addr_t *addr, char buf[33])
+{
+	const uint8_t *ip = addr->s6_addr;
+	char digits[] = "0123456789abcdef";
+	int i;
+	for (i = 0; i < 16; i++) {
+		buf[2 * i] = digits[ip[i] >> 4];
+		buf[2 * i + 1] = digits[ip[i] & 0xf];
+	}
+	buf[32] = '\0';
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_if_inet6(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	netstack_t *ns;
+	ip_stack_t *ipst;
+	ill_t *ill;
+	ipif_t *ipif;
+	ill_walk_context_t	ctx;
+	char ifname[LIFNAMSIZ], ip6out[33];
+
+	ns = netstack_get_current();
+	if (ns == NULL)
+		return;
+	ipst = ns->netstack_ip;
+
+	rw_enter(&ipst->ips_ill_g_lock, RW_READER);
+	ill = ILL_START_WALK_V6(&ctx, ipst);
+
+	for (; ill != NULL; ill = ill_next(&ctx, ill)) {
+		for (ipif = ill->ill_ipif; ipif != NULL;
+		    ipif = ipif->ipif_next) {
+			uint_t index = ill->ill_phyint->phyint_ifindex;
+			int plen = ip_mask_to_plen_v6(&ipif->ipif_v6net_mask);
+			unsigned int scope = lx_ipv6_scope_convert(
+			    &ipif->ipif_v6lcl_addr);
+			/* Always report PERMANENT flag */
+			int flag = 0x80;
+
+			(void) snprintf(ifname, LIFNAMSIZ, "%s", ill->ill_name);
+			lx_ifname_convert(ifname, LX_IF_FROMNATIVE);
+			lxpr_inet6_out(&ipif->ipif_v6lcl_addr, ip6out);
+
+			lxpr_uiobuf_printf(uiobuf, "%32s %02x %02x %02x %02x"
+			    " %8s\n", ip6out, index, plen, scope, flag, ifname);
+		}
+	}
+	rw_exit(&ipst->ips_ill_g_lock);
+	netstack_rele(ns);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_igmp(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_ip_mr_cache(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_ip_mr_vif(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+static void
+lxpr_format_route_ipv6(ire_t *ire, lxpr_uiobuf_t *uiobuf)
+{
+	uint32_t flags;
+	char name[IFNAMSIZ];
+	char ipv6addr[33];
+
+	lxpr_inet6_out(&ire->ire_addr_v6, ipv6addr);
+	lxpr_uiobuf_printf(uiobuf, "%s %02x ", ipv6addr,
+	    ip_mask_to_plen_v6(&ire->ire_mask_v6));
+
+	/* punt on this for now */
+	lxpr_uiobuf_printf(uiobuf, "%s %02x ",
+	    "00000000000000000000000000000000", 0);
+
+	lxpr_inet6_out(&ire->ire_gateway_addr_v6, ipv6addr);
+	lxpr_uiobuf_printf(uiobuf, "%s", ipv6addr);
+
+	flags = ire->ire_flags &
+	    (RTF_UP|RTF_GATEWAY|RTF_HOST|RTF_DYNAMIC|RTF_MODIFIED);
+	/* Linux's RTF_LOCAL equivalent */
+	if (ire->ire_metrics.iulp_local)
+		flags |= 0x80000000;
+
+	if (ire->ire_ill != NULL) {
+		ill_get_name(ire->ire_ill, name, sizeof (name));
+		lx_ifname_convert(name, LX_IF_FROMNATIVE);
+	} else {
+		name[0] = '\0';
+	}
+
+	lxpr_uiobuf_printf(uiobuf, " %08x %08x %08x %08x %8s\n",
+	    0, /* metric */
+	    ire->ire_refcnt,
+	    0,
+	    flags,
+	    name);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_ipv6_route(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	netstack_t *ns;
+	ip_stack_t *ipst;
+
+	ns = netstack_get_current();
+	if (ns == NULL)
+		return;
+	ipst = ns->netstack_ip;
+
+	/*
+	 * LX branded zones are expected to have exclusive IP stack, hence
+	 * using ALL_ZONES as the zoneid filter.
+	 */
+	ire_walk_v6(&lxpr_format_route_ipv6, uiobuf, ALL_ZONES, ipst);
+
+	netstack_rele(ns);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_mcfilter(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_netstat(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_raw(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+#define	LXPR_SKIP_ROUTE(type)	\
+	(((IRE_IF_CLONE | IRE_BROADCAST | IRE_MULTICAST | \
+	IRE_NOROUTE | IRE_LOOPBACK | IRE_LOCAL) & type) != 0)
+
+static void
+lxpr_format_route_ipv4(ire_t *ire, lxpr_uiobuf_t *uiobuf)
+{
+	uint32_t flags;
+	char name[IFNAMSIZ];
+	ill_t *ill;
+	ire_t *nire;
+	ipif_t *ipif;
+	ipaddr_t gateway;
+
+	if (LXPR_SKIP_ROUTE(ire->ire_type) || ire->ire_testhidden != 0)
+		return;
+
+	/* These route flags have direct Linux equivalents */
+	flags = ire->ire_flags &
+	    (RTF_UP|RTF_GATEWAY|RTF_HOST|RTF_DYNAMIC|RTF_MODIFIED);
+
+	/*
+	 * Search for a suitable IRE for naming purposes.
+	 * On Linux, the default route is typically associated with the
+	 * interface used to access gateway.  The default IRE on Illumos
+	 * typically lacks an ill reference but its parent might have one.
+	 */
+	nire = ire;
+	do {
+		ill = nire->ire_ill;
+		nire = nire->ire_dep_parent;
+	} while (ill == NULL && nire != NULL);
+	if (ill != NULL) {
+		ill_get_name(ill, name, sizeof (name));
+		lx_ifname_convert(name, LX_IF_FROMNATIVE);
+	} else {
+		name[0] = '*';
+		name[1] = '\0';
+	}
+
+	/*
+	 * Linux suppresses the gateway address for directly connected
+	 * interface networks.  To emulate this behavior, we walk all addresses
+	 * of a given route interface.  If one matches the gateway, it is
+	 * displayed as NULL.
+	 */
+	gateway = ire->ire_gateway_addr;
+	if ((ill = ire->ire_ill) != NULL) {
+		for (ipif = ill->ill_ipif; ipif != NULL;
+		    ipif = ipif->ipif_next) {
+			if (ipif->ipif_lcl_addr == gateway) {
+				gateway = 0;
+				break;
+			}
+		}
+	}
+
+	lxpr_uiobuf_printf(uiobuf, "%s\t%08X\t%08X\t%04X\t%d\t%u\t"
+	    "%d\t%08X\t%d\t%u\t%u\n",
+	    name,
+	    ire->ire_addr,
+	    gateway,
+	    flags, 0, 0,
+	    0, /* priority */
+	    ire->ire_mask,
+	    0, 0, /* mss, window */
+	    ire->ire_metrics.iulp_rtt);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_route(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	netstack_t *ns;
+	ip_stack_t *ipst;
+
+	lxpr_uiobuf_printf(uiobuf, "Iface\tDestination\tGateway \tFlags\t"
+	    "RefCnt\tUse\tMetric\tMask\t\tMTU\tWindow\tIRTT\n");
+
+	ns = netstack_get_current();
+	if (ns == NULL)
+		return;
+	ipst = ns->netstack_ip;
+
+	/*
+	 * LX branded zones are expected to have exclusive IP stack, hence
+	 * using ALL_ZONES as the zoneid filter.
+	 */
+	ire_walk_v4(&lxpr_format_route_ipv4, uiobuf, ALL_ZONES, ipst);
+
+	netstack_rele(ns);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_rpc(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_rt_cache(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_sockstat(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+typedef struct lxpr_snmp_table {
+	const char *lst_proto;
+	const char **lst_fields;
+} lxpr_snmp_table_t;
+
+static const char *lxpr_snmp_ip_fields[] = {
+	"forwarding", "defaultTTL", "inReceives", "inHdrErrors",
+	"inAddrErrors", "forwDatagrams", "inUnknownProtos", "inDiscards",
+	"inDelivers", "outRequests", "outDiscards", "outNoRoutes",
+	"reasmTimeout", "reasmReqds", "reasmOKs", "reasmFails", "fragOKs",
+	"fragFails", "fragCreates",
+	NULL
+};
+
+static const char *lxpr_snmp_icmp_fields[] = {
+	"inMsgs", "inErrors", "inCsumErrors", "inDestUnreachs", "inTimeExcds",
+	"inParmProbs", "inSrcQuenchs", "inRedirects", "inEchos", "inEchoReps",
+	"inTimestamps", "inTimestampReps", "inAddrMasks", "inAddrMaskReps",
+	"outMsgs", "outErrors", "outDestUnreachs", "outTimeExcds",
+	"outParmProbs", "outSrcQuenchs", "outRedirects", "outEchos",
+	"outEchoReps", "outTimestamps", "outTimestampReps", "outAddrMasks",
+	"outAddrMaskReps",
+	NULL
+};
+
+static const char *lxpr_snmp_tcp_fields[] = {
+	"rtoAlgorithm", "rtoMin", "rtoMax", "maxConn", "activeOpens",
+	"passiveOpens", "attemptFails", "estabResets", "currEstab", "inSegs",
+	"outSegs", "retransSegs", "inErrs", "outRsts", "inCsumErrors",
+	NULL
+};
+
+static const char *lxpr_snmp_udp_fields[] = {
+	"inDatagrams", "noPorts", "inErrors", "outDatagrams", "rcvbufErrors",
+	"sndbufErrors", "inCsumErrors",
+	NULL
+};
+
+static lxpr_snmp_table_t lxpr_snmp_ip = { "ip", lxpr_snmp_ip_fields };
+static lxpr_snmp_table_t lxpr_snmp_icmp = { "icmp", lxpr_snmp_icmp_fields };
+static lxpr_snmp_table_t lxpr_snmp_tcp = { "tcp", lxpr_snmp_tcp_fields };
+static lxpr_snmp_table_t lxpr_snmp_udp = { "udp", lxpr_snmp_udp_fields };
+
+static lxpr_snmp_table_t *lxpr_net_snmptab[] = {
+	&lxpr_snmp_ip,
+	&lxpr_snmp_icmp,
+	&lxpr_snmp_tcp,
+	&lxpr_snmp_udp,
+	NULL
+};
+
+static void
+lxpr_kstat_print_tab(lxpr_uiobuf_t *uiobuf, lxpr_snmp_table_t *table,
+    kstat_t *kn)
+{
+	kstat_named_t *klist;
+	char upname[KSTAT_STRLEN], upfield[KSTAT_STRLEN];
+	int i, j, num;
+	size_t size;
+
+	klist = (kstat_named_t *)lxpr_kstat_read(kn, B_TRUE, &size, &num);
+	if (klist == NULL)
+		return;
+
+	/* Print the header line, fields capitalized */
+	(void) strncpy(upname, table->lst_proto, KSTAT_STRLEN);
+	upname[0] = toupper(upname[0]);
+	lxpr_uiobuf_printf(uiobuf, "%s:", upname);
+	for (i = 0; table->lst_fields[i] != NULL; i++) {
+		(void) strncpy(upfield, table->lst_fields[i], KSTAT_STRLEN);
+		upfield[0] = toupper(upfield[0]);
+		lxpr_uiobuf_printf(uiobuf, " %s", upfield);
+	}
+	lxpr_uiobuf_printf(uiobuf, "\n%s:", upname);
+
+	/* Then loop back through to print the value line. */
+	for (i = 0; table->lst_fields[i] != NULL; i++) {
+		kstat_named_t *kpoint = NULL;
+		for (j = 0; j < num; j++) {
+			if (strncmp(klist[j].name, table->lst_fields[i],
+			    KSTAT_STRLEN) == 0) {
+				kpoint = &klist[j];
+				break;
+			}
+		}
+		if (kpoint == NULL) {
+			/* Output 0 for unknown fields */
+			lxpr_uiobuf_printf(uiobuf, " 0");
+		} else {
+			switch (kpoint->data_type) {
+			case KSTAT_DATA_INT32:
+				lxpr_uiobuf_printf(uiobuf, " %d",
+				    kpoint->value.i32);
+				break;
+			case KSTAT_DATA_UINT32:
+				lxpr_uiobuf_printf(uiobuf, " %u",
+				    kpoint->value.ui32);
+				break;
+			case KSTAT_DATA_INT64:
+				lxpr_uiobuf_printf(uiobuf, " %ld",
+				    kpoint->value.l);
+				break;
+			case KSTAT_DATA_UINT64:
+				lxpr_uiobuf_printf(uiobuf, " %lu",
+				    kpoint->value.ul);
+				break;
+			}
+		}
+	}
+	lxpr_uiobuf_printf(uiobuf, "\n");
+	kmem_free(klist, size);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_snmp(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	kstat_t *ksr;
+	kstat_t ks0;
+	lxpr_snmp_table_t **table = lxpr_net_snmptab;
+	int i, t, nidx;
+	size_t sidx;
+
+	ks0.ks_kid = 0;
+	ksr = (kstat_t *)lxpr_kstat_read(&ks0, B_FALSE, &sidx, &nidx);
+	if (ksr == NULL)
+		return;
+
+	for (t = 0; table[t] != NULL; t++) {
+		for (i = 0; i < nidx; i++) {
+			if (strncmp(ksr[i].ks_class, "mib2", KSTAT_STRLEN) != 0)
+				continue;
+			if (strncmp(ksr[i].ks_name, table[t]->lst_proto,
+			    KSTAT_STRLEN) == 0) {
+				lxpr_kstat_print_tab(uiobuf, table[t], &ksr[i]);
+				break;
+			}
+		}
+	}
+	kmem_free(ksr, sidx);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_stat(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+}
+
+static int
+lxpr_convert_tcp_state(int st)
+{
+	/*
+	 * Derived from the enum located in the Linux kernel sources:
+	 * include/net/tcp_states.h
+	 */
+	switch (st) {
+	case TCPS_ESTABLISHED:
+		return (1);
+	case TCPS_SYN_SENT:
+		return (2);
+	case TCPS_SYN_RCVD:
+		return (3);
+	case TCPS_FIN_WAIT_1:
+		return (4);
+	case TCPS_FIN_WAIT_2:
+		return (5);
+	case TCPS_TIME_WAIT:
+		return (6);
+	case TCPS_CLOSED:
+		return (7);
+	case TCPS_CLOSE_WAIT:
+		return (8);
+	case TCPS_LAST_ACK:
+		return (9);
+	case TCPS_LISTEN:
+		return (10);
+	case TCPS_CLOSING:
+		return (11);
+	default:
+		/* No translation for TCPS_IDLE, TCPS_BOUND or anything else */
+		return (0);
+	}
+}
+
+static void
+lxpr_format_tcp(lxpr_uiobuf_t *uiobuf, ushort_t ipver)
+{
+	int i, sl = 0;
+	connf_t *connfp;
+	conn_t *connp;
+	netstack_t *ns;
+	ip_stack_t *ipst;
+
+	ASSERT(ipver == IPV4_VERSION || ipver == IPV6_VERSION);
+	if (ipver == IPV4_VERSION) {
+		lxpr_uiobuf_printf(uiobuf, "  sl  local_address rem_address   "
+		    "st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout "
+		    "inode\n");
+	} else {
+		lxpr_uiobuf_printf(uiobuf, "  sl  "
+		    "local_address                         "
+		    "remote_address                        "
+		    "st tx_queue rx_queue tr tm->when retrnsmt   "
+		    "uid  timeout inode\n");
+	}
+	/*
+	 * Due to differences between the Linux and illumos TCP
+	 * implementations, some data will be omitted from the output here.
+	 *
+	 * Valid fields:
+	 *  - local_address
+	 *  - remote_address
+	 *  - st
+	 *  - tx_queue
+	 *  - rx_queue
+	 *  - uid
+	 *  - inode
+	 *
+	 * Omitted/invalid fields
+	 *  - tr
+	 *  - tm->when
+	 *  - retrnsmt
+	 *  - timeout
+	 */
+
+	ns = netstack_get_current();
+	if (ns == NULL)
+		return;
+	ipst = ns->netstack_ip;
+
+	for (i = 0; i < CONN_G_HASH_SIZE; i++) {
+		connfp = &ipst->ips_ipcl_globalhash_fanout[i];
+		connp = NULL;
+		while ((connp =
+		    ipcl_get_next_conn(connfp, connp, IPCL_TCPCONN)) != NULL) {
+			tcp_t *tcp;
+			vattr_t attr;
+			sonode_t *so = (sonode_t *)connp->conn_upper_handle;
+			vnode_t *vp = (so != NULL) ? so->so_vnode : NULL;
+			if (connp->conn_ipversion != ipver)
+				continue;
+			tcp = connp->conn_tcp;
+			if (ipver == IPV4_VERSION) {
+				lxpr_uiobuf_printf(uiobuf,
+				    "%4d: %08X:%04X %08X:%04X ",
+				    ++sl,
+				    connp->conn_laddr_v4,
+				    ntohs(connp->conn_lport),
+				    connp->conn_faddr_v4,
+				    ntohs(connp->conn_fport));
+			} else {
+				lxpr_uiobuf_printf(uiobuf, "%4d: "
+				    "%08X%08X%08X%08X:%04X "
+				    "%08X%08X%08X%08X:%04X ",
+				    ++sl,
+				    connp->conn_laddr_v6.s6_addr32[0],
+				    connp->conn_laddr_v6.s6_addr32[1],
+				    connp->conn_laddr_v6.s6_addr32[2],
+				    connp->conn_laddr_v6.s6_addr32[3],
+				    ntohs(connp->conn_lport),
+				    connp->conn_faddr_v6.s6_addr32[0],
+				    connp->conn_faddr_v6.s6_addr32[1],
+				    connp->conn_faddr_v6.s6_addr32[2],
+				    connp->conn_faddr_v6.s6_addr32[3],
+				    ntohs(connp->conn_fport));
+			}
+
+			/* fetch the simulated inode for the socket */
+			if (vp == NULL ||
+			    VOP_GETATTR(vp, &attr, 0, CRED(), NULL) != 0)
+				attr.va_nodeid = 0;
+
+			lxpr_uiobuf_printf(uiobuf,
+			    "%02X %08X:%08X %02X:%08X %08X "
+			    "%5u %8d %lu %d %p %u %u %u %u %d\n",
+			    lxpr_convert_tcp_state(tcp->tcp_state),
+			    tcp->tcp_rcv_cnt, tcp->tcp_unsent, /* rx/tx queue */
+			    0, 0, /* tr, when */
+			    0, /* per-connection rexmits aren't tracked today */
+			    connp->conn_cred->cr_uid,
+			    0, /* timeout */
+			    /* inode + more */
+			    (ino_t)attr.va_nodeid, 0, NULL, 0, 0, 0, 0, 0);
+		}
+	}
+	netstack_rele(ns);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_tcp(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	lxpr_format_tcp(uiobuf, IPV4_VERSION);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_tcp6(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	lxpr_format_tcp(uiobuf, IPV6_VERSION);
+}
+
+static void
+lxpr_format_udp(lxpr_uiobuf_t *uiobuf, ushort_t ipver)
+{
+	int i, sl = 0;
+	connf_t *connfp;
+	conn_t *connp;
+	netstack_t *ns;
+	ip_stack_t *ipst;
+
+	ASSERT(ipver == IPV4_VERSION || ipver == IPV6_VERSION);
+	if (ipver == IPV4_VERSION) {
+		lxpr_uiobuf_printf(uiobuf, "  sl  local_address rem_address"
+		    "   st tx_queue rx_queue tr tm->when retrnsmt   uid"
+		    "  timeout inode ref pointer drops\n");
+	} else {
+		lxpr_uiobuf_printf(uiobuf, "  sl  "
+		    "local_address                         "
+		    "remote_address                        "
+		    "st tx_queue rx_queue tr tm->when retrnsmt   "
+		    "uid  timeout inode ref pointer drops\n");
+	}
+	/*
+	 * Due to differences between the Linux and illumos UDP
+	 * implementations, some data will be omitted from the output here.
+	 *
+	 * Valid fields:
+	 *  - local_address
+	 *  - remote_address
+	 *  - st: limited
+	 *  - uid
+	 *
+	 * Omitted/invalid fields
+	 *  - tx_queue
+	 *  - rx_queue
+	 *  - tr
+	 *  - tm->when
+	 *  - retrnsmt
+	 *  - timeout
+	 *  - inode
+	 */
+
+	ns = netstack_get_current();
+	if (ns == NULL)
+		return;
+	ipst = ns->netstack_ip;
+
+	for (i = 0; i < CONN_G_HASH_SIZE; i++) {
+		connfp = &ipst->ips_ipcl_globalhash_fanout[i];
+		connp = NULL;
+		while ((connp =
+		    ipcl_get_next_conn(connfp, connp, IPCL_UDPCONN)) != NULL) {
+			udp_t *udp;
+			int state = 0;
+			vattr_t attr;
+			sonode_t *so = (sonode_t *)connp->conn_upper_handle;
+			vnode_t *vp = (so != NULL) ? so->so_vnode : NULL;
+			if (connp->conn_ipversion != ipver)
+				continue;
+			udp = connp->conn_udp;
+			if (ipver == IPV4_VERSION) {
+				lxpr_uiobuf_printf(uiobuf,
+				    "%4d: %08X:%04X %08X:%04X ",
+				    ++sl,
+				    connp->conn_laddr_v4,
+				    ntohs(connp->conn_lport),
+				    connp->conn_faddr_v4,
+				    ntohs(connp->conn_fport));
+			} else {
+				lxpr_uiobuf_printf(uiobuf, "%4d: "
+				    "%08X%08X%08X%08X:%04X "
+				    "%08X%08X%08X%08X:%04X ",
+				    ++sl,
+				    connp->conn_laddr_v6.s6_addr32[0],
+				    connp->conn_laddr_v6.s6_addr32[1],
+				    connp->conn_laddr_v6.s6_addr32[2],
+				    connp->conn_laddr_v6.s6_addr32[3],
+				    ntohs(connp->conn_lport),
+				    connp->conn_faddr_v6.s6_addr32[0],
+				    connp->conn_faddr_v6.s6_addr32[1],
+				    connp->conn_faddr_v6.s6_addr32[2],
+				    connp->conn_faddr_v6.s6_addr32[3],
+				    ntohs(connp->conn_fport));
+			}
+
+			switch (udp->udp_state) {
+			case TS_UNBND:
+			case TS_IDLE:
+				state = 7;
+				break;
+			case TS_DATA_XFER:
+				state = 1;
+				break;
+			}
+
+			/* fetch the simulated inode for the socket */
+			if (vp == NULL ||
+			    VOP_GETATTR(vp, &attr, 0, CRED(), NULL) != 0)
+				attr.va_nodeid = 0;
+
+			lxpr_uiobuf_printf(uiobuf,
+			    "%02X %08X:%08X %02X:%08X %08X "
+			    "%5u %8d %lu %d %p %d\n",
+			    state,
+			    0, 0, /* rx/tx queue */
+			    0, 0, /* tr, when */
+			    0, /* retrans */
+			    connp->conn_cred->cr_uid,
+			    0, /* timeout */
+			    /* inode, ref, pointer, drops */
+			    (ino_t)attr.va_nodeid, 0, NULL, 0);
+		}
+	}
+	netstack_rele(ns);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_udp(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	lxpr_format_udp(uiobuf, IPV4_VERSION);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_udp6(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	lxpr_format_udp(uiobuf, IPV6_VERSION);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_net_unix(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	sonode_t *so;
+	zoneid_t zoneid = getzoneid();
+
+	lxpr_uiobuf_printf(uiobuf, "Num       RefCount Protocol Flags    Type "
+	    "St Inode Path\n");
+
+	mutex_enter(&socklist.sl_lock);
+	for (so = socklist.sl_list; so != NULL;
+	    so = _SOTOTPI(so)->sti_next_so) {
+		vnode_t *vp = so->so_vnode;
+		vattr_t attr;
+		sotpi_info_t *sti;
+		const char *name = NULL;
+		int status = 0;
+		int type = 0;
+		int flags = 0;
+
+		/* Only process active sonodes in this zone */
+		if (so->so_count == 0 || so->so_zoneid != zoneid)
+			continue;
+
+		/*
+		 * Grab the inode, if possible.
+		 * This must be done before entering so_lock.
+		 */
+		if (vp == NULL ||
+		    VOP_GETATTR(vp, &attr, 0, CRED(), NULL) != 0)
+			attr.va_nodeid = 0;
+
+		mutex_enter(&so->so_lock);
+		sti = _SOTOTPI(so);
+
+		if (sti->sti_laddr_sa != NULL &&
+		    sti->sti_laddr_len > 0) {
+			name = sti->sti_laddr_sa->sa_data;
+		} else if (sti->sti_faddr_sa != NULL &&
+		    sti->sti_faddr_len > 0) {
+			name = sti->sti_faddr_sa->sa_data;
+		}
+
+		/*
+		 * Derived from enum values in Linux kernel source:
+		 * include/uapi/linux/net.h
+		 */
+		if ((so->so_state & SS_ISDISCONNECTING) != 0) {
+			status = 4;
+		} else if ((so->so_state & SS_ISCONNECTING) != 0) {
+			status = 2;
+		} else if ((so->so_state & SS_ISCONNECTED) != 0) {
+			status = 3;
+		} else {
+			status = 1;
+			/* Add ACC flag for stream-type server sockets */
+			if (so->so_type != SOCK_DGRAM &&
+			    sti->sti_laddr_sa != NULL)
+				flags |= 0x10000;
+		}
+
+		/* Convert to Linux type */
+		switch (so->so_type) {
+		case SOCK_DGRAM:
+			type = 2;
+			break;
+		case SOCK_SEQPACKET:
+			type = 5;
+			break;
+		default:
+			type = 1;
+		}
+
+		lxpr_uiobuf_printf(uiobuf, "%p: %08X %08X %08X %04X %02X %5llu",
+		    so,
+		    so->so_count,
+		    0, /* proto, always 0 */
+		    flags,
+		    type,
+		    status,
+		    (ino_t)attr.va_nodeid);
+
+		/*
+		 * Due to shortcomings in the abstract socket emulation, they
+		 * cannot be properly represented here (as @<path>).
+		 *
+		 * This will be the case until they are better implemented.
+		 */
+		if (name != NULL)
+			lxpr_uiobuf_printf(uiobuf, " %s\n", name);
+		else
+			lxpr_uiobuf_printf(uiobuf, "\n");
+		mutex_exit(&so->so_lock);
+	}
+	mutex_exit(&socklist.sl_lock);
+}
+
+/*
+ * lxpr_read_kmsg(): read the contents of the kernel message queue. We
+ * translate this into the reception of console messages for this zone; each
+ * read copies out a single zone console message, or blocks until the next one
+ * is produced, unless we're open non-blocking, in which case we return after
+ * 1ms.
+ */
+
+#define	LX_KMSG_PRI	"<0>"
+
+/* ARGSUSED */
+static void
+lxpr_read_kmsg(lxpr_node_t *lxpnp, struct lxpr_uiobuf *uiobuf, ldi_handle_t lh)
+{
+	mblk_t		*mp;
+	timestruc_t	to;
+	timestruc_t	*tp = NULL;
+
+	ASSERT(lxpnp->lxpr_type == LXPR_KMSG);
+
+	if (lxpr_uiobuf_nonblock(uiobuf)) {
+		to.tv_sec = 0;
+		to.tv_nsec = 1000000; /* 1msec */
+		tp = &to;
+	}
+
+	if (ldi_getmsg(lh, &mp, tp) == 0) {
+		/*
+		 * lx procfs doesn't like successive reads to the same file
+		 * descriptor unless we do an explicit rewind each time.
+		 */
+		lxpr_uiobuf_seek(uiobuf, 0);
+
+		lxpr_uiobuf_printf(uiobuf, "%s%s", LX_KMSG_PRI,
+		    mp->b_cont->b_rptr);
+
+		freemsg(mp);
+	}
+}
+
+/*
+ * lxpr_read_loadavg(): read the contents of the "loadavg" file.  We do just
+ * enough for uptime and other simple lxproc readers to work
+ */
+extern int nthread;
+
+static void
+lxpr_read_loadavg(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	ulong_t avenrun1;
+	ulong_t avenrun5;
+	ulong_t avenrun15;
+	ulong_t avenrun1_cs;
+	ulong_t avenrun5_cs;
+	ulong_t avenrun15_cs;
+	int loadavg[3];
+	int *loadbuf;
+	cpupart_t *cp;
+	zone_t *zone = LXPTOZ(lxpnp);
+
+	uint_t nrunnable = 0;
+	rctl_qty_t nlwps;
+
+	ASSERT(lxpnp->lxpr_type == LXPR_LOADAVG);
+
+	mutex_enter(&cpu_lock);
+
+	/*
+	 * Need to add up values over all CPU partitions. If pools are active,
+	 * only report the values of the zone's partition, which by definition
+	 * includes the current CPU.
+	 */
+	if (pool_pset_enabled()) {
+		psetid_t psetid = zone_pset_get(curproc->p_zone);
+
+		ASSERT(curproc->p_zone != &zone0);
+		cp = CPU->cpu_part;
+
+		nrunnable = cp->cp_nrunning + cp->cp_nrunnable;
+		(void) cpupart_get_loadavg(psetid, &loadavg[0], 3);
+		loadbuf = &loadavg[0];
+	} else {
+		cp = cp_list_head;
+		do {
+			nrunnable += cp->cp_nrunning + cp->cp_nrunnable;
+		} while ((cp = cp->cp_next) != cp_list_head);
+
+		loadbuf = zone == global_zone ?
+		    &avenrun[0] : zone->zone_avenrun;
+	}
+
+	/*
+	 * If we're in the non-global zone, we'll report the total number of
+	 * LWPs in the zone for the "nproc" parameter of /proc/loadavg,
+	 * otherwise will just use nthread (which will include kernel threads,
+	 * but should be good enough for lxproc).
+	 */
+	nlwps = zone == global_zone ? nthread : zone->zone_nlwps;
+
+	mutex_exit(&cpu_lock);
+
+	avenrun1 = loadbuf[0] >> FSHIFT;
+	avenrun1_cs = ((loadbuf[0] & (FSCALE-1)) * 100) >> FSHIFT;
+	avenrun5 = loadbuf[1] >> FSHIFT;
+	avenrun5_cs = ((loadbuf[1] & (FSCALE-1)) * 100) >> FSHIFT;
+	avenrun15 = loadbuf[2] >> FSHIFT;
+	avenrun15_cs = ((loadbuf[2] & (FSCALE-1)) * 100) >> FSHIFT;
+
+	lxpr_uiobuf_printf(uiobuf,
+	    "%ld.%02d %ld.%02d %ld.%02d %d/%d %d\n",
+	    avenrun1, avenrun1_cs,
+	    avenrun5, avenrun5_cs,
+	    avenrun15, avenrun15_cs,
+	    nrunnable, nlwps, 0);
+}
+
+/*
+ * lxpr_read_meminfo(): read the contents of the "meminfo" file.
+ */
+static void
+lxpr_read_meminfo(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	zone_t *zone = LXPTOZ(lxpnp);
+	int global = zone == global_zone;
+	long total_mem, free_mem, total_swap;
+
+	ASSERT(lxpnp->lxpr_type == LXPR_MEMINFO);
+
+	if (global || zone->zone_phys_mem_ctl == UINT64_MAX) {
+		total_mem = physmem * PAGESIZE;
+		free_mem = freemem * PAGESIZE;
+	} else {
+		total_mem = zone->zone_phys_mem_ctl;
+		free_mem = zone->zone_phys_mem_ctl - zone->zone_phys_mem;
+	}
+
+	if (global || zone->zone_max_swap_ctl == UINT64_MAX) {
+		total_swap = k_anoninfo.ani_max * PAGESIZE;
+	} else {
+		mutex_enter(&zone->zone_mem_lock);
+		total_swap = zone->zone_max_swap_ctl;
+		mutex_exit(&zone->zone_mem_lock);
+	}
+
+	/*
+	 * SwapFree
+	 * On illumos we reserve swap up front, whereas on Linux they just
+	 * wing it and kill a random process if they run out of backing store
+	 * for virtual memory. Our swap reservation doesn't translate to that
+	 * model, so just inform the caller that no swap is being used.
+	 */
+	lxpr_uiobuf_printf(uiobuf,
+	    "MemTotal:  %8lu kB\n"
+	    "MemFree:   %8lu kB\n"
+	    "MemShared: %8u kB\n"
+	    "Buffers:   %8u kB\n"
+	    "Cached:    %8u kB\n"
+	    "SwapCached:%8u kB\n"
+	    "Active:    %8u kB\n"
+	    "Inactive:  %8u kB\n"
+	    "HighTotal: %8u kB\n"
+	    "HighFree:  %8u kB\n"
+	    "LowTotal:  %8u kB\n"
+	    "LowFree:   %8u kB\n"
+	    "SwapTotal: %8lu kB\n"
+	    "SwapFree:  %8lu kB\n",
+	    btok(total_mem),				/* MemTotal */
+	    btok(free_mem),				/* MemFree */
+	    0,						/* MemShared */
+	    0,						/* Buffers */
+	    0,						/* Cached */
+	    0,						/* SwapCached */
+	    0,						/* Active */
+	    0,						/* Inactive */
+	    0,						/* HighTotal */
+	    0,						/* HighFree */
+	    btok(total_mem),				/* LowTotal */
+	    btok(free_mem),				/* LowFree */
+	    btok(total_swap),				/* SwapTotal */
+	    btok(total_swap));				/* SwapFree */
+}
+
+/*
+ * lxpr_read_mounts():
+ *
+ * Note: we currently also use this for /proc/{pid}/mounts since we don't
+ * yet support mount namespaces.
+ */
+/* ARGSUSED */
+static void
+lxpr_read_mounts(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	zone_t *zone = LXPTOZ(lxpnp);
+	list_t *mounts;
+	lxpr_mount_entry_t *lme;
+
+	mounts = lxpr_enumerate_mounts(zone);
+
+	/*
+	 * now we can run through what we've extracted without holding
+	 * vfs_list_read_lock()
+	 */
+	lme = list_remove_head(mounts);
+	while (lme != NULL) {
+		char *resource, *mntpt, *fstype, *rwflag;
+		vnode_t *vp;
+		int error;
+
+		mntpt = (char *)refstr_value(lme->lme_mntpt);
+		resource = (char *)refstr_value(lme->lme_resource);
+
+		if (mntpt == NULL || mntpt[0] == '\0') {
+			goto nextp;
+		}
+		mntpt = ZONE_PATH_TRANSLATE(mntpt, zone);
+		error = lookupname(mntpt, UIO_SYSSPACE, FOLLOW, NULLVPP, &vp);
+		if (error != 0) {
+			goto nextp;
+		} else if ((vp->v_flag & VROOT) == 0 && !lme->lme_force) {
+			VN_RELE(vp);
+			goto nextp;
+		}
+		VN_RELE(vp);
+
+		if (resource != NULL && resource[0] != '\0') {
+			if (resource[0] == '/') {
+				resource = ZONE_PATH_VISIBLE(resource, zone) ?
+				    ZONE_PATH_TRANSLATE(resource, zone) : mntpt;
+			}
+		} else {
+			resource = "none";
+		}
+
+		/*  Make things look more like Linux. */
+		fstype = vfssw[lme->lme_fstype].vsw_name;
+		if (lxpr_clean_mntent(&mntpt, &fstype, &resource) != 0 &&
+		    !lme->lme_force) {
+			goto nextp;
+		}
+		rwflag = ((lme->lme_flag & VFS_RDONLY) == 0) ? "rw" : "ro";
+
+		lxpr_uiobuf_printf(uiobuf, "%s %s %s %s 0 0\n",
+		    resource, mntpt, fstype, rwflag);
+
+nextp:
+		refstr_rele(lme->lme_mntpt);
+		refstr_rele(lme->lme_resource);
+		kmem_free(lme, sizeof (lxpr_mount_entry_t));
+		lme = list_remove_head(mounts);
+	}
+
+	list_destroy(mounts);
+	kmem_free(mounts, sizeof (list_t));
+}
+
+/*
+ * lxpr_read_partitions():
+ *
+ * Over the years, /proc/partitions has been made considerably smaller -- to
+ * the point that it really is only major number, minor number, number of
+ * blocks (which we report as 0), and partition name.
+ *
+ * We support this because some things want to see it to make sense of
+ * /proc/diskstats, and also because "fdisk -l" and a few other things look
+ * here to find all disks on the system.
+ */
+/* ARGSUSED */
+static void
+lxpr_read_partitions(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	lx_zone_data_t *lxzd;
+	lx_virt_disk_t *vd;
+
+	ASSERT(lxpnp->lxpr_type == LXPR_PARTITIONS);
+
+	lxpr_uiobuf_printf(uiobuf, "major minor  #blocks  name\n\n");
+
+	lxzd = ztolxzd(curproc->p_zone);
+	if (lxzd == NULL)
+		return;
+	ASSERT(lxzd->lxzd_vdisks != NULL);
+
+	vd = list_head(lxzd->lxzd_vdisks);
+	while (vd != NULL) {
+		lxpr_uiobuf_printf(uiobuf, "%4d  %7d %10d %s\n",
+		    getmajor(vd->lxvd_emul_dev), getminor(vd->lxvd_emul_dev),
+		    0, vd->lxvd_name);
+		vd = list_next(lxzd->lxzd_vdisks, vd);
+	}
+}
+
+/*
+ * There aren't many actual devices inside a zone but we want to provide the
+ * major numbers for the pseudo devices that do exist, including  our pts/ptm
+ * device, as well as the zvol virtual disk device. We simply hardcode the
+ * emulated major numbers that are used elsewhere in the code and that match
+ * the expected Linux major numbers. See lx devfs where some of the major
+ * numbers have no defined constants.
+ */
+/* ARGSUSED */
+static void
+lxpr_read_devices(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	ASSERT(lxpnp->lxpr_type == LXPR_DEVICES);
+
+	lxpr_uiobuf_printf(uiobuf, "Character devices:\n");
+	lxpr_uiobuf_printf(uiobuf, "%3d /dev/tty\n", LX_TTY_MAJOR);
+	lxpr_uiobuf_printf(uiobuf, "%3d /dev/console\n", LX_TTY_MAJOR);
+	lxpr_uiobuf_printf(uiobuf, "%3d /dev/ptmx\n", LX_TTY_MAJOR);
+	lxpr_uiobuf_printf(uiobuf, "%3d ptm\n", LX_PTM_MAJOR);
+	lxpr_uiobuf_printf(uiobuf, "%3d pts\n", LX_PTS_MAJOR_MIN);
+
+	lxpr_uiobuf_printf(uiobuf, "\nBlock devices:\n");
+	lxpr_uiobuf_printf(uiobuf, "%3d zvol\n", LX_MAJOR_DISK);
+}
+
+/*
+ * lxpr_read_diskstats():
+ *
+ * See the block comment above the per-device output-generating line for the
+ * details of the format.
+ */
+/* ARGSUSED */
+static void
+lxpr_read_diskstats(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	zone_t *zone = LXPTOZ(lxpnp);
+	lx_zone_data_t *lxzd;
+	kstat_t kn;
+	int num;
+	zone_vfs_kstat_t *kip;
+	size_t size;
+	lx_virt_disk_t *vd;
+
+	ASSERT(lxpnp->lxpr_type == LXPR_DISKSTATS);
+
+	lxzd = ztolxzd(zone);
+	if (lxzd == NULL)
+		return;
+	ASSERT(lxzd->lxzd_vdisks != NULL);
+
+	/*
+	 * Use the zone_vfs kstat, which is a superset of a kstat_io_t, since
+	 * it tracks IO at the zone level.
+	 */
+	(void) strlcpy(kn.ks_module, "zone_vfs", sizeof (kn.ks_module));
+	(void) strlcpy(kn.ks_name, zone->zone_name, sizeof (kn.ks_name));
+	kn.ks_instance = getzoneid();
+
+	kip = (zone_vfs_kstat_t *)lxpr_kstat_read(&kn, B_TRUE, &size, &num);
+	if (kip == NULL)
+		return;
+
+	if (size < sizeof (kstat_io_t)) {
+		kmem_free(kip, size);
+		return;
+	}
+
+	/*
+	 * Because the zone vfs stats are tracked at the zone level we use
+	 * the same kstat for the zone's virtual disk (the zpool) and any
+	 * zvols that might also visible within the zone.
+	 */
+	vd = list_head(lxzd->lxzd_vdisks);
+	while (vd != NULL) {
+		/*
+		 * /proc/diskstats is defined to have one line of output for
+		 * each block device, with each line containing the following
+		 * 14 fields:
+		 *
+		 *	1 - major number
+		 *	2 - minor mumber
+		 *	3 - device name
+		 *	4 - reads completed successfully
+		 * 	5 - reads merged
+		 *	6 - sectors read
+		 *	7 - time spent reading (ms)
+		 *	8 - writes completed
+		 *	9 - writes merged
+		 *	10 - sectors written
+		 *	11 - time spent writing (ms)
+		 *	12 - I/Os currently in progress
+		 *	13 - time spent doing I/Os (ms)
+		 *	14 - weighted time spent doing I/Os (ms)
+		 *
+		 * One small hiccup:  we don't actually keep track of time
+		 * spent reading vs. time spent writing -- we keep track of
+		 * time waiting vs. time actually performing I/O.  While we
+		 * could divide the total time by the I/O mix (making the
+		 * obviously wrong assumption that I/O operations all take the
+		 * same amount of time), this has the undesirable side-effect
+		 * of moving backwards.  Instead, we report the total time
+		 * (read + write) for all three stats (read, write, total).
+		 * This is also a lie of sorts, but it should be more
+		 * immediately clear to the user that reads and writes are
+		 * each being double-counted as the other.
+		 *
+		 * Since certain consumers interpret the major/minor numbers to
+		 * infer device names, some translation is required to avoid
+		 * output which results in totally unexpected results.
+		 */
+
+		lxpr_uiobuf_printf(uiobuf, "%4d %7d %s ",
+		    getmajor(vd->lxvd_emul_dev),
+		    getminor(vd->lxvd_emul_dev),
+		    vd->lxvd_name);
+
+		if (vd->lxvd_type == LXVD_ZFS_DS) {
+			/*
+			 * Use the zone-wide vfs stats for any zfs datasets
+			 * represented via virtual devices.
+			 */
+#define	KV(N)	kip->zv_ ## N.value.ui64
+#define	NS_PER_MS	(uint64_t)(NANOSEC / MILLISEC)
+			lxpr_uiobuf_printf(uiobuf,
+			    "%llu %llu %llu %llu "
+			    "%llu %llu %llu %llu "
+			    "%llu %llu %llu\n",
+			    (uint64_t)KV(reads), 0LL,
+			    KV(nread) / (uint64_t)LXPR_SECTOR_SIZE,
+			    (KV(rtime) + KV(wtime)) / NS_PER_MS,
+			    (uint64_t)KV(writes), 0LL,
+			    KV(nwritten) / (uint64_t)LXPR_SECTOR_SIZE,
+			    (KV(rtime) + KV(wtime)) / NS_PER_MS,
+			    (uint64_t)(KV(rcnt) + KV(wcnt)),
+			    (KV(rtime) + KV(wtime)) / NS_PER_MS,
+			    (KV(rlentime) + KV(wlentime)) / NS_PER_MS);
+#undef	KV
+#undef	NS_PER_MS
+		} else {
+			/*
+			 * Report nearly-zeroed statistics for other devices.
+			 *
+			 * Since iostat will ignore devices which report no
+			 * succesful reads or writes, a single read of one
+			 * sector, taking 1ms, is reported.
+			 */
+			lxpr_uiobuf_printf(uiobuf,
+			    "1 0 1 1 0 0 0 0 0 0 0\n");
+		}
+
+		vd = list_next(lxzd->lxzd_vdisks, vd);
+	}
+
+	kmem_free(kip, size);
+}
+
+/*
+ * lxpr_read_version(): read the contents of the "version" file.
+ */
+/* ARGSUSED */
+static void
+lxpr_read_version(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	lx_zone_data_t *lxzd = ztolxzd(LXPTOZ(lxpnp));
+	lx_proc_data_t *lxpd = ptolxproc(curproc);
+	char release[LX_KERN_RELEASE_MAX];
+	char version[LX_KERN_VERSION_MAX];
+
+	mutex_enter(&lxzd->lxzd_lock);
+	(void) strlcpy(release, lxzd->lxzd_kernel_release, sizeof (release));
+	(void) strlcpy(version, lxzd->lxzd_kernel_version, sizeof (version));
+	mutex_exit(&lxzd->lxzd_lock);
+
+	/* Use per-process overrides, if specified */
+	if (lxpd != NULL && lxpd->l_uname_release[0] != '\0') {
+		(void) strlcpy(release, lxpd->l_uname_release,
+		    sizeof (release));
+	}
+	if (lxpd != NULL && lxpd->l_uname_version[0] != '\0') {
+		(void) strlcpy(version, lxpd->l_uname_version,
+		    sizeof (version));
+	}
+
+	lxpr_uiobuf_printf(uiobuf,
+	    "%s version %s (%s version %d.%d.%d) %s\n",
+	    LX_UNAME_SYSNAME, release,
+#if defined(__GNUC__)
+	    "gcc", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__,
+#else
+	    "cc", 1, 0, 0,
+#endif
+	    version);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_vmstat(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	cpu_t *cp, *cpstart;
+	int pools_enabled;
+
+	ulong_t pgpgin_cum    = 0;
+	ulong_t pgpgout_cum   = 0;
+	ulong_t pgswapout_cum = 0;
+	ulong_t pgswapin_cum  = 0;
+
+	mutex_enter(&cpu_lock);
+	pools_enabled = pool_pset_enabled();
+	/* Calculate cumulative stats */
+	cp = cpstart = CPU->cpu_part->cp_cpulist;
+	do {
+		/* Only count CPUs which are present and active. */
+		if ((cp->cpu_flags & CPU_EXISTS) == 0) {
+			continue;
+		}
+
+		pgpgin_cum += CPU_STATS(cp, vm.pgpgin);
+		pgpgout_cum += CPU_STATS(cp, vm.pgpgout);
+		pgswapin_cum += CPU_STATS(cp, vm.pgswapin);
+		pgswapout_cum += CPU_STATS(cp, vm.pgswapout);
+
+		if (pools_enabled)
+			cp = cp->cpu_next_part;
+		else
+			cp = cp->cpu_next;
+	} while (cp != cpstart);
+	mutex_exit(&cpu_lock);
+
+	/*
+	 * Needless to say, the metrics presented by vmstat are very specific
+	 * to the internals of the Linux kernel.  There is little per-zone
+	 * information which can be translated in a meaningful way to fit the
+	 * expected fields.  For the time being, the output is kept sparse.
+	 */
+	lxpr_uiobuf_printf(uiobuf,
+	    "pgpgin %lu\n"
+	    "pgpgout %lu\n"
+	    "pswpin %lu\n"
+	    "pswpout %lu\n",
+	    pgpgin_cum,
+	    pgpgout_cum,
+	    pgswapin_cum,
+	    pgswapout_cum);
+}
+
+/*
+ * lxpr_read_stat(): read the contents of the "stat" file.
+ *
+ */
+/* ARGSUSED */
+static void
+lxpr_read_stat(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	cpu_t *cp, *cpstart;
+	int pools_enabled;
+	ulong_t idle_cum = 0;
+	ulong_t sys_cum  = 0;
+	ulong_t user_cum = 0;
+	ulong_t irq_cum = 0;
+	ulong_t cpu_nrunnable_cum = 0;
+	ulong_t w_io_cum = 0;
+
+	ulong_t pgpgin_cum    = 0;
+	ulong_t pgpgout_cum   = 0;
+	ulong_t pgswapout_cum = 0;
+	ulong_t pgswapin_cum  = 0;
+	ulong_t intr_cum = 0;
+	ulong_t pswitch_cum = 0;
+	ulong_t forks_cum = 0;
+	hrtime_t msnsecs[NCMSTATES];
+	/* is the emulated release > 2.4 */
+	boolean_t newer_than24 = lx_kern_release_cmp(LXPTOZ(lxpnp), "2.4") > 0;
+	zone_t *zone = LXPTOZ(lxpnp);
+	const char *fmtstr0, *fmtstr1;
+	/* temporary variable since scalehrtime modifies data in place */
+	hrtime_t tmptime;
+
+	ASSERT(lxpnp->lxpr_type == LXPR_STAT);
+
+	mutex_enter(&cpu_lock);
+	pools_enabled = pool_pset_enabled();
+
+	/* Calculate cumulative stats */
+	cp = cpstart = CPU->cpu_part->cp_cpulist;
+	do {
+		int i;
+
+		/*
+		 * Don't count CPUs that aren't even in the system
+		 * or aren't up yet.
+		 */
+		if ((cp->cpu_flags & CPU_EXISTS) == 0) {
+			continue;
+		}
+
+		get_cpu_mstate(cp, msnsecs);
+
+		idle_cum += NSEC_TO_TICK(msnsecs[CMS_IDLE]);
+		sys_cum  += NSEC_TO_TICK(msnsecs[CMS_SYSTEM]);
+		user_cum += NSEC_TO_TICK(msnsecs[CMS_USER]);
+
+		pgpgin_cum += CPU_STATS(cp, vm.pgpgin);
+		pgpgout_cum += CPU_STATS(cp, vm.pgpgout);
+		pgswapin_cum += CPU_STATS(cp, vm.pgswapin);
+		pgswapout_cum += CPU_STATS(cp, vm.pgswapout);
+
+
+		if (newer_than24) {
+			cpu_nrunnable_cum += cp->cpu_disp->disp_nrunnable;
+			w_io_cum += CPU_STATS(cp, sys.iowait);
+			for (i = 0; i < NCMSTATES; i++) {
+				tmptime = cp->cpu_intracct[i];
+				scalehrtime(&tmptime);
+				irq_cum += NSEC_TO_TICK(tmptime);
+			}
+		}
+
+		for (i = 0; i < PIL_MAX; i++)
+			intr_cum += CPU_STATS(cp, sys.intr[i]);
+
+		pswitch_cum += CPU_STATS(cp, sys.pswitch);
+		forks_cum += CPU_STATS(cp, sys.sysfork);
+		forks_cum += CPU_STATS(cp, sys.sysvfork);
+
+		if (pools_enabled)
+			cp = cp->cpu_next_part;
+		else
+			cp = cp->cpu_next;
+	} while (cp != cpstart);
+
+	if (lx_kern_release_cmp(zone, "2.6.33") >= 0) {
+		fmtstr0 = "cpu %lu 0 %lu %lu 0 %lu 0 0 0 0\n";
+		fmtstr1 = "cpu%d %lu 0 %lu %lu 0 %lu 0 0 0 0\n";
+	} else if (lx_kern_release_cmp(zone, "2.6.24") >= 0) {
+		fmtstr0 = "cpu %lu 0 %lu %lu 0 %lu 0 0 0\n";
+		fmtstr1 = "cpu%d %lu 0 %lu %lu 0 %lu 0 0 0\n";
+	} else if (lx_kern_release_cmp(zone, "2.6.11") >= 0) {
+		fmtstr0 = "cpu %lu 0 %lu %lu 0 %lu 0 0\n";
+		fmtstr1 = "cpu%d %lu 0 %lu %lu 0 %lu 0 0\n";
+	} else if (lx_kern_release_cmp(zone, "2.5.41") >= 0) {
+		fmtstr0 = "cpu %lu 0 %lu %lu 0 %lu 0\n";
+		fmtstr1 = "cpu%d %lu 0 %lu %lu 0 %lu 0\n";
+	} else {
+		/* Note: we pass an unused param to these fmt strings */
+		fmtstr0 = "cpu %lu 0 %lu %lu\n";
+		fmtstr1 = "cpu%d %lu 0 %lu %lu\n";
+	}
+
+	lxpr_uiobuf_printf(uiobuf, fmtstr0,
+	    user_cum, sys_cum, idle_cum, irq_cum);
+
+	/* Do per processor stats */
+	do {
+		int i;
+
+		ulong_t idle_ticks;
+		ulong_t sys_ticks;
+		ulong_t user_ticks;
+		ulong_t irq_ticks = 0;
+
+		/*
+		 * Don't count CPUs that aren't even in the system
+		 * or aren't up yet.
+		 */
+		if ((cp->cpu_flags & CPU_EXISTS) == 0) {
+			continue;
+		}
+
+		get_cpu_mstate(cp, msnsecs);
+
+		idle_ticks = NSEC_TO_TICK(msnsecs[CMS_IDLE]);
+		sys_ticks  = NSEC_TO_TICK(msnsecs[CMS_SYSTEM]);
+		user_ticks = NSEC_TO_TICK(msnsecs[CMS_USER]);
+
+		for (i = 0; i < NCMSTATES; i++) {
+			tmptime = cp->cpu_intracct[i];
+			scalehrtime(&tmptime);
+			irq_ticks += NSEC_TO_TICK(tmptime);
+		}
+
+		lxpr_uiobuf_printf(uiobuf, fmtstr1,
+		    cp->cpu_id, user_ticks, sys_ticks, idle_ticks, irq_ticks);
+
+		if (pools_enabled)
+			cp = cp->cpu_next_part;
+		else
+			cp = cp->cpu_next;
+	} while (cp != cpstart);
+
+	mutex_exit(&cpu_lock);
+
+	if (newer_than24) {
+		lxpr_uiobuf_printf(uiobuf,
+		    "page %lu %lu\n"
+		    "swap %lu %lu\n"
+		    "intr %lu\n"
+		    "ctxt %lu\n"
+		    "btime %lu\n"
+		    "processes %lu\n"
+		    "procs_running %lu\n"
+		    "procs_blocked %lu\n",
+		    pgpgin_cum, pgpgout_cum,
+		    pgswapin_cum, pgswapout_cum,
+		    intr_cum,
+		    pswitch_cum,
+		    boot_time,
+		    forks_cum,
+		    cpu_nrunnable_cum,
+		    w_io_cum);
+	} else {
+		lxpr_uiobuf_printf(uiobuf,
+		    "page %lu %lu\n"
+		    "swap %lu %lu\n"
+		    "intr %lu\n"
+		    "ctxt %lu\n"
+		    "btime %lu\n"
+		    "processes %lu\n",
+		    pgpgin_cum, pgpgout_cum,
+		    pgswapin_cum, pgswapout_cum,
+		    intr_cum,
+		    pswitch_cum,
+		    boot_time,
+		    forks_cum);
+	}
+}
+
+/*
+ * lxpr_read_swaps():
+ *
+ * We don't support swap files or partitions, but some programs like to look
+ * here just to check we have some swap on the system, so we lie and show
+ * our entire swap cap as one swap partition. See lxpr_read_meminfo for an
+ * explanation on why we report 0 used swap.
+ *
+ * It is important to use formatting identical to the Linux implementation
+ * so that consumers do not break. See swap_show() in mm/swapfile.c.
+ */
+/* ARGSUSED */
+static void
+lxpr_read_swaps(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	zone_t *zone = curzone;
+	uint64_t totswap, usedswap;
+
+	if (zone == global_zone || zone->zone_max_swap_ctl == UINT64_MAX) {
+		totswap = (k_anoninfo.ani_max * PAGESIZE) >> 10;
+	} else {
+		mutex_enter(&zone->zone_mem_lock);
+		/* Uses units of 1 kb (2^10). */
+		totswap = zone->zone_max_swap_ctl >> 10;
+		mutex_exit(&zone->zone_mem_lock);
+	}
+	usedswap = 0;
+
+	lxpr_uiobuf_printf(uiobuf,
+	    "Filename\t\t\t\tType\t\tSize\tUsed\tPriority\n");
+	lxpr_uiobuf_printf(uiobuf, "%-40s%s\t%llu\t%llu\t%d\n",
+	    "/dev/swap", "partition", totswap, usedswap, -1);
+}
+
+/*
+ * inotify tunables exported via /proc.
+ */
+extern int inotify_maxevents;
+extern int inotify_maxinstances;
+extern int inotify_maxwatches;
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_fs_inotify_max_queued_events(lxpr_node_t *lxpnp,
+    lxpr_uiobuf_t *uiobuf)
+{
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_FS_INOTIFY_MAX_QUEUED_EVENTS);
+	lxpr_uiobuf_printf(uiobuf, "%d\n", inotify_maxevents);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_fs_inotify_max_user_instances(lxpr_node_t *lxpnp,
+    lxpr_uiobuf_t *uiobuf)
+{
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_FS_INOTIFY_MAX_USER_INSTANCES);
+	lxpr_uiobuf_printf(uiobuf, "%d\n", inotify_maxinstances);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_fs_inotify_max_user_watches(lxpr_node_t *lxpnp,
+    lxpr_uiobuf_t *uiobuf)
+{
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_FS_INOTIFY_MAX_USER_WATCHES);
+	lxpr_uiobuf_printf(uiobuf, "%d\n", inotify_maxwatches);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_kernel_caplcap(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_CAPLCAP);
+	lxpr_uiobuf_printf(uiobuf, "%d\n", LX_CAP_MAX_VALID);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_kernel_corepatt(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	zone_t *zone = curproc->p_zone;
+	struct core_globals *cg;
+	refstr_t *rp;
+	corectl_path_t *ccp;
+	char tr[MAXPATHLEN];
+
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_COREPATT);
+
+	cg = zone_getspecific(core_zone_key, zone);
+	ASSERT(cg != NULL);
+
+	/* If core dumps are disabled, return an empty string. */
+	if ((cg->core_options & CC_PROCESS_PATH) == 0) {
+		lxpr_uiobuf_printf(uiobuf, "\n");
+		return;
+	}
+
+	ccp = cg->core_default_path;
+	mutex_enter(&ccp->ccp_mtx);
+	if ((rp = ccp->ccp_path) != NULL)
+		refstr_hold(rp);
+	mutex_exit(&ccp->ccp_mtx);
+
+	if (rp == NULL) {
+		lxpr_uiobuf_printf(uiobuf, "\n");
+		return;
+	}
+
+	bzero(tr, sizeof (tr));
+	if (lxpr_core_path_s2l(refstr_value(rp), tr, sizeof (tr)) != 0) {
+		refstr_rele(rp);
+		lxpr_uiobuf_printf(uiobuf, "\n");
+		return;
+	}
+
+	refstr_rele(rp);
+	lxpr_uiobuf_printf(uiobuf, "%s\n", tr);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_kernel_hostname(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_HOSTNAME);
+	lxpr_uiobuf_printf(uiobuf, "%s\n", uts_nodename());
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_kernel_msgmni(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	rctl_qty_t val;
+
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_MSGMNI);
+
+	mutex_enter(&curproc->p_lock);
+	val = rctl_enforced_value(rc_zone_msgmni,
+	    curproc->p_zone->zone_rctls, curproc);
+	mutex_exit(&curproc->p_lock);
+
+	lxpr_uiobuf_printf(uiobuf, "%u\n", (uint_t)val);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_kernel_ngroups_max(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_NGROUPS_MAX);
+	lxpr_uiobuf_printf(uiobuf, "%d\n", ngroups_max);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_kernel_osrel(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	lx_zone_data_t *br_data;
+	char version[LX_KERN_VERSION_MAX];
+
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_OSREL);
+	br_data = ztolxzd(curproc->p_zone);
+	if (curproc->p_zone->zone_brand == &lx_brand) {
+		mutex_enter(&br_data->lxzd_lock);
+		(void) strlcpy(version, br_data->lxzd_kernel_version,
+		    sizeof (version));
+		mutex_exit(&br_data->lxzd_lock);
+
+		lxpr_uiobuf_printf(uiobuf, "%s\n", version);
+	} else {
+		lxpr_uiobuf_printf(uiobuf, "\n");
+	}
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_kernel_pid_max(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_PID_MAX);
+	lxpr_uiobuf_printf(uiobuf, "%d\n", maxpid);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_kernel_rand_bootid(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	/*
+	 * This file isn't documented on the Linux proc(5) man page but
+	 * according to the blog of the author of systemd/journald (the
+	 * consumer), he says:
+	 *    boot_id: A random ID that is regenerated on each boot. As such it
+	 *    can be used to identify the local machine's current boot. It's
+	 *    universally available on any recent Linux kernel. It's a good and
+	 *    safe choice if you need to identify a specific boot on a specific
+	 *    booted kernel.
+	 *
+	 * We'll just generate a random ID if necessary. On Linux the format
+	 * appears to resemble a uuid but since it is not documented to be a
+	 * uuid, we don't worry about that.
+	 */
+	lx_zone_data_t *br_data;
+	char bootid[LX_BOOTID_LEN];
+
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_RAND_BOOTID);
+
+	if (curproc->p_zone->zone_brand != &lx_brand) {
+		lxpr_uiobuf_printf(uiobuf, "0\n");
+		return;
+	}
+
+	br_data = ztolxzd(curproc->p_zone);
+	mutex_enter(&br_data->lxzd_lock);
+	if (br_data->lxzd_bootid[0] == '\0') {
+		int i;
+
+		for (i = 0; i < 5; i++) {
+			u_longlong_t n;
+			char s[32];
+
+			(void) random_get_bytes((uint8_t *)&n, sizeof (n));
+			switch (i) {
+			case 0:	(void) snprintf(s, sizeof (s), "%08llx", n);
+				s[8] = '\0';
+				break;
+			case 4:	(void) snprintf(s, sizeof (s), "%012llx", n);
+				s[12] = '\0';
+				break;
+			default: (void) snprintf(s, sizeof (s), "%04llx", n);
+				s[4] = '\0';
+				break;
+			}
+			if (i > 0)
+				(void) strlcat(br_data->lxzd_bootid, "-",
+				    sizeof (br_data->lxzd_bootid));
+			(void) strlcat(br_data->lxzd_bootid, s,
+			    sizeof (br_data->lxzd_bootid));
+		}
+	}
+	(void) strlcpy(bootid, br_data->lxzd_bootid, sizeof (bootid));
+	mutex_exit(&br_data->lxzd_lock);
+
+	lxpr_uiobuf_printf(uiobuf, "%s\n", bootid);
+
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_kernel_sem(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	proc_t *pp = curproc;
+	rctl_qty_t vmsl, vopm, vmni, vmns;
+
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_SEM);
+
+	mutex_enter(&pp->p_lock);
+	vmsl = rctl_enforced_value(rc_process_semmsl, pp->p_rctls, pp);
+	vopm = rctl_enforced_value(rc_process_semopm, pp->p_rctls, pp);
+	vmni = rctl_enforced_value(rc_zone_semmni, pp->p_zone->zone_rctls, pp);
+	mutex_exit(&pp->p_lock);
+	vmns = vmsl * vmni;
+	if (vmns < vmsl || vmns < vmni) {
+		vmns = ULLONG_MAX;
+	}
+	/*
+	 * Format: semmsl semmns semopm semmni
+	 *  - semmsl: Limit semaphores in a sempahore set.
+	 *  - semmns: Limit semaphores in all semaphore sets
+	 *  - semopm: Limit operations in a single semop call
+	 *  - semmni: Limit number of semaphore sets
+	 */
+	lxpr_uiobuf_printf(uiobuf, "%llu\t%llu\t%llu\t%llu\n",
+	    vmsl, vmns, vopm, vmni);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_kernel_shmall(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	rctl_qty_t val;
+
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_SHMALL);
+
+	mutex_enter(&curproc->p_lock);
+	val = rctl_enforced_value(rc_zone_shmmax,
+	    curproc->p_zone->zone_rctls, curproc);
+	mutex_exit(&curproc->p_lock);
+
+	/* value is in pages */
+	lxpr_uiobuf_printf(uiobuf, "%u\n", (uint_t)btop(val));
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_kernel_shmmax(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	rctl_qty_t val;
+
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_SHMMAX);
+
+	mutex_enter(&curproc->p_lock);
+	val = rctl_enforced_value(rc_zone_shmmax,
+	    curproc->p_zone->zone_rctls, curproc);
+	mutex_exit(&curproc->p_lock);
+
+	if (val > FOURGB)
+		val = FOURGB;
+
+	lxpr_uiobuf_printf(uiobuf, "%u\n", (uint_t)val);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_kernel_shmmni(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	rctl_qty_t val;
+
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_SHMMNI);
+
+	mutex_enter(&curproc->p_lock);
+	val = rctl_enforced_value(rc_zone_shmmni,
+	    curproc->p_zone->zone_rctls, curproc);
+	mutex_exit(&curproc->p_lock);
+
+	if (val > FOURGB)
+		val = FOURGB;
+
+	lxpr_uiobuf_printf(uiobuf, "%u\n", (uint_t)val);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_kernel_threads_max(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_THREADS_MAX);
+	lxpr_uiobuf_printf(uiobuf, "%d\n", curproc->p_zone->zone_nlwps_ctl);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_net_core_somaxc(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	netstack_t *ns;
+	tcp_stack_t	*tcps;
+
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_CORE_SOMAXCON);
+
+	ns = netstack_get_current();
+	if (ns == NULL) {
+		lxpr_uiobuf_printf(uiobuf, "%d\n", SOMAXCONN);
+		return;
+	}
+
+	tcps = ns->netstack_tcp;
+	lxpr_uiobuf_printf(uiobuf, "%d\n", tcps->tcps_conn_req_max_q);
+	netstack_rele(ns);
+}
+
+/*
+ * ip_local_port_range
+ *
+ * The low & high port number range.
+ * integers; default: 32768 61000
+ *
+ * illumos: tcp_smallest_anon_port & tcp_largest_anon_port
+ * Not in tcp(7p) man page.
+ */
+/* ARGSUSED */
+static void
+lxpr_read_sys_net_ipv4_ip_lport_range(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	netstack_t *ns;
+	tcp_stack_t	*tcps;
+
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_IP_LPORT_RANGE);
+
+	ns = netstack_get_current();
+	if (ns == NULL) {
+		lxpr_uiobuf_seterr(uiobuf, ENXIO);
+		return;
+	}
+
+	tcps = ns->netstack_tcp;
+	lxpr_uiobuf_printf(uiobuf, "%d\t%d\n",
+	    tcps->tcps_smallest_anon_port, tcps->tcps_largest_anon_port);
+	netstack_rele(ns);
+}
+
+/*
+ * tcp_fin_timeout
+ *
+ * This specifies how many seconds to wait for a final FIN packet before the
+ * socket is forcibly closed. This is strictly a violation of the TCP
+ * specification, but required to prevent denial-of-service attacks.
+ * integer; default: 60;
+ *
+ * illumos: tcp_fin_wait_2_flush_interval
+ * Not in tcp(7p) man page but see comment in uts/common/inet/tcp/tcp_input.c
+ * in the tcp_input_data() function on the use of tcp_fin_wait_2_flush_interval.
+ * The value is in milliseconds.
+ */
+/* ARGSUSED */
+static void
+lxpr_read_sys_net_ipv4_tcp_fin_to(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	netstack_t *ns;
+	tcp_stack_t	*tcps;
+
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_FIN_TO);
+
+	ns = netstack_get_current();
+	if (ns == NULL) {
+		lxpr_uiobuf_seterr(uiobuf, ENXIO);
+		return;
+	}
+
+	tcps = ns->netstack_tcp;
+	lxpr_uiobuf_printf(uiobuf, "%d\n",
+	    tcps->tcps_fin_wait_2_flush_interval / 1000);
+	netstack_rele(ns);
+}
+
+/*
+ * tcp_keepalive_intvl
+ *
+ * The number of seconds between TCP keep-alive probes. default: 75
+ * Linux retries tcp_keepalive_probes (9) times before timing out.
+ *
+ * illumos:
+ * We have tcp_ka_rinterval but there is no corresponding tcps_* tunable for
+ * this. The closest is tcps_keepalive_abort_interval which specifies the
+ * time threshold for aborting a TCP connection in milliseconds. Linux retries
+ * 9 times (giving a total of 11.25 minutes) so we emulate this by dividing out
+ * tcps_keepalive_abort_interval by 9.
+ */
+/* ARGSUSED */
+static void
+lxpr_read_sys_net_ipv4_tcp_ka_int(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	netstack_t *ns;
+	tcp_stack_t	*tcps;
+
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_KA_INT);
+
+	ns = netstack_get_current();
+	if (ns == NULL) {
+		lxpr_uiobuf_seterr(uiobuf, ENXIO);
+		return;
+	}
+
+	tcps = ns->netstack_tcp;
+	lxpr_uiobuf_printf(uiobuf, "%d\n",
+	    (tcps->tcps_keepalive_abort_interval / 1000) / 9);
+	netstack_rele(ns);
+}
+
+/*
+ * tcp_keepalive_time
+ *
+ * The number of seconds a connection needs to be idle before TCP begins
+ * sending out keep-alive probes. The default value is 7200 seconds (2 hours).
+ *
+ * illumos: tcp_keepalive_interval
+ * The interval for sending out the first probe in milliseconds. The default is
+ * two hours.
+ */
+/* ARGSUSED */
+static void
+lxpr_read_sys_net_ipv4_tcp_ka_tim(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	netstack_t *ns;
+	tcp_stack_t	*tcps;
+
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_KA_TIM);
+
+	ns = netstack_get_current();
+	if (ns == NULL) {
+		lxpr_uiobuf_seterr(uiobuf, ENXIO);
+		return;
+	}
+
+	tcps = ns->netstack_tcp;
+	lxpr_uiobuf_printf(uiobuf, "%d\n",
+	    (tcps->tcps_keepalive_interval / 1000));
+	netstack_rele(ns);
+}
+
+/*
+ * tcp_sack
+ *
+ * Enable RFC 2018 TCP Selective Acknowledgements. Boolean, default: enabled
+ *
+ * illumos: tcp_sack_permitted
+ * tcp_sack_permitted 0 == disabled, 1 == no initiate but accept,
+ * 2 == initiate and accept. default is 2.
+ */
+/* ARGSUSED */
+static void
+lxpr_read_sys_net_ipv4_tcp_sack(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	netstack_t *ns;
+	tcp_stack_t	*tcps;
+
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_SACK);
+
+	ns = netstack_get_current();
+	if (ns == NULL) {
+		lxpr_uiobuf_seterr(uiobuf, ENXIO);
+		return;
+	}
+
+	tcps = ns->netstack_tcp;
+	lxpr_uiobuf_printf(uiobuf, "%d\n",
+	    (tcps->tcps_sack_permitted  == 0 ? 0 : 1));
+	netstack_rele(ns);
+}
+
+/*
+ * tcp_window_scaling
+ *
+ * RFC 1323 TCP window scaling. This feature allows the use of a large window
+ * (> 64K) on a TCP connection. Boolean; default: enabled
+ *
+ * illumos: tcp_wscale_always
+ * tcp_wscale_always is set to 1, the window scale option will always be
+ * set when connecting to a remote system. If tcp_wscale_always is 0, the
+ * window scale option will be set only if the user has requested a send or
+ * receive window larger than 64K. The default value of is 1.
+ */
+/* ARGSUSED */
+static void
+lxpr_read_sys_net_ipv4_tcp_winscale(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	netstack_t *ns;
+	tcp_stack_t	*tcps;
+
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_WINSCALE);
+
+	ns = netstack_get_current();
+	if (ns == NULL) {
+		lxpr_uiobuf_seterr(uiobuf, ENXIO);
+		return;
+	}
+
+	tcps = ns->netstack_tcp;
+	lxpr_uiobuf_printf(uiobuf, "%d\n", tcps->tcps_wscale_always);
+	netstack_rele(ns);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_vm_max_map_cnt(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_VM_MAX_MAP_CNT);
+	/* We don't limit mappings, just say we have a large limit. */
+	lxpr_uiobuf_printf(uiobuf, "%d\n", 16777215);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_vm_minfr_kb(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_VM_MINFR_KB);
+	lxpr_uiobuf_printf(uiobuf, "%d\n", 0);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_vm_nhpages(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_VM_NHUGEP);
+	lxpr_uiobuf_printf(uiobuf, "%d\n", 0);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_vm_overcommit_mem(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_VM_OVERCOMMIT_MEM);
+	lxpr_uiobuf_printf(uiobuf, "%d\n", 0);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_sys_vm_swappiness(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_VM_SWAPPINESS);
+	lxpr_uiobuf_printf(uiobuf, "%d\n", 0);
+}
+
+/*
+ * lxpr_read_uptime(): read the contents of the "uptime" file.
+ *
+ * format is: "%.2lf, %.2lf",uptime_secs, idle_secs
+ * Use fixed point arithmetic to get 2 decimal places
+ */
+/* ARGSUSED */
+static void
+lxpr_read_uptime(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	cpu_t *cp, *cpstart;
+	int pools_enabled;
+	ulong_t idle_cum = 0;
+	ulong_t cpu_count = 0;
+	ulong_t idle_s;
+	ulong_t idle_cs;
+	ulong_t up_s;
+	ulong_t up_cs;
+	hrtime_t birthtime;
+	hrtime_t centi_sec = 10000000;  /* 10^7 */
+
+	ASSERT(lxpnp->lxpr_type == LXPR_UPTIME);
+
+	/* Calculate cumulative stats */
+	mutex_enter(&cpu_lock);
+	pools_enabled = pool_pset_enabled();
+
+	cp = cpstart = CPU->cpu_part->cp_cpulist;
+	do {
+		/*
+		 * Don't count CPUs that aren't even in the system
+		 * or aren't up yet.
+		 */
+		if ((cp->cpu_flags & CPU_EXISTS) == 0) {
+			continue;
+		}
+
+		idle_cum += CPU_STATS(cp, sys.cpu_ticks_idle);
+		idle_cum += CPU_STATS(cp, sys.cpu_ticks_wait);
+		cpu_count += 1;
+
+		if (pools_enabled)
+			cp = cp->cpu_next_part;
+		else
+			cp = cp->cpu_next;
+	} while (cp != cpstart);
+	mutex_exit(&cpu_lock);
+
+	/* Getting the Zone zsched process startup time */
+	birthtime = LXPTOZ(lxpnp)->zone_zsched->p_mstart;
+	up_cs = (gethrtime() - birthtime) / centi_sec;
+	up_s = up_cs / 100;
+	up_cs %= 100;
+
+	ASSERT(cpu_count > 0);
+	idle_cum /= cpu_count;
+	idle_s = idle_cum / hz;
+	idle_cs = idle_cum % hz;
+	idle_cs *= 100;
+	idle_cs /= hz;
+
+	lxpr_uiobuf_printf(uiobuf,
+	    "%ld.%02d %ld.%02d\n", up_s, up_cs, idle_s, idle_cs);
+}
+
+/*
+ * Report a list of each cgroup subsystem supported by our emulated cgroup fs.
+ * This needs to exist for systemd to run but for now we don't report any
+ * cgroup subsystems as being installed. The commented example below shows
+ * how to print a subsystem entry.
+ */
+/* ARGSUSED */
+static void
+lxpr_read_cgroups(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	lxpr_uiobuf_printf(uiobuf, "%s\t%s\t%s\t%s\n",
+	    "#subsys_name", "hierarchy", "num_cgroups", "enabled");
+
+	/*
+	 * lxpr_uiobuf_printf(uiobuf, "%s\t%s\t%s\t%s\n",
+	 *   "cpu,cpuacct", "2", "1", "1");
+	 */
+}
+
+/*
+ * Report the zone boot arguments.
+ */
+static void
+lxpr_read_cmdline(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	zone_t *zone = LXPTOZ(lxpnp);
+	lxpr_uiobuf_printf(uiobuf, "%s\n", zone->zone_bootargs);
+}
+
+
+typedef enum {
+	LXCS_ALWAYS = 0,
+	LXCS_CPUID1_ECX,
+	LXCS_CPUID1_EDX,
+	LXCS_CPUID7_EBX,
+	LXCS_CPUIDD1_EAX,
+	LXCS_CPUIDX1_ECX,
+	LXCS_CPUIDX1_EDX,
+	LXCS_REG_MAX
+} lx_cpuinfo_source_t;
+
+typedef struct {
+	lx_cpuinfo_source_t	lxcm_source;
+	uint32_t		lxcm_flag;
+	const char		*lxcm_name;
+} lx_cpuinfo_mapping_t;
+
+/*
+ * This listing is derived from the X86_FEATURE flags data in the Linux kernel.
+ * Some entries are missing detectino routines.  They remain in the list,
+ * although commented out, to preserve proper order should they be fixed later.
+ */
+lx_cpuinfo_mapping_t lx_cpuinfo_mappings[] = {
+	/* CPUID EDX: */
+	{ LXCS_CPUID1_EDX, CPUID_INTC_EDX_FPU,		"fpu" },
+	{ LXCS_CPUID1_EDX, CPUID_INTC_EDX_VME,		"vme" },
+	{ LXCS_CPUID1_EDX, CPUID_INTC_EDX_DE,		"de" },
+	{ LXCS_CPUID1_EDX, CPUID_INTC_EDX_PSE,		"pse" },
+	{ LXCS_CPUID1_EDX, CPUID_INTC_EDX_TSC,		"tsc" },
+	{ LXCS_CPUID1_EDX, CPUID_INTC_EDX_MSR,		"msr" },
+	{ LXCS_CPUID1_EDX, CPUID_INTC_EDX_PAE,		"pae" },
+	{ LXCS_CPUID1_EDX, CPUID_INTC_EDX_MCE,		"mce" },
+	{ LXCS_CPUID1_EDX, CPUID_INTC_EDX_CX8,		"cx8" },
+	{ LXCS_CPUID1_EDX, CPUID_INTC_EDX_APIC,		"apic" },
+	/* reserved */
+	{ LXCS_CPUID1_EDX, CPUID_INTC_EDX_SEP,		"sep" },
+	{ LXCS_CPUID1_EDX, CPUID_INTC_EDX_MTRR,		"mtrr" },
+	{ LXCS_CPUID1_EDX, CPUID_INTC_EDX_PGE,		"pge" },
+	{ LXCS_CPUID1_EDX, CPUID_INTC_EDX_MCA,		"mca" },
+	{ LXCS_CPUID1_EDX, CPUID_INTC_EDX_CMOV,		"cmov" },
+	{ LXCS_CPUID1_EDX, CPUID_INTC_EDX_PAT,		"pat" },
+	{ LXCS_CPUID1_EDX, CPUID_INTC_EDX_PSE36,	"pse36" },
+	{ LXCS_CPUID1_EDX, CPUID_INTC_EDX_PSN,		"pn" },
+	{ LXCS_CPUID1_EDX, CPUID_INTC_EDX_CLFSH,	"clflush" },
+	/* reserved */
+	{ LXCS_CPUID1_EDX, CPUID_INTC_EDX_DS,		"dts" },
+	{ LXCS_CPUID1_EDX, CPUID_INTC_EDX_ACPI,		"acpi" },
+	{ LXCS_CPUID1_EDX, CPUID_INTC_EDX_MMX,		"mmx" },
+	{ LXCS_CPUID1_EDX, CPUID_INTC_EDX_FXSR,		"fxsr" },
+	{ LXCS_CPUID1_EDX, CPUID_INTC_EDX_SSE,		"sse" },
+	{ LXCS_CPUID1_EDX, CPUID_INTC_EDX_SSE2,		"sse2" },
+	{ LXCS_CPUID1_EDX, CPUID_INTC_EDX_SS,		"ss" },
+	{ LXCS_CPUID1_EDX, CPUID_INTC_EDX_HTT,		"ht" },
+	{ LXCS_CPUID1_EDX, CPUID_INTC_EDX_TM,		"tm" },
+	/* reserved */
+	{ LXCS_CPUID1_EDX, CPUID_INTC_EDX_PBE,		"pbe" },
+
+	/* AMD-defined CPU features, CPUID level 0x80000001, word 1 */
+#if defined(__amd64)
+	{ LXCS_ALWAYS, 1,				"syscall" },
+#endif
+	/* Present in the Linux listing but not in recent AMD docs: "mp" */
+	{ LXCS_CPUIDX1_EDX, CPUID_AMD_EDX_NX,		"nx" },
+	{ LXCS_CPUIDX1_EDX, CPUID_AMD_EDX_MMXamd,	"mmxext" },
+	{ LXCS_CPUIDX1_EDX, CPUID_AMD_EDX_FFXSR,	"fxsr_opt" },
+	{ LXCS_CPUIDX1_EDX, CPUID_AMD_EDX_1GPG,		"pdpe1gb" },
+	{ LXCS_CPUIDX1_EDX, CPUID_AMD_EDX_TSCP,		 "rdtscp" },
+	{ LXCS_CPUIDX1_EDX, CPUID_AMD_EDX_LM,		"lm" },
+	{ LXCS_CPUIDX1_EDX, CPUID_AMD_EDX_3DNowx,	"3dnowext" },
+	{ LXCS_CPUIDX1_EDX, CPUID_AMD_EDX_3DNow,	"3dnow" },
+
+	/* CPUID ECX: */
+	{ LXCS_CPUID1_ECX, CPUID_INTC_ECX_SSE3,		"pni" },
+	{ LXCS_CPUID1_ECX, CPUID_INTC_ECX_PCLMULQDQ,	"pclmulqdq" },
+	{ LXCS_CPUID1_ECX, CPUID_INTC_ECX_DTES64,	"dtes64" },
+	{ LXCS_CPUID1_ECX, CPUID_INTC_ECX_MON,		"monitor" },
+	{ LXCS_CPUID1_ECX, CPUID_INTC_ECX_DSCPL,	"ds_cpl" },
+	{ LXCS_CPUID1_ECX, CPUID_INTC_ECX_VMX,		"vmx" },
+	{ LXCS_CPUID1_ECX, CPUID_INTC_ECX_SMX,		"smx" },
+	{ LXCS_CPUID1_ECX, CPUID_INTC_ECX_EST,		"est" },
+	{ LXCS_CPUID1_ECX, CPUID_INTC_ECX_TM2,		"tm2" },
+	{ LXCS_CPUID1_ECX, CPUID_INTC_ECX_SSSE3,	"ssse3" },
+	{ LXCS_CPUID1_ECX, CPUID_INTC_ECX_CID,		"cid" },
+	{ LXCS_CPUID1_ECX, 0x00000800,			"sdbg" },
+	{ LXCS_CPUID1_ECX, CPUID_INTC_ECX_FMA,		"fma" },
+	{ LXCS_CPUID1_ECX, CPUID_INTC_ECX_CX16,		"cx16" },
+	{ LXCS_CPUID1_ECX, CPUID_INTC_ECX_ETPRD,	"xtpr" },
+	{ LXCS_CPUID1_ECX, CPUID_INTC_ECX_PDCM,		"pdcm" },
+	/* reserved */
+	{ LXCS_CPUID1_ECX, CPUID_INTC_ECX_PCID,		"pcid" },
+	{ LXCS_CPUID1_ECX, CPUID_INTC_ECX_DCA,		"dca" },
+	{ LXCS_CPUID1_ECX, CPUID_INTC_ECX_SSE4_1,	"sse4_1" },
+	{ LXCS_CPUID1_ECX, CPUID_INTC_ECX_SSE4_2,	"sse4_2" },
+	{ LXCS_CPUID1_ECX, CPUID_INTC_ECX_X2APIC,	"x2apic" },
+	{ LXCS_CPUID1_ECX, CPUID_INTC_ECX_MOVBE,	"movbe" },
+	{ LXCS_CPUID1_ECX, CPUID_INTC_ECX_POPCNT,	"popcnt" },
+	{ LXCS_CPUID1_ECX, CPUID_INTC_ECX_TSCDL,	"tsc_deadline_timer" },
+	{ LXCS_CPUID1_ECX, CPUID_INTC_ECX_AES,		"aes" },
+	{ LXCS_CPUID1_ECX, CPUID_INTC_ECX_XSAVE,	"xsave" },
+	/* osxsave */
+	{ LXCS_CPUID1_ECX, CPUID_INTC_ECX_AVX,		"avx" },
+	{ LXCS_CPUID1_ECX, CPUID_INTC_ECX_F16C,		"f16c" },
+	{ LXCS_CPUID1_ECX, CPUID_INTC_ECX_RDRAND,	"rdrand" },
+	/* not used */
+
+	/*
+	 * Other features, Linux-defined mapping
+	 * This range is used for feature bits which conflict or are synthesized
+	 * Skipped:
+	 * "recovery",
+	 * "longrun",
+	 * "lrti",
+	 * "cxmmx",
+	 * "k6_mtrr",
+	 * "cyrix_arr",
+	 * "centaur_mcr",
+	 * "constant_tsc",
+	 * "up",
+	 * "arch_perfmon",
+	 * "pebs",
+	 * "bts",
+	 * "rep_good",
+	 * "nopl",
+	 * "xtopology",
+	 * "tsc_reliable",
+	 * "nonstop_tsc",
+	 * "extd_apicid",
+	 * "amd_dcm",
+	 * "aperfmperf",
+	 * "eagerfpu",
+	 * "nonstop_tsc_s3",
+	 *
+	 * "hypervisor",
+	 * "rng",
+	 * "rng_en",
+	 * "ace",
+	 * "ace_en",
+	 * "ace2",
+	 * "ace2_en",
+	 * "phe",
+	 * "phe_en",
+	 * "pmm",
+	 * "pmm_en",
+	 */
+
+	/*
+	 * More extended AMD flags: CPUID level 0x80000001, ecx, word 6
+	 */
+	{ LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_AHF64,	"lahf_lm" },
+	{ LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_CMP_LGCY,	"cmp_legacy" },
+	{ LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_SVM,		"svm" },
+	{ LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_EAS,		"extapic" },
+	{ LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_CR8D,		"cr8_legacy" },
+	{ LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_LZCNT,	"abm" },
+	{ LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_SSE4A,	"sse4a" },
+	{ LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_MAS,		"misalignsse" },
+	{ LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_3DNP,		"3dnowprefetch" },
+	{ LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_OSVW,		"osvw" },
+	{ LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_IBS,		"ibs" },
+	{ LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_SSE5,		"xop" },
+	{ LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_SKINIT,	"skinit" },
+	{ LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_WDT,		"wdt" },
+	{ LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_LWP,		"lwp" },
+	{ LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_FMA4,		"fma4" },
+	{ LXCS_CPUIDX1_ECX, 0x00020000,			"tce" },
+
+	{ LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_NIDMSR,	"nodeid_msr" },
+
+	{ LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_TBM,		"tbm" },
+	{ LXCS_CPUIDX1_ECX, CPUID_AMD_ECX_TOPOEXT,	"topoext" },
+	{ LXCS_CPUIDX1_ECX, 0x00800000,			"perfctr_core" },
+	{ LXCS_CPUIDX1_ECX, 0x01000000,			"perfctr_nb" },
+	{ LXCS_CPUIDX1_ECX, 0x02000000,			"bpext" },
+	{ LXCS_CPUIDX1_ECX, 0x04000000,			"perfctr_l2" },
+	{ LXCS_CPUIDX1_ECX, 0x08000000,			"mwaitx" },
+
+	/*
+	 * Aux flags and virt bits.
+	 * Skipped:
+	 * "cpb",
+	 * "epb",
+	 * "hw_pstate",
+	 * "proc_feedback",
+	 * "intel_pt",
+	 * "tpr_shadow",
+	 * "vnmi",
+	 * "flexpriority",
+	 * "ept",
+	 * "vpid",
+	 * "vmmcall",
+	 */
+
+	/*
+	 * Intel-defined CPU features, CPUID level 0x00000007:0 (ebx), word 9
+	 */
+	{ LXCS_CPUID7_EBX, 0x00000001,			"fsgsbase" },
+	{ LXCS_CPUID7_EBX, 0x00000002,			"tsc_adjust" },
+	{ LXCS_CPUID7_EBX, CPUID_INTC_EBX_7_0_BMI1,	"bmi1" },
+	{ LXCS_CPUID7_EBX, 0x00000010,			"hle" },
+	{ LXCS_CPUID7_EBX, CPUID_INTC_EBX_7_0_AVX2,	"avx2" },
+	{ LXCS_CPUID7_EBX, CPUID_INTC_EBX_7_0_SMEP,	"smep" },
+	{ LXCS_CPUID7_EBX, CPUID_INTC_EBX_7_0_BMI2,	"bmi2" },
+	{ LXCS_CPUID7_EBX, 0x00000200,			"erms" },
+	{ LXCS_CPUID7_EBX, 0x00000400,			"invpcid" },
+	{ LXCS_CPUID7_EBX, 0x00000800,			"rtm" },
+	{ LXCS_CPUID7_EBX, 0x00000000,			"cqm" },
+	{ LXCS_CPUID7_EBX, 0x00004000,			"mpx" },
+	{ LXCS_CPUID7_EBX, 0x00010000,			"avx512f" },
+
+	{ LXCS_CPUID7_EBX, CPUID_INTC_EBX_7_0_RDSEED,	"rdseed" },
+	{ LXCS_CPUID7_EBX, CPUID_INTC_EBX_7_0_ADX,	"adx" },
+	{ LXCS_CPUID7_EBX, CPUID_INTC_EBX_7_0_SMAP,	"smap" },
+
+	{ LXCS_CPUID7_EBX, 0x00400000,			"pcommit" },
+	{ LXCS_CPUID7_EBX, 0x00800000,			"clflushopt" },
+	{ LXCS_CPUID7_EBX, 0x01000000,			"clwb" },
+
+	{ LXCS_CPUID7_EBX, 0x04000000,			"avx512pf" },
+	{ LXCS_CPUID7_EBX, 0x08000000,			"avx512er" },
+	{ LXCS_CPUID7_EBX, 0x10000000,			"avx512cd" },
+	{ LXCS_CPUID7_EBX, 0x20000000,			"sha_ni" },
+
+	/*
+	 * Extended state features, CPUID level 0x0000000d:1 (eax)
+	 */
+	{ LXCS_CPUIDD1_EAX, 0x00000001,			"xsaveopt" },
+	{ LXCS_CPUIDD1_EAX, 0x00000002,			"xsavec" },
+	{ LXCS_CPUIDD1_EAX, 0x00000004,			"xgetbv1" },
+	{ LXCS_CPUIDD1_EAX, 0x00000008,			"xsaves" },
+
+	/*
+	 * Skipped:
+	 * "cqm_llc",
+	 * "cqm_occup_llc",
+	 * "clzero",
+	 */
+
+	/*
+	 * Thermal and Power Management Leaf, CPUID level 0x00000006 (eax)
+	 * Skipped:
+	 * "dtherm",
+	 * "ida",
+	 * "arat",
+	 * "pln",
+	 * "pts",
+	 * "hwp",
+	 * "hwp_notify",
+	 * "hwp_act_window",
+	 * "hwp_epp",
+	 * "hwp_pkg_req",
+	 */
+
+	/*
+	 * AMD SVM Feature Identification, CPUID level 0x8000000a (edx)
+	 * Skipped:
+	 * "npt",
+	 * "lbrv",
+	 * "svm_lock",
+	 * "nrip_save",
+	 * "tsc_scale",
+	 * "vmcb_clean",
+	 * "flushbyasid",
+	 * "decodeassists",
+	 * "pausefilter",
+	 * "pfthreshold",
+	 */
+};
+
+#define	LX_CPUINFO_MAPPING_MAX	\
+	(sizeof (lx_cpuinfo_mappings) / sizeof (lx_cpuinfo_mappings[0]))
+
+/* ARGSUSED */
+static void
+lxpr_read_cpuinfo(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	int i;
+	cpu_t *cp, *cpstart;
+	int pools_enabled;
+	char brandstr[CPU_IDSTRLEN];
+
+	ASSERT(lxpnp->lxpr_type == LXPR_CPUINFO);
+
+	mutex_enter(&cpu_lock);
+	pools_enabled = pool_pset_enabled();
+
+	cp = cpstart = CPU->cpu_part->cp_cpulist;
+	do {
+		struct cpuid_regs cpr;
+		uint32_t maxeax, xmaxeax, cpuid_res[LXCS_REG_MAX] = { 0 };
+
+		cpr.cp_eax = 0;
+		maxeax = cpuid_insn(cp, &cpr);
+		cpr.cp_eax = 0x80000000;
+		xmaxeax = cpuid_insn(cp, &cpr);
+
+		cpuid_res[LXCS_ALWAYS] = 1;
+		if (maxeax >= 1) {
+			cpr.cp_eax = 1;
+			(void) cpuid_insn(cp, &cpr);
+			cpuid_res[LXCS_CPUID1_ECX] = cpr.cp_ecx;
+			cpuid_res[LXCS_CPUID1_EDX] = cpr.cp_edx;
+		}
+		if (maxeax >= 7) {
+			cpr.cp_eax = 7;
+			(void) cpuid_insn(cp, &cpr);
+			cpuid_res[LXCS_CPUID7_EBX] = cpr.cp_ebx;
+		}
+		if (maxeax >= 0xd) {
+			cpr.cp_eax = 0xd;
+			cpr.cp_ecx = 1;
+			(void) cpuid_insn(cp, &cpr);
+			cpuid_res[LXCS_CPUIDD1_EAX] = cpr.cp_eax;
+		}
+		if (xmaxeax >= 0x80000001) {
+			cpr.cp_eax = 0x80000001;
+			(void) cpuid_insn(cp, &cpr);
+			cpuid_res[LXCS_CPUIDX1_ECX] = cpr.cp_ecx;
+			cpuid_res[LXCS_CPUIDX1_EDX] = cpr.cp_edx;
+		}
+
+		(void) cpuid_getbrandstr(cp, brandstr, CPU_IDSTRLEN);
+
+		lxpr_uiobuf_printf(uiobuf,
+		    "processor\t: %d\n"
+		    "vendor_id\t: %s\n"
+		    "cpu family\t: %d\n"
+		    "model\t\t: %d\n"
+		    "model name\t: %s\n"
+		    "stepping\t: %d\n"
+		    "cpu MHz\t\t: %u.%03u\n",
+		    cp->cpu_id, cpuid_getvendorstr(cp), cpuid_getfamily(cp),
+		    cpuid_getmodel(cp), brandstr, cpuid_getstep(cp),
+		    (uint32_t)(cpu_freq_hz / 1000000),
+		    ((uint32_t)(cpu_freq_hz / 1000)) % 1000);
+
+		lxpr_uiobuf_printf(uiobuf, "cache size\t: %u KB\n",
+		    getl2cacheinfo(cp, NULL, NULL, NULL) / 1024);
+
+		if (is_x86_feature(x86_featureset, X86FSET_HTT)) {
+			/*
+			 * 'siblings' is used for HT-style threads
+			 */
+			lxpr_uiobuf_printf(uiobuf,
+			    "physical id\t: %lu\n"
+			    "siblings\t: %u\n",
+			    pg_plat_hw_instance_id(cp, PGHW_CHIP),
+			    cpuid_get_ncpu_per_chip(cp));
+		}
+
+		/*
+		 * Since we're relatively picky about running on older hardware,
+		 * we can be somewhat cavalier about the answers to these ones.
+		 *
+		 * In fact, given the hardware we support, we just say:
+		 *
+		 *	fdiv_bug	: no	(if we're on a 64-bit kernel)
+		 *	hlt_bug		: no
+		 *	f00f_bug	: no
+		 *	coma_bug	: no
+		 *	wp		: yes	(write protect in supervsr mode)
+		 */
+		lxpr_uiobuf_printf(uiobuf,
+		    "fdiv_bug\t: %s\n"
+		    "hlt_bug \t: no\n"
+		    "f00f_bug\t: no\n"
+		    "coma_bug\t: no\n"
+		    "fpu\t\t: %s\n"
+		    "fpu_exception\t: %s\n"
+		    "cpuid level\t: %d\n"
+		    "flags\t\t:",
+#if defined(__i386)
+		    fpu_pentium_fdivbug ? "yes" : "no",
+#else
+		    "no",
+#endif /* __i386 */
+		    fpu_exists ? "yes" : "no", fpu_exists ? "yes" : "no",
+		    maxeax);
+
+		/* Print CPUID feature flags */
+		for (i = 0; i < LX_CPUINFO_MAPPING_MAX; i++) {
+			lx_cpuinfo_mapping_t *lxm = &lx_cpuinfo_mappings[i];
+
+			ASSERT(lxm->lxcm_source < LXCS_REG_MAX);
+			if (cpuid_res[lxm->lxcm_source] & lxm->lxcm_flag) {
+				lxpr_uiobuf_printf(uiobuf, " %s",
+				    lxm->lxcm_name);
+			}
+		}
+
+		lxpr_uiobuf_printf(uiobuf, "\n\n");
+
+		if (pools_enabled)
+			cp = cp->cpu_next_part;
+		else
+			cp = cp->cpu_next;
+	} while (cp != cpstart);
+
+	mutex_exit(&cpu_lock);
+}
+
+/* ARGSUSED */
+static void
+lxpr_read_fd(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	ASSERT(lxpnp->lxpr_type == LXPR_PID_FD_FD);
+	lxpr_uiobuf_seterr(uiobuf, EFAULT);
+}
+
+/*
+ * Report a list of file systems loaded in the kernel. We only report the ones
+ * which we support and which may be checked by various components to see if
+ * they are loaded.
+ */
+/* ARGSUSED */
+static void
+lxpr_read_filesystems(lxpr_node_t *lxpnp, lxpr_uiobuf_t *uiobuf)
+{
+	lxpr_uiobuf_printf(uiobuf, "%s\t%s\n", "nodev", "autofs");
+	lxpr_uiobuf_printf(uiobuf, "%s\t%s\n", "nodev", "cgroup");
+	lxpr_uiobuf_printf(uiobuf, "%s\t%s\n", "nodev", "nfs");
+	lxpr_uiobuf_printf(uiobuf, "%s\t%s\n", "nodev", "proc");
+	lxpr_uiobuf_printf(uiobuf, "%s\t%s\n", "nodev", "sysfs");
+	lxpr_uiobuf_printf(uiobuf, "%s\t%s\n", "nodev", "tmpfs");
+}
+
+/*
+ * lxpr_getattr(): Vnode operation for VOP_GETATTR()
+ */
+static int
+lxpr_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
+    caller_context_t *ct)
+{
+	register lxpr_node_t *lxpnp = VTOLXP(vp);
+	lxpr_nodetype_t type = lxpnp->lxpr_type;
+	extern uint_t nproc;
+	int error;
+
+	/*
+	 * Return attributes of underlying vnode if ATTR_REAL
+	 *
+	 * but keep fd files with the symlink permissions
+	 */
+	if (lxpnp->lxpr_realvp != NULL && (flags & ATTR_REAL)) {
+		vnode_t *rvp = lxpnp->lxpr_realvp;
+
+		/*
+		 * withold attribute information to owner or root
+		 */
+		if ((error = VOP_ACCESS(rvp, 0, 0, cr, ct)) != 0) {
+			return (error);
+		}
+
+		/*
+		 * now its attributes
+		 */
+		if ((error = VOP_GETATTR(rvp, vap, flags, cr, ct)) != 0) {
+			return (error);
+		}
+
+		/*
+		 * if it's a file in lx /proc/pid/fd/xx then set its
+		 * mode and keep it looking like a symlink, fifo or socket
+		 */
+		if (type == LXPR_PID_FD_FD) {
+			vap->va_mode = lxpnp->lxpr_mode;
+			vap->va_type = lxpnp->lxpr_realvp->v_type;
+			vap->va_size = 0;
+			vap->va_nlink = 1;
+		}
+		return (0);
+	}
+
+	/* Default attributes, that may be overridden below */
+	bzero(vap, sizeof (*vap));
+	vap->va_atime = vap->va_mtime = vap->va_ctime = lxpnp->lxpr_time;
+	vap->va_nlink = 1;
+	vap->va_type = vp->v_type;
+	vap->va_mode = lxpnp->lxpr_mode;
+	vap->va_fsid = vp->v_vfsp->vfs_dev;
+	vap->va_blksize = DEV_BSIZE;
+	vap->va_uid = lxpnp->lxpr_uid;
+	vap->va_gid = lxpnp->lxpr_gid;
+	vap->va_nodeid = lxpnp->lxpr_ino;
+
+	switch (type) {
+	case LXPR_PROCDIR:
+		vap->va_nlink = nproc + 2 + PROCDIRFILES;
+		vap->va_size = (nproc + 2 + PROCDIRFILES) * LXPR_SDSIZE;
+		break;
+	case LXPR_PIDDIR:
+		vap->va_nlink = PIDDIRFILES;
+		vap->va_size = PIDDIRFILES * LXPR_SDSIZE;
+		break;
+	case LXPR_PID_TASK_IDDIR:
+		vap->va_nlink = TIDDIRFILES;
+		vap->va_size = TIDDIRFILES * LXPR_SDSIZE;
+		break;
+	case LXPR_SELF:
+		vap->va_uid = crgetruid(curproc->p_cred);
+		vap->va_gid = crgetrgid(curproc->p_cred);
+		break;
+	case LXPR_PID_FD_FD:
+	case LXPR_PID_TID_FD_FD:
+		/*
+		 * Restore VLNK type for lstat-type activity.
+		 * See lxpr_readlink for more details.
+		 */
+		if ((flags & FOLLOW) == 0)
+			vap->va_type = VLNK;
+	default:
+		break;
+	}
+
+	vap->va_nblocks = (fsblkcnt64_t)btod(vap->va_size);
+	return (0);
+}
+
+/*
+ * lxpr_access(): Vnode operation for VOP_ACCESS()
+ */
+static int
+lxpr_access(vnode_t *vp, int mode, int flags, cred_t *cr, caller_context_t *ct)
+{
+	lxpr_node_t *lxpnp = VTOLXP(vp);
+	lxpr_nodetype_t type = lxpnp->lxpr_type;
+	int shift = 0;
+	proc_t *tp;
+
+	/* lx /proc is primarily a read only file system */
+	if ((mode & VWRITE) && !lxpr_is_writable(type)) {
+		return (EROFS);
+	}
+
+	/*
+	 * If this is a restricted file, check access permissions.
+	 */
+	switch (type) {
+	case LXPR_PIDDIR:
+		return (0);
+	case LXPR_PID_CURDIR:
+	case LXPR_PID_ENV:
+	case LXPR_PID_EXE:
+	case LXPR_PID_LIMITS:
+	case LXPR_PID_MAPS:
+	case LXPR_PID_MEM:
+	case LXPR_PID_ROOTDIR:
+	case LXPR_PID_FDDIR:
+	case LXPR_PID_FD_FD:
+	case LXPR_PID_TID_FDDIR:
+	case LXPR_PID_TID_FD_FD:
+		if ((tp = lxpr_lock(lxpnp->lxpr_pid, ZOMB_OK)) == NULL)
+			return (ENOENT);
+		if (tp != curproc && secpolicy_proc_access(cr) != 0 &&
+		    priv_proc_cred_perm(cr, tp, NULL, mode) != 0) {
+			lxpr_unlock(tp);
+			return (EACCES);
+		}
+		lxpr_unlock(tp);
+	default:
+		break;
+	}
+
+	if (lxpnp->lxpr_realvp != NULL) {
+		/*
+		 * For these we use the underlying vnode's accessibility.
+		 */
+		return (VOP_ACCESS(lxpnp->lxpr_realvp, mode, flags, cr, ct));
+	}
+
+	/* If user is root allow access regardless of permission bits */
+	if (secpolicy_proc_access(cr) == 0)
+		return (0);
+
+	/*
+	 * Access check is based on only one of owner, group, public.  If not
+	 * owner, then check group.  If not a member of the group, then check
+	 * public access.
+	 */
+	if (crgetuid(cr) != lxpnp->lxpr_uid) {
+		shift += 3;
+		if (!groupmember((uid_t)lxpnp->lxpr_gid, cr))
+			shift += 3;
+	}
+
+	mode &= ~(lxpnp->lxpr_mode << shift);
+
+	if (mode == 0)
+		return (0);
+
+	return (EACCES);
+}
+
+/* ARGSUSED */
+static vnode_t *
+lxpr_lookup_not_a_dir(vnode_t *dp, char *comp)
+{
+	return (NULL);
+}
+
+/*
+ * lxpr_lookup(): Vnode operation for VOP_LOOKUP()
+ */
+/* ARGSUSED */
+static int
+lxpr_lookup(vnode_t *dp, char *comp, vnode_t **vpp, pathname_t *pathp,
+    int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct,
+    int *direntflags, pathname_t *realpnp)
+{
+	lxpr_node_t *lxpnp = VTOLXP(dp);
+	lxpr_nodetype_t type = lxpnp->lxpr_type;
+	int error;
+
+	ASSERT(dp->v_type == VDIR);
+	ASSERT(type < LXPR_NFILES);
+
+	/*
+	 * we should never get here because the lookup
+	 * is done on the realvp for these nodes
+	 */
+	ASSERT(type != LXPR_PID_FD_FD &&
+	    type != LXPR_PID_CURDIR &&
+	    type != LXPR_PID_ROOTDIR);
+
+	/*
+	 * restrict lookup permission to owner or root
+	 */
+	if ((error = lxpr_access(dp, VEXEC, 0, cr, ct)) != 0) {
+		return (error);
+	}
+
+	/*
+	 * Just return the parent vnode if that's where we are trying to go.
+	 */
+	if (strcmp(comp, "..") == 0) {
+		VN_HOLD(lxpnp->lxpr_parent);
+		*vpp = lxpnp->lxpr_parent;
+		return (0);
+	}
+
+	/*
+	 * Special handling for directory searches.  Note: null component name
+	 * denotes that the current directory is being searched.
+	 */
+	if ((dp->v_type == VDIR) && (*comp == '\0' || strcmp(comp, ".") == 0)) {
+		VN_HOLD(dp);
+		*vpp = dp;
+		return (0);
+	}
+
+	*vpp = (lxpr_lookup_function[type](dp, comp));
+	return ((*vpp == NULL) ? ENOENT : 0);
+}
+
+/*
+ * Do a sequential search on the given directory table
+ */
+static vnode_t *
+lxpr_lookup_common(vnode_t *dp, char *comp, proc_t *p,
+    lxpr_dirent_t *dirtab, int dirtablen)
+{
+	lxpr_node_t *lxpnp;
+	int count;
+
+	for (count = 0; count < dirtablen; count++) {
+		if (strcmp(dirtab[count].d_name, comp) == 0) {
+			lxpnp = lxpr_getnode(dp, dirtab[count].d_type, p, 0);
+			dp = LXPTOV(lxpnp);
+			ASSERT(dp != NULL);
+			return (dp);
+		}
+	}
+	return (NULL);
+}
+
+static vnode_t *
+lxpr_lookup_piddir(vnode_t *dp, char *comp)
+{
+	proc_t *p;
+
+	ASSERT(VTOLXP(dp)->lxpr_type == LXPR_PIDDIR);
+
+	p = lxpr_lock(VTOLXP(dp)->lxpr_pid, ZOMB_OK);
+	if (p == NULL)
+		return (NULL);
+
+	dp = lxpr_lookup_common(dp, comp, p, piddir, PIDDIRFILES);
+
+	lxpr_unlock(p);
+
+	return (dp);
+}
+
+/*
+ * Lookup one of the process's task ID's.
+ */
+static vnode_t *
+lxpr_lookup_taskdir(vnode_t *dp, char *comp)
+{
+	lxpr_node_t *dlxpnp = VTOLXP(dp);
+	lxpr_node_t *lxpnp;
+	proc_t *p;
+	pid_t real_pid;
+	uint_t tid;
+	int c;
+	kthread_t *t;
+
+	ASSERT(dlxpnp->lxpr_type == LXPR_PID_TASKDIR);
+
+	/*
+	 * convert the string rendition of the filename to a thread ID
+	 */
+	tid = 0;
+	while ((c = *comp++) != '\0') {
+		int otid;
+		if (c < '0' || c > '9')
+			return (NULL);
+
+		otid = tid;
+		tid = 10 * tid + c - '0';
+		/* integer overflow */
+		if (tid / 10 != otid)
+			return (NULL);
+	}
+
+	/*
+	 * get the proc to work with and lock it
+	 */
+	real_pid = get_real_pid(dlxpnp->lxpr_pid);
+	p = lxpr_lock(real_pid, NO_ZOMB);
+	if ((p == NULL))
+		return (NULL);
+
+	/*
+	 * Bail if this is a system process.
+	 */
+	if ((p->p_flag & SSYS) || (p->p_as == &kas)) {
+		lxpr_unlock(p);
+		return (NULL);
+	}
+
+	if (p->p_brand == &lx_brand) {
+		t = lxpr_get_thread(p, tid);
+	} else {
+		/*
+		 * Only the main thread is visible for non-branded processes.
+		 */
+		t = p->p_tlist;
+		if (tid != p->p_pid || t == NULL) {
+			t = NULL;
+		} else {
+			thread_lock(t);
+		}
+	}
+	if (t == NULL) {
+		lxpr_unlock(p);
+		return (NULL);
+	}
+	thread_unlock(t);
+
+	/*
+	 * Allocate and fill in a new lx /proc taskid node.
+	 * Instead of the last arg being a fd, it is a tid.
+	 */
+	lxpnp = lxpr_getnode(dp, LXPR_PID_TASK_IDDIR, p, tid);
+	dp = LXPTOV(lxpnp);
+	ASSERT(dp != NULL);
+	lxpr_unlock(p);
+	return (dp);
+}
+
+/*
+ * Lookup one of the process's task ID's.
+ */
+static vnode_t *
+lxpr_lookup_task_tid_dir(vnode_t *dp, char *comp)
+{
+	lxpr_node_t *dlxpnp = VTOLXP(dp);
+	lxpr_node_t *lxpnp;
+	proc_t *p;
+	pid_t real_pid;
+	kthread_t *t;
+	int i;
+
+	ASSERT(dlxpnp->lxpr_type == LXPR_PID_TASK_IDDIR);
+
+	/*
+	 * get the proc to work with and lock it
+	 */
+	real_pid = get_real_pid(dlxpnp->lxpr_pid);
+	p = lxpr_lock(real_pid, NO_ZOMB);
+	if ((p == NULL))
+		return (NULL);
+
+	/*
+	 * Bail if this is a system process.
+	 */
+	if ((p->p_flag & SSYS) || (p->p_as == &kas)) {
+		lxpr_unlock(p);
+		return (NULL);
+	}
+
+	/* need to confirm tid is still there */
+	t = lxpr_get_thread(p, dlxpnp->lxpr_desc);
+	if (t == NULL) {
+		lxpr_unlock(p);
+		return (NULL);
+	}
+	thread_unlock(t);
+
+	/*
+	 * allocate and fill in the new lx /proc taskid dir node
+	 */
+	for (i = 0; i < TIDDIRFILES; i++) {
+		if (strcmp(tiddir[i].d_name, comp) == 0) {
+			lxpnp = lxpr_getnode(dp, tiddir[i].d_type, p,
+			    dlxpnp->lxpr_desc);
+			dp = LXPTOV(lxpnp);
+			ASSERT(dp != NULL);
+			lxpr_unlock(p);
+			return (dp);
+		}
+	}
+
+	lxpr_unlock(p);
+	return (NULL);
+}
+
+/*
+ * Lookup one of the process's open files.
+ */
+static vnode_t *
+lxpr_lookup_fddir(vnode_t *dp, char *comp)
+{
+	ASSERT(VTOLXP(dp)->lxpr_type == LXPR_PID_FDDIR ||
+	    VTOLXP(dp)->lxpr_type == LXPR_PID_TID_FDDIR);
+
+	return (lxpr_lookup_fdnode(dp, comp));
+}
+
+static vnode_t *
+lxpr_lookup_netdir(vnode_t *dp, char *comp)
+{
+	ASSERT(VTOLXP(dp)->lxpr_type == LXPR_NETDIR);
+
+	dp = lxpr_lookup_common(dp, comp, NULL, netdir, NETDIRFILES);
+
+	return (dp);
+}
+
+static vnode_t *
+lxpr_lookup_procdir(vnode_t *dp, char *comp)
+{
+	ASSERT(VTOLXP(dp)->lxpr_type == LXPR_PROCDIR);
+
+	/*
+	 * We know all the names of files & dirs in our file system structure
+	 * except those that are pid names.  These change as pids are created/
+	 * deleted etc., so we just look for a number as the first char to see
+	 * if we are we doing pid lookups.
+	 *
+	 * Don't need to check for "self" as it is implemented as a symlink
+	 */
+	if (*comp >= '0' && *comp <= '9') {
+		pid_t pid = 0;
+		lxpr_node_t *lxpnp = NULL;
+		proc_t *p;
+		int c;
+
+		while ((c = *comp++) != '\0')
+			pid = 10 * pid + c - '0';
+
+		/*
+		 * Can't continue if the process is still loading or it doesn't
+		 * really exist yet (or maybe it just died!)
+		 */
+		p = lxpr_lock(pid, ZOMB_OK);
+		if (p == NULL)
+			return (NULL);
+
+		if (secpolicy_basic_procinfo(CRED(), p, curproc) != 0) {
+			lxpr_unlock(p);
+			return (NULL);
+		}
+
+		/*
+		 * allocate and fill in a new lx /proc node
+		 */
+		lxpnp = lxpr_getnode(dp, LXPR_PIDDIR, p, 0);
+
+		lxpr_unlock(p);
+
+		dp = LXPTOV(lxpnp);
+		ASSERT(dp != NULL);
+
+		return (dp);
+	}
+
+	/* Lookup fixed names */
+	return (lxpr_lookup_common(dp, comp, NULL, lx_procdir, PROCDIRFILES));
+}
+
+static vnode_t *
+lxpr_lookup_sysdir(vnode_t *dp, char *comp)
+{
+	ASSERT(VTOLXP(dp)->lxpr_type == LXPR_SYSDIR);
+	return (lxpr_lookup_common(dp, comp, NULL, sysdir, SYSDIRFILES));
+}
+
+static vnode_t *
+lxpr_lookup_sys_kerneldir(vnode_t *dp, char *comp)
+{
+	ASSERT(VTOLXP(dp)->lxpr_type == LXPR_SYS_KERNELDIR);
+	return (lxpr_lookup_common(dp, comp, NULL, sys_kerneldir,
+	    SYS_KERNELDIRFILES));
+}
+
+static vnode_t *
+lxpr_lookup_sys_kdir_randdir(vnode_t *dp, char *comp)
+{
+	ASSERT(VTOLXP(dp)->lxpr_type == LXPR_SYS_KERNEL_RANDDIR);
+	return (lxpr_lookup_common(dp, comp, NULL, sys_randdir,
+	    SYS_RANDDIRFILES));
+}
+
+static vnode_t *
+lxpr_lookup_sys_netdir(vnode_t *dp, char *comp)
+{
+	ASSERT(VTOLXP(dp)->lxpr_type == LXPR_SYS_NETDIR);
+	return (lxpr_lookup_common(dp, comp, NULL, sys_netdir,
+	    SYS_NETDIRFILES));
+}
+
+static vnode_t *
+lxpr_lookup_sys_net_coredir(vnode_t *dp, char *comp)
+{
+	ASSERT(VTOLXP(dp)->lxpr_type == LXPR_SYS_NET_COREDIR);
+	return (lxpr_lookup_common(dp, comp, NULL, sys_net_coredir,
+	    SYS_NET_COREDIRFILES));
+}
+
+static vnode_t *
+lxpr_lookup_sys_net_ipv4dir(vnode_t *dp, char *comp)
+{
+	ASSERT(VTOLXP(dp)->lxpr_type == LXPR_SYS_NET_IPV4DIR);
+	return (lxpr_lookup_common(dp, comp, NULL, sys_net_ipv4dir,
+	    SYS_NET_IPV4DIRFILES));
+}
+
+static vnode_t *
+lxpr_lookup_sys_vmdir(vnode_t *dp, char *comp)
+{
+	ASSERT(VTOLXP(dp)->lxpr_type == LXPR_SYS_VMDIR);
+	return (lxpr_lookup_common(dp, comp, NULL, sys_vmdir,
+	    SYS_VMDIRFILES));
+}
+
+static vnode_t *
+lxpr_lookup_sys_fsdir(vnode_t *dp, char *comp)
+{
+	ASSERT(VTOLXP(dp)->lxpr_type == LXPR_SYS_FSDIR);
+	return (lxpr_lookup_common(dp, comp, NULL, sys_fsdir,
+	    SYS_FSDIRFILES));
+}
+
+static vnode_t *
+lxpr_lookup_sys_fs_inotifydir(vnode_t *dp, char *comp)
+{
+	ASSERT(VTOLXP(dp)->lxpr_type == LXPR_SYS_FS_INOTIFYDIR);
+	return (lxpr_lookup_common(dp, comp, NULL, sys_fs_inotifydir,
+	    SYS_FS_INOTIFYDIRFILES));
+}
+
+/*
+ * lxpr_readdir(): Vnode operation for VOP_READDIR()
+ */
+/* ARGSUSED */
+static int
+lxpr_readdir(vnode_t *dp, uio_t *uiop, cred_t *cr, int *eofp,
+    caller_context_t *ct, int flags)
+{
+	lxpr_node_t *lxpnp = VTOLXP(dp);
+	lxpr_nodetype_t type = lxpnp->lxpr_type;
+	ssize_t uresid;
+	off_t uoffset;
+	int error;
+
+	ASSERT(dp->v_type == VDIR);
+	ASSERT(type < LXPR_NFILES);
+
+	/*
+	 * we should never get here because the readdir
+	 * is done on the realvp for these nodes
+	 */
+	ASSERT(type != LXPR_PID_FD_FD &&
+	    type != LXPR_PID_CURDIR &&
+	    type != LXPR_PID_ROOTDIR);
+
+	/*
+	 * restrict readdir permission to owner or root
+	 */
+	if ((error = lxpr_access(dp, VREAD, 0, cr, ct)) != 0)
+		return (error);
+
+	uoffset = uiop->uio_offset;
+	uresid = uiop->uio_resid;
+
+	/* can't do negative reads */
+	if (uoffset < 0 || uresid <= 0)
+		return (EINVAL);
+
+	/* can't read directory entries that don't exist! */
+	if (uoffset % LXPR_SDSIZE)
+		return (ENOENT);
+
+	return (lxpr_readdir_function[type](lxpnp, uiop, eofp));
+}
+
+/* ARGSUSED */
+static int
+lxpr_readdir_not_a_dir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+	return (ENOTDIR);
+}
+
+/*
+ * This has the common logic for returning directory entries
+ */
+static int
+lxpr_readdir_common(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp,
+    lxpr_dirent_t *dirtab, int dirtablen)
+{
+	/* bp holds one dirent64 structure */
+	longlong_t bp[DIRENT64_RECLEN(LXPNSIZ) / sizeof (longlong_t)];
+	dirent64_t *dirent = (dirent64_t *)bp;
+	ssize_t oresid;	/* save a copy for testing later */
+	ssize_t uresid;
+
+	oresid = uiop->uio_resid;
+
+	/* clear out the dirent buffer */
+	bzero(bp, sizeof (bp));
+
+	/*
+	 * Satisfy user request
+	 */
+	while ((uresid = uiop->uio_resid) > 0) {
+		int dirindex;
+		off_t uoffset;
+		int reclen;
+		int error;
+
+		uoffset = uiop->uio_offset;
+		dirindex  = (uoffset / LXPR_SDSIZE) - 2;
+
+		if (uoffset == 0) {
+
+			dirent->d_ino = lxpnp->lxpr_ino;
+			dirent->d_name[0] = '.';
+			dirent->d_name[1] = '\0';
+			reclen = DIRENT64_RECLEN(1);
+
+		} else if (uoffset == LXPR_SDSIZE) {
+
+			dirent->d_ino = lxpr_parentinode(lxpnp);
+			dirent->d_name[0] = '.';
+			dirent->d_name[1] = '.';
+			dirent->d_name[2] = '\0';
+			reclen = DIRENT64_RECLEN(2);
+
+		} else if (dirindex >= 0 && dirindex < dirtablen) {
+			int slen = strlen(dirtab[dirindex].d_name);
+
+			dirent->d_ino = lxpr_inode(dirtab[dirindex].d_type,
+			    lxpnp->lxpr_pid, 0);
+
+			VERIFY(slen < LXPNSIZ);
+			(void) strcpy(dirent->d_name, dirtab[dirindex].d_name);
+			reclen = DIRENT64_RECLEN(slen);
+
+		} else {
+			/* Run out of table entries */
+			if (eofp) {
+				*eofp = 1;
+			}
+			return (0);
+		}
+
+		dirent->d_off = (off64_t)(uoffset + LXPR_SDSIZE);
+		dirent->d_reclen = (ushort_t)reclen;
+
+		/*
+		 * if the size of the data to transfer is greater
+		 * that that requested then we can't do it this transfer.
+		 */
+		if (reclen > uresid) {
+			/*
+			 * Error if no entries have been returned yet.
+			 */
+			if (uresid == oresid) {
+				return (EINVAL);
+			}
+			break;
+		}
+
+		/*
+		 * uiomove() updates both uiop->uio_resid and uiop->uio_offset
+		 * by the same amount.  But we want uiop->uio_offset to change
+		 * in increments of LXPR_SDSIZE, which is different from the
+		 * number of bytes being returned to the user.  So we set
+		 * uiop->uio_offset separately, ignoring what uiomove() does.
+		 */
+		if ((error = uiomove((caddr_t)dirent, reclen, UIO_READ,
+		    uiop)) != 0)
+			return (error);
+
+		uiop->uio_offset = uoffset + LXPR_SDSIZE;
+	}
+
+	/* Have run out of space, but could have just done last table entry */
+	if (eofp) {
+		*eofp =
+		    (uiop->uio_offset >= ((dirtablen+2) * LXPR_SDSIZE)) ? 1 : 0;
+	}
+	return (0);
+}
+
+
+static int
+lxpr_readdir_procdir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+	/* bp holds one dirent64 structure */
+	longlong_t bp[DIRENT64_RECLEN(LXPNSIZ) / sizeof (longlong_t)];
+	dirent64_t *dirent = (dirent64_t *)bp;
+	ssize_t oresid;	/* save a copy for testing later */
+	ssize_t uresid;
+	off_t uoffset;
+	zoneid_t zoneid;
+	pid_t pid;
+	int error;
+	int ceof;
+
+	ASSERT(lxpnp->lxpr_type == LXPR_PROCDIR);
+
+	oresid = uiop->uio_resid;
+	zoneid = LXPTOZ(lxpnp)->zone_id;
+
+	/*
+	 * We return directory entries in the order: "." and ".." then the
+	 * unique lxproc files, then the directories corresponding to the
+	 * running processes.  We have defined this as the ordering because
+	 * it allows us to more easily keep track of where we are betwen calls
+	 * to getdents().  If the number of processes changes between calls
+	 * then we can't lose track of where we are in the lxproc files.
+	 */
+
+	/* Do the fixed entries */
+	error = lxpr_readdir_common(lxpnp, uiop, &ceof, lx_procdir,
+	    PROCDIRFILES);
+
+	/* Finished if we got an error or if we couldn't do all the table */
+	if (error != 0 || ceof == 0)
+		return (error);
+
+	/* clear out the dirent buffer */
+	bzero(bp, sizeof (bp));
+
+	/* Do the process entries */
+	while ((uresid = uiop->uio_resid) > 0) {
+		proc_t *p;
+		int len;
+		int reclen;
+		int i;
+
+		uoffset = uiop->uio_offset;
+
+		/*
+		 * Stop when entire proc table has been examined.
+		 */
+		i = (uoffset / LXPR_SDSIZE) - 2 - PROCDIRFILES;
+		if (i < 0 || i >= v.v_proc) {
+			/* Run out of table entries */
+			if (eofp) {
+				*eofp = 1;
+			}
+			return (0);
+		}
+		mutex_enter(&pidlock);
+
+		/*
+		 * Skip indices for which there is no pid_entry, PIDs for
+		 * which there is no corresponding process, a PID of 0,
+		 * and anything the security policy doesn't allow
+		 * us to look at.
+		 */
+		if ((p = pid_entry(i)) == NULL || p->p_stat == SIDL ||
+		    p->p_pid == 0 ||
+		    secpolicy_basic_procinfo(CRED(), p, curproc) != 0) {
+			mutex_exit(&pidlock);
+			goto next;
+		}
+		mutex_exit(&pidlock);
+
+		/*
+		 * Convert pid to the Linux default of 1 if we're the zone's
+		 * init process, or 0 if zsched, otherwise use the value from
+		 * the proc structure
+		 */
+		if (p->p_pid == curproc->p_zone->zone_proc_initpid) {
+			pid = 1;
+		} else if (p->p_pid == curproc->p_zone->zone_zsched->p_pid) {
+			pid = 0;
+		} else {
+			pid = p->p_pid;
+		}
+
+		/*
+		 * If this /proc was mounted in the global zone, view
+		 * all procs; otherwise, only view zone member procs.
+		 */
+		if (zoneid != GLOBAL_ZONEID && p->p_zone->zone_id != zoneid) {
+			goto next;
+		}
+
+		ASSERT(p->p_stat != 0);
+
+		dirent->d_ino = lxpr_inode(LXPR_PIDDIR, pid, 0);
+		len = snprintf(dirent->d_name, LXPNSIZ, "%d", pid);
+		ASSERT(len < LXPNSIZ);
+		reclen = DIRENT64_RECLEN(len);
+
+		dirent->d_off = (off64_t)(uoffset + LXPR_SDSIZE);
+		dirent->d_reclen = (ushort_t)reclen;
+
+		/*
+		 * if the size of the data to transfer is greater
+		 * that that requested then we can't do it this transfer.
+		 */
+		if (reclen > uresid) {
+			/*
+			 * Error if no entries have been returned yet.
+			 */
+			if (uresid == oresid)
+				return (EINVAL);
+			break;
+		}
+
+		/*
+		 * uiomove() updates both uiop->uio_resid and uiop->uio_offset
+		 * by the same amount.  But we want uiop->uio_offset to change
+		 * in increments of LXPR_SDSIZE, which is different from the
+		 * number of bytes being returned to the user.  So we set
+		 * uiop->uio_offset separately, in the increment of this for
+		 * the loop, ignoring what uiomove() does.
+		 */
+		if ((error = uiomove((caddr_t)dirent, reclen, UIO_READ,
+		    uiop)) != 0)
+			return (error);
+next:
+		uiop->uio_offset = uoffset + LXPR_SDSIZE;
+	}
+
+	if (eofp != NULL) {
+		*eofp = (uiop->uio_offset >=
+		    ((v.v_proc + PROCDIRFILES + 2) * LXPR_SDSIZE)) ? 1 : 0;
+	}
+
+	return (0);
+}
+
+static int
+lxpr_readdir_piddir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+	proc_t *p;
+	pid_t find_pid;
+
+	ASSERT(lxpnp->lxpr_type == LXPR_PIDDIR);
+
+	/* can't read its contents if it died */
+	mutex_enter(&pidlock);
+
+	if (lxpnp->lxpr_pid == 1) {
+		find_pid = curproc->p_zone->zone_proc_initpid;
+	} else if (lxpnp->lxpr_pid == 0) {
+		find_pid = curproc->p_zone->zone_zsched->p_pid;
+	} else {
+		find_pid = lxpnp->lxpr_pid;
+	}
+	p = prfind(find_pid);
+
+	if (p == NULL || p->p_stat == SIDL) {
+		mutex_exit(&pidlock);
+		return (ENOENT);
+	}
+	mutex_exit(&pidlock);
+
+	return (lxpr_readdir_common(lxpnp, uiop, eofp, piddir, PIDDIRFILES));
+}
+
+static int
+lxpr_readdir_netdir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+	ASSERT(lxpnp->lxpr_type == LXPR_NETDIR);
+	return (lxpr_readdir_common(lxpnp, uiop, eofp, netdir, NETDIRFILES));
+}
+
+static int
+lxpr_readdir_taskdir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+	/* bp holds one dirent64 structure */
+	longlong_t bp[DIRENT64_RECLEN(LXPNSIZ) / sizeof (longlong_t)];
+	dirent64_t *dirent = (dirent64_t *)bp;
+	ssize_t oresid;	/* save a copy for testing later */
+	ssize_t uresid;
+	off_t uoffset;
+	int error, ceof, tiddirsize, tasknum;
+	proc_t *p;
+	pid_t real_pid;
+	kthread_t *t;
+	boolean_t branded;
+
+	ASSERT(lxpnp->lxpr_type == LXPR_PID_TASKDIR);
+
+	oresid = uiop->uio_resid;
+
+	real_pid = get_real_pid(lxpnp->lxpr_pid);
+	p = lxpr_lock(real_pid, ZOMB_OK);
+	if (p == NULL) {
+		return (ENOENT);
+	}
+	if (p->p_stat == SIDL) {
+		lxpr_unlock(p);
+		return (ENOENT);
+	}
+
+	/*
+	 * Just emit static entries for system processes and zombies.
+	 */
+	if ((p->p_stat == SZOMB) || (p->p_flag & (SSYS | SEXITING)) ||
+	    (p->p_as == &kas)) {
+		lxpr_unlock(p);
+		return (lxpr_readdir_common(lxpnp, uiop, eofp, 0, 0));
+	}
+
+	/*
+	 * Drop p_lock, but keep the process P_PR_LOCK'd to prevent it from
+	 * going away while we iterate over its threads.
+	 */
+	tiddirsize = p->p_lwpcnt;
+	branded = (p->p_brand == &lx_brand);
+	mutex_exit(&p->p_lock);
+
+	/* Do the fixed entries (in this case just "." & "..") */
+	error = lxpr_readdir_common(lxpnp, uiop, &ceof, 0, 0);
+
+	/* Finished if we got an error or if we couldn't do all the table */
+	if (error != 0 || ceof == 0)
+		goto out;
+
+	if ((t = p->p_tlist) == NULL) {
+		if (eofp != NULL)
+			*eofp = 1;
+		goto out;
+	}
+
+	/* clear out the dirent buffer */
+	bzero(bp, sizeof (bp));
+
+	/*
+	 * Loop until user's request is satisfied or until all thread's have
+	 * been returned.
+	 */
+	for (tasknum = 0; (uresid = uiop->uio_resid) > 0; tasknum++) {
+		int i, reclen, len;
+		uint_t emul_tid;
+		lx_lwp_data_t *lwpd;
+
+		uoffset = uiop->uio_offset;
+
+		/*
+		 * Stop at the end of the thread list
+		 */
+		i = (uoffset / LXPR_SDSIZE) - 2;
+		if (i < 0 || i >= tiddirsize) {
+			if (eofp) {
+				*eofp = 1;
+			}
+			goto out;
+		}
+
+		if (i != tasknum)
+			goto next;
+
+		if (!branded) {
+			/*
+			 * Emulating the goofy linux task model is impossible
+			 * to do for native processes.  We can compromise by
+			 * presenting only the main thread to the consumer.
+			 */
+			emul_tid = p->p_pid;
+		} else {
+			if ((lwpd = ttolxlwp(t)) == NULL) {
+				goto next;
+			}
+			emul_tid = lwpd->br_pid;
+			/*
+			 * Convert pid to Linux default of 1 if we're the
+			 * zone's init.
+			 */
+			if (emul_tid == curproc->p_zone->zone_proc_initpid)
+				emul_tid = 1;
+		}
+
+		dirent->d_ino = lxpr_inode(LXPR_PID_TASK_IDDIR, lxpnp->lxpr_pid,
+		    emul_tid);
+		len = snprintf(dirent->d_name, LXPNSIZ, "%d", emul_tid);
+		ASSERT(len < LXPNSIZ);
+		reclen = DIRENT64_RECLEN(len);
+
+		dirent->d_off = (off64_t)(uoffset + LXPR_SDSIZE);
+		dirent->d_reclen = (ushort_t)reclen;
+
+		if (reclen > uresid) {
+			/*
+			 * Error if no entries have been returned yet.
+			 */
+			if (uresid == oresid)
+				error = EINVAL;
+			goto out;
+		}
+
+		/*
+		 * uiomove() updates both uiop->uio_resid and uiop->uio_offset
+		 * by the same amount.  But we want uiop->uio_offset to change
+		 * in increments of LXPR_SDSIZE, which is different from the
+		 * number of bytes being returned to the user.  So we set
+		 * uiop->uio_offset separately, in the increment of this for
+		 * the loop, ignoring what uiomove() does.
+		 */
+		if ((error = uiomove((caddr_t)dirent, reclen, UIO_READ,
+		    uiop)) != 0)
+			goto out;
+
+next:
+		uiop->uio_offset = uoffset + LXPR_SDSIZE;
+
+		if ((t = t->t_forw) == p->p_tlist || !branded) {
+			if (eofp != NULL)
+				*eofp = 1;
+			goto out;
+		}
+	}
+
+	if (eofp != NULL)
+		*eofp = 0;
+
+out:
+	mutex_enter(&p->p_lock);
+	lxpr_unlock(p);
+	return (error);
+}
+
+static int
+lxpr_readdir_task_tid_dir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+	proc_t *p;
+	pid_t real_pid;
+	kthread_t *t;
+
+	ASSERT(lxpnp->lxpr_type == LXPR_PID_TASK_IDDIR);
+
+	mutex_enter(&pidlock);
+
+	real_pid = get_real_pid(lxpnp->lxpr_pid);
+	p = prfind(real_pid);
+
+	/* can't read its contents if it died */
+	if (p == NULL || p->p_stat == SIDL) {
+		mutex_exit(&pidlock);
+		return (ENOENT);
+	}
+
+	mutex_exit(&pidlock);
+
+	/* need to confirm tid is still there */
+	t = lxpr_get_thread(p, lxpnp->lxpr_desc);
+	if (t == NULL) {
+		/* we can't find this specific thread */
+		return (NULL);
+	}
+	thread_unlock(t);
+
+	return (lxpr_readdir_common(lxpnp, uiop, eofp, tiddir, TIDDIRFILES));
+}
+
+static int
+lxpr_readdir_fddir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+	/* bp holds one dirent64 structure */
+	longlong_t bp[DIRENT64_RECLEN(LXPNSIZ) / sizeof (longlong_t)];
+	dirent64_t *dirent = (dirent64_t *)bp;
+	ssize_t oresid;	/* save a copy for testing later */
+	ssize_t uresid;
+	off_t uoffset;
+	int error, ceof, fddirsize;
+	proc_t *p;
+	uf_info_t *fip;
+
+	ASSERT(lxpnp->lxpr_type == LXPR_PID_FDDIR ||
+	    lxpnp->lxpr_type == LXPR_PID_TID_FDDIR);
+
+	oresid = uiop->uio_resid;
+
+	/* can't read its contents if it died */
+	p = lxpr_lock(lxpnp->lxpr_pid, ZOMB_OK);
+	if (p == NULL)
+		return (ENOENT);
+
+	/*
+	 * For exiting/exited processes or those belonging to the system, only
+	 * emit the fixed entries.
+	 */
+	if ((p->p_stat == SZOMB) || (p->p_flag & (SSYS | SEXITING)) ||
+	    (p->p_as == &kas)) {
+		lxpr_unlock(p);
+		return (lxpr_readdir_common(lxpnp, uiop, eofp, 0, 0));
+	}
+
+	/*
+	 * Drop p_lock, but keep the process P_PR_LOCK'd to prevent it from
+	 * going away while we iterate over its fi_list.
+	 */
+	mutex_exit(&p->p_lock);
+
+	/* Get open file info */
+	fip = (&(p)->p_user.u_finfo);
+	mutex_enter(&fip->fi_lock);
+	fddirsize = fip->fi_nfiles;
+
+	/* Do the fixed entries (in this case just "." & "..") */
+	error = lxpr_readdir_common(lxpnp, uiop, &ceof, 0, 0);
+
+	/* Finished if we got an error or if we couldn't do all the table */
+	if (error != 0 || ceof == 0)
+		goto out;
+
+	/* clear out the dirent buffer */
+	bzero(bp, sizeof (bp));
+
+	/*
+	 * Loop until user's request is satisfied or until
+	 * all file descriptors have been examined.
+	 */
+	for (; (uresid = uiop->uio_resid) > 0;
+	    uiop->uio_offset = uoffset + LXPR_SDSIZE) {
+		int reclen;
+		int fd;
+		int len;
+
+		uoffset = uiop->uio_offset;
+
+		/*
+		 * Stop at the end of the fd list
+		 */
+		fd = (uoffset / LXPR_SDSIZE) - 2;
+		if (fd < 0 || fd >= fddirsize) {
+			if (eofp) {
+				*eofp = 1;
+			}
+			goto out;
+		}
+
+		if (fip->fi_list[fd].uf_file == NULL)
+			continue;
+
+		dirent->d_ino = lxpr_inode(LXPR_PID_FD_FD, lxpnp->lxpr_pid, fd);
+		len = snprintf(dirent->d_name, LXPNSIZ, "%d", fd);
+		ASSERT(len < LXPNSIZ);
+		reclen = DIRENT64_RECLEN(len);
+
+		dirent->d_off = (off64_t)(uoffset + LXPR_SDSIZE);
+		dirent->d_reclen = (ushort_t)reclen;
+
+		if (reclen > uresid) {
+			/*
+			 * Error if no entries have been returned yet.
+			 */
+			if (uresid == oresid)
+				error = EINVAL;
+			goto out;
+		}
+
+		if ((error = uiomove((caddr_t)dirent, reclen, UIO_READ,
+		    uiop)) != 0)
+			goto out;
+	}
+
+	if (eofp != NULL) {
+		*eofp =
+		    (uiop->uio_offset >= ((fddirsize+2) * LXPR_SDSIZE)) ? 1 : 0;
+	}
+
+out:
+	mutex_exit(&fip->fi_lock);
+	mutex_enter(&p->p_lock);
+	lxpr_unlock(p);
+	return (error);
+}
+
+static int
+lxpr_readdir_sysdir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+	ASSERT(lxpnp->lxpr_type == LXPR_SYSDIR);
+	return (lxpr_readdir_common(lxpnp, uiop, eofp, sysdir, SYSDIRFILES));
+}
+
+static int
+lxpr_readdir_sys_fsdir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_FSDIR);
+	return (lxpr_readdir_common(lxpnp, uiop, eofp, sys_fsdir,
+	    SYS_FSDIRFILES));
+}
+
+static int
+lxpr_readdir_sys_fs_inotifydir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_FS_INOTIFYDIR);
+	return (lxpr_readdir_common(lxpnp, uiop, eofp, sys_fs_inotifydir,
+	    SYS_FS_INOTIFYDIRFILES));
+}
+
+static int
+lxpr_readdir_sys_kerneldir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNELDIR);
+	return (lxpr_readdir_common(lxpnp, uiop, eofp, sys_kerneldir,
+	    SYS_KERNELDIRFILES));
+}
+
+static int
+lxpr_readdir_sys_kdir_randdir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_RANDDIR);
+	return (lxpr_readdir_common(lxpnp, uiop, eofp, sys_randdir,
+	    SYS_RANDDIRFILES));
+}
+
+static int
+lxpr_readdir_sys_netdir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_NETDIR);
+	return (lxpr_readdir_common(lxpnp, uiop, eofp, sys_netdir,
+	    SYS_NETDIRFILES));
+}
+
+static int
+lxpr_readdir_sys_net_coredir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_COREDIR);
+	return (lxpr_readdir_common(lxpnp, uiop, eofp, sys_net_coredir,
+	    SYS_NET_COREDIRFILES));
+}
+
+static int
+lxpr_readdir_sys_net_ipv4dir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4DIR);
+	return (lxpr_readdir_common(lxpnp, uiop, eofp, sys_net_ipv4dir,
+	    SYS_NET_IPV4DIRFILES));
+}
+
+static int
+lxpr_readdir_sys_vmdir(lxpr_node_t *lxpnp, uio_t *uiop, int *eofp)
+{
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_VMDIR);
+	return (lxpr_readdir_common(lxpnp, uiop, eofp, sys_vmdir,
+	    SYS_VMDIRFILES));
+}
+
+#define	isdigit(c)	((c) >= '0' && (c) <= '9')
+#define	isspace(c)	((c) == ' ' || (c) == '\t' || (c) == '\n')
+
+/*
+ * Obtain a numeric value from the null-terminated input string.
+ * We don't have strtok in the kernel, so tokenize this ourselves and
+ * validate the input.
+ */
+static int
+lxpr_tokenize_num(char *str, long *pv, char **ep)
+{
+	char *pstart, *pc, c, *endptr;
+	long v;
+
+	for (pc = str; isspace(*pc); pc++)
+		;
+
+	for (pstart = pc; isdigit(*pc); pc++)
+		;
+	if (pc == pstart || (!isspace(*pc) && *pc != '\0'))
+		return (EINVAL);
+	c = *pc;
+	*pc = '\0';
+
+	if (ddi_strtol(pstart, &endptr, 10, &v) != 0) {
+		*pc = c;
+		return (EINVAL);
+	}
+	if (*endptr != '\0') {
+		*pc = c;
+		return (EINVAL);
+	}
+
+	if (pv != NULL)
+		*pv = v;
+	if (ep != NULL)
+		*ep = ++pc;
+
+	return (0);
+}
+
+/* ARGSUSED */
+static int
+lxpr_write_tcp_property(lxpr_node_t *lxpnp, struct uio *uio,
+    struct cred *cr, caller_context_t *ct, char *prop,
+    int (*xlate)(char *, int))
+{
+	int error;
+	int res = 0;
+	size_t olen;
+	char val[16];	/* big enough for a uint numeric string */
+	netstack_t *ns;
+	mod_prop_info_t *ptbl = NULL;
+	mod_prop_info_t *pinfo = NULL;
+
+	if (uio->uio_loffset != 0)
+		return (EINVAL);
+
+	if (uio->uio_resid == 0)
+		return (0);
+
+	olen = uio->uio_resid;
+	if (olen > sizeof (val) - 1)
+		return (EINVAL);
+
+	bzero(val, sizeof (val));
+	error = uiomove(val, olen, UIO_WRITE, uio);
+	if (error != 0)
+		return (error);
+
+	if (val[olen - 1] == '\n')
+		val[olen - 1] = '\0';
+
+	if (val[0] == '\0') /* no input */
+		return (EINVAL);
+
+	ns = netstack_get_current();
+	if (ns == NULL)
+		return (EINVAL);
+
+	if (xlate != NULL && xlate(val, sizeof (val)) != 0) {
+		netstack_rele(ns);
+		return (EINVAL);
+	}
+
+	ptbl = ns->netstack_tcp->tcps_propinfo_tbl;
+	pinfo = mod_prop_lookup(ptbl, prop, MOD_PROTO_TCP);
+	if (pinfo == NULL || pinfo->mpi_setf(ns, cr, pinfo, NULL, val, 0) != 0)
+		res = EINVAL;
+
+	netstack_rele(ns);
+	return (res);
+}
+
+static int
+lxpr_write_sys_net_core_somaxc(lxpr_node_t *lxpnp, struct uio *uio,
+    struct cred *cr, caller_context_t *ct)
+{
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_CORE_SOMAXCON);
+	return (lxpr_write_tcp_property(lxpnp, uio, cr, ct,
+	    "_conn_req_max_q", NULL));
+}
+
+static int
+lxpr_xlate_sec2ms(char *val, int size)
+{
+	long sec;
+	char *ep;
+
+	if (lxpr_tokenize_num(val, &sec, &ep) != 0)
+		return (EINVAL);
+	if (*ep != '\0')
+		return (EINVAL);
+	if (snprintf(val, size, "%ld", sec * 1000) >= size)
+		return (EINVAL);
+	return (0);
+}
+
+static int
+lxpr_xlate_ka_intvl(char *val, int size)
+{
+	long sec;
+	char *ep;
+
+	if (lxpr_tokenize_num(val, &sec, &ep) != 0)
+		return (EINVAL);
+	if (*ep != '\0')
+		return (EINVAL);
+	if (snprintf(val, size, "%ld", sec * 1000 * 9) >= size)
+		return (EINVAL);
+	return (0);
+}
+
+static int
+lxpr_xlate_sack(char *val, int size)
+{
+	long flag;
+	char *ep;
+
+	if (lxpr_tokenize_num(val, &flag, &ep) != 0)
+		return (EINVAL);
+	if (*ep != '\0')
+		return (EINVAL);
+	if (flag != 0 && flag != 1)
+		return (EINVAL);
+	/* see comment on lxpr_read_sys_net_ipv4_tcp_sack */
+	if (snprintf(val, size, "%d", (flag == 0 ? 0 : 2)) >= size)
+		return (EINVAL);
+	return (0);
+}
+
+/*
+ * We expect two port numbers on a line as input for the range, and we have to
+ * set two properties on the netstack_tcp, so we can't reuse
+ * lxpr_write_tcp_property.
+ */
+/* ARGSUSED */
+static int
+lxpr_write_sys_net_ipv4_ip_lport_range(lxpr_node_t *lxpnp, struct uio *uio,
+    struct cred *cr, caller_context_t *ct)
+{
+	int res;
+	size_t olen;
+	char vals[32];	/* big enough for a line w/ 2 16-bit numeric strings */
+	char *ep;
+	long low, high;
+	netstack_t *ns;
+	tcp_stack_t *tcps;
+	mod_prop_info_t *ptbl = NULL;
+	mod_prop_info_t *pinfo = NULL;
+
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_IP_LPORT_RANGE);
+
+	if (uio->uio_loffset != 0)
+		return (EINVAL);
+
+	if (uio->uio_resid == 0)
+		return (0);
+
+	olen = uio->uio_resid;
+	if (olen > sizeof (vals) - 1)
+		return (EINVAL);
+
+	bzero(vals, sizeof (vals));
+	res = uiomove(vals, olen, UIO_WRITE, uio);
+	if (res != 0)
+		return (res);
+
+	if (lxpr_tokenize_num(vals, &low, &ep) != 0)
+		return (EINVAL);
+
+	if (lxpr_tokenize_num(ep, &high, &ep) != 0)
+		return (EINVAL);
+
+	if (*ep != '\0') {
+		/* make sure no other tokens on the line */
+		*ep++ = '\0';
+		for (; isspace(*ep); ep++)
+			;
+		if (*ep != '\0')
+			return (EINVAL);
+	}
+
+	if (low > high || high > 65535)
+		return (EINVAL);
+
+	ns = netstack_get_current();
+	if (ns == NULL)
+		return (EINVAL);
+
+	tcps = ns->netstack_tcp;
+	if (low < tcps->tcps_smallest_nonpriv_port) {
+		netstack_rele(ns);
+		return (EINVAL);
+	}
+
+	ptbl = ns->netstack_tcp->tcps_propinfo_tbl;
+
+	(void) snprintf(vals, sizeof (vals), "%ld", low);
+	pinfo = mod_prop_lookup(ptbl, "smallest_anon_port", MOD_PROTO_TCP);
+	if (pinfo == NULL || pinfo->mpi_setf(ns, cr, pinfo, NULL, vals, 0) != 0)
+		res = EINVAL;
+
+	(void) snprintf(vals, sizeof (vals), "%ld", high);
+	pinfo = mod_prop_lookup(ptbl, "largest_anon_port", MOD_PROTO_TCP);
+	if (pinfo == NULL || pinfo->mpi_setf(ns, cr, pinfo, NULL, vals, 0) != 0)
+		res = EINVAL;
+
+	netstack_rele(ns);
+	return (res);
+}
+
+static int
+lxpr_write_sys_net_ipv4_tcp_fin_to(lxpr_node_t *lxpnp, struct uio *uio,
+    struct cred *cr, caller_context_t *ct)
+{
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_FIN_TO);
+	return (lxpr_write_tcp_property(lxpnp, uio, cr, ct,
+	    "_fin_wait_2_flush_interval", lxpr_xlate_sec2ms));
+}
+
+static int
+lxpr_write_sys_net_ipv4_tcp_ka_int(lxpr_node_t *lxpnp, struct uio *uio,
+    struct cred *cr, caller_context_t *ct)
+{
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_KA_INT);
+	return (lxpr_write_tcp_property(lxpnp, uio, cr, ct,
+	    "_keepalive_abort_interval", lxpr_xlate_ka_intvl));
+}
+
+static int
+lxpr_write_sys_net_ipv4_tcp_ka_tim(lxpr_node_t *lxpnp, struct uio *uio,
+    struct cred *cr, caller_context_t *ct)
+{
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_KA_TIM);
+	return (lxpr_write_tcp_property(lxpnp, uio, cr, ct,
+	    "_keepalive_interval", lxpr_xlate_sec2ms));
+}
+
+static int
+lxpr_write_sys_net_ipv4_tcp_sack(lxpr_node_t *lxpnp, struct uio *uio,
+    struct cred *cr, caller_context_t *ct)
+{
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_SACK);
+	return (lxpr_write_tcp_property(lxpnp, uio, cr, ct, "sack",
+	    lxpr_xlate_sack));
+}
+
+static int
+lxpr_write_sys_net_ipv4_tcp_winscale(lxpr_node_t *lxpnp, struct uio *uio,
+    struct cred *cr, caller_context_t *ct)
+{
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_NET_IPV4_TCP_WINSCALE);
+	return (lxpr_write_tcp_property(lxpnp, uio, cr, ct, "_wscale_always",
+	    NULL));
+}
+
+/* ARGSUSED */
+static int
+lxpr_write_sys_kernel_corepatt(lxpr_node_t *lxpnp, struct uio *uio,
+    struct cred *cr, caller_context_t *ct)
+{
+	zone_t *zone = curproc->p_zone;
+	struct core_globals *cg;
+	refstr_t *rp, *nrp;
+	corectl_path_t *ccp;
+	char val[MAXPATHLEN];
+	char valtr[MAXPATHLEN];
+	size_t olen;
+	int error;
+
+	ASSERT(lxpnp->lxpr_type == LXPR_SYS_KERNEL_COREPATT);
+
+	cg = zone_getspecific(core_zone_key, zone);
+	ASSERT(cg != NULL);
+
+	if (secpolicy_coreadm(cr) != 0)
+		return (EPERM);
+
+	if (uio->uio_loffset != 0)
+		return (EINVAL);
+
+	if (uio->uio_resid == 0)
+		return (0);
+
+	olen = uio->uio_resid;
+	if (olen > sizeof (val) - 1)
+		return (EINVAL);
+
+	bzero(val, sizeof (val));
+	error = uiomove(val, olen, UIO_WRITE, uio);
+	if (error != 0)
+		return (error);
+
+	if (val[olen - 1] == '\n')
+		val[olen - 1] = '\0';
+
+	if (val[0] == '|')
+		return (EINVAL);
+
+	if ((error = lxpr_core_path_l2s(val, valtr, sizeof (valtr))) != 0)
+		return (error);
+
+	nrp = refstr_alloc(valtr);
+
+	ccp = cg->core_default_path;
+	mutex_enter(&ccp->ccp_mtx);
+	rp = ccp->ccp_path;
+	refstr_hold((ccp->ccp_path = nrp));
+	cg->core_options |= CC_PROCESS_PATH;
+	mutex_exit(&ccp->ccp_mtx);
+
+	if (rp != NULL)
+		refstr_rele(rp);
+
+	return (0);
+}
+
+/* ARGSUSED */
+static int
+lxpr_write_pid_loginuid(lxpr_node_t *lxpnp, struct uio *uio, struct cred *cr,
+    caller_context_t *ct)
+{
+	int error;
+	size_t olen;
+	char val[16];	/* big enough for a uint numeric string */
+	char *ep;
+	long u;
+	proc_t *p;
+	lx_proc_data_t *pd;
+
+	ASSERT(lxpnp->lxpr_type == LXPR_PID_LOGINUID);
+
+	if (uio->uio_loffset != 0)
+		return (EINVAL);
+
+	if (uio->uio_resid == 0)
+		return (0);
+
+	olen = uio->uio_resid;
+	if (olen > sizeof (val) - 1)
+		return (EINVAL);
+
+	bzero(val, sizeof (val));
+	error = uiomove(val, olen, UIO_WRITE, uio);
+	if (error != 0)
+		return (error);
+
+	if (lxpr_tokenize_num(val, &u, &ep) != 0)
+		return (EINVAL);
+	if (*ep != '\0')
+		return (EINVAL);
+
+	if ((p = lxpr_lock(lxpnp->lxpr_pid, NO_ZOMB)) == NULL)
+		return (ENXIO);
+
+	if ((pd = ptolxproc(p)) != NULL) {
+		pd->l_loginuid = (uid_t)u;
+	}
+	lxpr_unlock(p);
+
+	return (0);
+}
+
+/*
+ * lxpr_readlink(): Vnode operation for VOP_READLINK()
+ */
+/* ARGSUSED */
+static int
+lxpr_readlink(vnode_t *vp, uio_t *uiop, cred_t *cr, caller_context_t *ct)
+{
+	char bp[MAXPATHLEN + 1];
+	size_t buflen = sizeof (bp);
+	lxpr_node_t *lxpnp = VTOLXP(vp);
+	vnode_t *rvp = lxpnp->lxpr_realvp;
+	pid_t pid;
+	int error = 0;
+
+	/*
+	 * Linux does something very "clever" for /proc/<pid>/fd/<num> entries.
+	 * Open FDs are represented as symlinks, the link contents
+	 * corresponding to the open resource.  For plain files or devices,
+	 * this isn't absurd since one can dereference the symlink to query
+	 * the underlying resource.  For sockets or pipes, it becomes ugly in a
+	 * hurry.  To maintain this human-readable output, those FD symlinks
+	 * point to bogus targets such as "socket:[<inodenum>]".  This requires
+	 * circumventing vfs since the stat/lstat behavior on those FD entries
+	 * will be unusual. (A stat must retrieve information about the open
+	 * socket or pipe.  It cannot fail because the link contents point to
+	 * an absent file.)
+	 *
+	 * To accomplish this, lxpr_getnode returns an vnode typed VNON for FD
+	 * entries.  This bypasses code paths which would normally
+	 * short-circuit on symlinks and allows us to emulate the vfs behavior
+	 * expected by /proc consumers.
+	 */
+	if (vp->v_type != VLNK && lxpnp->lxpr_type != LXPR_PID_FD_FD)
+		return (EINVAL);
+
+	/* Try to produce a symlink name for anything that has a realvp */
+	if (rvp != NULL) {
+		if ((error = lxpr_access(vp, VREAD, 0, CRED(), ct)) != 0)
+			return (error);
+		if ((error = vnodetopath(NULL, rvp, bp, buflen, CRED())) != 0) {
+			/*
+			 * Special handling possible for /proc/<pid>/fd/<num>
+			 * Generate <type>:[<inode>] links, if allowed.
+			 */
+			if (lxpnp->lxpr_type != LXPR_PID_FD_FD ||
+			    lxpr_readlink_fdnode(lxpnp, bp, buflen) != 0) {
+				return (error);
+			}
+		}
+	} else {
+		switch (lxpnp->lxpr_type) {
+		case LXPR_SELF:
+			/*
+			 * Convert pid to the Linux default of 1 if we're the
+			 * zone's init process or 0 if zsched.
+			 */
+			if (curproc->p_pid ==
+			    curproc->p_zone->zone_proc_initpid) {
+				pid = 1;
+			} else if (curproc->p_pid ==
+			    curproc->p_zone->zone_zsched->p_pid) {
+				pid = 0;
+			} else {
+				pid = curproc->p_pid;
+			}
+
+			/*
+			 * Don't need to check result as every possible int
+			 * will fit within MAXPATHLEN bytes.
+			 */
+			(void) snprintf(bp, buflen, "%d", pid);
+			break;
+		case LXPR_PID_CURDIR:
+		case LXPR_PID_ROOTDIR:
+		case LXPR_PID_EXE:
+			return (EACCES);
+		default:
+			/*
+			 * Need to return error so that nothing thinks
+			 * that the symlink is empty and hence "."
+			 */
+			return (EINVAL);
+		}
+	}
+
+	/* copy the link data to user space */
+	return (uiomove(bp, strlen(bp), UIO_READ, uiop));
+}
+
+
+/*
+ * lxpr_inactive(): Vnode operation for VOP_INACTIVE()
+ * Vnode is no longer referenced, deallocate the file
+ * and all its resources.
+ */
+/* ARGSUSED */
+static void
+lxpr_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
+{
+	lxpr_freenode(VTOLXP(vp));
+}
+
+/*
+ * lxpr_sync(): Vnode operation for VOP_SYNC()
+ */
+static int
+lxpr_sync()
+{
+	/*
+	 * Nothing to sync but this function must never fail
+	 */
+	return (0);
+}
+
+/*
+ * lxpr_cmp(): Vnode operation for VOP_CMP()
+ */
+static int
+lxpr_cmp(vnode_t *vp1, vnode_t *vp2, caller_context_t *ct)
+{
+	vnode_t *rvp;
+
+	while (vn_matchops(vp1, lxpr_vnodeops) &&
+	    (rvp = VTOLXP(vp1)->lxpr_realvp) != NULL) {
+		vp1 = rvp;
+	}
+
+	while (vn_matchops(vp2, lxpr_vnodeops) &&
+	    (rvp = VTOLXP(vp2)->lxpr_realvp) != NULL) {
+		vp2 = rvp;
+	}
+
+	if (vn_matchops(vp1, lxpr_vnodeops) || vn_matchops(vp2, lxpr_vnodeops))
+		return (vp1 == vp2);
+	return (VOP_CMP(vp1, vp2, ct));
+}
+
+/*
+ * lxpr_realvp(): Vnode operation for VOP_REALVP()
+ */
+static int
+lxpr_realvp(vnode_t *vp, vnode_t **vpp, caller_context_t *ct)
+{
+	vnode_t *rvp;
+
+	if ((rvp = VTOLXP(vp)->lxpr_realvp) != NULL) {
+		vp = rvp;
+		if (VOP_REALVP(vp, &rvp, ct) == 0)
+			vp = rvp;
+	}
+
+	*vpp = vp;
+	return (0);
+}
+
+/* ARGSUSED */
+static int
+lxpr_write(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
+    caller_context_t *ct)
+{
+	lxpr_node_t	*lxpnp = VTOLXP(vp);
+	lxpr_nodetype_t	type = lxpnp->lxpr_type;
+	int i;
+
+	for (i = 0; wr_tab[i].wft_type != LXPR_INVALID; i++) {
+		if (wr_tab[i].wft_type == type) {
+			if (wr_tab[i].wft_wrf != NULL) {
+				return (wr_tab[i].wft_wrf(lxpnp, uiop, cr, ct));
+			}
+			break;
+		}
+	}
+
+	/* pretend we wrote the whole thing */
+	uiop->uio_offset += uiop->uio_resid;
+	uiop->uio_resid = 0;
+	return (0);
+}
+
+/* Needed for writable files which are first "truncated" */
+/* ARGSUSED */
+static int
+lxpr_space(vnode_t *vp, int cmd, flock64_t *bfp, int flag, offset_t offset,
+    cred_t *cred, caller_context_t *ct)
+{
+	int error;
+
+	if (cmd != F_FREESP)
+		return (EINVAL);
+	if ((error = lxpr_access(vp, VWRITE, 0, cred, ct)) != 0)
+		return (error);
+
+	return (0);
+}
+
+/*
+ * Needed for writable files which are first "truncated". We only support
+ * truncation.
+ */
+/* ARGSUSED */
+static int
+lxpr_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
+    caller_context_t *ct)
+{
+	int error;
+
+	if (vap->va_mask != AT_SIZE)
+		return (EINVAL);
+	if ((error = lxpr_access(vp, VWRITE, 0, cr, ct)) != 0)
+		return (error);
+
+	return (0);
+}
+
+/*
+ * We need to allow open with O_CREAT for the writable files.
+ */
+/* ARGSUSED */
+static int
+lxpr_create(vnode_t *dvp, char *nm, vattr_t *vap, enum vcexcl exclusive,
+    int mode, vnode_t **vpp, cred_t *cr, int flag, caller_context_t *ct,
+    vsecattr_t *vsecp)
+{
+	lxpr_node_t *lxpnp = VTOLXP(dvp);
+	lxpr_nodetype_t type = lxpnp->lxpr_type;
+	vnode_t *vp = NULL;
+	int error;
+
+	ASSERT(type < LXPR_NFILES);
+
+	/*
+	 * restrict create permission to owner or root
+	 */
+	if ((error = lxpr_access(dvp, VEXEC, 0, cr, ct)) != 0) {
+		return (error);
+	}
+
+	if (*nm == '\0')
+		return (EPERM);
+
+	if (dvp->v_type != VDIR)
+		return (EPERM);
+
+	if (exclusive == EXCL)
+		return (EEXIST);
+
+	/*
+	 * No writable files in top-level proc dir. We check this to avoid
+	 * getting a non-proc node via "..".
+	 */
+	if (type != LXPR_PROCDIR &&
+	    lxpr_lookup(dvp, nm, &vp, NULL, 0, NULL, cr, ct, NULL, NULL) == 0) {
+		lxpr_nodetype_t ftype = VTOLXP(vp)->lxpr_type;
+		if (!lxpr_is_writable(ftype)) {
+			VN_RELE(vp);
+			vp = NULL;
+		}
+	}
+
+	if (vp != NULL) {
+		ASSERT(vp->v_type != VDIR);
+
+		/* confirm permissions against existing file */
+		if ((error = lxpr_access(vp, mode, 0, cr, ct)) != 0) {
+			VN_RELE(vp);
+			return (error);
+		}
+
+		*vpp = vp;
+		return (0);
+	}
+
+	/*
+	 * Linux proc does not allow creation of addition, non-subsystem
+	 * specific files inside the hierarchy.  ENOENT is tossed when such
+	 * actions are attempted.
+	 */
+	return (ENOENT);
+}
--- /dev/null	Wed Oct  5 00:32:06 2016
+++ new/usr/src/uts/common/brand/lx/sys/lx_autofs.h	Wed Oct  5 00:32:06 2016
@@ -0,0 +1,511 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#ifndef	_LX_AUTOFS_H
+#define	_LX_AUTOFS_H
+
+/*
+ * The lxautofs filesystem and driver exist to emulate the Linux autofs
+ * filesystem and /dev/autofs device (this code emulates both). The
+ * purpose is to provide support for the Linux "automount" automounter.
+ *
+ * The device ioctls map fairly closely to the filesystem ioctls. The device
+ * ioctls have superseded the filesystem ioctls and the automounter will
+ * use the device ioctls if the device exists.
+ *
+ * The device ioctls are used by the automounter to perform recovery
+ * in cases where the automounter is restarted while mounts are present. It
+ * also allows for better management operations when a filesystem is mounted
+ * on top of an autofs mountpoint, as in the case of an NFS direct mount on
+ * top of an autofs mount.
+ *
+ *
+ * +++ Linux automounter background.
+ *
+ * Linux has two automounters: "amd" (not used in any popular, modern distro)
+ * and "automount".
+ *
+ * "automount" is the normal Linux automounter.  It utilizes a kernel
+ * filesystem (autofs) and device (/dev/autofs) to provide its functionality.
+ * Basically, it mounts the autofs filesystem at any automounter controlled
+ * mountpoint. This filesystem then intercepts and redirects lookup operations
+ * to the userland automounter process via a pipe. The pipe to the automounter
+ * is established via a mount option when the autofs filesystem is mounted or
+ * via the setpipefd ioctl if the automounter restarts. When the automounter
+ * receives a request via this pipe, it does lookups (or unmounts) to whatever
+ * backing store it's configured to use, does mkdir operations on the autofs
+ * filesystem, mounts remote NFS filesystems on any directories it manages or
+ * just created, and signals the autofs device via an ioctl to let it know
+ * that the lookup (or expire) can continue. Other management operations (such
+ * as querying expiration for unmounting) are performed using the autofs device.
+ *
+ *
+ * +++ Linux autofs documentation.
+ *
+ * Within the Linux src tree, see the file:
+ * Documentation/filesystems/autofs4-mount-control.txt
+ * This documents some of the autofs behavior and the device driver ioctls.
+ *
+ * The following URL (https://lwn.net/Articles/606960/) documents autofs in
+ * general. This patch was targeted for Documentation/filesystems/autofs4.txt,
+ * but seems to have never integrated into the Linux src tree.
+ *
+ *
+ * +++ Linux autofs (and automount daemon) notes
+ *
+ * Since we're mimicking the behavior of the Linux autofs filesystem and
+ * device, we document some of the observed behavior here.
+ *
+ * There are multiple versions of the autofs filesystem kernel API protocol
+ * and modern implementations of the user-land automount daemon would depend
+ * on v5, although the filesystem API has been superseded by the driver ioctl
+ * API, which is roughly similar.
+ *
+ * We'll describe the filesystem ioctls first, since support for those was
+ * implemented first. The device ioctls roughly correspond to the filesystem
+ * ioctls and were implemented last, but the automounter will use those
+ * ioctls, instead of the filesystem ioctls, when the device is present.
+ *
+ * Our original autofs implementation was developed in the mid-2000s around the
+ * v2 protocol, but that is currently obsolete. Our current implementation is
+ * based around the v5 protocol API. There was no autofs device support at that
+ * time.
+ *
+ * The autoumounter supports 3 different, mutually exclusive, mount options for
+ * each mountpoint:
+ *   - indirect (this was all you got with the v2 support)
+ *   - direct
+ *   - offset
+ *
+ * An 'indirect' mountpoint is managed with dynamic mounts below that
+ * mountpoint. For example, if '/home' were an indirect autofs mount, then
+ * accessing a username under /home would traverse the 'lookup' code described
+ * below, cause a local subdirectory to be created, and a mount, usually NFS,
+ * onto that username subdirectory.
+ *
+ * A 'direct' mountpoint is an autofs mountpoint which will trigger the
+ * mounting of another filesystem overtop that mountpoint when accessed.
+ *
+ * An 'offset' mountpoint behaves like a 'direct' mountpoint but it is
+ * created dynamically by the automounter underneath an 'indirect' mountpoint.
+ * For example, if '/net' were an indirect autosfs mountpoint and the host
+ * 'jurassic' exported two NFS filesystems; '/var/crash' and '/var/core', then
+ * accessing '/net/jurassic' would trigger the automounter to create two
+ * subdirectories; '/net/jurassic/var/crash' and '/net/jurassic/var/core'. The
+ * automounter would then mount an autofs offset mount onto each one of these
+ * directories. Accessing either of those directories would then trigger
+ * automounter to perform another mount on top, as is done with a 'direct'
+ * mount.
+ *
+ * General behavior
+ *
+ * A) Autofs allows root owned, non-automounter processes to create
+ * directories in the autofs filesystem.  The autofs filesystem treats the
+ * automounter's process group as special, but it doesn't prevent root
+ * processes outside of the automounter's process group from creating new
+ * directories in the autofs filesystem.
+ *
+ * B) Autofs doesn't allow creation of any non-directory entries in the
+ * autofs filesystem.  No entity can create files (e.g. /bin/touch or
+ * VOP_CREATE/VOP_SYMLINK/etc.)  The only entries that can exist within
+ * the autofs filesystem are directories.
+ *
+ * C) Autofs only intercepts vop lookup operations.  Notably, it does _not_
+ * intercept and re-direct vop readdir operations.  This means that the
+ * observed behavior of the Linux automounter can be considerably different
+ * from that of the illumos automounter.  Specifically, on illumos if an autofs
+ * mountpoint is mounted _without_ the -nobrowse option then if a user does
+ * an ls operation (which translates into a vop readdir operation) then the
+ * automounter will intercept that operation and list all the possible
+ * directories and mountpoints without actually mounting any filesystems.
+ * Essentially, all automounter managed mountpoints on Linux will behave
+ * like "-nobrowse" mountpoints on illumos.  Here's an example to illustrate
+ * this.  If /ws was mounted on illumos without the -nobrowse option and an
+ * auto_ws yp map was setup as the backing store for this mountpoint, then an
+ * "ls /ws" would list all the keys in the map as valid directories, but an
+ * "ls /ws" on Linux would list an emptry directory.
+ *
+ * D) NFS mounts are performed by the automount process.  When the automount
+ * process gets a redirected lookup request, it determines _all_ the
+ * possible remote mountpoints for that request, creates directory paths
+ * via mkdir, and mounts the remote filesystems on the newly created paths.
+ * This is described in the offset mount example above. Once the automounter
+ * completed the mounts it would signal the autofs filesystem (via an ioctl)
+ * that the lookup could continue.
+ *
+ * E.1) Autofs only redirects vop lookup operations for path entries that
+ * don't already exist in the autofs filesystem.  So for the example above,
+ * an initial (after the start of the automounter) "ls /net/jurassic" would
+ * result in a request to the automounter.  A subsequest "ls /net/jurassic"
+ * would not result in a request to the automounter.  Even if
+ * /net/jurassic/var/crash and /net/jurassic/var/core were manually unmounted
+ * after the initial "ls /net/jurassic", a subsequest "ls /net/jurassic"
+ * would not result in a new request to the automounter.
+ *
+ * E.2) Autofs lookup requests that are sent to the automounter only include
+ * the root directory path component.  So for example, after starting up
+ * the automounter if a user were to do a "ls /net/jurassic/var/crash", the
+ * initial lookup request actually sent to the automounter would just be for
+ * "jurassic" (the same request as if the user had done "ls /net/jurassic").
+ * After the initial mounting of the two offset mounts onto crash and core the
+ * lookup would continue and a final lookup request would be sent to the
+ * automounter for "crash" (but this would be on a different vfs from the
+ * /net vfs).
+ *
+ * E.3) The two statements above aren't entirely entirely true.  The Linux
+ * autofs filesystem will also redirect lookup operations for leaf
+ * directories that don't have a filesystem mounted on them.  Using the
+ * example above, if a user did a "ls /net/jurassic", then manually
+ * unmounted /net/jurassic/var/crash, and then did an "ls
+ * /net/jurassic/var/crash", this would result in a request for
+ * "jurassic/var/crash" being sent to the automounter.  The strange thing
+ * (a Linux bug perhaps) is that the automounter won't do anything with this
+ * request and the lookup will fail.
+ *
+ * F) The autofs filesystem communication protocol (what ioctls it supports
+ * and what data it passes to the automount process) is versioned. The
+ * userland automount daemon (as of version v5.0.7) expects v5 of the protocol
+ * (by running the AUTOFS_IOC_PROTOSUBVER ioctl), and exits if that is not
+ * supported. For v2-v5 the structure passed through the pipe always begins
+ * with a common header followed by different fields depending on the packet
+ * type. In addition the different versions support additional ioctls.
+ *
+ * v2 - basic lookup request
+ * v3 - adds expiring (umounting)
+ * v4 - adds expire multi
+ * v5 - adds missing indirect, expire indirect, missing direct & expire direct.
+ *      Defines a new protocol structure layout.
+ *      The v5 'missing indirect' and 'missing direct' ioctls are analogous to
+ *      the v2 'missing' ioctl. These ioctls are used to initiate a mount via
+ *	a lookup. The 'expire' ioctls are used by the automounter to query if
+ *	it is possible to unmount the filesystem. 'direct' and 'indirect'
+ *	refer to the mount option type that the automounter performed and
+ *	correlate to an automounter direct or indirect map mointpoint.
+ *
+ * G) The automounter periodically issues an 'expire' ioctl to autofs to
+ * obtain the name of a mountpoint which the automounter can unmount.
+ * Unmounting is dicussed in more detail below.
+ *
+ * H) The device ioctls roughly correspond to the filesystem ioctls, but
+ * instead of being tied to an auotfs mountpoint vnode, they can be called any
+ * time. The argument structure uses either a path or an autofs pipe file
+ * descriptor to indicate what is being operated on.
+ *
+ * +++ lxautofs notes
+ *
+ * 1) In general, the lxautofs filesystem tries to mimic the behavior of the
+ * Linux autofs filesystem with the following exceptions:
+ *
+ * 	1.1) We don't bother to implement the E.3 functionality listed above
+ * 	since it doesn't appear to be of any use.
+ *
+ * 	1.2) We only fully implement v2 and v5 of the autofs protocol.
+ *
+ * 2) In general, the approach taken for lxautofs is to keep it as simple
+ * as possible and to minimize it's memory usage.  To do this all information
+ * about the contents of the lxautofs filesystem are mirrored in the
+ * underlying filesystem that lxautofs is mounted on and most vop operations
+ * are simply passed onto this underlying filesystem.  This means we don't
+ * have to implement most of the complex operations that a full filesystem
+ * normally has to implement.  It also means that most of our filesystem state
+ * (wrt the contents of the filesystem) doesn't actually have to be stored
+ * in memory, we can simply go to the underlying filesystem to get it when
+ * it's requested.  For the purposes of discussion, we'll call the underlying
+ * filesystem the "backing store."
+ *
+ * The backing store is actually a directory called ".lxautofs" which is created
+ * in the directory where the lxautofs filesystem is mounted. When the
+ * lxautofs filesystem is unmounted this backing store directory is deleted.
+ * If this directory exists at mount time (perhaps the system crashed while a
+ * previous lxautofs instance was mounted at the same location) it will be
+ * deleted. There are a few implications of using a backing store worth
+ * mentioning.
+ *
+ * 	2.1) lxautofs can't be mounted on a read only filesystem.  If this
+ * 	proves to be a problem we can probably move the location of the
+ * 	backing store.
+ *
+ * 	2.2) If the backing store filesystem runs out of space then the
+ * 	automounter process won't be able to create more directories and mount
+ * 	new filesystems.  Of course, strange failures usually happen when
+ * 	filesystems run out of space.
+ *
+ * 3) Why aren't we using gfs?  gfs has two different usage models.
+ *
+ * 	3.1) I'm my own filesystem but i'm using gfs to help with managing
+ * 	readdir operations.
+ *
+ * 	3.2) I'm a gfs filesystem and gfs is managing all my vnodes
+ *
+ * We're not using the 3.1 interfaces because we don't implement readdir
+ * ourselves.  We pass all readdir operations onto the backing store
+ * filesystem and utilize its readdir implementation.
+ *
+ * We're not using the 3.2 interfaces because they are really designed for
+ * in memory filesystems where all of the filesystem state is stored in
+ * memory.  They don't lend themselves to filesystems where part of the
+ * state is in memory and part of the state is on disk.
+ *
+ * For more information on gfs take a look at the block comments in the
+ * top of gfs.c
+ *
+ * 4) Unmounting
+ *
+ * The automounter has a timeout associated with each mount. It informs autofs
+ * of this timeout using the LX_AUTOFS_DEV_IOC_TIMEOUT_CMD ioctl after autofs
+ * has been mounted on the mountpoint.
+ *
+ * After the automounter has mounted something associated with the mountpoint
+ * then periodically (<timeout>/4 seconds) the automounter will issue the
+ * LX_AUTOFS_DEV_IOC_EXPIRE_CMD ioctl on the autofs mount. autofs is expected
+ * to respond with an underlying mountpoint entry which is a candidate for
+ * unmounting. The automounter will attempt to unmount the filesystem
+ * (which may fail if it is busy, since this is obviously racy) and then
+ * acknowledge the expire ioctl. The successful acknowledgement is independent
+ * of the success of unmounting the underlying filesystem.
+ *
+ * Unmount handling varies based on which type of mount the autofs was mounted
+ * with (indirect, direct or offset).
+ *
+ * To support 'indirect' mount expiration, the autofs vfs keeps track of the
+ * filesystems mounted immediately under the autofs mountpoint (in
+ * lav_mnt_list) after a lookup has completed successfully. Upon receipt of the
+ * LX_AUTOFS_IOC_DEV_EXPIRE_CMD ioctl, autofs removes the first element from
+ * the list, attempts to check if it is busy and if not, returns that mountpoint
+ * over the fifo (if busy the entry is added to the end of the list). When the
+ * ioctl is acknowledged, if the mountpoint still exists, that means the unmount
+ * failed and the entry is added at the back of the list. If there are no
+ * elements or the first one is busy, EAGAIN is returned for the 'expire' ioctl
+ * and the autoumounter will check again in <timeout>/4 seconds.
+ *
+ * For example, if /home is an autofs indirect mount, then there are typically
+ * many different {username}-specific NFS mounts under that /home autofs mount.
+ * autofs uses the lav_mnt_list to respond to 'expire' ioctls in a round-robin
+ * fashion so that the automounter can unmount user file systems that aren't in
+ * use.
+ *
+ * Expiring 'direct' mounts is similar, but since there is only a single mount,
+ * the lav_mnt_list only will have at most one entry if there is a filesystem
+ * mounted overtop of the autofs mount.
+ *
+ * Expiring 'offset' mounts is more complicated because there are at least
+ * two different autofs VFSs involved (the top-level and one for each offset
+ * mount underneath). The actual offset mount is handled exactly like a 'direct'
+ * mount. The top-level is an indirect mount and is handled in a similar way
+ * as described above for indirect mounts, but special handling is needed for
+ * each offset mount below.
+ *
+ * This can be explained using the same 'jurassic' example described earlier
+ * (/net is an autofs 'indirect' mount and the host 'jurassic' has two exported
+ * file systems; /var/crash and /var/core). If the user accesses
+ * /net/jurassic/var/crash then the automounter would setup the system so that
+ * the following mounts exist:
+ *   - /net (the original autofs indirect mount which triggers everything)
+ *   - /net/jurassic/var/crash (autofs offset mount)
+ *   - /net/jurassic/var/crash (NFS mount on top of the autofs offset mount)
+ *   - /net/jurassic/var/core (autofs offset mount)
+ *
+ * For expiration the automounter will issue the LX_AUTOFS_IOC_EXPIRE_MULTI
+ * ioctl on each autofs vfs for which something is mounted, so we would receive
+ * an expire ioctl on /net and another on /net/jusrassic/var/crash. The vfs for
+ * /net will be tracking "jurassic", but we detect it is busy and won't do
+ * anything at first. The vfs for "crash" will work like a direct mount and
+ * acknowledge the expire ioctl to the automounter once that filesystem times
+ * out and is no longer busy. The automounter will then unmount the "crash"
+ * NFS mount.
+ *
+ * Once the "crash" NFS mount has been unmounted by the automounter, we're left
+ * with the two autofs offset mounts under jurassic. The automounter will not
+ * try to unmount either of those, so we have to do that. Once we get another
+ * expire ioctl on /net and check "jurassic", we'll see there are only autofs
+ * mounts under /net/jurassic. We umount those using the lx_autofs_umount_offset
+ * function and respond to the automounter expire ioctl with "jurassic", in the
+ * same way as we would for any other indirect mount.
+ *
+ * 5) Recovery
+ *
+ * If the automounter is restarted for any reason, it needs to cope with
+ * pre-existing autofs mounts, as well as other automount-initiated mounts (e.g.
+ * a direct mount on top of an autofs mountpoint). The automounter uses the
+ * /proc/mounts file to correlate mounts to the managed mountpoints. It then
+ * uses the /dev/autofs device to openmount each of the autofs devices and
+ * reinitialize them using the various dev ioctls (timeout, requester, etc.).
+ *
+ * In general, the autoumounter will closemount the mountpoint once it's done,
+ * but it doesn't in the case of an offset mountpoint with nothing mounted
+ * on top. In this case the automounter expects autofs to expire that mountpoint
+ * before it will closemount (so things can subsequently cleanup). We handle
+ * this special case in the expire code path.
+ */
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/*
+ * Note that the name of the actual file system is lxautofs, not lx_autofs, but
+ * the code uses lx_autofs to prefix the various names. This is because file
+ * system names are limited to 8 characters.
+ */
+#define	LX_AUTOFS_NAME			"lxautofs"
+
+#define	LX_AUTOFS_MINORNAME		"autofs"
+
+/*
+ * Mount options supported.
+ */
+#define	LX_MNTOPT_FD			"fd"
+#define	LX_MNTOPT_PGRP			"pgrp"
+#define	LX_MNTOPT_MINPROTO		"minproto"
+#define	LX_MNTOPT_MAXPROTO		"maxproto"
+#define	LX_MNTOPT_INDIRECT		"indirect"
+#define	LX_MNTOPT_DIRECT		"direct"
+#define	LX_MNTOPT_OFFSET		"offset"
+
+/*
+ * Version/subversion of the Linux kernel automount protocol we support.
+ *
+ * We fully support v2 and v5. We'll return ENOTSUP for all of the ioctls we
+ * don't yet handle.
+ */
+#define	LX_AUTOFS_PROTO_VERS5		5
+#define	LX_AUTOFS_PROTO_SUBVERSION	2
+#define	LX_AUTOFS_PROTO_VERS2		2
+
+/* packet types */
+typedef enum laph_ptype {
+	LX_AUTOFS_PTYPE_MISSING,	/* 0 */
+	LX_AUTOFS_PTYPE_EXPIRE,		/* 1 */
+	LX_AUTOFS_PTYPE_EXPIRE_MULTI,	/* 2 */
+	LX_AUTOFS_PTYPE_MISSING_INDIR,	/* 3 */
+	LX_AUTOFS_PTYPE_EXPIRE_INDIR,	/* 4 */
+	LX_AUTOFS_PTYPE_MISSING_DIRECT,	/* 5 */
+	LX_AUTOFS_PTYPE_EXPIRE_DIRECT	/* 6 */
+} laph_ptype_t;
+
+/*
+ * Common header for all versions of the protocol.
+ */
+typedef struct lx_autofs_pkt_hdr {
+	int		laph_protover;	/* protocol version number */
+	laph_ptype_t	laph_type;
+	int		laph_id;	/* every pkt must have a unique id */
+} lx_autofs_pkt_hdr_t;
+
+/*
+ * Command structure sent to automount process from lxautofs via a pipe.
+ * This structure is the same for v2-v4 of the automount protocol
+ * (the communication pipe is established at mount time).
+ */
+typedef struct lx_autofs_v2_pkt {
+	lx_autofs_pkt_hdr_t lap_hdr;
+	int	lap_name_len;	/* don't include newline or NULL */
+	char	lap_name[256];	/* path component to lookup */
+} lx_autofs_v2_pkt_t;
+
+/* v4 multi-expire */
+typedef struct lx_autofs_v4_exp_pkt {
+	lx_autofs_pkt_hdr_t lape_hdr;
+	int lape_len;
+	char lape_name[MAXNAMELEN];
+} lx_autofs_v4_exp_pkt_t;
+
+/* v5 */
+typedef struct lx_autofs_v5_pkt {
+	lx_autofs_pkt_hdr_t lap_hdr;
+	uint32_t lap_dev;
+	uint64_t lap_ino;
+	uint32_t lap_uid;
+	uint32_t lap_gid;
+	uint32_t lap_pid;
+	uint32_t lap_tgid;
+	uint32_t lap_name_len;
+	char	lap_name[256];
+} lx_autofs_v5_pkt_t;
+
+union lx_autofs_pkt {
+	lx_autofs_v2_pkt_t	lap_v2;
+	lx_autofs_v5_pkt_t	lap_v5;
+};
+
+#define	lap_protover	lap_v2.lap_hdr.laph_protover
+#define	lap_type	lap_v2.lap_hdr.laph_type
+#define	lap_id		lap_v2.lap_hdr.laph_id
+
+/*
+ * Ioctls fully supported (v2 protocol).
+ */
+#define	LX_AUTOFS_IOC_READY		0x00009360 /* arg: int */
+#define	LX_AUTOFS_IOC_FAIL		0x00009361 /* arg: int */
+#define	LX_AUTOFS_IOC_CATATONIC		0x00009362 /* arg: <none> */
+
+/*
+ * Ioctls supported (v3/v4 protocol).
+ */
+#define	LX_AUTOFS_IOC_PROTOVER		0x80049363 /* arg: int */
+#define	LX_AUTOFS_IOC_SETTIMEOUT	0xc0089364 /* arg: ulong_t */
+
+/*
+ * Ioctls not supported (v3/v4 protocol).
+ */
+					/* arg: lx_autofs_v3_exp_pkt_t * */
+#define	LX_AUTOFS_IOC_EXPIRE		0x81109365
+
+/*
+ * Ioctls supported (v5 protocol).
+ */
+#define	LX_AUTOFS_IOC_PROTOSUBVER	0x80049367 /* arg: int */
+#define	LX_AUTOFS_IOC_ASKUMOUNT		0x80049370 /* arg: int */
+#define	LX_AUTOFS_IOC_EXPIRE_MULTI	0x40049366 /* arg: int */
+#define	LX_AUTOFS_IOC_EXPIRE_INDIRECT	LX_AUTOFS_IOC_EXPIRE_MULTI
+#define	LX_AUTOFS_IOC_EXPIRE_DIRECT	LX_AUTOFS_IOC_EXPIRE_MULTI
+
+/*
+ * autofs device ioctls
+ */
+#define	LX_AUTOFS_DEV_IOC_VERSION_CMD		0xc0189371
+#define	LX_AUTOFS_DEV_IOC_PROTOVER_CMD		0xc0189372
+#define	LX_AUTOFS_DEV_IOC_PROTOSUBVER_CMD	0xc0189373
+#define	LX_AUTOFS_DEV_IOC_OPENMOUNT_CMD		0xc0189374
+#define	LX_AUTOFS_DEV_IOC_CLOSEMOUNT_CMD	0xc0189375
+#define	LX_AUTOFS_DEV_IOC_READY_CMD		0xc0189376
+#define	LX_AUTOFS_DEV_IOC_FAIL_CMD		0xc0189377
+#define	LX_AUTOFS_DEV_IOC_SETPIPEFD_CMD		0xc0189378
+#define	LX_AUTOFS_DEV_IOC_CATATONIC_CMD		0xc0189379
+#define	LX_AUTOFS_DEV_IOC_TIMEOUT_CMD		0xc018937a
+#define	LX_AUTOFS_DEV_IOC_REQUESTER_CMD		0xc018937b
+#define	LX_AUTOFS_DEV_IOC_EXPIRE_CMD		0xc018937c
+#define	LX_AUTOFS_DEV_IOC_ASKUMOUNT_CMD		0xc018937d
+#define	LX_AUTOFS_DEV_IOC_ISMOUNTPOINT_CMD	0xc018937e
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _LX_AUTOFS_H */
--- /dev/null	Wed Oct  5 00:32:07 2016
+++ new/usr/src/uts/common/brand/lx/sys/lx_autofs_impl.h	Wed Oct  5 00:32:07 2016
@@ -0,0 +1,162 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#ifndef	_LX_AUTOFS_IMPL_H
+#define	_LX_AUTOFS_IMPL_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#include <sys/file.h>
+#include <sys/id_space.h>
+#include <sys/modhash.h>
+#include <sys/vnode.h>
+
+#include <sys/lx_autofs.h>
+
+/*
+ * Space key.
+ * Used to persist data across lx_autofs filesystem module unloads.
+ */
+#define	LX_AUTOFS_SPACE_KEY_UDEV	LX_AUTOFS_NAME "_udev"
+
+/*
+ * Name of the backing store directory.
+ */
+#define	LX_AUTOFS_BS_DIR		"." LX_AUTOFS_NAME
+
+#define	LX_AUTOFS_VFS_ID_HASH_SIZE	15
+#define	LX_AUTOFS_VFS_PATH_HASH_SIZE	15
+#define	LX_AUTOFS_VFS_VN_HASH_SIZE	15
+
+enum lx_autofs_mnttype	{ LXAMT_NONE, LXAMT_INDIR, LXAMT_DIRECT, LXAMT_OFFSET };
+
+typedef struct lx_autofs_mntent {
+	list_node_t	lxafme_lst;
+	uint64_t	lxafme_ts;	/* time stamp */
+	uint_t		lxafme_len;
+	char		*lxafme_path;
+} lx_autofs_mntent_t;
+
+/*
+ * VFS data object.
+ */
+typedef struct lx_autofs_vfs {
+	/* Info about the underlying filesystem and backing store. */
+	vnode_t		*lav_mvp;
+	char		*lav_bs_name;
+	vnode_t		*lav_bs_vp;
+
+	/* Info about the automounter process managing this filesystem. */
+	int		lav_fd;
+	pid_t		lav_pgrp;
+	file_t		*lav_fifo_wr;
+	file_t		*lav_fifo_rd;
+
+	/* The mount's dev and ino values for v5 protocol msg */
+	uint64_t	lav_dev;
+	u_longlong_t	lav_ino;
+
+	/* options from the mount */
+	enum lx_autofs_mnttype lav_mnttype;
+	int		lav_min_proto;
+
+	/*
+	 * ioctl-set timeout value. The automounter will perform an expire
+	 * ioctl every timeout/4 seconds. We use this to expire a mount once
+	 * it is inactive for the full timeout.
+	 */
+	ulong_t		lav_timeout;
+
+	/* ioctl-set catatonic value (prevents future mounts).  */
+	boolean_t	lav_catatonic;
+
+	/* Mount initiator's uid/gid for recovery handling. */
+	uid_t		lav_uid;
+	gid_t		lav_gid;
+
+	/* Each automount requests needs a unique id. */
+	id_space_t	*lav_ids;
+
+	/* All remaining structure members are protected by lav_lock. */
+	kmutex_t	lav_lock;
+	/* openmount counter */
+	int		lav_openmnt_cnt;
+
+
+	/* Hashes to keep track of outstanding automounter requests. */
+	mod_hash_t	*lav_path_hash;
+	mod_hash_t	*lav_id_hash;
+
+	/* We need to keep track of all our vnodes. */
+	vnode_t		*lav_root;
+	mod_hash_t	*lav_vn_hash;
+
+	/* list of current mounts */
+	list_t		lav_mnt_list;
+} lx_autofs_vfs_t;
+
+enum lx_autofs_callres	{ LXACR_NONE, LXACR_READY, LXACR_FAIL };
+
+/*
+ * Structure to keep track of automounter requests sent to user-land.
+ */
+typedef struct lx_autofs_automnt_req {
+	/* Packet that gets sent to the automounter. */
+	union lx_autofs_pkt laar_pkt;
+	int		laar_pkt_size;
+
+	/* Reference count.  Always updated atomically. */
+	uint_t		laar_ref;
+
+	/*
+	 * Fields to keep track and sync threads waiting on a lookup.
+	 * Fields are protected by lalr_lock.
+	 */
+	kmutex_t	laar_lock;
+	kcondvar_t	laar_cv;
+	int		laar_complete;
+
+	enum lx_autofs_callres laar_result;
+} lx_autofs_automnt_req_t;
+
+/*
+ * Generic stack structure.
+ */
+typedef struct stack_elem {
+	list_node_t	se_list;
+	caddr_t		se_ptr1;
+	caddr_t		se_ptr2;
+	caddr_t		se_ptr3;
+} stack_elem_t;
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _LX_AUTOFS_IMPL_H */
--- /dev/null	Wed Oct  5 00:32:07 2016
+++ new/usr/src/uts/common/brand/lx/sys/lx_brand.h	Wed Oct  5 00:32:07 2016
@@ -0,0 +1,680 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#ifndef _LX_BRAND_H
+#define	_LX_BRAND_H
+
+#ifndef _ASM
+#include <sys/types.h>
+#include <sys/cpuvar.h>
+#include <sys/zone.h>
+#include <sys/ksocket.h>
+#include <sys/vfs.h>
+#include <sys/sunddi.h>
+#include <sys/sunldi.h>
+#endif
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#define	LX_BRANDNAME	"lx"
+
+/*
+ * Brand uname info
+ */
+#define	LX_UNAME_SYSNAME	"Linux"
+#define	LX_UNAME_RELEASE_2_6	"2.6.18"
+#define	LX_UNAME_RELEASE_2_4	"2.4.21"
+#define	LX_UNAME_VERSION	"BrandZ virtual linux"
+#define	LX_UNAME_MACHINE32	"i686"
+#define	LX_UNAME_MACHINE64	"x86_64"
+
+#define	LX_LIB_PATH32	"/native/usr/lib/lx_brand.so.1"
+#define	LX_LIB_PATH64	"/native/usr/lib/amd64/lx_brand.so.1"
+
+#define	LX_VDSO_PATH32	"/native/usr/lib/brand/lx/lx_vdso.so.1"
+#define	LX_VDSO_PATH64	"/native/usr/lib/brand/lx/amd64/lx_vdso.so.1"
+
+#if defined(_LP64)
+#define	LX_LIB_PATH		LX_LIB_PATH64
+#define	LX_UNAME_MACHINE	LX_UNAME_MACHINE64
+#define	LX_VDSO_PATH		LX_VDSO_PATH64
+#else
+#define	LX_LIB_PATH		LX_LIB_PATH32
+#define	LX_UNAME_MACHINE	LX_UNAME_MACHINE32
+#define	LX_VDSO_PATH		LX_VDSO_PATH32
+#endif
+
+/*
+ * This must be large enough for both the 32-bit table and 64-bit table.
+ */
+#define	LX_NSYSCALLS		358
+
+/* Highest capability we know about */
+#define	LX_CAP_MAX_VALID	36
+
+/*
+ * brand(2) subcommands
+ *
+ * Everything >= 128 is a brand-specific subcommand.
+ * > 192 is reserved for in-kernel emulated system calls.
+ */
+#define	B_LPID_TO_SPAIR		128
+#define	B_GET_CURRENT_CONTEXT	129
+#define	B_EMULATION_DONE	130
+/* formerly B_PTRACE_KERNEL	131 */
+#define	B_SET_AFFINITY_MASK	132
+#define	B_GET_AFFINITY_MASK	133
+#define	B_PTRACE_CLONE_BEGIN	134
+#define	B_PTRACE_STOP_FOR_OPT	135
+#define	B_UNSUPPORTED		136
+#define	B_STORE_ARGS		137
+#define	B_GETPID		138
+#define	B_JUMP_TO_LINUX		139
+#define	B_SET_THUNK_PID		140
+#define	B_EXIT_AS_SIG		141
+/* formerly B_HELPER_WAITID	142 */
+#define	B_HELPER_CLONE		143
+#define	B_HELPER_SETGROUPS	144
+#define	B_HELPER_SIGQUEUE	145
+#define	B_HELPER_TGSIGQUEUE	146
+#define	B_SET_NATIVE_STACK	147
+#define	B_SIGEV_THREAD_ID	148
+#define	B_OVERRIDE_KERN_VER	149
+#define	B_PTRACE_SIG_RETURN	150
+#define	B_GET_PERSONALITY	151
+
+#ifndef _ASM
+/*
+ * Support for Linux PTRACE_SETOPTIONS handling.
+ */
+typedef enum lx_ptrace_options {
+	LX_PTRACE_O_TRACESYSGOOD =	0x0001,
+	LX_PTRACE_O_TRACEFORK =		0x0002,
+	LX_PTRACE_O_TRACEVFORK =	0x0004,
+	LX_PTRACE_O_TRACECLONE =	0x0008,
+	LX_PTRACE_O_TRACEEXEC =		0x0010,
+	LX_PTRACE_O_TRACEVFORKDONE =	0x0020,
+	LX_PTRACE_O_TRACEEXIT =		0x0040,
+	LX_PTRACE_O_TRACESECCOMP =	0x0080
+} lx_ptrace_options_t;
+
+#define	LX_PTRACE_O_ALL							\
+	(LX_PTRACE_O_TRACESYSGOOD | LX_PTRACE_O_TRACEFORK | 		\
+	LX_PTRACE_O_TRACEVFORK | LX_PTRACE_O_TRACECLONE | 		\
+	LX_PTRACE_O_TRACEEXEC | LX_PTRACE_O_TRACEVFORKDONE |		\
+	LX_PTRACE_O_TRACEEXIT | LX_PTRACE_O_TRACESECCOMP)
+#endif /* !_ASM */
+
+/* siginfo si_status for traced events */
+#define	LX_PTRACE_EVENT_FORK		0x100
+#define	LX_PTRACE_EVENT_VFORK		0x200
+#define	LX_PTRACE_EVENT_CLONE		0x300
+#define	LX_PTRACE_EVENT_EXEC		0x400
+#define	LX_PTRACE_EVENT_VFORK_DONE	0x500
+#define	LX_PTRACE_EVENT_EXIT		0x600
+#define	LX_PTRACE_EVENT_SECCOMP		0x700
+
+/*
+ * Brand-private values for the "pr_what" member of lwpstatus, for use with the
+ * PR_BRAND stop reason.  These reasons are validated in lx_stop_notify();
+ * update it if you add new reasons here.
+ */
+#define	LX_PR_SYSENTRY		1
+#define	LX_PR_SYSEXIT		2
+#define	LX_PR_SIGNALLED		3
+#define	LX_PR_EVENT		4
+
+
+#define	LX_VERSION_1		1
+#define	LX_VERSION		LX_VERSION_1
+
+#define	LX_ATTR_KERN_RELEASE	ZONE_ATTR_BRAND_ATTRS
+#define	LX_ATTR_KERN_VERSION	(ZONE_ATTR_BRAND_ATTRS + 1)
+
+/*
+ * Aux vector containing phdr of Linux executable and ehdr of interpreter
+ * (if any), both of which are used by lx_librtld_db to ascertain r_debug.
+ * We repurpose the 3rd brand-specific aux vector slot for the Linux
+ * AT_SYSINFO_EHDR entry (we modify the a_type in the brand library).
+ */
+#define	AT_SUN_BRAND_LX_PHDR		AT_SUN_BRAND_AUX1
+#define	AT_SUN_BRAND_LX_INTERP		AT_SUN_BRAND_AUX2
+#define	AT_SUN_BRAND_LX_CLKTCK		AT_SUN_BRAND_AUX3
+#define	AT_SUN_BRAND_LX_SYSINFO_EHDR	AT_SUN_BRAND_AUX4
+
+/* Aux vectors containing real/effective user/group IDs */
+#define	AT_LX_UID		11
+#define	AT_LX_EUID		12
+#define	AT_LX_GID		13
+#define	AT_LX_EGID		14
+/* Aux vector containing hz value */
+#define	AT_CLKTCK	17
+/* Aux vector containing secure boolean */
+#define	AT_SECURE	23
+/* Aux vector containing vDSO addr */
+#define	AT_SYSINFO_EHDR	33
+
+/*
+ * Usermode emulation routines are run on an alternate stack allocated by
+ * the brand library.  Every LWP in a process will incur this overhead beyond
+ * the regular thread stack:
+ */
+#define	LX_NATIVE_STACK_PAGE_COUNT	64
+
+/*
+ * When returning in a new child process created with vfork(2) (or CLONE_VFORK)
+ * we discard some of the native stack to prevent corruption of the parent
+ * emulation state.
+ */
+#define	LX_NATIVE_STACK_VFORK_GAP	0x3000
+
+#ifndef	_ASM
+
+extern struct brand lx_brand;
+
+typedef struct lx_brand_registration {
+	uint_t lxbr_version;		/* version number */
+	void *lxbr_handler;		/* base address of handler */
+	uint32_t lxbr_flags;		/* LX_PROC_* registration flags */
+} lx_brand_registration_t;
+
+typedef struct lx_brand_registration32 {
+	uint_t lxbr_version;		/* version number */
+	uint32_t lxbr_handler;		/* base address of handler */
+	uint32_t lxbr_flags;		/* LX_PROC_* registration flags */
+} lx_brand_registration32_t;
+
+#endif /* _ASM */
+
+/*
+ * GDT usage
+ */
+#define	GDT_TLSMIN	(GDT_BRANDMIN)
+#define	GDT_TLSMAX	(GDT_TLSMIN + 2)
+#define	LX_TLSNUM	(GDT_TLSMAX - GDT_TLSMIN)
+
+#ifndef _ASM
+
+/*
+ * Stores information needed by the lx linker to launch the main
+ * lx executable.
+ */
+typedef struct lx_elf_data64 {
+	uintptr_t	ed_phdr;
+	uintptr_t	ed_phent;
+	uintptr_t	ed_phnum;
+	uintptr_t	ed_entry;
+	uintptr_t	ed_base;
+	uintptr_t	ed_ldentry;
+} lx_elf_data64_t;
+
+typedef struct lx_elf_data32 {
+	uint32_t	ed_phdr;
+	uint32_t	ed_phent;
+	uint32_t	ed_phnum;
+	uint32_t	ed_entry;
+	uint32_t	ed_base;
+	uint32_t	ed_ldentry;
+} lx_elf_data32_t;
+
+#if defined(_LP64)
+typedef lx_elf_data64_t lx_elf_data_t;
+#else
+typedef lx_elf_data32_t lx_elf_data_t;
+#endif
+
+typedef enum lx_proc_flags {
+	/* flags configurable via brandsys() and members of LX_PROC_ALL */
+	LX_PROC_INSTALL_MODE	= 0x01,
+	LX_PROC_STRICT_MODE	= 0x02,
+	/* internal flags */
+	LX_PROC_CHILD_DEATHSIG	= 0x04,
+	LX_PROC_AIO_USED	= 0x08
+} lx_proc_flags_t;
+
+#define	LX_PROC_ALL	(LX_PROC_INSTALL_MODE | LX_PROC_STRICT_MODE)
+
+/* Maximum length for fields of LX uname */
+#define	LX_SYS_UTS_LN	65
+
+/* Max. length of kernel release string */
+#define	LX_KERN_RELEASE_MAX	LX_SYS_UTS_LN
+#define	LX_KERN_VERSION_MAX	LX_SYS_UTS_LN
+
+#ifdef	_KERNEL
+
+/*
+ * Entry points for cgroup integration.
+ */
+extern void (*lx_cgrp_initlwp)(vfs_t *, uint_t, id_t, pid_t);
+extern void (*lx_cgrp_freelwp)(vfs_t *, uint_t, id_t, pid_t);
+
+#define	LX_RLFAKE_LOCKS		0
+#define	LX_RLFAKE_NICE		1
+#define	LX_RLFAKE_RTPRIO	2
+#define	LX_RLFAKE_RTTIME	3
+
+#define	LX_RLFAKE_NLIMITS	4
+
+#define	LX_RLIM64_INFINITY	(~0ULL)
+
+typedef struct {
+	uint64_t	rlim_cur;
+	uint64_t	rlim_max;
+} lx_rlimit64_t;
+
+typedef struct lx_proc_data {
+	uintptr_t l_handler;	/* address of user-space handler */
+	pid_t l_ppid;		/* pid of originating parent proc */
+	uid_t l_loginuid;	/* /proc/{pid}/loginuid */
+	int64_t l_ptrace;	/* count of process lwps observed by ptrace */
+	lx_elf_data_t l_elf_data; /* ELF data for linux executable */
+	/* signal to deliver to parent when this thread group dies */
+	int l_signal;
+	/* native signal to deliver to process when parent dies */
+	int l_parent_deathsig;
+	lx_proc_flags_t l_flags;
+
+	lx_rlimit64_t l_fake_limits[LX_RLFAKE_NLIMITS];
+
+	/* original start/end bounds of arg/env string data */
+	uintptr_t l_args_start;
+	uintptr_t l_envs_start;
+	uintptr_t l_envs_end;
+
+	/* Override zone-wide settings for uname release and version */
+	char l_uname_release[LX_KERN_RELEASE_MAX];
+	char l_uname_version[LX_KERN_VERSION_MAX];
+
+	/* Linux process personality */
+	unsigned int l_personality;
+
+	/* VDSO location */
+	uintptr_t l_vdso;
+} lx_proc_data_t;
+
+#endif	/* _KERNEL */
+
+/*
+ * Linux process personality(2) flags stored in l_personality
+ */
+#define	LX_PER_UNAME26			0x0020000
+#define	LX_PER_ADDR_NO_RANDOMIZE	0x0040000
+#define	LX_PER_FDPIC_FUNCPTRS		0x0080000
+#define	LX_PER_MMAP_PAGE_ZERO		0x0100000
+#define	LX_PER_ADDR_COMPAT_LAYOUT	0x0200000
+#define	LX_PER_READ_IMPLIES_EXEC	0x0400000
+#define	LX_PER_ADDR_LIMIT_32BIT		0x0800000
+#define	LX_PER_SHORT_INODE		0x1000000
+#define	LX_PER_WHOLE_SECONDS		0x2000000
+#define	LX_PER_STICKY_TIMEOUTS		0x4000000
+#define	LX_PER_ADDR_LIMIT_3GB		0x8000000
+
+#define	LX_PER_LINUX	0x00
+#define	LX_PER_SUNOS	(0x06 | LX_PER_STICKY_TIMEOUTS)
+#define	LX_PER_MASK	0xff
+
+/*
+ * A data type big enough to bitmap all Linux possible cpus.
+ * The bitmap size is defined as 1024 cpus in the Linux 2.4 and 2.6 man pages
+ * for sched_getaffinity() and sched_getaffinity().
+ */
+#define	LX_NCPU		(1024)
+#define	LX_AFF_ULONGS	(LX_NCPU / (8 * sizeof (ulong_t)))
+typedef ulong_t lx_affmask_t[LX_AFF_ULONGS];
+
+/* Length of proc boot_id string */
+#define	LX_BOOTID_LEN	37
+
+/*
+ * Flag values for uc_brand_data[0] in the ucontext_t:
+ */
+#define	LX_UC_STACK_NATIVE	0x00001
+#define	LX_UC_STACK_BRAND	0x00002
+#define	LX_UC_RESTORE_NATIVE_SP	0x00010
+#define	LX_UC_FRAME_IS_SYSCALL	0x00100
+#define	LX_UC_RESTART_SYSCALL	0x01000
+#define	LX_UC_IGNORE_LINK	0x10000
+
+#ifdef	_KERNEL
+
+typedef struct lx_lwp_data lx_lwp_data_t;
+
+/*
+ * Flag values for "lxpa_flags" on a ptrace(2) accord.
+ */
+typedef enum lx_accord_flags {
+	LX_ACC_TOMBSTONE = 0x01
+} lx_accord_flags_t;
+
+/*
+ * Flags values for "br_ptrace_flags" in the LWP-specific data.
+ */
+typedef enum lx_ptrace_flags {
+	LX_PTF_SYSCALL = 0x01,		/* handling syscall or a trap */
+	LX_PTF_EXITING = 0x02,
+	LX_PTF_STOPPING = 0x04,
+	LX_PTF_INHERIT = 0x08,
+	LX_PTF_STOPPED = 0x10,
+	LX_PTF_PARENT_WAIT = 0x20,
+	LX_PTF_CLDPEND = 0x40,
+	LX_PTF_CLONING = 0x80,
+	LX_PTF_WAITPEND = 0x100,
+	LX_PTF_NOSTOP = 0x200,		/* disable syscall stop event */
+	LX_PTF_INSYSCALL = 0x400	/* between syscall enter & exit */
+} lx_ptrace_flags_t;
+
+/*
+ * A ptrace(2) accord represents the relationship between a tracer LWP and the
+ * set of LWPs that it is tracing: the tracees.  This data structure belongs
+ * primarily to the tracer, but is reference counted so that it may be freed by
+ * whoever references it last.
+ */
+typedef struct lx_ptrace_accord {
+	kmutex_t		lxpa_lock;
+	uint_t			lxpa_refcnt;
+	lx_accord_flags_t	lxpa_flags;
+
+	/*
+	 * The tracer must hold "pidlock" while clearing these fields for
+	 * exclusion of waitid(), etc.
+	 */
+	lx_lwp_data_t		*lxpa_tracer;
+	kcondvar_t		*lxpa_cvp;
+
+	/*
+	 * The "lxpa_tracees_lock" mutex protects the tracee list.
+	 */
+	kmutex_t		lxpa_tracees_lock;
+	list_t			lxpa_tracees;
+} lx_ptrace_accord_t;
+
+/*
+ * These values are stored in the per-LWP data for a tracee when it is attached
+ * to a tracer.  They record the method that was used to attach.
+ */
+typedef enum lx_ptrace_attach {
+	LX_PTA_NONE = 0x00,		/* not attached */
+	LX_PTA_ATTACH = 0x01,		/* due to tracer using PTRACE_ATTACH */
+	LX_PTA_TRACEME = 0x02,		/* due to child using PTRACE_TRACEME */
+	LX_PTA_INHERIT_CLONE = 0x04,	/* due to PTRACE_CLONE clone(2) flag */
+	LX_PTA_INHERIT_OPTIONS = 0x08	/* due to PTRACE_SETOPTIONS options */
+} lx_ptrace_attach_t;
+
+typedef enum lx_stack_mode {
+	LX_STACK_MODE_PREINIT = 0,
+	LX_STACK_MODE_INIT,
+	LX_STACK_MODE_NATIVE,
+	LX_STACK_MODE_BRAND
+} lx_stack_mode_t;
+
+struct lx_pid {
+	pid_t	s_pid;			/* the SunOS pid and ... */
+	id_t	s_tid;			/* ... tid pair */
+	pid_t	l_pid;			/* the corresponding linux pid */
+	time_t	l_start;		/* birthday of this pid */
+	struct pid *l_pidp;
+	struct lx_pid *stol_next;	/* link in stol hash table */
+	struct lx_pid *ltos_next;	/* link in ltos hash table */
+};
+
+/*
+ * lx-specific data in the klwp_t
+ */
+struct lx_lwp_data {
+	uint_t	br_lwp_flags;		/* misc. flags */
+	klwp_t	*br_lwp;		/* back pointer to container lwp */
+	int	br_signal;		/* signal to send to parent when */
+					/* clone()'ed child terminates */
+	int	br_exitwhy;		/* reason for thread (process) exit */
+	int	br_exitwhat;		/* exit code / killing signal */
+	lx_affmask_t br_affinitymask;	/* bitmask of CPU sched affinities */
+	struct user_desc br_tls[LX_TLSNUM];
+			/* descriptors used by libc for TLS */
+	ulong_t	br_lx_fsbase;		/* lx fsbase for 64-bit thread ptr */
+	ulong_t	br_ntv_fsbase;		/* native fsbase 64-bit thread ptr */
+	ulong_t	br_lx_gsbase;		/* lx user-land gsbase */
+	ulong_t	br_ntv_gsbase;		/* native user-land gsbase */
+	pid_t	br_pid;			/* converted pid for this thread */
+	pid_t	br_tgid;		/* thread group ID for this thread */
+	pid_t	br_ppid;		/* parent pid for this thread */
+	id_t	br_ptid;		/* parent tid for this thread */
+	void	*br_clear_ctidp;	/* clone thread id ptr */
+	void	*br_set_ctidp;		/* clone thread id ptr */
+	void	*br_robust_list;	/* robust lock list, if any */
+
+	/*
+	 * The following struct is used by some system calls to pass extra
+	 * flags into the kernel without impinging on the namespace for
+	 * illumos.
+	 */
+	void	*br_scall_args;
+	int	br_args_size; /* size in bytes of br_scall_args */
+
+	boolean_t br_waitid_emulate;
+	int br_waitid_flags;
+
+	lx_ptrace_flags_t br_ptrace_flags; /* ptrace flags for this LWP */
+	lx_ptrace_options_t br_ptrace_options; /* PTRACE_SETOPTIONS options */
+	lx_ptrace_options_t br_ptrace_clone_option; /* current clone(2) type */
+
+	lx_ptrace_attach_t br_ptrace_attach; /* how did we get attached */
+	lx_ptrace_accord_t *br_ptrace_accord; /* accord for this tracer LWP */
+	lx_ptrace_accord_t *br_ptrace_tracer; /* accord tracing this LWP */
+	list_node_t br_ptrace_linkage;	/* linkage for lxpa_tracees list */
+
+	ushort_t br_ptrace_whystop; 	/* stop reason, 0 for no stop */
+	ushort_t br_ptrace_whatstop;	/* stop sub-reason */
+
+	int32_t br_ptrace_stopsig;	/* stop signal, 0 for no signal */
+	/*
+	 * Track the last (native) signal number processed by a ptrace.
+	 * This allows the tracee to properly handle ignored signals after
+	 * the tracer has been notified and the tracee restarted.
+	 */
+	int32_t br_ptrace_donesig;
+	uintptr_t br_ptrace_stopucp;	/* usermode ucontext_t pointer */
+
+	uint_t	br_ptrace_event;
+	ulong_t	br_ptrace_eventmsg;
+
+	int	br_syscall_num;		/* current system call number */
+	boolean_t br_syscall_restart;	/* should restart on EINTR */
+
+	/*
+	 * Store the LX_STACK_MODE for this LWP, and the current extent of the
+	 * native (emulation) stack.  This is similar, in principle, to the
+	 * sigaltstack mechanism for signal handling.  We also use this mode
+	 * flag to determine how to process system calls from this LWP.
+	 */
+	lx_stack_mode_t	br_stack_mode;
+	uintptr_t br_ntv_stack;
+	uintptr_t br_ntv_stack_current;
+
+	/*
+	 * If this pid is set, we return it with getpid().  This allows the
+	 * thunking server to interpose on the pid returned to the Linux
+	 * syslog software.
+	 */
+	pid_t	br_lx_thunk_pid;
+
+	/*
+	 * If strict mode is enabled (via LX_STRICT in the environment), any
+	 * call to lx_unsupported() will set this boolean to B_TRUE.  This will
+	 * cause us to drop SIGSYS on the LWP as it attempts to return to
+	 * usermode.
+	 */
+	boolean_t br_strict_failure;
+
+	/*
+	 * Some syscalls emulated in-kernel still call back out to the
+	 * userspace emulation for certain functions.  When that is the case,
+	 * the syscall_return logic must be bypassed at the end of the
+	 * in-kernel syscall code.  The NORMALRETURN and JUSTRETURN constants
+	 * are used to choose the behavior.
+	 */
+	char br_eosys;
+
+	/*
+	 * Hold a pre-allocated lx_pid structure to be used during lx_initlwp.
+	 */
+	struct lx_pid *br_lpid;
+
+	/*
+	 * ID of the cgroup this thread belongs to.
+	 */
+	uint_t br_cgroupid;
+};
+
+/*
+ * Upper limit on br_args_size, low because this value can persist until
+ * overridden with another value, and the size is given from userland.
+ */
+#define	LX_BR_ARGS_SIZE_MAX	(1024)
+
+/*
+ * brand specific data
+ *
+ * We currently only support a single cgroup mount in an lx zone so we only have
+ * one ptr (lxzd_cgroup) but this could be changed to a list if cgroups is ever
+ * enhanced to support different mounts with different subsystem controllers.
+ */
+typedef struct lx_zone_data {
+	kmutex_t lxzd_lock;			/* protects all members */
+	char lxzd_kernel_release[LX_KERN_RELEASE_MAX];
+	char lxzd_kernel_version[LX_KERN_VERSION_MAX];
+	ksocket_t lxzd_ioctl_sock;
+	char lxzd_bootid[LX_BOOTID_LEN];	/* procfs boot_id */
+	vfs_t *lxzd_cgroup;			/* cgroup for this zone */
+	list_t *lxzd_vdisks;			/* virtual disks (zvols) */
+	dev_t lxzd_zfs_dev;			/* major num for zfs */
+} lx_zone_data_t;
+
+#define	BR_CPU_BOUND	0x0001
+
+#define	ttolxlwp(t)	((struct lx_lwp_data *)ttolwpbrand(t))
+#define	lwptolxlwp(l)	((struct lx_lwp_data *)lwptolwpbrand(l))
+#define	ttolxproc(t)	\
+	(((t)->t_procp->p_brand == &lx_brand) ? \
+	(struct lx_proc_data *)(t)->t_procp->p_brand_data : NULL)
+#define	ptolxproc(p)	\
+	(((p)->p_brand == &lx_brand) ? \
+	(struct lx_proc_data *)(p)->p_brand_data : NULL)
+#define	ztolxzd(z)		\
+	(((z)->zone_brand == &lx_brand) ?  \
+	(lx_zone_data_t *)(z)->zone_brand_data : NULL)
+
+/* Macro for converting to system call arguments. */
+#define	LX_ARGS(scall) ((struct lx_##scall##_args *)\
+	(ttolxlwp(curthread)->br_scall_args))
+
+typedef enum lx_virt_disk_type {
+	LXVD_NONE,
+	LXVD_ZFS_DS,
+	LXVD_ZVOL
+} lx_virt_disk_type_t;
+
+typedef struct lx_virt_disk {
+	list_node_t		lxvd_link;
+	char			lxvd_name[MAXNAMELEN];
+	lx_virt_disk_type_t	lxvd_type;
+	dev_t			lxvd_emul_dev;
+	dev_t			lxvd_real_dev;
+	uint64_t		lxvd_volsize;
+	uint64_t		lxvd_blksize;
+	char			lxvd_real_name[MAXPATHLEN];
+} lx_virt_disk_t;
+
+/*
+ * Determine the upper bound on the system call number:
+ */
+#if defined(_LP64)
+#define	LX_MAX_SYSCALL(lwp)						\
+	((lwp_getdatamodel(lwp) == DATAMODEL_NATIVE) ?			\
+	    lx_nsysent64 : lx_nsysent32)
+#else
+#define	LX_MAX_SYSCALL(lwp)	lx_nsysent32
+#endif
+
+extern int lx_kern_release_cmp(zone_t *, const char *);
+
+extern void lx_lwp_set_native_stack_current(lx_lwp_data_t *, uintptr_t);
+extern void lx_divert(klwp_t *, uintptr_t);
+extern int lx_runexe(klwp_t *, void *);
+extern void lx_switch_to_native(klwp_t *);
+
+extern int lx_syscall_enter(void);
+extern void lx_syscall_return(klwp_t *, int, long);
+
+extern void lx_trace_sysenter(int, uintptr_t *);
+extern void lx_trace_sysreturn(int, long);
+
+extern void lx_emulate_user(klwp_t *, int, uintptr_t *);
+#if defined(_SYSCALL32_IMPL)
+extern void lx_emulate_user32(klwp_t *, int, uintptr_t *);
+#endif
+
+extern int lx_debug;
+#define	lx_print	if (lx_debug) printf
+
+extern void lx_pid_assign(kthread_t *, struct lx_pid *);
+extern void lx_pid_reassign(kthread_t *);
+extern void lx_pid_rele(pid_t, id_t);
+extern pid_t lx_lpid_to_spair(pid_t, pid_t *, id_t *);
+extern pid_t lx_lwp_ppid(klwp_t *, pid_t *, id_t *);
+extern void lx_pid_init(void);
+extern void lx_pid_fini(void);
+
+/*
+ * In-Kernel Linux System Call Description.
+ */
+typedef struct lx_sysent {
+	char	*sy_name;
+	long	(*sy_callc)();
+	char	sy_flags;
+	char	sy_narg;
+} lx_sysent_t;
+
+#if defined(_LP64)
+extern lx_sysent_t lx_sysent64[LX_NSYSCALLS + 1];
+extern int lx_nsysent64;
+#endif
+extern lx_sysent_t lx_sysent32[LX_NSYSCALLS + 1];
+extern int lx_nsysent32;
+
+#endif	/* _KERNEL */
+#endif /* _ASM */
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _LX_BRAND_H */
--- /dev/null	Wed Oct  5 00:32:07 2016
+++ new/usr/src/uts/common/brand/lx/sys/lx_fcntl.h	Wed Oct  5 00:32:07 2016
@@ -0,0 +1,161 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
+ */
+
+#ifndef _SYS_LX_FCNTL_H
+#define	_SYS_LX_FCNTL_H
+
+#include <sys/vnode.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/*
+ * Lx open/fcntl flags
+ */
+#define	LX_O_RDONLY		00
+#define	LX_O_WRONLY		01
+#define	LX_O_RDWR		02
+#define	LX_O_ACCMODE		(LX_O_RDONLY | LX_O_WRONLY | LX_O_RDWR)
+#define	LX_O_CREAT		0100
+#define	LX_O_EXCL		0200
+#define	LX_O_NOCTTY		0400
+#define	LX_O_TRUNC		01000
+#define	LX_O_APPEND		02000
+#define	LX_O_NONBLOCK		04000
+#define	LX_O_NDELAY		LX_O_NONBLOCK
+#define	LX_O_SYNC		010000
+#define	LX_O_FSYNC		LX_O_SYNC
+#define	LX_O_ASYNC		020000
+#define	LX_O_DIRECT		040000
+#define	LX_O_LARGEFILE		0100000
+#define	LX_O_DIRECTORY		0200000
+#define	LX_O_NOFOLLOW		0400000
+#define	LX_O_CLOEXEC		02000000
+#define	LX_O_PATH		010000000
+
+#define	LX_F_DUPFD		0
+#define	LX_F_GETFD		1
+#define	LX_F_SETFD		2
+#define	LX_F_GETFL		3
+#define	LX_F_SETFL		4
+#define	LX_F_GETLK		5
+#define	LX_F_SETLK		6
+#define	LX_F_SETLKW		7
+#define	LX_F_SETOWN		8
+#define	LX_F_GETOWN		9
+#define	LX_F_SETSIG		10
+#define	LX_F_GETSIG		11
+
+#define	LX_F_GETLK64		12
+#define	LX_F_SETLK64		13
+#define	LX_F_SETLKW64		14
+
+#define	LX_F_SETLEASE		1024
+#define	LX_F_GETLEASE		1025
+#define	LX_F_NOTIFY		1026
+#define	LX_F_CANCELLK		1029
+#define	LX_F_DUPFD_CLOEXEC	1030
+#define	LX_F_SETPIPE_SZ		1031
+#define	LX_F_GETPIPE_SZ		1032
+
+#define	LX_F_RDLCK		0
+#define	LX_F_WRLCK		1
+#define	LX_F_UNLCK		2
+
+/* Test for emulated O_PATH setting in file_t flags */
+#define	LX_IS_O_PATH(f)		(((f)->f_flag & (FREAD|FWRITE)) == 0)
+
+extern int lx_vp_at(int, char *, vnode_t **, int);
+
+/*
+ * Lx flock codes.
+ */
+#define	LX_NAME_MAX		255
+#define	LX_LOCK_SH		1	/* shared */
+#define	LX_LOCK_EX		2	/* exclusive */
+#define	LX_LOCK_NB		4	/* non-blocking */
+#define	LX_LOCK_UN		8	/* unlock */
+
+/*
+ * On Linux the constants AT_REMOVEDIR and AT_EACCESS have the same value.
+ * AT_REMOVEDIR is used only by unlinkat and AT_EACCESS is used only by
+ * faccessat.
+ */
+#define	LX_AT_FDCWD		(-100)
+#define	LX_AT_SYMLINK_NOFOLLOW	0x100
+#define	LX_AT_REMOVEDIR		0x200
+#define	LX_AT_EACCESS		0x200
+#define	LX_AT_SYMLINK_FOLLOW	0x400
+#define	LX_AT_NO_AUTOMOUNT	0x800
+#define	LX_AT_EMPTY_PATH	0x1000
+
+typedef struct lx_flock {
+	short		l_type;
+	short		l_whence;
+	long		l_start;
+	long		l_len;
+	int		l_pid;
+} lx_flock_t;
+
+typedef struct lx_flock64 {
+	short		l_type;
+	short		l_whence;
+	long long	l_start;
+	long long	l_len;
+	int		l_pid;
+} lx_flock64_t;
+
+#if defined(_KERNEL)
+
+/*
+ * 64-bit kernel view of 32-bit usermode structs.
+ */
+#pragma pack(4)
+typedef struct lx_flock32 {
+	int16_t		l_type;
+	int16_t		l_whence;
+	int32_t		l_start;
+	int32_t		l_len;
+	int32_t		l_pid;
+} lx_flock32_t;
+
+typedef struct lx_flock64_32 {
+	int16_t		l_type;
+	int16_t		l_whence;
+	int64_t		l_start;
+	int64_t		l_len;
+	int32_t		l_pid;
+} lx_flock64_32_t;
+#pragma pack()
+
+#endif /* _KERNEL && _SYSCALL32_IMPL */
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_LX_FCNTL_H */
--- /dev/null	Wed Oct  5 00:32:07 2016
+++ new/usr/src/uts/common/brand/lx/sys/lx_futex.h	Wed Oct  5 00:32:07 2016
@@ -0,0 +1,121 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 2014, Joyent, Inc.  All rights reserved.
+ */
+
+#ifndef _SYS_LX_FUTEX_H
+#define	_SYS_LX_FUTEX_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#define	FUTEX_WAIT		0
+#define	FUTEX_WAKE		1
+#define	FUTEX_FD		2
+#define	FUTEX_REQUEUE		3
+#define	FUTEX_CMP_REQUEUE	4
+#define	FUTEX_WAKE_OP		5
+#define	FUTEX_LOCK_PI		6
+#define	FUTEX_UNLOCK_PI		7
+#define	FUTEX_TRYLOCK_PI	8
+#define	FUTEX_WAIT_BITSET	9
+#define	FUTEX_WAKE_BITSET	10
+#define	FUTEX_WAIT_REQUEUE_PI	11
+#define	FUTEX_CMP_REQUEUE_PI	12
+#define	FUTEX_MAX_CMD		FUTEX_CMP_REQUEUE_PI
+
+/*
+ * Flags that can be OR'd into a futex operation.
+ */
+#define	FUTEX_CMD_MASK		0x007f
+#define	FUTEX_PRIVATE_FLAG	0x0080
+#define	FUTEX_CLOCK_REALTIME	0x0100
+
+#define	FUTEX_BITSET_MATCH_ANY	0xffffffff
+/*
+ * FUTEX_WAKE_OP operations
+ */
+#define	FUTEX_OP_SET		0	/* *(int *)UADDR2 = OPARG; */
+#define	FUTEX_OP_ADD		1	/* *(int *)UADDR2 += OPARG; */
+#define	FUTEX_OP_OR		2	/* *(int *)UADDR2 |= OPARG; */
+#define	FUTEX_OP_ANDN		3	/* *(int *)UADDR2 &= ~OPARG; */
+#define	FUTEX_OP_XOR		4	/* *(int *)UADDR2 ^= OPARG; */
+
+/*
+ * FUTEX_WAKE_OP comparison operations
+ */
+#define	FUTEX_OP_CMP_EQ		0	/* if (oldval == CMPARG) wake */
+#define	FUTEX_OP_CMP_NE		1	/* if (oldval != CMPARG) wake */
+#define	FUTEX_OP_CMP_LT		2	/* if (oldval < CMPARG) wake */
+#define	FUTEX_OP_CMP_LE		3	/* if (oldval <= CMPARG) wake */
+#define	FUTEX_OP_CMP_GT		4	/* if (oldval > CMPARG) wake */
+#define	FUTEX_OP_CMP_GE		5	/* if (oldval >= CMPARG) wake */
+
+/*
+ * The encoding of the FUTEX_WAKE_OP operation in 32 bits:
+ *
+ *	+--+-- - --+-- - --+-- - --+-- - --+
+ *	|S |OP     |CMP    |OPARG  |CMPARG |
+ *	+--+-- - --+-- - --+-- - --+-- - --+
+ *	|31|30 - 28|27 - 24|23 - 12|11 -  0|
+ *
+ * The S bit denotes that the OPARG should be (1 << OPARG) instead of OPARG.
+ * (Yes, this whole thing is entirely absurd -- see the block comment in
+ * lx_futex.c for an explanation of this nonsense.)  Macros to extract the
+ * various components from the operation, given the above encoding:
+ */
+#define	FUTEX_OP_OP(x)		(((x) >> 28) & 7)
+#define	FUTEX_OP_CMP(x)		(((x) >> 24) & 15)
+#define	FUTEX_OP_OPARG(x)	(((x) >> 31) ? (1 << (((x) << 8) >> 20)) : \
+				((((x) << 8) >> 20)))
+#define	FUTEX_OP_CMPARG(x)	(((x) << 20) >> 20)
+
+#ifdef _KERNEL
+
+#define	FUTEX_WAITERS			0x80000000
+#define	FUTEX_OWNER_DIED		0x40000000
+#define	FUTEX_TID_MASK			0x3fffffff
+
+#define	FUTEX_ROBUST_LOCK_PI		1
+#define	FUTEX_ROBUST_LIST_LIMIT		2048
+
+extern long lx_futex(uintptr_t addr, int cmd, int val, uintptr_t lx_timeout,
+    uintptr_t addr2, int val2);
+extern void lx_futex_init(void);
+extern int lx_futex_fini(void);
+extern long lx_set_robust_list(void *listp, size_t len);
+extern long lx_get_robust_list(pid_t pid, void **listp, size_t *lenp);
+extern void lx_futex_robust_exit(uintptr_t addr, uint32_t tid);
+
+#endif /* _KERNEL */
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_LX_FUTEX_H */
--- /dev/null	Wed Oct  5 00:32:07 2016
+++ new/usr/src/uts/common/brand/lx/sys/lx_impl.h	Wed Oct  5 00:32:07 2016
@@ -0,0 +1,52 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2014 Joyent, Inc.  All rights reserved.
+ */
+
+#ifndef	_LX_IMPL_H
+#define	_LX_IMPL_H
+
+#include <sys/types.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+typedef void (lx_systrace_f)(ulong_t, ulong_t, ulong_t, ulong_t, ulong_t,
+    ulong_t, ulong_t);
+
+
+extern lx_systrace_f *lx_systrace_entry_ptr;
+extern lx_systrace_f *lx_systrace_return_ptr;
+
+extern void lx_brand_systrace_enable(void);
+extern void lx_brand_systrace_disable(void);
+
+extern void lx_unsupported(char *);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _LX_IMPL_H */
--- /dev/null	Wed Oct  5 00:32:07 2016
+++ new/usr/src/uts/common/brand/lx/sys/lx_ldt.h	Wed Oct  5 00:32:07 2016
@@ -0,0 +1,91 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * Copyright 2014 Joyent, Inc.  All rights reserved.
+ */
+
+#ifndef _SYS_LINUX_LDT_H
+#define	_SYS_LINUX_LDT_H
+
+#include <sys/segments.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+struct ldt_info {
+	uint_t	entry_number;
+	uint_t	base_addr;
+	uint_t	limit;
+	uint_t	seg_32bit:1,
+		contents:2,
+		read_exec_only:1,
+		limit_in_pages:1,
+		seg_not_present:1,
+		useable:1;
+};
+
+#define	LDT_INFO_EMPTY(info)						\
+	((info)->base_addr == 0 && (info)->limit == 0 &&		\
+	(info)->contents == 0 && (info)->read_exec_only == 1 &&		\
+	(info)->seg_32bit == 0 && (info)->limit_in_pages == 0 &&	\
+	(info)->seg_not_present == 1 && (info)->useable == 0)
+
+#if defined(__amd64)
+#define	SETMODE(desc)	(desc)->usd_long = SDP_SHORT;
+#else
+#define	SETMODE(desc)
+#endif
+
+#define	LDT_INFO_TO_DESC(info, desc)	{				\
+	USEGD_SETBASE(desc, (info)->base_addr);				\
+	USEGD_SETLIMIT(desc, (info)->limit);				\
+	(desc)->usd_type = ((info)->contents << 2) | 			\
+	    ((info)->read_exec_only ^ 1) << 1 | 0x10;			\
+	(desc)->usd_dpl = SEL_UPL;					\
+	(desc)->usd_p = (info)->seg_not_present ^ 1;			\
+	(desc)->usd_def32 = (info)->seg_32bit;				\
+	(desc)->usd_gran = (info)->limit_in_pages;			\
+	(desc)->usd_avl = (info)->useable;				\
+	SETMODE(desc);							\
+}
+
+#define	DESC_TO_LDT_INFO(desc, info)	{				\
+	bzero((info), sizeof (*(info)));				\
+	(info)->base_addr = USEGD_GETBASE(desc);			\
+	(info)->limit = USEGD_GETLIMIT(desc);				\
+	(info)->seg_not_present = (desc)->usd_p ^ 1;			\
+	(info)->contents = ((desc)->usd_type >> 2) & 3;			\
+	(info)->read_exec_only = (((desc)->usd_type >> 1) & 1) ^ 1;	\
+	(info)->seg_32bit = (desc)->usd_def32;				\
+	(info)->limit_in_pages = (desc)->usd_gran;			\
+	(info)->useable = (desc)->usd_avl;				\
+}
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_LINUX_LDT_H */
--- /dev/null	Wed Oct  5 00:32:07 2016
+++ new/usr/src/uts/common/brand/lx/sys/lx_misc.h	Wed Oct  5 00:32:07 2016
@@ -0,0 +1,119 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#ifndef _SYS__LX_MISC_H
+#define	_SYS__LX_MISC_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#include <inet/ip.h>
+#include <inet/ip6.h>
+#include <sys/siginfo.h>
+#include <sys/lx_brand.h>
+
+#ifdef _KERNEL
+
+extern void lx_setrval(klwp_t *, int, int);
+extern void lx_exec();
+extern void lx_exitlwp(klwp_t *);
+extern void lx_freelwp(klwp_t *);
+extern void *lx_lwpdata_alloc(proc_t *);
+extern void lx_lwpdata_free(void *);
+extern void lx_initlwp(klwp_t *, void *);
+extern void lx_initlwp_post(klwp_t *);
+extern void lx_forklwp(klwp_t *, klwp_t *);
+
+extern void lx_set_gdt(int, user_desc_t *);
+extern void lx_clear_gdt(int);
+
+extern longlong_t lx_nosys();
+
+extern greg_t lx_fixsegreg(greg_t, model_t);
+extern uintptr_t lx_fsbase(klwp_t *, uintptr_t);
+extern void lx_exit_with_sig(proc_t *, sigqueue_t *);
+extern boolean_t lx_wait_filter(proc_t *, proc_t *);
+extern void lx_sigfd_translate(k_siginfo_t *);
+extern int stol_ksiginfo_copyout(k_siginfo_t *, void *);
+
+extern int ltos_at_flag(int, int, boolean_t);
+#if defined(_SYSCALL32_IMPL)
+extern int stol_ksiginfo32_copyout(k_siginfo_t *, void *);
+#endif
+extern void lx_read_argv_bounds(proc_t *p);
+
+typedef enum lx_regs_location {
+	LX_REG_LOC_UNAVAIL,
+	LX_REG_LOC_LWP,
+	LX_REG_LOC_UCP
+} lx_regs_location_t;
+
+extern lx_regs_location_t lx_regs_location(lx_lwp_data_t *, void **, boolean_t);
+
+
+typedef enum lx_if_action {
+	LX_IF_FROMNATIVE,
+	LX_IF_TONATIVE
+} lx_if_action_t;
+
+/* Linux ARP protocol hardware identifiers */
+#define	LX_ARPHRD_ETHER		1	/* Ethernet */
+#define	LX_ARPHRD_LOOPBACK	772	/* Loopback */
+#define	LX_ARPHRD_VOID		0xffff	/* Unknown */
+
+/* IPv6 address scope values used in /proc/net/if_inet6 */
+#define	LX_IPV6_ADDR_LOOPBACK	0x0010U
+#define	LX_IPV6_ADDR_LINKLOCAL	0x0020U
+#define	LX_IPV6_ADDR_SITELOCAL	0x0040U
+#define	LX_IPV6_ADDR_COMPATv4	0x0080U
+
+extern void lx_ifname_convert(char *, lx_if_action_t);
+extern void lx_ifflags_convert(uint64_t *, lx_if_action_t);
+extern unsigned int lx_ipv6_scope_convert(const in6_addr_t *);
+extern void lx_stol_hwaddr(const struct sockaddr_dl *, struct sockaddr *,
+    int *);
+
+extern boolean_t lx_ptrace_stop(ushort_t);
+extern void lx_stop_notify(proc_t *, klwp_t *, ushort_t, ushort_t);
+extern void lx_ptrace_init(void);
+extern void lx_ptrace_fini(void);
+extern int lx_waitid_helper(idtype_t, id_t, k_siginfo_t *, int, boolean_t *,
+    int *);
+extern void lx_ptrace_exit(proc_t *, klwp_t *);
+extern void lx_ptrace_inherit_tracer(lx_lwp_data_t *, lx_lwp_data_t *);
+extern int lx_ptrace_stop_for_option(int, boolean_t, ulong_t, uintptr_t);
+extern int lx_ptrace_set_clone_inherit(int, boolean_t);
+extern int lx_sigcld_repost(proc_t *, sigqueue_t *);
+extern int lx_ptrace_issig_stop(proc_t *, klwp_t *);
+extern boolean_t lx_ptrace_sig_ignorable(proc_t *, klwp_t *, int);
+
+extern int lx_helper_clone(int64_t *, int, void *, void *, void *);
+extern int lx_helper_setgroups(int, gid_t *);
+extern int lx_helper_rt_sigqueueinfo(pid_t, int, siginfo_t *);
+extern int lx_helper_rt_tgsigqueueinfo(pid_t, pid_t, int, siginfo_t *);
+
+extern boolean_t lx_vsyscall_iscall(klwp_t *, uintptr_t, int *);
+extern void lx_vsyscall_enter(proc_t *, klwp_t *, int);
+
+extern void lx_check_strict_failure(lx_lwp_data_t *);
+
+#endif
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS__LX_MISC_H */
--- /dev/null	Wed Oct  5 00:32:07 2016
+++ new/usr/src/uts/common/brand/lx/sys/lx_ptm.h	Wed Oct  5 00:32:07 2016
@@ -0,0 +1,44 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef	_SYS_PTM_LINUX_H
+#define	_SYS_PTM_LINUX_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#define	LX_PTM_DRV		"lx_ptm"
+#define	LX_PTM_MINOR_NODE	"lx_ptmajor"
+
+#define	LX_PTM_DEV_TO_PTS(dev)	(getminor(dev) - 1)
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_PTM_LINUX_H */
--- /dev/null	Wed Oct  5 00:32:07 2016
+++ new/usr/src/uts/common/brand/lx/sys/lx_sched.h	Wed Oct  5 00:32:07 2016
@@ -0,0 +1,60 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYS_LINUX_SCHED_H
+#define	_SYS_LINUX_SCHED_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <sys/procset.h>
+#include <sys/priocntl.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/*
+ * Linux scheduler policies.
+ */
+#define	LX_SCHED_OTHER		0
+#define	LX_SCHED_FIFO		1
+#define	LX_SCHED_RR		2
+
+#define	LX_PRI_MAX		99
+
+typedef	int l_pid_t;
+
+struct lx_sched_param {
+	int	lx_sched_prio;
+};
+
+extern int sched_setprocset(procset_t *, l_pid_t);
+extern long do_priocntlsys(int, procset_t *, void *);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_LINUX_SCHED_H */
--- /dev/null	Wed Oct  5 00:32:07 2016
+++ new/usr/src/uts/common/brand/lx/sys/lx_siginfo.h	Wed Oct  5 00:32:07 2016
@@ -0,0 +1,190 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
+ */
+
+#ifndef _LX_SIGINFO_H
+#define	_LX_SIGINFO_H
+
+#include <sys/lx_types.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/*
+ * lx_siginfo_t lsi_code values
+ *
+ *	LX_SI_ASYNCNL:	Sent by asynch name lookup completion
+ *	LX_SI_DETHREAD:	Sent by execve() killing subsidiary threads
+ *	LX_SI_SIGIO:	Sent by queued SIGIO
+ *	LX_SI_ASYNCIO:	Sent by asynchronous I/O completion
+ *	LX_SI_MESGQ:	Sent by real time message queue state change
+ *	LX_SI_TIMER:	Sent by timer expiration
+ *	LX_SI_QUEUE:	Sent by sigqueue
+ *	LX_SI_USER:	Sent by kill, sigsend, raise, etc.
+ *	LX_SI_KERNEL:	Sent by kernel
+ *	LX_SI_CODE_NOT_EXIST: Error code. When translating from Linux to
+ *	    illumos errors, if there is no translation available, this value
+ *	    should be used. This value should have no meaning as an si_code in
+ *	    illumos or Linux.
+ *
+ * At present, LX_SI_ASYNCNL, LX_SI_DETHREAD, and LX_SI_SIGIO are unused by
+ * BrandZ.
+ */
+#define	LX_SI_CODE_NOT_EXIST	(-61)
+#define	LX_SI_ASYNCNL		(-60)
+#define	LX_SI_DETHREAD		(-7)
+#define	LX_SI_TKILL		(-6)
+#define	LX_SI_SIGIO		(-5)
+#define	LX_SI_ASYNCIO		(-4)
+#define	LX_SI_MESGQ		(-3)
+#define	LX_SI_TIMER		(-2)
+#define	LX_SI_QUEUE		(-1)
+#define	LX_SI_USER		(0)
+#define	LX_SI_KERNEL		(0x80)
+
+#define	LX_SI_MAX_SIZE		128
+#define	LX_SI_PAD_SIZE_32	((LX_SI_MAX_SIZE / sizeof (int)) - 3)
+#define	LX_SI_PAD_SIZE_64	((LX_SI_MAX_SIZE / sizeof (int)) - 4)
+
+#if defined(_LP64)
+/*
+ * Because of the odd number (3) of ints before the union, we need to account
+ * for the smaller padding needed on x64 due to the union being offset to an 8
+ * byte boundary.
+ */
+#define	LX_SI_PAD_SIZE		LX_SI_PAD_SIZE_64
+#else
+#define	LX_SI_PAD_SIZE		LX_SI_PAD_SIZE_32
+#endif
+
+typedef struct lx_siginfo {
+	int lsi_signo;
+	int lsi_errno;
+	int lsi_code;
+	union {
+		int _pad[LX_SI_PAD_SIZE];
+
+		struct {
+			pid_t _pid;
+			lx_uid16_t _uid;
+		} _kill;
+
+		struct {
+			uint_t _timer1;
+			uint_t _timer2;
+		} _timer;
+
+		struct {
+			pid_t _pid;
+			lx_uid16_t _uid;
+			union sigval _sigval;
+		} _rt;
+
+		struct {
+			pid_t _pid;
+			lx_uid16_t _uid;
+			int _status;
+			clock_t _utime;
+			clock_t _stime;
+		} _sigchld;
+
+		struct {
+			void *_addr;
+		} _sigfault;
+
+		struct {
+			int _band;
+			int _fd;
+		} _sigpoll;
+	} _sifields;
+} lx_siginfo_t;
+
+#if defined(_KERNEL) && defined(_SYSCALL32_IMPL)
+/*
+ * 64-bit kernel view of the 32-bit "lx_siginfo_t" object.
+ */
+#pragma pack(4)
+typedef struct lx_siginfo32 {
+	int lsi_signo;
+	int lsi_errno;
+	int lsi_code;
+	union {
+		int _pad[LX_SI_PAD_SIZE_32];
+
+		struct {
+			pid32_t _pid;
+			lx_uid16_t _uid;
+		} _kill;
+
+		struct {
+			uint_t _timer1;
+			uint_t _timer2;
+		} _timer;
+
+		struct {
+			pid32_t _pid;
+			lx_uid16_t _uid;
+			union sigval32 _sigval;
+		} _rt;
+
+		struct {
+			pid32_t _pid;
+			lx_uid16_t _uid;
+			int _status;
+			clock32_t _utime;
+			clock32_t _stime;
+		} _sigchld;
+
+		struct {
+			caddr32_t _addr;
+		} _sigfault;
+
+		struct {
+			int _band;
+			int _fd;
+		} _sigpoll;
+	} _sifields;
+} lx_siginfo32_t;
+#pragma pack()
+#endif /* defined(_KERNEL) && defined(_SYSCALL32_IMPL) */
+
+#define	lsi_pid		_sifields._kill._pid
+#define	lsi_uid		_sifields._kill._uid
+#define	lsi_status	_sifields._sigchld._status
+#define	lsi_utime	_sifields._sigchld._utime
+#define	lsi_stime	_sifields._sigchld._stime
+#define	lsi_value	_sifields._rt._sigval
+#define	lsi_int		_sifields._rt._sigval.sivalx_int
+#define	lsi_ptr		_sifields._rt._sigval.sivalx_ptr
+#define	lsi_addr	_sifields._sigfault._addr
+#define	lsi_band	_sifields._sigpoll._band
+#define	lsi_fd		_sifields._sigpoll._fd
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _LX_SIGINFO_H */
--- /dev/null	Wed Oct  5 00:32:07 2016
+++ new/usr/src/uts/common/brand/lx/sys/lx_signal.h	Wed Oct  5 00:32:07 2016
@@ -0,0 +1,32 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+#ifndef _LX_SIGNAL_H
+#define	_LX_SIGNAL_H
+
+#include <lx_signum.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+extern void lx_ltos_sigset(lx_sigset_t *, k_sigset_t *);
+extern void lx_stol_sigset(k_sigset_t *, lx_sigset_t *);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _LX_SIGNAL_H */
--- /dev/null	Wed Oct  5 00:32:07 2016
+++ new/usr/src/uts/common/brand/lx/sys/lx_socket.h	Wed Oct  5 00:32:07 2016
@@ -0,0 +1,434 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#ifndef _SYS_LX_SOCKET_H
+#define	_SYS_LX_SOCKET_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/*
+ * Linux address family definitions
+ * Some of these are not supported
+ */
+#define	LX_AF_UNSPEC		0  /* Unspecified */
+#define	LX_AF_UNIX		1  /* local file/pipe name */
+#define	LX_AF_INET		2  /* IP protocol family */
+#define	LX_AF_AX25		3  /* Amateur Radio AX.25 */
+#define	LX_AF_IPX		4  /* Novell Internet Protocol */
+#define	LX_AF_APPLETALK		5  /* Appletalk */
+#define	LX_AF_NETROM		6  /* Amateur radio */
+#define	LX_AF_BRIDGE		7  /* Multiprotocol bridge */
+#define	LX_AF_ATMPVC		8  /* ATM PVCs */
+#define	LX_AF_X25		9  /* X.25 */
+#define	LX_AF_INET6		10 /* IPV 6 */
+#define	LX_AF_ROSE		11 /* Amateur Radio X.25 */
+#define	LX_AF_DECNET		12 /* DECnet */
+#define	LX_AF_NETBEUI		13 /* 802.2LLC */
+#define	LX_AF_SECURITY		14 /* Security callback */
+#define	LX_AF_KEY		15 /* key management */
+#define	LX_AF_ROUTE		16 /* Alias to emulate 4.4BSD */
+#define	LX_AF_NETLINK		LX_AF_ROUTE
+#define	LX_AF_PACKET		17 /* Packet family */
+#define	LX_AF_ASH		18 /* Ash ? */
+#define	LX_AF_ECONET		19 /* Acorn Econet */
+#define	LX_AF_ATMSVC		20 /* ATM SVCs */
+#define	LX_AF_SNA		22 /* Linux SNA */
+#define	LX_AF_IRDA		23 /* IRDA sockets */
+#define	LX_AF_PPPOX		24 /* PPPoX sockets */
+#define	LX_AF_WANPIPE		25 /* Wanpipe API sockets */
+#define	LX_AF_LLC		26
+/* gap in Linux defines for 27 and 28 */
+#define	LX_AF_CAN		29
+#define	LX_AF_TIPC		30
+#define	LX_AF_BLUETOOTH		31 /* Bluetooth sockets */
+#define	LX_AF_IUCV		32
+#define	LX_AF_RXRPC		33
+
+/* limit of AF mappings */
+#define	LX_AF_MAX		LX_AF_RXRPC
+
+#define	AF_NOTSUPPORTED		-1
+#define	AF_INVAL		-2
+
+/*
+ * Options for use with [gs]etsockopt at the SOL_SOCKET level.
+ */
+#define	LX_SOL_SOCKET				1
+
+#define	LX_SCM_RIGHTS				1
+#define	LX_SCM_CRED				2
+
+#define	LX_SO_DEBUG				1
+#define	LX_SO_REUSEADDR				2
+#define	LX_SO_TYPE				3
+#define	LX_SO_ERROR				4
+#define	LX_SO_DONTROUTE				5
+#define	LX_SO_BROADCAST				6
+#define	LX_SO_SNDBUF				7
+#define	LX_SO_RCVBUF				8
+#define	LX_SO_KEEPALIVE				9
+#define	LX_SO_OOBINLINE				10
+#define	LX_SO_NO_CHECK				11
+#define	LX_SO_PRIORITY				12
+#define	LX_SO_LINGER				13
+#define	LX_SO_BSDCOMPAT				14
+#define	LX_SO_REUSEPORT				15
+/*
+ * For Linux see unix(7) man page SO_PASSCRED description. For Illumos see
+ * socket.h(3HEAD) man page SO_RECVUCRED description.
+ */
+#define	LX_SO_PASSCRED				16
+#define	LX_SO_PEERCRED				17
+#define	LX_SO_RCVLOWAT				18
+#define	LX_SO_SNDLOWAT				19
+#define	LX_SO_RCVTIMEO				20
+#define	LX_SO_SNDTIMEO				21
+/* Security levels - as per NRL IPv6 - don't actually do anything */
+#define	LX_SO_SECURITY_AUTHENTICATION		22
+#define	LX_SO_SECURITY_ENCRYPTION_TRANSPORT	23
+#define	LX_SO_SECURITY_ENCRYPTION_NETWORK	24
+#define	LX_SO_BINDTODEVICE			25
+/* Socket filtering */
+#define	LX_SO_ATTACH_FILTER			26
+#define	LX_SO_DETACH_FILTER			27
+#define	LX_SO_PEERNAME				28
+#define	LX_SO_TIMESTAMP				29
+#define	LX_SCM_TIMESTAMP			LX_SO_TIMESTAMP
+#define	LX_SO_ACCEPTCONN			30
+
+#define	LX_SO_PEERSEC				31
+#define	LX_SO_SNDBUFFORCE			32
+#define	LX_SO_RCVBUFFORCE			33
+#define	LX_SO_PASSSEC				34
+#define	LX_SO_TIMESTAMPNS			35
+#define	LX_SCM_TIMESTAMPNS			LX_SO_TIMESTAMPNS
+#define	LX_SO_MARK				36
+#define	LX_SO_TIMESTAMPING			37
+#define	LX_SCM_TIMESTAMPING			LX_SO_TIMESTAMPING
+#define	LX_SO_PROTOCOL				38
+#define	LX_SO_DOMAIN				39
+#define	LX_SO_RXQ_OVFL				40
+#define	LX_SO_WIFI_STATUS			41
+#define	LX_SCM_WIFI_STATUS			LX_SO_WIFI_STATUS
+#define	LX_SO_PEEK_OFF				42
+#define	LX_SO_NOFCS				43
+#define	LX_SO_LOCK_FILTER			44
+#define	LX_SO_SELECT_ERR_QUEUE			45
+#define	LX_SO_BUSY_POLL				46
+#define	LX_SO_MAX_PACING_RATE			47
+#define	LX_SO_BPF_EXTENSIONS			48
+
+/*
+ * Options for use with [gs]etsockopt at the RAW level.
+ * IPPROTO_RAW
+ */
+#define	LX_ICMP_FILTER				1
+
+/*
+ * Options for use with [gs]etsockopt at the PACKET level.
+ * SOL_PACKET
+ */
+#define	LX_SOL_PACKET				263
+
+#define	LX_PACKET_ADD_MEMBERSHIP		1
+#define	LX_PACKET_DROP_MEMBERSHIP		2
+#define	LX_PACKET_RECV_OUTPUT			3
+#define	LX_PACKET_RX_RING			5
+#define	LX_PACKET_STATISTICS			6
+
+/*
+ * Options for use with [gs]etsockopt at the NETLINK level.
+ * SOL_NETLINK
+ */
+#define	LX_SOL_NETLINK				270
+
+/*
+ * Linux socket type definitions
+ */
+#define	LX_SOCK_STREAM		1	/* Connection-based byte streams */
+#define	LX_SOCK_DGRAM		2	/* Connectionless, datagram */
+#define	LX_SOCK_RAW		3	/* Raw protocol interface */
+#define	LX_SOCK_RDM		4	/* Reliably-delivered message */
+#define	LX_SOCK_SEQPACKET	5	/* Sequenced packet stream */
+#define	LX_SOCK_PACKET		10	/* Linux specific */
+#define	LX_SOCK_MAX		11
+
+/*
+ * The Linux socket type can be or-ed with other flags (e.g. SOCK_CLOEXEC).
+ */
+#define	LX_SOCK_TYPE_MASK	0xf
+
+/*
+ * Linux flags for socket, socketpair and accept4. These are or-ed into the
+ * socket type value. In the Linux net.h header these come from fcntl.h (note
+ * that they are in octal in the Linux header).
+ */
+#define	LX_SOCK_CLOEXEC		0x80000
+#define	LX_SOCK_NONBLOCK	0x800
+
+#define	SOCK_NOTSUPPORTED	-1
+#define	SOCK_INVAL		-2
+
+/*
+ * PF_PACKET protocol definitions.
+ */
+#define	LX_ETH_P_802_3	0x0001
+#define	LX_ETH_P_ALL	0x0003
+#define	LX_ETH_P_802_2	0x0004
+#define	LX_ETH_P_IP	0x0800
+#define	LX_ETH_P_ARP	0x0806
+#define	LX_ETH_P_IPV6	0x86DD
+
+/*
+ * IP Protocol levels. Some of these match the Illumos IPPROTO_* values.
+ */
+#define	LX_IPPROTO_IP		0
+#define	LX_IPPROTO_ICMP		1
+#define	LX_IPPROTO_IGMP		2
+#define	LX_IPPROTO_TCP		6
+#define	LX_IPPROTO_UDP		17
+#define	LX_IPPROTO_IPV6		41
+#define	LX_IPPROTO_ICMPV6	58
+#define	LX_IPPROTO_RAW		255
+
+/*
+ * Options for use with [gs]etsockopt at the IP level.
+ * IPPROTO_IP
+ */
+#define	LX_IP_TOS		1
+#define	LX_IP_TTL		2
+#define	LX_IP_HDRINCL		3
+#define	LX_IP_OPTIONS		4
+#define	LX_IP_ROUTER_ALERT	5
+#define	LX_IP_RECVOPTS		6
+#define	LX_IP_RETOPTS		7
+#define	LX_IP_PKTINFO		8
+#define	LX_IP_PKTOPTIONS	9
+#define	LX_IP_MTU_DISCOVER	10
+#define	LX_IP_RECVERR		11
+#define	LX_IP_RECVTTL		12
+#define	LX_IP_RECVTOS		13
+#define	LX_IP_MTU		14
+#define	LX_IP_FREEBIND		15
+#define	LX_IP_IPSEC_POLICY	16
+#define	LX_IP_XFRM_POLICY	17
+#define	LX_IP_PASSSEC		18
+#define	LX_IP_TRANSPARENT	19
+#define	LX_IP_ORIGDSTADDR	20
+#define	LX_IP_MINTTL		21
+#define	LX_IP_NODEFRAG		22
+/* Linux apparently leaves a gap here */
+#define	LX_IP_MULTICAST_IF	32
+#define	LX_IP_MULTICAST_TTL	33
+#define	LX_IP_MULTICAST_LOOP	34
+#define	LX_IP_ADD_MEMBERSHIP	35
+#define	LX_IP_DROP_MEMBERSHIP	36
+#define	LX_IP_UNBLOCK_SOURC	37
+#define	LX_IP_BLOCK_SOURCE	38
+#define	LX_IP_ADD_SOURCE_MEMBERSHIP 39
+#define	LX_IP_DROP_SOURCE_MEMBERSHIP 40
+#define	LX_IP_MSFILTER		41
+#define	LX_MCAST_JOIN_GROUP	42
+#define	LX_MCAST_BLOCK_SOURCE	43
+#define	LX_MCAST_UNBLOCK_SOURCE	44
+#define	LX_MCAST_LEAVE_GROUP	45
+#define	LX_MCAST_JOIN_SOURCE_GROUP 46
+#define	LX_MCAST_LEAVE_SOURCE_GROUP 47
+#define	LX_MCAST_MSFILTER	48
+#define	LX_IP_MULTICAST_ALL	49
+#define	LX_IP_UNICAST_IF	50
+
+/*
+ * LX_IP_MTU_DISCOVER values
+ */
+#define	LX_IP_PMTUDISC_DONT		0
+#define	LX_IP_PMTUDISC_WANT		1
+#define	LX_IP_PMTUDISC_DO		2
+#define	LX_IP_PMTUDISC_PROBE		3
+#define	LX_IP_PMTUDISC_INTERFACE	4
+#define	LX_IP_PMTUDISC_OMIT		5
+
+/*
+ * Options for use with [gs]etsockopt at the IP level.
+ * IPPROTO_IPV6
+ */
+
+#define	LX_IPV6_ADDRFORM	1
+#define	LX_IPV6_2292PKTINFO	2
+#define	LX_IPV6_2292HOPOPTS	3
+#define	LX_IPV6_2292DSTOPTS	4
+#define	LX_IPV6_2292RTHDR	5
+#define	LX_IPV6_2292PKTOPTIONS	6
+#define	LX_IPV6_CHECKSUM	7
+#define	LX_IPV6_2292HOPLIMIT	8
+#define	LX_IPV6_NEXTHOP		9
+#define	LX_IPV6_AUTHHDR		10
+#define	LX_IPV6_UNICAST_HOPS	16
+#define	LX_IPV6_MULTICAST_IF	17
+#define	LX_IPV6_MULTICAST_HOPS	18
+#define	LX_IPV6_MULTICAST_LOOP	19
+#define	LX_IPV6_JOIN_GROUP	20
+#define	LX_IPV6_LEAVE_GROUP	21
+#define	LX_IPV6_ROUTER_ALERT	22
+#define	LX_IPV6_MTU_DISCOVER	23
+#define	LX_IPV6_MTU		24
+#define	LX_IPV6_RECVERR		25
+#define	LX_IPV6_V6ONLY		26
+#define	LX_IPV6_JOIN_ANYCAST	27
+#define	LX_IPV6_LEAVE_ANYCAST	28
+#define	LX_IPV6_IPSEC_POLICY	34
+#define	LX_IPV6_XFRM_POLICY	35
+
+#define	LX_IPV6_RECVPKTINFO	49
+#define	LX_IPV6_PKTINFO		50
+#define	LX_IPV6_RECVHOPLIMIT	51
+#define	LX_IPV6_HOPLIMIT	52
+#define	LX_IPV6_RECVHOPOPTS	53
+#define	LX_IPV6_HOPOPTS		54
+#define	LX_IPV6_RTHDRDSTOPTS	55
+#define	LX_IPV6_RECVRTHDR	56
+#define	LX_IPV6_RTHDR		57
+#define	LX_IPV6_RECVDSTOPTS	58
+#define	LX_IPV6_DSTOPTS		59
+#define	LX_IPV6_RECVTCLASS	66
+#define	LX_IPV6_TCLASS		67
+
+/*
+ * Options for use with [gs]etsockopt at the IP level.
+ * IPPROTO_ICMPV6
+ */
+
+#define	LX_ICMP6_FILTER		1
+
+/*
+ * Options for use with [gs]etsockopt at the TCP level.
+ * IPPROTO_TCP
+ */
+#define	LX_TCP_NODELAY		1  /* Don't delay send to coalesce packets  */
+#define	LX_TCP_MAXSEG		2  /* Set maximum segment size  */
+#define	LX_TCP_CORK		3  /* Control sending of partial frames  */
+#define	LX_TCP_KEEPIDLE		4  /* Start keeplives after this period */
+#define	LX_TCP_KEEPINTVL	5  /* Interval between keepalives */
+#define	LX_TCP_KEEPCNT		6  /* Number of keepalives before death */
+#define	LX_TCP_SYNCNT		7  /* Number of SYN retransmits */
+#define	LX_TCP_LINGER2		8  /* Life time of orphaned FIN-WAIT-2 state */
+#define	LX_TCP_DEFER_ACCEPT	9  /* Wake up listener only when data arrive */
+#define	LX_TCP_WINDOW_CLAMP	10 /* Bound advertised window */
+#define	LX_TCP_INFO		11 /* Information about this connection. */
+#define	LX_TCP_QUICKACK		12 /* Bock/reenable quick ACKs.  */
+#define	LX_TCP_CONGESTION	13 /* Congestion control algorithm */
+#define	LX_TCP_MD5SIG		14 /* TCP MD5 Signature (RFC2385) */
+#define	LX_TCP_THIN_LINEAR_TIMEOUTS 16 /* Use linear timeouts on thin streams */
+#define	LX_TCP_THIN_DUPACK	17 /* Fast retrans. after 1 dupack */
+#define	LX_TCP_USER_TIMEOUT	18 /* How long for loss retry before timeout */
+#define	LX_TCP_REPAIR		19 /* TCP socket under repair */
+#define	LX_TCP_REPAIR_QUEUE	20
+#define	LX_TCP_QUEUE_SEQ	21
+#define	LX_TCP_REPAIR_OPTIONS	22
+#define	LX_TCP_FASTOPEN		23 /* Enable FastOpen on listeners */
+#define	LX_TCP_TIMESTAMP	24
+#define	LX_TCP_NOTSENT_LOWAT	25 /* limit number of unsent bytes */
+
+/*
+ * Options for use with [gs]etsockopt at the IGMP level.
+ * IPPROTO_IGMP
+ */
+#define	LX_IGMP_MINLEN				8
+#define	LX_IGMP_MAX_HOST_REPORT_DELAY		10
+#define	LX_IGMP_HOST_MEMBERSHIP_QUERY		0x11
+#define	LX_IGMP_HOST_MEMBERSHIP_REPORT		0x12
+#define	LX_IGMP_DVMRP				0x13
+#define	LX_IGMP_PIM				0x14
+#define	LX_IGMP_TRACE				0x15
+#define	LX_IGMP_HOST_NEW_MEMBERSHIP_REPORT	0x16
+#define	LX_IGMP_HOST_LEAVE_MESSAGE		0x17
+#define	LX_IGMP_MTRACE_RESP			0x1e
+#define	LX_IGMP_MTRACE				0x1f
+
+/*
+ * Linux socket flags for use with recv(2)/send(2)/recvmsg(2)/sendmsg(2)
+ */
+#define	LX_MSG_OOB		0x1
+#define	LX_MSG_PEEK		0x2
+#define	LX_MSG_DONTROUTE	0x4
+#define	LX_MSG_CTRUNC		0x8
+#define	LX_MSG_PROXY		0x10
+#define	LX_MSG_TRUNC		0x20
+#define	LX_MSG_DONTWAIT		0x40
+#define	LX_MSG_EOR		0x80
+#define	LX_MSG_WAITALL		0x100
+#define	LX_MSG_FIN		0x200
+#define	LX_MSG_SYN		0x400
+#define	LX_MSG_CONFIRM		0x800
+#define	LX_MSG_RST		0x1000
+#define	LX_MSG_ERRQUEUE		0x2000
+#define	LX_MSG_NOSIGNAL		0x4000
+#define	LX_MSG_MORE		0x8000
+#define	LX_MSG_WAITFORONE	0x10000
+#define	LX_MSG_FASTOPEN		0x20000000
+#define	LX_MSG_CMSG_CLOEXEC	0x40000000
+
+typedef struct lx_msghdr {
+	void		*msg_name;	/* optional address */
+	socklen_t	msg_namelen;	/* size of address */
+	struct iovec	*msg_iov;	/* scatter/gather array */
+	size_t		msg_iovlen;	/* # elements in msg_iov */
+	void		*msg_control;	/* ancillary data */
+	size_t		msg_controllen;	/* ancillary data buffer len */
+	int		msg_flags;	/* flags on received message */
+} lx_msghdr_t;
+
+
+#if defined(_LP64)
+
+typedef struct lx_msghdr32 {
+	caddr32_t	msg_name;	/* optional address */
+	uint32_t	msg_namelen;	/* size of address */
+	caddr32_t	msg_iov;	/* scatter/gather array */
+	int32_t		msg_iovlen;	/* # elements in msg_iov */
+	caddr32_t	msg_control;	/* ancillary data */
+	uint32_t	msg_controllen;	/* ancillary data buffer len */
+	int32_t		msg_flags;	/* flags on received message */
+} lx_msghdr32_t;
+
+#endif
+
+typedef struct lx_sockaddr_in6 {
+	sa_family_t	sin6_family;
+	in_port_t	sin6_port;
+	uint32_t	sin6_flowinfo;
+	struct in6_addr	sin6_addr;
+	uint32_t	sin6_scope_id;  /* Depends on scope of sin6_addr */
+	/* one 32-bit field shorter than illumos */
+} lx_sockaddr_in6_t;
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_LX_SOCKET_H */
--- /dev/null	Wed Oct  5 00:32:07 2016
+++ new/usr/src/uts/common/brand/lx/sys/lx_syscalls.h	Wed Oct  5 00:32:07 2016
@@ -0,0 +1,306 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#ifndef _SYS_LINUX_SYSCALLS_H
+#define	_SYS_LINUX_SYSCALLS_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#ifdef	_KERNEL
+
+extern long lx_accept();
+extern long lx_accept4();
+extern long lx_access();
+extern long lx_alarm();
+extern long lx_arch_prctl();
+extern long lx_bind();
+extern long lx_brk();
+extern long lx_chdir();
+extern long lx_chmod();
+extern long lx_chown();
+extern long lx_chown16();
+extern long lx_chroot();
+extern long lx_clock_getres();
+extern long lx_clock_gettime();
+extern long lx_clock_settime();
+extern long lx_close();
+extern long lx_connect();
+extern long lx_creat();
+extern long lx_dup();
+extern long lx_dup2();
+extern long lx_dup3();
+extern long lx_epoll_create();
+extern long lx_epoll_create1();
+extern long lx_epoll_ctl();
+extern long lx_epoll_pwait();
+extern long lx_epoll_wait();
+extern long lx_faccessat();
+extern long lx_fadvise64();
+extern long lx_fadvise64_32();
+extern long lx_fadvise64_64();
+extern long lx_fallocate();
+extern long lx_fallocate32();
+extern long lx_fchdir();
+extern long lx_fchmod();
+extern long lx_fchmodat();
+extern long lx_fchown();
+extern long lx_fchown16();
+extern long lx_fchownat();
+extern long lx_fcntl();
+extern long lx_fcntl64();
+extern long lx_fgetxattr();
+extern long lx_flistxattr();
+extern long lx_fremovexattr();
+extern long lx_fsetxattr();
+extern long lx_fstat32();
+extern long lx_fstat64();
+extern long lx_fstatat64();
+extern long lx_futex();
+extern long lx_get_robust_list();
+extern long lx_get_thread_area();
+extern long lx_getcpu();
+extern long lx_getcwd();
+extern long lx_getdents_32();
+extern long lx_getdents_64();
+extern long lx_getdents64();
+extern long lx_getegid();
+extern long lx_getegid16();
+extern long lx_geteuid();
+extern long lx_geteuid16();
+extern long lx_getgid();
+extern long lx_getgid16();
+extern long lx_getitimer();
+extern long lx_getpeername();
+extern long lx_getpgid();
+extern long lx_getpgrp();
+extern long lx_getsockname();
+extern long lx_getpid();
+extern long lx_getppid();
+extern long lx_getrandom();
+extern long lx_getresgid();
+extern long lx_getresgid16();
+extern long lx_getresuid();
+extern long lx_getresuid16();
+extern long lx_getrlimit();
+extern long lx_getrusage();
+extern long lx_getsid();
+extern long lx_getsockopt();
+extern long lx_gettid();
+extern long lx_gettimeofday();
+extern long lx_getuid();
+extern long lx_getuid16();
+extern long lx_getxattr();
+extern long lx_io_setup();
+extern long lx_ioctl();
+extern long lx_ioprio_get();
+extern long lx_ioprio_set();
+extern long lx_kill();
+extern long lx_lchown();
+extern long lx_lchown16();
+extern long lx_lgetxattr();
+extern long lx_link();
+extern long lx_linkat();
+extern long lx_listen();
+extern long lx_llistxattr();
+extern long lx_llseek();
+extern long lx_lremovexattr();
+extern long lx_lseek32();
+extern long lx_lseek64();
+extern long lx_lsetxattr();
+extern long lx_lstat32();
+extern long lx_lstat64();
+extern long lx_listxattr();
+extern long lx_mincore();
+extern long lx_mkdir();
+extern long lx_mkdirat();
+extern long lx_modify_ldt();
+extern long lx_mount();
+extern long lx_munmap();
+extern long lx_nanosleep();
+extern long lx_nice();
+extern long lx_oldgetrlimit();
+extern long lx_open();
+extern long lx_openat();
+extern long lx_pause();
+extern long lx_personality();
+extern long lx_pipe();
+extern long lx_pipe2();
+extern long lx_poll();
+extern long lx_ppoll();
+extern long lx_pread();
+extern long lx_pread32();
+extern long lx_preadv();
+extern long lx_preadv32();
+extern long lx_prctl();
+extern long lx_prlimit64();
+extern long lx_pselect();
+extern long lx_ptrace();
+extern long lx_pwrite();
+extern long lx_pwrite32();
+extern long lx_pwritev();
+extern long lx_pwritev32();
+extern long lx_read();
+extern long lx_readlink();
+extern long lx_readlinkat();
+extern long lx_readv();
+extern long lx_reboot();
+extern long lx_recv();
+extern long lx_recvmsg();
+extern long lx_recvfrom();
+extern long lx_rename();
+extern long lx_renameat();
+extern long lx_sched_getparam();
+extern long lx_sched_getscheduler();
+extern long lx_sched_rr_get_interval();
+extern long lx_sched_setparam();
+extern long lx_sched_setscheduler();
+extern long lx_sched_yield();
+extern long lx_select();
+extern long lx_send();
+extern long lx_sendmsg();
+extern long lx_sendto();
+extern long lx_set_robust_list();
+extern long lx_set_thread_area();
+extern long lx_set_tid_address();
+extern long lx_setdomainname();
+extern long lx_setfsuid();
+extern long lx_setfsuid16();
+extern long lx_setfsgid();
+extern long lx_setfsgid16();
+extern long lx_setgid();
+extern long lx_setgid16();
+extern long lx_sethostname();
+extern long lx_setpgid();
+extern long lx_setregid();
+extern long lx_setregid16();
+extern long lx_setresgid();
+extern long lx_setresgid16();
+extern long lx_setresuid();
+extern long lx_setresuid16();
+extern long lx_setreuid();
+extern long lx_setreuid16();
+extern long lx_setrlimit();
+extern long lx_setsid();
+extern long lx_setuid();
+extern long lx_setuid16();
+extern long lx_setxattr();
+extern long lx_setsockopt();
+extern long lx_symlink();
+extern long lx_symlinkat();
+extern long lx_shutdown();
+extern long lx_socket();
+extern long lx_socketcall();
+extern long lx_socketpair();
+extern long lx_stat32();
+extern long lx_stat64();
+extern long lx_stime();
+extern long lx_sync();
+extern long lx_sync_file_range();
+extern long lx_syncfs();
+extern long lx_sysinfo32();
+extern long lx_sysinfo64();
+extern long lx_syslog();
+extern long lx_removexattr();
+extern long lx_tgkill();
+extern long lx_time();
+extern long lx_tkill();
+extern long lx_umask();
+extern long lx_umount();
+extern long lx_umount2();
+extern long lx_uname();
+extern long lx_unlink();
+extern long lx_unlinkat();
+extern long lx_vhangup();
+extern long lx_wait4();
+extern long lx_waitid();
+extern long lx_waitpid();
+extern long lx_write();
+extern long lx_writev();
+
+#if defined(_LP64)
+/*
+ * Linux vsyscall addresses:
+ */
+#define	LX_VSYS_gettimeofday	(uintptr_t)0xffffffffff600000
+#define	LX_VSYS_time		(uintptr_t)0xffffffffff600400
+#define	LX_VSYS_getcpu		(uintptr_t)0xffffffffff600800
+
+#define	LX_VSYSCALL_ADDR		(uintptr_t)0xffffffffff600000
+#define	LX_VSYSCALL_SIZE		(uintptr_t)0x1000
+#endif
+
+#endif	/* _KERNEL */
+
+/*
+ * System call numbers for revectoring:
+ */
+
+#if defined(__amd64)
+#define	LX_SYS_close		3
+#define	LX_SYS_gettimeofday	96
+#define	LX_SYS_mount		165
+#define	LX_SYS_time		201
+#define	LX_SYS_io_setup		206
+#define	LX_SYS_clock_gettime	228
+#define	LX_SYS_getcpu		309
+
+#define	LX_SYS32_close		6
+#define	LX_SYS32_gettimeofday	78
+#define	LX_SYS32_time		13
+#define	LX_SYS32_mount		21
+#define	LX_SYS32_clock_gettime	265
+#define	LX_SYS32_io_setup	245
+#define	LX_SYS32_getcpu		318
+#elif defined(__i386)
+#define	LX_SYS_close		6
+#define	LX_SYS_mount		21
+#define	LX_SYS_gettimeofday	78
+#define	LX_SYS_time		13
+#define	LX_SYS_clock_gettime	265
+#define	LX_SYS_io_setup		245
+#define	LX_SYS_getcpu		318
+#else
+#error "Architecture not supported"
+#endif /* defined(__amd64) */
+
+/*
+ * The current code in the VDSO operates under the expectation that it will be
+ * mapped at a fixed offset from the comm page.  This simplifies the act of
+ * locating said page without any other reference.  The VDSO must fit within
+ * this offset, matching the same value as COMM_PAGE_ALIGN.
+ * See: uts/i86pc/sys/comm_page.h
+ */
+#define	LX_VDSO_SIZE		0x4000
+#define	LX_VDSO_ADDR_MASK	~(LX_VDSO_SIZE - 1)
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_LINUX_SYSCALLS_H */
--- /dev/null	Wed Oct  5 00:32:07 2016
+++ new/usr/src/uts/common/brand/lx/sys/lx_types.h	Wed Oct  5 00:32:07 2016
@@ -0,0 +1,144 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.  All rights reserved.
+ */
+
+#ifndef _SYS_LX_TYPES_H
+#define	_SYS_LX_TYPES_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#ifndef	_KERNEL
+
+#define	SHRT_MIN	(-32768)	/* min value of a "short int" */
+#define	SHRT_MAX	32767		/* max value of a "short int" */
+#define	USHRT_MAX	65535		/* max of "unsigned short int" */
+#define	INT_MIN		(-2147483647-1) /* min value of an "int" */
+#define	INT_MAX		2147483647	/* max value of an "int" */
+#define	UINT_MAX	4294967295U	/* max value of an "unsigned int" */
+
+#ifndef LLONG_MAX
+#define	LLONG_MAX	9223372036854775807LL
+#endif
+
+#if defined(_LP64)
+#define	LONG_MAX	9223372036854775807L
+#define	ULONG_MAX	18446744073709551615UL
+#else
+#define	LONG_MAX	2147483647L	/* max value of a 32-bit "long int" */
+#define	ULONG_MAX	4294967295UL	/* max value of a 32-bit "ulong int" */
+#endif
+
+#endif /* !_KERNEL */
+
+
+typedef	uint64_t	lx_dev_t;
+typedef	uint16_t	lx_dev16_t;
+typedef	uint32_t	lx_ino_t;
+typedef	uint64_t	lx_ino64_t;
+typedef	uint32_t	lx_uid_t;
+typedef	uint16_t	lx_uid16_t;
+typedef	uint32_t	lx_gid_t;
+typedef	uint16_t	lx_gid16_t;
+typedef	uint32_t	lx_off_t;
+typedef	uint64_t	lx_off64_t;
+typedef	uint32_t	lx_blksize_t;
+typedef	uint32_t	lx_blkcnt_t;
+typedef	uint64_t	lx_blkcnt64_t;
+typedef	uint32_t	lx_mode_t;
+typedef	uint16_t	lx_mode16_t;
+
+/*
+ * Linux mangles major/minor numbers into dev_t differently than SunOS.
+ */
+#ifdef _LP64
+#define	LX_MAKEDEVICE(maj, min) \
+	(((min) & 0xff) | (((maj) & 0xfff) << 8) | \
+	((uint64_t)((min) & ~0xff) << 12) | ((uint64_t)((maj) & ~0xfff) << 32))
+
+#define	LX_GETMAJOR(lx_dev)	((((lx_dev) >> 8) & 0xfff) | \
+	((((uint64_t)(lx_dev)) >> 32) & ~0xfff))
+
+#else
+#define	LX_MAKEDEVICE(maj, min) \
+	(((min) & 0xff) | (((maj) & 0xfff) << 8) | (((min) & ~0xff) << 12))
+
+#define	LX_GETMAJOR(lx_dev)	(((lx_dev) >> 8) & 0xfff)
+#endif
+
+#define	LX_GETMINOR(lx_dev)	(((lx_dev) & 0xff) | (((lx_dev) >> 12) & ~0xff))
+/* Linux supports 20 bits for the minor, and 12 bits for the major number */
+#define	LX_MAXMIN	0xfffff
+#define	LX_MAXMAJ	0xfff
+
+/*
+ * Certain Linux tools care deeply about major/minor number mapping.
+ * Map virtual disks (zfs datasets, zvols, etc) into a safe reserved range.
+ */
+#define	LX_MAJOR_DISK	203
+
+/* LX ptm driver major/minor number */
+#define	LX_PTM_MAJOR		5
+#define	LX_PTM_MINOR		2
+
+/* LX pts driver major number range */
+#define	LX_PTS_MAJOR_MIN	136
+#define	LX_PTS_MAJOR_MAX	143
+
+/* LX tty/cons driver major number */
+#define	LX_TTY_MAJOR		5
+
+#define	LX_UID16_TO_UID32(uid16)	\
+	(((uid16) == (lx_uid16_t)-1) ? ((lx_uid_t)-1) : (lx_uid_t)(uid16))
+
+#define	LX_GID16_TO_GID32(gid16)     \
+	(((gid16) == (lx_gid16_t)-1) ? ((lx_gid_t)-1) : (lx_gid_t)(gid16))
+
+/* Overflow values default to NFS nobody. */
+
+#define	UID16_OVERFLOW	((lx_uid16_t)65534)
+#define	GID16_OVERFLOW	((lx_gid16_t)65534)
+
+/*
+ * All IDs with high word non-zero are converted to default overflow values to
+ * avoid inadvertent truncation to zero (root) (!).
+ */
+#define	LX_UID32_TO_UID16(uid32)	\
+	((((uid32) & 0xffff0000) == 0)  ? ((lx_uid16_t)(uid32)) : \
+	    (((uid32) == ((lx_uid_t)-1)) ? ((lx_uid16_t)-1) : UID16_OVERFLOW))
+
+#define	LX_GID32_TO_GID16(gid32)	\
+	((((gid32) & 0xffff0000) == 0)  ? ((lx_gid16_t)(gid32)) : \
+	    (((gid32) == ((lx_gid_t)-1)) ? ((lx_gid16_t)-1) : GID16_OVERFLOW))
+
+#define	LX_32TO64(lo, hi)	\
+	((uint64_t)((uint64_t)(lo) | ((uint64_t)(hi) << 32)))
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_LX_TYPES_H */
--- /dev/null	Wed Oct  5 00:32:07 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_access.c	Wed Oct  5 00:32:07 2016
@@ -0,0 +1,223 @@
+/*
+ * 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 (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T
+ *     All Rights Reserved
+ *
+ * Portions of this source code were derived from Berkeley 4.3 BSD
+ * under license from the Regents of the University of California.
+ *
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#include <sys/param.h>
+#include <sys/isa_defs.h>
+#include <sys/types.h>
+#include <sys/sysmacros.h>
+#include <sys/cred_impl.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/pathname.h>
+#include <sys/vnode.h>
+#include <sys/uio.h>
+#include <sys/cmn_err.h>
+#include <sys/debug.h>
+#include <sys/file.h>
+#include <fs/fs_subr.h>
+#include <c2/audit.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/mode.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_fcntl.h>
+
+/*
+ * Determine accessibility of file.
+ */
+
+#define	E_OK	010	/* use effective ids */
+#define	R_OK	004
+#define	W_OK	002
+#define	X_OK	001
+
+/*
+ * Convert Linux LX_AT_* flags to SunOS AT_* flags but skip verifying allowed
+ * flags have been passed. This also allows EACCESS/REMOVEDIR to be translated
+ * correctly since on linux they have the same value.
+ *
+ * Some code can actually pass in other bits in the flag. We may have to simply
+ * ignore these, as indicated by the enforce parameter.
+ */
+int
+ltos_at_flag(int lflag, int allow, boolean_t enforce)
+{
+	int sflag = 0;
+
+	if ((lflag & LX_AT_EACCESS) && (allow & AT_EACCESS)) {
+		lflag &= ~LX_AT_EACCESS;
+		sflag |= AT_EACCESS;
+	}
+
+	if ((lflag & LX_AT_REMOVEDIR) && (allow & AT_REMOVEDIR)) {
+		lflag &= ~LX_AT_REMOVEDIR;
+		sflag |= AT_REMOVEDIR;
+	}
+
+	if ((lflag & LX_AT_SYMLINK_NOFOLLOW) && (allow & AT_SYMLINK_NOFOLLOW)) {
+		lflag &= ~LX_AT_SYMLINK_NOFOLLOW;
+		sflag |= AT_SYMLINK_NOFOLLOW;
+	}
+
+	/* right now SunOS doesn't have a _FOLLOW flag, so use a fake one */
+	if ((lflag & LX_AT_SYMLINK_FOLLOW) && (allow & LX_AT_SYMLINK_FOLLOW)) {
+		lflag &= ~LX_AT_SYMLINK_FOLLOW;
+		sflag |= LX_AT_SYMLINK_FOLLOW;
+	}
+
+	/* If lflag is not zero than some flags did not hit the above code. */
+	if (enforce && lflag)
+		return (-1);
+
+	return (sflag);
+}
+
+/*
+ * For illumos, access() does this:
+ *    If the process has appropriate privileges, an implementation may indicate
+ *    success for X_OK even if none of the execute file permission bits are set.
+ *
+ * But for Linux, access() does this:
+ *    If the calling process is privileged (i.e., its real UID is zero), then
+ *    an X_OK check is successful for a regular file if execute permission is
+ *    enabled for any of the file owner, group, or other.
+ *
+ * Linux used to behave more like illumos on older kernels:
+ *    In  kernel  2.4 (and earlier) there is some strangeness in the handling
+ *    of X_OK tests for superuser.  If all categories of  execute  permission
+ *    are  disabled for a nondirectory file, then the only access() test that
+ *    returns -1 is when mode is specified as just X_OK; if R_OK or  W_OK  is
+ *    also  specified in mode, then access() returns 0 for such files.
+ *
+ * So we need to handle the case where a privileged process is checking for
+ * X_OK but none of the execute bits are set on the file. We'll keep the old
+ * 2.4 behavior for 2.4 emulation but use the new behavior for any other
+ * kernel rev.
+ */
+static int
+lx_common_access(char *fname, int fmode, vnode_t *startvp)
+{
+	vnode_t *vp;
+	cred_t *tmpcr;
+	int error;
+	int mode;
+	cred_t *cr;
+	int estale_retry = 0;
+
+	if (fmode & ~(E_OK|R_OK|W_OK|X_OK))
+		return (EINVAL);
+
+	mode = ((fmode & (R_OK|W_OK|X_OK)) << 6);
+
+	cr = CRED();
+
+	/* OK to use effective uid/gid, i.e., no need to crdup(CRED())? */
+	if ((fmode & E_OK) != 0 ||
+	    (cr->cr_uid == cr->cr_ruid && cr->cr_gid == cr->cr_rgid)) {
+		tmpcr = cr;
+		crhold(tmpcr);
+	} else {
+		tmpcr = crdup(cr);
+		tmpcr->cr_uid = cr->cr_ruid;
+		tmpcr->cr_gid = cr->cr_rgid;
+		tmpcr->cr_ruid = cr->cr_uid;
+		tmpcr->cr_rgid = cr->cr_gid;
+	}
+
+lookup:
+	if ((error = lookupnameatcred(fname, UIO_USERSPACE, FOLLOW, NULLVPP,
+	    &vp, startvp, tmpcr)) != 0) {
+		if ((error == ESTALE) && fs_need_estale_retry(estale_retry++))
+			goto lookup;
+		crfree(tmpcr);
+		return (error);
+	}
+
+	if (mode != 0) {
+		error = VOP_ACCESS(vp, mode, 0, tmpcr, NULL);
+		if (error != 0) {
+			if ((error == ESTALE) &&
+			    fs_need_estale_retry(estale_retry++)) {
+				VN_RELE(vp);
+				goto lookup;
+			}
+
+		} else if ((fmode & X_OK) != 0 && cr->cr_ruid == 0 &&
+		    lx_kern_release_cmp(curproc->p_zone, "2.4.0") > 0) {
+			/* check for incorrect execute success */
+			vattr_t va;
+
+			va.va_mask = AT_MODE;
+			if ((error = VOP_GETATTR(vp, &va, 0, cr, NULL)) == 0) {
+				mode_t m = VTTOIF(va.va_type) | va.va_mode;
+
+				if ((m & S_IFMT) == S_IFREG &&
+				    !(m & (S_IXUSR | S_IXGRP | S_IXOTH))) {
+					/* no execute bits set in the mode */
+					error = EACCES;
+				}
+			}
+		}
+	}
+
+	crfree(tmpcr);
+	VN_RELE(vp);
+	return (error);
+}
+
+int
+lx_faccessat(int atfd, char *fname, int fmode, int flag)
+{
+	vnode_t *startvp;
+	int error;
+
+	if (atfd == LX_AT_FDCWD)
+		atfd = AT_FDCWD;
+
+	if ((flag = ltos_at_flag(flag, AT_EACCESS, B_FALSE)) < 0)
+		return (set_errno(EINVAL));
+
+	if (fname == NULL)
+		return (set_errno(EFAULT));
+	if ((error = fgetstartvp(atfd, fname, &startvp)) != 0)
+		return (set_errno(error));
+	if (AU_AUDITING() && startvp != NULL)
+		audit_setfsat_path(1);
+
+	/* Do not allow E_OK unless AT_EACCESS flag is set */
+	if ((flag & AT_EACCESS) == 0)
+		fmode &= ~E_OK;
+
+	error = lx_common_access(fname, fmode, startvp);
+	if (startvp != NULL)
+		VN_RELE(startvp);
+	if (error)
+		return (set_errno(error));
+	return (0);
+}
+
+int
+lx_access(char *fname, int fmode)
+{
+	return (lx_faccessat(LX_AT_FDCWD, fname, fmode, 0));
+}
--- /dev/null	Wed Oct  5 00:32:07 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_aio.c	Wed Oct  5 00:32:07 2016
@@ -0,0 +1,45 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+#include <sys/systm.h>
+#include <sys/mutex.h>
+#include <sys/brand.h>
+
+#include <sys/lx_brand.h>
+#include <sys/lx_syscalls.h>
+
+
+long
+lx_io_setup(unsigned int nr_events, void **ctxp)
+{
+	lx_proc_data_t *lxpd = ptolxproc(curproc);
+	uintptr_t uargs[2] = {(uintptr_t)nr_events, (uintptr_t)ctxp};
+
+	mutex_enter(&curproc->p_lock);
+	lxpd->l_flags |= LX_PROC_AIO_USED;
+	mutex_exit(&curproc->p_lock);
+
+	ttolxlwp(curthread)->br_eosys = JUSTRETURN;
+#if defined(_LP64)
+	if (get_udatamodel() != DATAMODEL_NATIVE) {
+		lx_emulate_user32(ttolwp(curthread), LX_SYS32_io_setup, uargs);
+	} else
+#endif
+	{
+		lx_emulate_user(ttolwp(curthread), LX_SYS_io_setup, uargs);
+	}
+	/* NOTREACHED */
+	return (0);
+}
--- /dev/null	Wed Oct  5 00:32:07 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_brk.c	Wed Oct  5 00:32:07 2016
@@ -0,0 +1,57 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+
+/*
+ * The brk() system call needs to be in-kernel because Linux expects a call to
+ * brk(0) to return the current breakpoint.  In Solaris, the process breakpoint
+ * is setup and managed by libc.  Due to the way we link our libraries and the
+ * need for Linux to manage its own breakpoint, this has to remain in the
+ * kernel.
+ */
+extern int brk(caddr_t);
+
+long
+lx_brk(caddr_t nva)
+{
+	proc_t *p = curproc;
+	klwp_t *lwp = ttolwp(curthread);
+
+	if (nva != 0) {
+		(void) brk(nva);
+
+		/*
+		 * Despite claims to the contrary in the manpage, when Linux
+		 * brk() fails, errno is left unchanged.
+		 */
+		lwp->lwp_errno = 0;
+	}
+	return ((long)(p->p_brkbase + p->p_brksize));
+}
--- /dev/null	Wed Oct  5 00:32:07 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_chmod.c	Wed Oct  5 00:32:07 2016
@@ -0,0 +1,107 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+#include <sys/systm.h>
+#include <sys/fcntl.h>
+#include <sys/thread.h>
+#include <sys/klwp.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_fcntl.h>
+
+long
+lx_vn_chmod(vnode_t *vp, int mode)
+{
+	vattr_t vattr;
+
+	vattr.va_mode = mode & MODEMASK;
+	vattr.va_mask = AT_MODE;
+
+	if (vn_is_readonly(vp)) {
+		return (EROFS);
+	}
+	return (VOP_SETATTR(vp, &vattr, 0, CRED(), NULL));
+}
+
+static long
+lx_fchmodat_wrapper(int fd, char *path, int mode)
+{
+	long error;
+	vnode_t *vp;
+
+	if ((error = lx_vp_at(fd, path, &vp, 0)) != 0) {
+		lx_proc_data_t *pd = ttolxproc(curthread);
+
+		/*
+		 * If the process is in "install mode", return success
+		 * if the operation failed due to an absent file.
+		 */
+		if (error == ENOENT &&
+		    (pd->l_flags & LX_PROC_INSTALL_MODE)) {
+			return (0);
+		}
+		return (set_errno(error));
+	}
+
+	error = lx_vn_chmod(vp, mode);
+	VN_RELE(vp);
+
+	if (error != 0) {
+		return (set_errno(error));
+	}
+	return (0);
+}
+
+long
+lx_fchmodat(int fd, char *path, int mode)
+{
+	return (lx_fchmodat_wrapper(fd, path, mode));
+}
+
+long
+lx_fchmod(int fd, int mode)
+{
+	file_t *fp;
+	vnode_t *vp;
+	long error;
+
+	/*
+	 * In order to do proper O_PATH handling, lx_fchmod cannot leverage
+	 * lx_fchmodat with a NULL path since the desired behavior differs.
+	 */
+	if ((fp = getf(fd)) == NULL) {
+		return (set_errno(EBADF));
+	}
+	if (LX_IS_O_PATH(fp)) {
+		releasef(fd);
+		return (set_errno(EBADF));
+	}
+	vp = fp->f_vnode;
+	VN_HOLD(vp);
+	releasef(fd);
+
+	error = lx_vn_chmod(vp, mode);
+	VN_RELE(vp);
+
+	if (error != 0) {
+		return (set_errno(error));
+	}
+	return (0);
+}
+
+long
+lx_chmod(char *path, int mode)
+{
+	return (lx_fchmodat_wrapper(LX_AT_FDCWD, path, mode));
+}
--- /dev/null	Wed Oct  5 00:32:08 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_chown.c	Wed Oct  5 00:32:08 2016
@@ -0,0 +1,180 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+#include <sys/systm.h>
+#include <sys/fcntl.h>
+#include <sys/zone.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_fcntl.h>
+#include <sys/lx_types.h>
+
+long
+lx_vn_chown(vnode_t *vp, uid_t uid, gid_t gid)
+{
+	vattr_t vattr;
+	zone_t *zone = crgetzone(CRED());
+
+	if ((uid != (uid_t)-1 && !VALID_UID(uid, zone)) ||
+	    (gid != (gid_t)-1 && !VALID_GID(gid, zone))) {
+		return (EINVAL);
+	}
+	vattr.va_uid = uid;
+	vattr.va_gid = gid;
+	vattr.va_mask = 0;
+	if (vattr.va_uid != -1)
+		vattr.va_mask |= AT_UID;
+	if (vattr.va_gid != -1)
+		vattr.va_mask |= AT_GID;
+
+	if (vn_is_readonly(vp)) {
+		return (EROFS);
+	}
+	return (VOP_SETATTR(vp, &vattr, 0, CRED(), NULL));
+}
+
+long
+lx_fchownat_wrapper(int fd, char *path, uid_t uid, gid_t gid, int native_flag)
+{
+	long error;
+	vnode_t *vp;
+
+	if ((error = lx_vp_at(fd, path, &vp, native_flag)) != 0) {
+		lx_proc_data_t *pd = ttolxproc(curthread);
+
+		/*
+		 * If the process is in "install mode", return success
+		 * if the operation failed due to an absent file.
+		 */
+		if (error == ENOENT &&
+		    (pd->l_flags & LX_PROC_INSTALL_MODE)) {
+			return (0);
+		}
+		return (set_errno(error));
+	}
+
+	error = lx_vn_chown(vp, uid, gid);
+	VN_RELE(vp);
+
+	if (error != 0) {
+		return (set_errno(error));
+	}
+	return (0);
+}
+
+long
+lx_fchown_wrapper(int fd, uid_t uid, gid_t gid)
+{
+	file_t *fp;
+	vnode_t *vp;
+	long error;
+
+	/*
+	 * In order to do proper O_PATH handling, lx_fchown cannot leverage
+	 * lx_fchownat with a NULL path since the desired behavior differs.
+	 */
+	if ((fp = getf(fd)) == NULL) {
+		return (set_errno(EBADF));
+	}
+	if (LX_IS_O_PATH(fp)) {
+		releasef(fd);
+		return (set_errno(EBADF));
+	}
+	vp = fp->f_vnode;
+	VN_HOLD(vp);
+	releasef(fd);
+
+	error = lx_vn_chown(vp, uid, gid);
+	VN_RELE(vp);
+
+	if (error != 0) {
+		return (set_errno(error));
+	}
+	return (0);
+}
+
+long
+lx_fchownat(int fd, char *path, uid_t uid, gid_t gid, int flag)
+{
+	int native_flag = 0;
+
+	if (flag & LX_AT_EMPTY_PATH) {
+		char c;
+
+		/*
+		 * According to fchownat(2), when AT_EMPTY_PATH is set: "if
+		 * path is an empty string, operate on the file referred to by
+		 * fd".  We pass NULL in place of the empty string, which
+		 * causes fchownat() to operate on the fd we passed without an
+		 * additional lookup.
+		 */
+		if (copyin(path, &c, sizeof (c)) != 0) {
+			return (set_errno(EFAULT));
+		}
+		if (c == '\0') {
+			path = NULL;
+		}
+
+		flag &= ~LX_AT_EMPTY_PATH;
+	}
+	if (flag & LX_AT_SYMLINK_NOFOLLOW) {
+		flag &= ~LX_AT_SYMLINK_NOFOLLOW;
+		native_flag |= AT_SYMLINK_NOFOLLOW;
+	}
+	if (flag != 0) {
+		return (set_errno(EINVAL));
+	}
+
+	return (lx_fchownat_wrapper(fd, path, uid, gid, native_flag));
+}
+
+long
+lx_fchown(int fd, uid_t uid, gid_t gid)
+{
+	return (lx_fchown_wrapper(fd, uid, gid));
+}
+
+long
+lx_lchown(char *path, uid_t uid, gid_t gid)
+{
+	return (lx_fchownat_wrapper(AT_FDCWD, path, uid, gid,
+	    AT_SYMLINK_NOFOLLOW));
+}
+
+long
+lx_chown(char *path, uid_t uid, gid_t gid)
+{
+	return (lx_fchownat_wrapper(AT_FDCWD, path, uid, gid, 0));
+}
+
+long
+lx_fchown16(int fd, lx_uid16_t uid, lx_gid16_t gid)
+{
+	return (lx_fchown_wrapper(fd, LX_UID16_TO_UID32(uid),
+	    LX_GID16_TO_GID32(gid)));
+}
+
+long
+lx_lchown16(char *path, uid_t uid, gid_t gid)
+{
+	return (lx_fchownat_wrapper(AT_FDCWD, path, LX_UID16_TO_UID32(uid),
+	    LX_GID16_TO_GID32(gid), AT_SYMLINK_NOFOLLOW));
+}
+
+long
+lx_chown16(char *path, lx_uid16_t uid, lx_gid16_t gid)
+{
+	return (lx_fchownat_wrapper(AT_FDCWD, path, LX_UID16_TO_UID32(uid),
+	    LX_GID16_TO_GID32(gid), 0));
+}
--- /dev/null	Wed Oct  5 00:32:08 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_clone.c	Wed Oct  5 00:32:08 2016
@@ -0,0 +1,143 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_ldt.h>
+#include <sys/lx_misc.h>
+#include <lx_signum.h>
+#include <lx_syscall.h>
+#include <sys/x86_archext.h>
+#include <sys/controlregs.h>
+
+/*
+ * Our lwp has already been created at this point, so this routine is
+ * responsible for setting up all the state needed to track this as a
+ * linux cloned thread.
+ */
+/* ARGSUSED */
+int
+lx_helper_clone(int64_t *rval, int flags, void *ptidp, void *tls, void *ctidp)
+{
+	struct lx_lwp_data *lwpd = ttolxlwp(curthread);
+	struct lx_proc_data *lproc = ttolxproc(curthread);
+	struct ldt_info info;
+	struct user_desc descr;
+	int tls_index;
+	int entry = -1;
+	int signo;
+
+	signo = flags & LX_CSIGNAL;
+	if (signo < 0 || signo > LX_NSIG)
+		return (set_errno(EINVAL));
+
+	if (!(flags & LX_CLONE_THREAD)) {
+		lproc->l_signal = signo;
+	} else {
+		if (flags & LX_CLONE_SETTLS) {
+			if (get_udatamodel() == DATAMODEL_ILP32) {
+				if (copyin((caddr_t)tls, &info, sizeof (info)))
+					return (set_errno(EFAULT));
+
+				if (LDT_INFO_EMPTY(&info))
+					return (set_errno(EINVAL));
+
+				entry = info.entry_number;
+				if (entry < GDT_TLSMIN || entry > GDT_TLSMAX)
+					return (set_errno(EINVAL));
+
+				tls_index = entry - GDT_TLSMIN;
+
+				/*
+				 * Convert the user-space structure into a real
+				 * x86 descriptor and copy it into this LWP's
+				 * TLS array.  We also load it into the GDT.
+				 */
+				LDT_INFO_TO_DESC(&info, &descr);
+				bcopy(&descr, &lwpd->br_tls[tls_index],
+				    sizeof (descr));
+				lx_set_gdt(entry, &lwpd->br_tls[tls_index]);
+			} else {
+				/*
+				 * Set the Linux %fsbase for this LWP.  We will
+				 * restore it the next time we return to Linux
+				 * via setcontext()/lx_restorecontext().
+				 */
+				lwpd->br_lx_fsbase = (uintptr_t)tls;
+			}
+		}
+
+		lwpd->br_clear_ctidp =
+		    (flags & LX_CLONE_CHILD_CLEARTID) ?  ctidp : NULL;
+
+		if (signo && ! (flags & LX_CLONE_DETACH))
+			lwpd->br_signal = signo;
+		else
+			lwpd->br_signal = 0;
+
+		if (flags & LX_CLONE_THREAD)
+			lwpd->br_tgid = curthread->t_procp->p_pid;
+
+		if (flags & LX_CLONE_PARENT)
+			lwpd->br_ppid = 0;
+
+		if ((flags & LX_CLONE_CHILD_SETTID) && (ctidp != NULL) &&
+		    (suword32(ctidp, lwpd->br_pid) != 0)) {
+			if (entry >= 0)
+				lx_clear_gdt(entry);
+			return (set_errno(EFAULT));
+		}
+		if ((flags & LX_CLONE_PARENT_SETTID) && (ptidp != NULL) &&
+		    (suword32(ptidp, lwpd->br_pid) != 0)) {
+			if (entry >= 0)
+				lx_clear_gdt(entry);
+			return (set_errno(EFAULT));
+		}
+	}
+
+	*rval = lwpd->br_pid;
+	return (0);
+}
+
+long
+lx_set_tid_address(int *tidp)
+{
+	struct lx_lwp_data *lwpd = ttolxlwp(curthread);
+	long rv;
+
+	lwpd->br_clear_ctidp = tidp;
+
+	if (curproc->p_pid == curproc->p_zone->zone_proc_initpid) {
+		rv = 1;
+	} else {
+		rv = lwpd->br_pid;
+	}
+
+	return (rv);
+}
--- /dev/null	Wed Oct  5 00:32:08 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_close.c	Wed Oct  5 00:32:08 2016
@@ -0,0 +1,57 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+#include <sys/systm.h>
+#include <sys/mutex.h>
+#include <sys/brand.h>
+
+#include <sys/lx_brand.h>
+#include <sys/lx_syscalls.h>
+
+
+extern int close(int);
+
+long
+lx_close(int fdes)
+{
+	lx_proc_data_t *lxpd = ptolxproc(curproc);
+	boolean_t aio_used;
+	uintptr_t uargs[1] = {(uintptr_t)fdes};
+
+	mutex_enter(&curproc->p_lock);
+	aio_used = ((lxpd->l_flags & LX_PROC_AIO_USED) != 0);
+	mutex_exit(&curproc->p_lock);
+
+	if (!aio_used) {
+		return (close(fdes));
+	}
+
+	/*
+	 * If the process potentially has any AIO contexts open, the userspace
+	 * emulation must be used so that libc can properly maintain its state.
+	 */
+
+	ttolxlwp(curthread)->br_eosys = JUSTRETURN;
+#if defined(_LP64)
+	if (get_udatamodel() != DATAMODEL_NATIVE) {
+		lx_emulate_user32(ttolwp(curthread), LX_SYS32_close, uargs);
+	} else
+#endif
+	{
+		lx_emulate_user(ttolwp(curthread), LX_SYS_close, uargs);
+	}
+	/* NOTREACHED */
+	return (0);
+}
--- /dev/null	Wed Oct  5 00:32:08 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_cpu.c	Wed Oct  5 00:32:08 2016
@@ -0,0 +1,36 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#include <sys/systm.h>
+#include <sys/thread.h>
+#include <sys/cpuvar.h>
+#include <sys/cmn_err.h>
+#include <sys/lx_impl.h>
+
+/*
+ * We support neither the second argument (NUMA node), nor the third (obsolete
+ * pre-2.6.24 caching functionality which was ultimately broken).
+ */
+/* ARGSUSED1 */
+long
+lx_getcpu(unsigned int *cpu, uintptr_t p2, uintptr_t p3)
+{
+	unsigned int curcpu = curthread->t_cpu->cpu_id;
+
+	if (copyout(&curcpu, cpu, sizeof (curcpu)) != 0)
+		return (set_errno(EFAULT));
+
+	return (0);
+}
--- /dev/null	Wed Oct  5 00:32:08 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_dup.c	Wed Oct  5 00:32:08 2016
@@ -0,0 +1,53 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/filio.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/lx_fcntl.h>
+#include <sys/lx_misc.h>
+
+/* From usr/src/uts/common/syscall/fcntl.c */
+extern int fcntl(int, int, intptr_t);
+
+long
+lx_dup(int fd)
+{
+	return (fcntl(fd, F_DUPFD, 0));
+}
+
+long
+lx_dup2(int oldfd, int newfd)
+{
+	return (fcntl(oldfd, F_DUP2FD, newfd));
+}
+
+long
+lx_dup3(int oldfd, int newfd, int flags)
+{
+	int rc;
+
+	/* The only valid flag is O_CLOEXEC. */
+	if (flags & ~LX_O_CLOEXEC)
+		return (set_errno(EINVAL));
+
+	/* Only DUP2FD_CLOEXEC returns EINVAL on the same fd's */
+	if (oldfd == newfd)
+		return (set_errno(EINVAL));
+
+	rc = fcntl(oldfd, (flags == 0) ? F_DUP2FD : F_DUP2FD_CLOEXEC, newfd);
+	return (rc);
+}
--- /dev/null	Wed Oct  5 00:32:08 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_epoll.c	Wed Oct  5 00:32:08 2016
@@ -0,0 +1,279 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/thread.h>
+#include <sys/proc.h>
+#include <sys/zone.h>
+#include <sys/brand.h>
+#include <sys/epoll.h>
+#include <sys/devpoll.h>
+#include <sys/fcntl.h>
+#include <sys/file.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/sunldi.h>
+#include <sys/vnode.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_types.h>
+#include <sys/lx_signal.h>
+
+static major_t devpoll_major = 0;
+
+static boolean_t
+lx_epoll_isvalid(file_t *fp)
+{
+	vnode_t *vp = fp->f_vnode;
+
+	if (vp->v_type == VCHR && getmajor(vp->v_rdev) == devpoll_major)
+		return (B_TRUE);
+	return (B_FALSE);
+}
+
+long
+lx_epoll_create1(int flags)
+{
+	int err, fd, rv;
+	int fmode = FREAD | FWRITE;
+	boolean_t cloexec = B_FALSE;
+	vnode_t *vp = NULL;
+	file_t *fp = NULL;
+
+	if (flags & EPOLL_CLOEXEC) {
+		cloexec = B_TRUE;
+		flags &= ~EPOLL_CLOEXEC;
+	}
+	if (flags != 0) {
+		/* No other flags accepted at this time */
+		return (set_errno(EINVAL));
+	}
+
+	if (falloc((vnode_t *)NULL, fmode, &fp, &fd) != 0) {
+		err = EMFILE;
+		goto error;
+	}
+	if (ldi_vp_from_name("/devices/pseudo/poll@0:poll", &vp) != 0) {
+		err = ENOENT;
+		goto error;
+	}
+	if ((err = VOP_OPEN(&vp, fmode | FKLYR, CRED(), NULL)) != 0) {
+		goto error;
+	}
+	err = VOP_IOCTL(vp, DP_EPOLLCOMPAT, 0, fmode, CRED(), &rv, NULL);
+	if (err != 0) {
+		(void) VOP_CLOSE(vp, fmode, 0, 0, CRED(), NULL);
+		goto error;
+	}
+
+	devpoll_major = getmajor(vp->v_rdev);
+
+	fp->f_vnode = vp;
+	mutex_exit(&fp->f_tlock);
+	setf(fd, fp);
+	if (cloexec) {
+		f_setfd(fd, FD_CLOEXEC);
+	}
+	return (fd);
+
+error:
+	if (fp != NULL) {
+		setf(fd, NULL);
+		unfalloc(fp);
+	}
+	if (vp != NULL) {
+		VN_RELE(vp);
+	}
+	return (set_errno(err));
+}
+
+long
+lx_epoll_create(int size)
+{
+	if (size <= 0) {
+		return (set_errno(EINVAL));
+	}
+
+	return (lx_epoll_create1(0));
+}
+
+
+/* Match values from libc implementation */
+#define	EPOLLIGNORED 	(EPOLLMSG | EPOLLWAKEUP)
+#define	EPOLLSWIZZLED	\
+	(EPOLLRDHUP | EPOLLONESHOT | EPOLLET | EPOLLWRBAND | EPOLLWRNORM)
+
+long
+lx_epoll_ctl(int fd, int op, int pfd, void *event)
+{
+	epoll_event_t epevent;
+	dvpoll_epollfd_t dpevent[2];
+	file_t *fp;
+	iovec_t aiov;
+	uio_t auio;
+	uint32_t events, ev = 0;
+	int error = 0, i = 0;
+
+	dpevent[i].dpep_pollfd.fd = pfd;
+	switch (op) {
+	case EPOLL_CTL_DEL:
+		dpevent[i].dpep_pollfd.events = POLLREMOVE;
+		break;
+
+	case EPOLL_CTL_MOD:
+		/*
+		 * In the modify case, we pass down two events:  one to
+		 * remove the event and another to add it back.
+		 */
+		dpevent[i++].dpep_pollfd.events = POLLREMOVE;
+		dpevent[i].dpep_pollfd.fd = pfd;
+		/* FALLTHROUGH */
+
+	case EPOLL_CTL_ADD:
+		if (copyin(event, &epevent, sizeof (epevent)) != 0)
+			return (set_errno(EFAULT));
+
+		/*
+		 * Mask off the events that we ignore, and then swizzle the
+		 * events for which our values differ from their epoll(7)
+		 * equivalents.
+		 */
+		events = epevent.events;
+		ev = events & ~(EPOLLIGNORED | EPOLLSWIZZLED);
+
+		if (events & EPOLLRDHUP)
+			ev |= POLLRDHUP;
+		if (events & EPOLLET)
+			ev |= POLLET;
+		if (events & EPOLLONESHOT)
+			ev |= POLLONESHOT;
+		if (events & EPOLLWRNORM)
+			ev |= POLLWRNORM;
+		if (events & EPOLLWRBAND)
+			ev |= POLLWRBAND;
+
+		dpevent[i].dpep_data = epevent.data.u64;
+		dpevent[i].dpep_pollfd.events = ev;
+		break;
+
+	default:
+		return (set_errno(EINVAL));
+	}
+
+	if ((fp = getf(fd)) == NULL) {
+		return (set_errno(EBADF));
+	} else if (!lx_epoll_isvalid(fp)) {
+		releasef(fd);
+		return (set_errno(EINVAL));
+	}
+
+	aiov.iov_base = (void *)dpevent;
+	aiov.iov_len = sizeof (dvpoll_epollfd_t) * (i + 1);
+	auio.uio_iov = &aiov;
+	auio.uio_iovcnt = 1;
+	auio.uio_resid = aiov.iov_len;
+	auio.uio_segflg = UIO_SYSSPACE;
+	auio.uio_loffset = 0;
+	auio.uio_fmode = fp->f_flag;
+
+	error = VOP_WRITE(fp->f_vnode, &auio, 1, fp->f_cred, NULL);
+
+	releasef(fd);
+	if (error == ELOOP) {
+		/*
+		 * In the case of descriptor loops, /dev/poll emits a more
+		 * descriptive error than Linux epoll consumers would expect.
+		 */
+		return (set_errno(EINVAL));
+	} else if (error != 0) {
+		return (set_errno(error));
+	}
+	return (0);
+}
+
+long
+lx_epoll_wait(int fd, void *events, int maxevents, int timeout)
+{
+	struct dvpoll arg;
+	file_t *fp;
+	int rv = 0, error, flag;
+
+	if (maxevents <= 0) {
+		return (set_errno(EINVAL));
+	}
+	if ((fp = getf(fd)) == NULL) {
+		return (set_errno(EBADF));
+	} else if (!lx_epoll_isvalid(fp)) {
+		releasef(fd);
+		return (set_errno(EINVAL));
+	}
+
+	arg.dp_nfds = maxevents;
+	arg.dp_timeout = timeout;
+	arg.dp_fds = (pollfd_t *)events;
+	flag = fp->f_flag | DATAMODEL_NATIVE | FKIOCTL;
+	error = VOP_IOCTL(fp->f_vnode, DP_POLL, (uintptr_t)&arg, flag,
+	    fp->f_cred, &rv, NULL);
+
+	releasef(fd);
+	if (error != 0) {
+		return (set_errno(error));
+	}
+	return (rv);
+}
+
+long
+lx_epoll_pwait(int fd, void *events, int maxevents, int timeout, void *sigmask)
+{
+	struct dvpoll arg;
+	file_t *fp;
+	int rv = 0, error, flag;
+	k_sigset_t ksig;
+
+	if (maxevents <= 0) {
+		return (set_errno(EINVAL));
+	}
+	if ((fp = getf(fd)) == NULL) {
+		return (set_errno(EBADF));
+	} else if (!lx_epoll_isvalid(fp)) {
+		releasef(fd);
+		return (set_errno(EINVAL));
+	}
+	if (sigmask != NULL) {
+		lx_sigset_t lsig;
+
+		if (copyin(sigmask, &lsig, sizeof (lsig)) != 0) {
+			releasef(fd);
+			return (set_errno(EFAULT));
+		}
+		lx_ltos_sigset(&lsig, &ksig);
+		arg.dp_setp = (sigset_t *)&ksig;
+	} else {
+		arg.dp_setp = NULL;
+	}
+
+	arg.dp_nfds = maxevents;
+	arg.dp_timeout = timeout;
+	arg.dp_fds = (pollfd_t *)events;
+	flag = fp->f_flag | DATAMODEL_NATIVE | FKIOCTL;
+	error = VOP_IOCTL(fp->f_vnode, DP_PPOLL, (uintptr_t)&arg, flag,
+	    fp->f_cred, &rv, NULL);
+
+	releasef(fd);
+	if (error != 0) {
+		return (set_errno(error));
+	}
+	return (rv);
+}
--- /dev/null	Wed Oct  5 00:32:08 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_fadvise.c	Wed Oct  5 00:32:08 2016
@@ -0,0 +1,103 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#include <sys/fcntl.h>
+#include <sys/lx_misc.h>
+
+/*
+ * Based on illumos posix_fadvise which does nothing. The only difference is
+ * that on Linux an fd refering to a pipe or FIFO returns EINVAL. The Linux
+ * POSIX_FADV_* values are the same as the illumos values. See how the 32-bit
+ * glibc calls fadvise64; the offeset is a 64-bit value, but the length is not.
+ * fadvise64_64 passes both the offset and length as 64-bit values. The 64-bit
+ * fadvise64 caller always passes 64-bit values for the offset and length.
+ */
+
+/*
+ * This is the fadvise64 function used by 64-bit callers, and by 32-bit callers
+ * after they have adjusted their arguments.
+ */
+/* ARGSUSED */
+int
+lx_fadvise64(int fd, off64_t offset, off64_t len, int advice)
+{
+	file_t *fp;
+	boolean_t is_fifo;
+
+	switch (advice) {
+	case POSIX_FADV_NORMAL:
+	case POSIX_FADV_RANDOM:
+	case POSIX_FADV_SEQUENTIAL:
+	case POSIX_FADV_WILLNEED:
+	case POSIX_FADV_DONTNEED:
+	case POSIX_FADV_NOREUSE:
+		break;
+	default:
+		return (set_errno(EINVAL));
+	}
+
+	if (len < 0)
+		return (set_errno(EINVAL));
+
+	if ((fp = getf(fd)) == NULL)
+		return (set_errno(EBADF));
+	is_fifo = (fp->f_vnode->v_type == VFIFO);
+	releasef(fd);
+
+	if (is_fifo)
+		return (set_errno(ESPIPE));
+
+	return (0);
+}
+
+/*
+ * This is the fadvise64 function used by 32-bit callers. Linux passes the
+ * 64-bit offset by concatenating consecutive arguments. We must perform the
+ * same conversion here.
+ */
+long
+lx_fadvise64_32(int fd, uint32_t off_lo, uint32_t off_hi, int32_t len,
+    int advice)
+{
+	off64_t offset;
+
+	offset = off_hi;
+	offset = offset << 32;
+	offset |= off_lo;
+
+	return (lx_fadvise64(fd, offset, (off64_t)len, advice));
+}
+
+/*
+ * This function is only used by 32-bit callers. Linux passes the 64-bit offset
+ * and length by concatenating consecutive arguments. We must perform the same
+ * conversion here.
+ */
+long
+lx_fadvise64_64(int fd, uint32_t off_lo, uint32_t off_hi, uint32_t len_lo,
+    uint32_t len_hi, int advice)
+{
+	off64_t offset;
+	off64_t len;
+
+	offset = off_hi;
+	offset = offset << 32;
+	offset |= off_lo;
+	len = len_hi;
+	len = len << 32;
+	len |= len_lo;
+
+	return (lx_fadvise64(fd, offset, len, advice));
+}
--- /dev/null	Wed Oct  5 00:32:08 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_fallocate.c	Wed Oct  5 00:32:08 2016
@@ -0,0 +1,251 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+#include <sys/systm.h>
+#include <sys/zone.h>
+#include <sys/types.h>
+#include <sys/filio.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/nbmlock.h>
+#include <sys/lx_impl.h>
+#include <sys/lx_brand.h>
+#include <sys/sdt.h>
+
+extern int flock_check(vnode_t *, flock64_t *, offset_t, offset_t);
+
+#define	LX_FALLOC_FL_KEEP_SIZE		0x01
+#define	LX_FALLOC_FL_PUNCH_HOLE		0x02
+#define	LX_FALLOC_FL_NO_HIDE_STALE	0x04
+#define	LX_FALLOC_FL_COLLAPSE_RANGE	0x08
+#define	LX_FALLOC_FL_ZERO_RANGE		0x10
+
+#define	LX_FALLOC_VALID	(LX_FALLOC_FL_KEEP_SIZE | LX_FALLOC_FL_PUNCH_HOLE | \
+	LX_FALLOC_FL_NO_HIDE_STALE | LX_FALLOC_FL_COLLAPSE_RANGE | \
+	LX_FALLOC_FL_ZERO_RANGE)
+
+#define	LX_FALLOC_UNSUPP	(LX_FALLOC_FL_NO_HIDE_STALE | \
+	LX_FALLOC_FL_COLLAPSE_RANGE)
+
+long
+lx_fallocate(int fd, int mode, off_t offset, off_t len)
+{
+	int error = 0;
+	file_t *fp;
+	vnode_t *vp;
+	int64_t tot;
+	struct flock64 bf;
+	vattr_t vattr;
+	u_offset_t f_offset;
+	boolean_t in_crit = B_FALSE;
+
+	/*
+	 * Error checking is in a specific order to make LTP happy.
+	 */
+
+	tot = offset + len;
+	if (tot > (LLONG_MAX / (int64_t)1024))
+		return (set_errno(EFBIG));
+
+	if (mode & LX_FALLOC_UNSUPP)
+		return (set_errno(EOPNOTSUPP));
+
+	if ((fp = getf(fd)) == NULL)
+		return (set_errno(EBADF));
+
+	if ((fp->f_flag & FWRITE) == 0) {
+		error = EBADF;
+		goto done;
+	}
+
+	vp = fp->f_vnode;
+	if (vp->v_type != VREG) {
+		error = EINVAL;
+		goto done;
+	}
+
+	if (offset < 0 || len <= 0) {
+		error = EINVAL;
+		goto done;
+	}
+
+	if (tot < 0LL) {
+		error = EFBIG;
+		goto done;
+	}
+
+	if ((mode & ~LX_FALLOC_VALID) != 0) {
+		error = EINVAL;
+		goto done;
+	}
+
+	/*
+	 * If this is the only flag then we don't actually do any work.
+	 */
+	if (mode == LX_FALLOC_FL_KEEP_SIZE)
+		goto done;
+
+	bzero(&bf, sizeof (bf));
+
+	vattr.va_mask = AT_SIZE;
+	if ((error = VOP_GETATTR(vp, &vattr, 0, CRED(), NULL)) != 0)
+		goto done;
+
+	if (mode == 0) {
+		/* Nothing to do if not extending the file */
+		if (vattr.va_size >= tot)
+			goto done;
+
+		/* Extend the file. */
+		bf.l_start = (off64_t)tot;
+		bf.l_len = (off64_t)0;
+
+	} else if (mode & LX_FALLOC_FL_PUNCH_HOLE) {
+		/*
+		 * Deallocate space in the file.
+		 */
+		if ((mode & LX_FALLOC_FL_KEEP_SIZE) == 0) {
+			/* this flag is required with punch hole */
+			error = EINVAL;
+			goto done;
+		}
+
+		if (mode &
+		    ~(LX_FALLOC_FL_PUNCH_HOLE | LX_FALLOC_FL_KEEP_SIZE)) {
+			error = EINVAL;
+			goto done;
+		}
+
+		/* Make sure we don't extend since keep_size is set. */
+		if (vattr.va_size < tot) {
+			if (offset > vattr.va_size)
+				goto done;
+			len = (off_t)vattr.va_size - offset;
+		}
+
+		bf.l_start = (off64_t)offset;
+		bf.l_len = (off64_t)len;
+
+	} else if (mode & LX_FALLOC_FL_ZERO_RANGE) {
+		/*
+		 * Zero out the space in the file.
+		 */
+		if (mode &
+		    ~(LX_FALLOC_FL_ZERO_RANGE | LX_FALLOC_FL_KEEP_SIZE)) {
+			error = EINVAL;
+			goto done;
+		}
+
+		/* Make sure we don't extend when keep_size is set. */
+		if (mode & LX_FALLOC_FL_KEEP_SIZE && vattr.va_size < tot) {
+			if (offset > vattr.va_size)
+				goto done;
+			len = vattr.va_size - offset;
+		}
+
+		bf.l_start = (off64_t)offset;
+		bf.l_len = (off64_t)len;
+	} else {
+		/* We should have already handled all flags */
+		VERIFY(0);
+	}
+
+	/*
+	 * Check for locks in the range.
+	 */
+	f_offset = fp->f_offset;
+	error = flock_check(vp, &bf, f_offset, MAXOFF_T);
+	if (error != 0)
+		goto done;
+
+	/*
+	 * Check for conflicting non-blocking mandatory locks.
+	 * We need to get the size again under nbl_start_crit.
+	 */
+	if (nbl_need_check(vp)) {
+		u_offset_t	begin;
+		ssize_t		length;
+
+		nbl_start_crit(vp, RW_READER);
+		in_crit = B_TRUE;
+		vattr.va_mask = AT_SIZE;
+		if ((error = VOP_GETATTR(vp, &vattr, 0, CRED(), NULL)) != 0)
+			goto done;
+
+		/*
+		 * Make sure we don't extend when keep_size is set.
+		 */
+		if (mode & LX_FALLOC_FL_KEEP_SIZE && vattr.va_size < tot) {
+			ASSERT(mode & (LX_FALLOC_FL_PUNCH_HOLE |
+			    LX_FALLOC_FL_ZERO_RANGE));
+
+			/*
+			 * If the size grew we can short-circuit the rest of
+			 * the work, otherwise adjust bf for the vop_space
+			 * call.
+			 */
+			if (offset >= vattr.va_size)
+				goto done;
+			len = vattr.va_size - offset;
+			bf.l_len = (off64_t)len;
+		}
+
+		if (offset > vattr.va_size) {
+			begin = vattr.va_size;
+			length = offset - vattr.va_size;
+		} else {
+			begin = offset;
+			length = vattr.va_size - offset;
+		}
+
+		if (nbl_conflict(vp, NBL_WRITE, begin, length, 0, NULL)) {
+			error = EACCES;
+			goto done;
+		}
+	}
+
+	error = VOP_SPACE(vp, F_FREESP, &bf, 0, f_offset, fp->f_cred, NULL);
+
+done:
+	if (in_crit)
+		nbl_end_crit(vp);
+
+	releasef(fd);
+	if (error != 0)
+		return (set_errno(error));
+
+	return (0);
+}
+
+long
+lx_fallocate32(int fd, int mode, uint32_t offl, uint32_t offh, uint32_t lenl,
+    uint32_t lenh)
+{
+	int64_t offset = 0, len = 0;
+
+	/*
+	 * From 32-bit callers, Linux passes the 64-bit offset and len by
+	 * concatenating consecutive arguments. We must perform the same
+	 * conversion here.
+	 */
+	offset = offh;
+	offset = offset << 32;
+	offset |= offl;
+	len = lenh;
+	len = len << 32;
+	len |= lenl;
+
+	return (lx_fallocate(fd, mode, (off_t)offset, (off_t)len));
+}
--- /dev/null	Wed Oct  5 00:32:08 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_fcntl.c	Wed Oct  5 00:32:08 2016
@@ -0,0 +1,660 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#include <sys/systm.h>
+#include <sys/zone.h>
+#include <sys/types.h>
+#include <sys/filio.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/cmn_err.h>
+#include <sys/pathname.h>
+#include <sys/lx_impl.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_fcntl.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_socket.h>
+#include <sys/fs/fifonode.h>
+#include <sys/strsubr.h>
+#include <sys/stream.h>
+
+extern int fcntl(int, int, intptr_t);
+extern int flock_check(vnode_t *, flock64_t *, offset_t, offset_t);
+
+
+int
+lx_vp_at(int fd, char *upath, vnode_t **vpp, int flag)
+{
+	vnode_t *startvp;
+	int error;
+
+	if (fd == LX_AT_FDCWD) {
+		fd = AT_FDCWD;
+	}
+
+	if ((error = fgetstartvp(fd, upath, &startvp)) != 0) {
+		return (error);
+	}
+
+	if (upath != NULL) {
+		uio_seg_t seg = UIO_USERSPACE;
+
+		error = lookupnameat(upath, seg,
+		    (flag == AT_SYMLINK_NOFOLLOW) ?  NO_FOLLOW : FOLLOW,
+		    NULLVPP, vpp, startvp);
+		if (startvp != NULL) {
+			VN_RELE(startvp);
+		}
+		return (error);
+	} else {
+		/* VN_HOLD was established in fgetstartvp */
+		*vpp = startvp;
+		VERIFY(*vpp);
+		return (0);
+	}
+}
+
+#define	LTOS_FLOCK(l, s)						\
+{									\
+	s->l_type = ltos_type(l->l_type);				\
+	s->l_whence = l->l_whence;					\
+	s->l_start = l->l_start;					\
+	s->l_len = l->l_len;						\
+	s->l_sysid = 0;			/* not defined in linux */	\
+	s->l_pid = (pid_t)l->l_pid;					\
+}
+
+#define	STOL_FLOCK(s, l)						\
+{									\
+	l->l_type = stol_type(s->l_type);				\
+	l->l_whence = s->l_whence;					\
+	l->l_start = s->l_start;					\
+	l->l_len = s->l_len;						\
+	l->l_pid = (int)s->l_pid;					\
+}
+
+static short
+ltos_type(short l_type)
+{
+	switch (l_type) {
+	case LX_F_RDLCK:
+		return (F_RDLCK);
+	case LX_F_WRLCK:
+		return (F_WRLCK);
+	case LX_F_UNLCK:
+		return (F_UNLCK);
+	default:
+		return (-1);
+	}
+}
+
+static short
+stol_type(short l_type)
+{
+	switch (l_type) {
+	case F_RDLCK:
+		return (LX_F_RDLCK);
+	case F_WRLCK:
+		return (LX_F_WRLCK);
+	case F_UNLCK:
+		return (LX_F_UNLCK);
+	default:
+		/* can't ever happen */
+		return (0);
+	}
+}
+
+static void
+ltos_flock(struct lx_flock *l, struct flock64 *s)
+{
+	LTOS_FLOCK(l, s)
+}
+
+static void
+stol_flock(struct flock64 *s, struct lx_flock *l)
+{
+	STOL_FLOCK(s, l)
+}
+
+static void
+ltos_flock64(struct lx_flock64_32 *l, struct flock64 *s)
+{
+	LTOS_FLOCK(l, s)
+}
+
+static void
+stol_flock64(struct flock64 *s, struct lx_flock64_32 *l)
+{
+	STOL_FLOCK(s, l)
+}
+
+static int
+lx_fcntl_getfl(int fd)
+{
+	int retval;
+	int rc;
+
+	retval = fcntl(fd, F_GETFL, 0);
+	if (ttolwp(curthread)->lwp_errno != 0)
+		return (ttolwp(curthread)->lwp_errno);
+
+	if ((retval & O_ACCMODE) == O_RDONLY)
+		rc = LX_O_RDONLY;
+	else if ((retval & O_ACCMODE) == O_WRONLY)
+		rc = LX_O_WRONLY;
+	else
+		rc = LX_O_RDWR;
+	/* O_NDELAY != O_NONBLOCK, so we need to check for both */
+	if (retval & O_NDELAY)
+		rc |= LX_O_NDELAY;
+	if (retval & O_NONBLOCK)
+		rc |= LX_O_NONBLOCK;
+	if (retval & O_APPEND)
+		rc |= LX_O_APPEND;
+	if (retval & O_SYNC)
+		rc |= LX_O_SYNC;
+	if (retval & O_LARGEFILE)
+		rc |= LX_O_LARGEFILE;
+	if (retval & FASYNC)
+		rc |= LX_O_ASYNC;
+
+	return (rc);
+}
+
+#define	LX_SETFL_MASK	(O_NONBLOCK | O_APPEND | O_SYNC |  FASYNC);
+
+static int
+lx_fcntl_setfl(int fd, ulong_t arg)
+{
+	int flags;
+
+	/*
+	 * When performing fcntl(F_SETFL), only certain flags are
+	 * allowed to be manipulated. A mask is used to preserve
+	 * other flags, such as those which are specified during
+	 * open(2). The mask on Linux excludes O_LARGEFILE from
+	 * being manipulated, whereas illumos expects the flag to
+	 * be set. In order to properly preserve the O_LARGEFILE
+	 * (FOFFMAX) state, we must first query for it via
+	 * fcntl(F_GETFL) so that the value can be carried
+	 * through.
+	 */
+	flags = fcntl(fd, F_GETFL, 0);
+	if (ttolwp(curthread)->lwp_errno != 0)
+		return (ttolwp(curthread)->lwp_errno);
+
+	flags &= ~LX_SETFL_MASK;
+
+	/* LX_O_NDELAY == LX_O_NONBLOCK, so we only check for one */
+	if (arg & LX_O_NDELAY)
+		flags |= O_NONBLOCK;
+	if (arg & LX_O_APPEND)
+		flags |= O_APPEND;
+	if (arg & LX_O_SYNC)
+		flags |= O_SYNC;
+	if (arg & LX_O_ASYNC)
+		flags |= FASYNC;
+
+	return (fcntl(fd, F_SETFL, flags));
+}
+
+/* The default unprivileged limit in Linux is 1MB */
+static int lx_pipe_max_size = 1048576;
+
+static int
+lx_fcntl_pipesz(int fd, int cmd, ulong_t arg)
+{
+	file_t *fp;
+	vnode_t *vp;
+	stdata_t *str;
+	int err = 0, res = 0;
+
+	if ((fp = getf(fd)) == NULL) {
+		return (set_errno(EBADF));
+	}
+	vp = fp->f_vnode;
+	if (vp->v_type != VFIFO || vp->v_op != fifo_vnodeops) {
+		err = EBADF;
+		goto out;
+	}
+	VERIFY((str = vp->v_stream) != NULL);
+
+	if (cmd == LX_F_SETPIPE_SZ) {
+		stdata_t *mate;
+		intptr_t val = arg;
+
+		if (val < PAGESIZE || val > lx_pipe_max_size) {
+			err = EINVAL;
+			goto out;
+		}
+		if (!STRMATED(str)) {
+			err = strqset(RD(str->sd_wrq), QHIWAT, 0, val);
+			goto out;
+		}
+
+		/*
+		 * Ensure consistent order so the set operation is always
+		 * attempted on the "higher" stream first.
+		 */
+		if (str > str->sd_mate) {
+			VERIFY((mate = str->sd_mate) != NULL);
+		} else {
+			mate = str;
+			VERIFY((str = mate->sd_mate) != NULL);
+		}
+
+		/*
+		 * While it is unfortunate that an error could occur for the
+		 * latter half of the stream pair, there is little to be done
+		 * about it aside from reporting the failure.
+		 */
+		if ((err = strqset(RD(str->sd_wrq), QHIWAT, 0, val)) != 0) {
+			goto out;
+		}
+		err = strqset(RD(mate->sd_wrq), QHIWAT, 0, val);
+	} else if (cmd == LX_F_GETPIPE_SZ) {
+		size_t val;
+
+		err = strqget(RD(str->sd_wrq), QHIWAT, 0, &val);
+		res = val;
+	} else {
+		/* NOTREACHED */
+		ASSERT(0);
+	}
+
+out:
+	releasef(fd);
+	if (err != 0) {
+		return (set_errno(err));
+	}
+	return (res);
+}
+
+static int
+lx_fcntl_common(int fd, int cmd, ulong_t arg)
+{
+	int		rc = 0;
+	pid_t		pid;
+	int		error;
+	int		rv;
+	int32_t		flag;
+	file_t		*fp;
+
+	/*
+	 * We depend on the call to fcntl to set the errno if necessary.
+	 */
+	ttolwp(curthread)->lwp_errno = 0;
+
+	switch (cmd) {
+	case LX_F_SETSIG:
+	case LX_F_GETSIG:
+	case LX_F_SETLEASE:
+	case LX_F_GETLEASE:
+	case LX_F_NOTIFY:
+	case LX_F_CANCELLK:
+		{
+			char buf[80];
+
+			(void) snprintf(buf, sizeof (buf),
+			    "unsupported fcntl command: %d", cmd);
+			lx_unsupported(buf);
+		}
+		return (set_errno(ENOTSUP));
+
+	case LX_F_DUPFD:
+		rc = fcntl(fd, F_DUPFD, arg);
+		break;
+
+	case LX_F_DUPFD_CLOEXEC:
+		rc = fcntl(fd, F_DUPFD_CLOEXEC, arg);
+		break;
+
+	case LX_F_GETFD:
+		rc = fcntl(fd, F_GETFD, 0);
+		break;
+
+	case LX_F_SETFD:
+		rc = fcntl(fd, F_SETFD, arg);
+		break;
+
+	case LX_F_GETFL:
+		rc = lx_fcntl_getfl(fd);
+		break;
+
+	case LX_F_SETFL:
+		rc = lx_fcntl_setfl(fd, arg);
+		break;
+
+	case LX_F_SETOWN:
+		pid = (pid_t)arg;
+		if (pid == 1) {
+			/* Setown for the init process uses the real pid. */
+			pid = curzone->zone_proc_initpid;
+		}
+
+		if ((fp = getf(fd)) == NULL)
+			return (set_errno(EBADF));
+
+		rv = 0;
+
+		flag = fp->f_flag | get_udatamodel() | FKIOCTL;
+		error = VOP_IOCTL(fp->f_vnode, FIOSETOWN, (intptr_t)&pid,
+		    flag, CRED(), &rv, NULL);
+		releasef(fd);
+		if (error != 0) {
+			/*
+			 * On illumos F_SETOWN is only defined for sockets, but
+			 * some apps hardcode to do this fcntl on other devices
+			 * (e.g. /dev/tty) to setup signal handling. If the
+			 * app is only setting itself to be the signal
+			 * handler, we pretend to succeed.
+			 */
+			if (error != EINVAL ||
+			    curthread->t_procp->p_pid != pid) {
+				return (set_errno(error));
+			}
+		}
+
+		rc = 0;
+		break;
+
+	case LX_F_GETOWN:
+		if ((fp = getf(fd)) == NULL)
+			return (set_errno(EBADF));
+
+		rv = 0;
+
+		flag = fp->f_flag | get_udatamodel() | FKIOCTL;
+		error = VOP_IOCTL(fp->f_vnode, FIOGETOWN, (intptr_t)&pid,
+		    flag, CRED(), &rv, NULL);
+		releasef(fd);
+		if (error != 0)
+			return (set_errno(error));
+
+		if (pid == curzone->zone_proc_initpid) {
+			/* Getown for the init process returns 1. */
+			pid = 1;
+		}
+
+		rc = pid;
+		break;
+
+	case LX_F_SETPIPE_SZ:
+	case LX_F_GETPIPE_SZ:
+		rc = lx_fcntl_pipesz(fd, cmd, arg);
+		break;
+
+	default:
+		return (set_errno(EINVAL));
+	}
+
+	return (rc);
+}
+
+static int
+lx_fcntl_lock_cmd_to_s(int lx_cmd)
+{
+	switch (lx_cmd) {
+	case LX_F_GETLK:
+		return (F_GETLK);
+	case LX_F_SETLK:
+		return (F_SETLK);
+	case LX_F_SETLKW:
+		return (F_SETLKW);
+	case LX_F_GETLK64:
+		return (F_GETLK64);
+	case LX_F_SETLK64:
+		return (F_SETLK64);
+	case LX_F_SETLKW64:
+		return (F_SETLKW64);
+	default:
+		VERIFY(0);
+		/*NOTREACHED*/
+		return (0);
+	}
+}
+
+/*
+ * This is a pain but we can't re-use the fcntl code for locking since it does
+ * its own copyin/copyout for the flock struct. Since we have to convert the
+ * struct we have to do our own copyin/out. Thus we replicate the fcntl code for
+ * these 3 cmds. Luckily it's not much.
+ */
+static int
+lx_fcntl_lock(int fd, int lx_cmd, void *arg)
+{
+	int cmd;
+	int error = 0;
+	file_t *fp;
+	vnode_t *vp;
+	int flag;
+	offset_t maxoffset;
+	u_offset_t offset;
+	model_t datamodel;
+	lx_flock_t lxflk;
+	lx_flock64_32_t lxflk64;
+	struct flock64 bf;
+
+	if ((fp = getf(fd)) == NULL)
+		return (set_errno(EBADF));
+
+	maxoffset = MAXOFF_T;
+	datamodel = DATAMODEL_NATIVE;
+#if defined(_SYSCALL32_IMPL)
+	if ((datamodel = get_udatamodel()) == DATAMODEL_ILP32)
+		maxoffset = MAXOFF32_T;
+#endif
+	vp = fp->f_vnode;
+	flag = fp->f_flag;
+	offset = fp->f_offset;
+
+	cmd = lx_fcntl_lock_cmd_to_s(lx_cmd);
+
+	switch (cmd) {
+	case F_GETLK:
+	case F_SETLK:
+	case F_SETLKW:
+		if (datamodel == DATAMODEL_NATIVE) {
+			if (copyin(arg, &lxflk, sizeof (lx_flock_t)) != 0) {
+				error = EFAULT;
+				break;
+			}
+		}
+#if defined(_SYSCALL32_IMPL)
+		else {
+			lx_flock32_t lxflk32;
+
+			if (copyin(arg, &lxflk32, sizeof (lxflk32)) != 0) {
+				error = EFAULT;
+				break;
+			}
+
+			lxflk.l_type = lxflk32.l_type;
+			lxflk.l_whence = lxflk32.l_whence;
+			lxflk.l_start = (off64_t)lxflk32.l_start;
+			lxflk.l_len = (off64_t)lxflk32.l_len;
+			lxflk.l_pid = lxflk32.l_pid;
+		}
+#endif /* _SYSCALL32_IMPL */
+
+		ltos_flock(&lxflk, &bf);
+
+		if ((error = flock_check(vp, &bf, offset, maxoffset)) != 0)
+			break;
+
+		if ((error = VOP_FRLOCK(vp, cmd, &bf, flag, offset, NULL,
+		    fp->f_cred, NULL)) != 0)
+			break;
+
+		if (cmd != F_GETLK)
+			break;
+
+		/*
+		 * The command is GETLK, return result.
+		 */
+		stol_flock(&bf, &lxflk);
+
+		/*
+		 * If no lock is found, only the type field is changed.
+		 */
+		if (lxflk.l_type == LX_F_UNLCK) {
+			/* l_type always first entry, always a short */
+			if (copyout(&lxflk.l_type, &((lx_flock_t *)arg)->l_type,
+			    sizeof (lxflk.l_type)))
+				error = EFAULT;
+			break;
+		}
+
+		if (bf.l_start > maxoffset || bf.l_len > maxoffset) {
+			error = EOVERFLOW;
+			break;
+		}
+
+		if (datamodel == DATAMODEL_NATIVE) {
+			if (copyout(&lxflk, arg, sizeof (lxflk)) != 0) {
+				error = EFAULT;
+				break;
+			}
+		}
+#if defined(_SYSCALL32_IMPL)
+		else {
+			lx_flock32_t lxflk32;
+
+			if (bf.l_start > MAXOFF32_T || bf.l_len > MAXOFF32_T) {
+				error = EOVERFLOW;
+				break;
+			}
+
+			lxflk32.l_type = lxflk.l_type;
+			lxflk32.l_whence = lxflk.l_whence;
+			lxflk32.l_start = lxflk.l_start;
+			lxflk32.l_len = lxflk.l_len;
+			lxflk32.l_pid = lxflk.l_pid;
+
+			if (copyout(&lxflk32, arg, sizeof (lxflk32)) != 0) {
+				error = EFAULT;
+				break;
+			}
+		}
+#endif /* _SYSCALL32_IMPL */
+		break;
+
+	case F_GETLK64:
+	case F_SETLK64:
+	case F_SETLKW64:
+		/*
+		 * Large File support is only used for ILP32 apps.
+		 */
+		if (datamodel != DATAMODEL_ILP32) {
+			error = EINVAL;
+			break;
+		}
+
+		if (cmd == F_GETLK64)
+			cmd = F_GETLK;
+		else if (cmd == F_SETLK64)
+			cmd = F_SETLK;
+		else if (cmd == F_SETLKW64)
+			cmd = F_SETLKW;
+
+		if (copyin(arg, &lxflk64, sizeof (lxflk64)) != 0) {
+			error = EFAULT;
+			break;
+		}
+
+		ltos_flock64(&lxflk64, &bf);
+
+		if ((error = flock_check(vp, &bf, offset, MAXOFFSET_T)) != 0)
+			break;
+
+		if ((error = VOP_FRLOCK(vp, cmd, &bf, flag, offset, NULL,
+		    fp->f_cred, NULL)) != 0)
+			break;
+
+		if (cmd != F_GETLK)
+			break;
+
+		/*
+		 * The command is GETLK, return result.
+		 */
+		stol_flock64(&bf, &lxflk64);
+
+		/*
+		 * If no lock is found, only the type field is changed.
+		 */
+		if (lxflk64.l_type == LX_F_UNLCK) {
+			/* l_type always first entry, always a short */
+			if (copyout(&lxflk64.l_type,
+			    &((lx_flock64_t *)arg)->l_type,
+			    sizeof (lxflk64.l_type)))
+				error = EFAULT;
+			break;
+		}
+
+		if (bf.l_start > maxoffset || bf.l_len > maxoffset) {
+			error = EOVERFLOW;
+			break;
+		}
+
+		if (copyout(&lxflk64, arg, sizeof (lxflk64)) != 0) {
+			error = EFAULT;
+			break;
+		}
+		break;
+	}
+
+	releasef(fd);
+	if (error)
+		return (set_errno(error));
+
+	return (0);
+}
+
+long
+lx_fcntl(int fd, int cmd, intptr_t arg)
+{
+	switch (cmd) {
+	case LX_F_GETLK64:
+	case LX_F_SETLK64:
+	case LX_F_SETLKW64:
+		/* The 64-bit fcntl commands must go through fcntl64(). */
+		return (set_errno(EINVAL));
+
+	case LX_F_GETLK:
+	case LX_F_SETLK:
+	case LX_F_SETLKW:
+		return (lx_fcntl_lock(fd, cmd, (void *)arg));
+
+	default:
+		return (lx_fcntl_common(fd, cmd, arg));
+	}
+}
+
+long
+lx_fcntl64(int fd, int cmd, intptr_t arg)
+{
+	switch (cmd) {
+	case LX_F_GETLK:
+	case LX_F_SETLK:
+	case LX_F_SETLKW:
+	case LX_F_GETLK64:
+	case LX_F_SETLKW64:
+	case LX_F_SETLK64:
+		return (lx_fcntl_lock(fd, cmd, (void *)arg));
+
+	default:
+		return (lx_fcntl_common(fd, cmd, (ulong_t)arg));
+	}
+}
--- /dev/null	Wed Oct  5 00:32:08 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_futex.c	Wed Oct  5 00:32:08 2016
@@ -0,0 +1,1104 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright 2015 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/debug.h>
+#include <vm/as.h>
+#include <vm/seg.h>
+#include <vm/seg_vn.h>
+#include <vm/page.h>
+#include <sys/priv.h>
+#include <sys/mman.h>
+#include <sys/timer.h>
+#include <sys/condvar.h>
+#include <sys/inttypes.h>
+#include <sys/cmn_err.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_futex.h>
+#include <sys/lx_impl.h>
+
+/*
+ * Futexes are a Linux-specific implementation of inter-process mutexes.
+ * They are designed to use shared memory for simple, uncontested
+ * operations, and rely on the kernel to resolve any contention issues.
+ *
+ * Most of the information in this section comes from the paper "Futexes
+ * Are Tricky", by Ulrich Drepper.  This paper is currently available at:
+ * http://people.redhat.com/~drepper/futex.pdf.
+ *
+ * A futex itself a 4-byte integer, which must be 4-byte aligned.  The
+ * value of this integer is expected to be modified using user-level atomic
+ * operations.  The futex(4) design itself does not impose any semantic
+ * constraints on the value stored in the futex; it is up to the
+ * application to define its own protocol.
+ *
+ * When the application decides that kernel intervention is required, it
+ * will use the futex(2) system call.  There are 5 different operations
+ * that can be performed on a futex, using this system call.  Since this
+ * interface has evolved over time, there are several different prototypes
+ * available to the user.  Fortunately, there is only a single kernel-level
+ * interface:
+ *
+ * long sys_futex(void *futex1, int cmd, int val1,
+ * 	struct timespec	*timeout, void *futex2, int val2)
+ *
+ * The kernel-level operations that may be performed on a futex are:
+ *
+ * FUTEX_WAIT
+ *
+ *	Atomically verify that futex1 contains the value val1.  If it
+ *	doesn't, return EWOULDBLOCK.  If it does contain the expected
+ *	value, the thread will sleep until somebody performs a FUTEX_WAKE
+ *	on the futex.  The caller may also specify a timeout, indicating
+ *	the maximum time the thread should sleep.  If the timer expires,
+ *	the call returns ETIMEDOUT.  If the thread is awoken with a signal,
+ *	the call returns EINTR.  Otherwise, the call returns 0.
+ *
+ * FUTEX_WAKE
+ *
+ *	Wake up val1 processes that are waiting on futex1.  The call
+ *	returns the number of blocked threads that were woken up.
+ *
+ * FUTEX_WAIT_BITSET/FUTEX_WAKE_BITSET
+ *
+ *	Similar to FUTEX_WAIT/FUTEX_WAKE, but each takes an additional argument
+ *	denoting a bit vector, with wakers will only waking waiters that match
+ *	in one or more bits.  These semantics are dubious enough, but the
+ *	interface has an inconsistency that is glaring even by the
+ *	embarrassingly low standards that Linux sets for itself:  the timeout
+ *	argument to FUTEX_WAIT_BITSET is absolute, not relative as it is for
+ *	FUTEX_WAIT.  And as if that weren't enough unnecessary complexity,
+ *	the caller may specify this absolute timeout to be against either
+ *	CLOCK_MONOTONIC or CLOCK_REALTIME -- but only for FUTEX_WAIT_BITSET,
+ *	of course!
+ *
+ * FUTEX_WAKE_OP
+ *
+ *	The implementation of a conditional variable in terms of futexes
+ *	actually uses two futexes:  one to assure sequential access and one to
+ *	represent the condition variable.  This implementation gives rise to a
+ *	particular performance problem whereby a thread is awoken on the futex
+ *	that represents the condition variable only to have to (potentially)
+ *	immediately wait on the futex that protects the condition variable.
+ *	(Do not confuse the futex that serves to protect the condition variable
+ *	with the pthread_mutex_t associated with pthread_cond_t -- which
+ *	represents a third futex.)  To (over)solve this problem, FUTEX_WAKE_OP
+ *	was invented, which performs an atomic compare-and-exchange on a
+ *	second address in a specified fashion (that is, with a specified
+ *	operation).  Here are the possible operations (OPARG is defined
+ *	to be 12 bit value embedded in the operation):
+ *
+ *	- FUTEX_OP_SET: Sets the value at the second address to OPARG
+ *	- FUTEX_OP_ADD: Adds the value to OPARG
+ *	- FUTEX_OP_OR: OR's the value with OPARG
+ *	- FUTEX_OP_ANDN: Performs a negated AND of the value with OPARG
+ *	- FUTEX_OP_XOR: XOR's the value with OPARG
+ *
+ *	After this compare-and-exchange on the second address, a FUTEX_WAKE is
+ *	performed on the first address and -- if the compare-and-exchange
+ *	matches a specified result based on a specified comparison operation --
+ *	a FUTEX_WAKE is performed on the second address.  Here are the possible
+ *	comparison operations:
+ *
+ *	- FUTEX_OP_CMP_EQ: If old value is CMPARG, wake
+ *	- FUTEX_OP_CMP_NE: If old value is not equal to CMPARG, wake
+ *	- FUTEX_OP_CMP_LT: If old value is less than CMPARG, wake
+ *	- FUTEX_OP_CMP_LE: If old value is less than or equal to CMPARG, wake
+ *	- FUTEX_OP_CMP_GT: If old value is greater than CMPARG, wake
+ *	- FUTEX_OP_CMP_GE: If old value is greater than or equal to CMPARG, wake
+ *
+ *	As a practical matter, the only way that this is used (or, some might
+ *	argue, is usable) is by the implementation of pthread_cond_signal(),
+ *	which uses FUTEX_WAKE_OP to -- in a single system call -- unlock the
+ *	futex that protects the condition variable and wake the futex that
+ *	represents the condition variable.  The second wake-up is conditional
+ *	because the futex that protects the condition variable (rather than the
+ *	one that represents it) may or may not have waiters.  Given that this
+ *	is the use case, FUTEX_WAKE_OP is falsely generic: despite allowing for
+ *	five different kinds of operations and six different kinds of
+ *	comparision operations, in practice only one is used.  (Namely, setting
+ *	to 0 and waking if the old value is greater than 1 -- which denotes
+ *	that waiters are present and the wakeup should be performed.) Moreover,
+ *	because FUTEX_WAKE_OP does not (and cannot) optimize anything in the
+ *	case that the pthread_mutex_t associated with the pthread_cond_t is
+ *	held at the time of a pthread_cond_signal(), this entire mechanism is
+ *	essentially for naught in this case.  As one can imagine (and can
+ *	verify on just about any source base that uses pthread_cond_signal()),
+ *	it is overwhelmingly the common case that the lock associated with the
+ *	pthread_cond_t is held at the time of pthread_cond_signal(), assuring
+ *	that the problem that all of this complexity was designed to solve
+ *	isn't, in fact, solved because the signalled thread simply wakes up
+ *	only to block again on the held mutex.  Cue a slow clap!
+ *
+ * FUTEX_CMP_REQUEUE
+ *
+ *	If the value stored in futex1 matches that passed in in val2, wake
+ *	up val1 processes that are waiting on futex1.  Otherwise, return
+ *	EAGAIN.
+ *
+ *	If there are more than val1 threads waiting on the futex, remove
+ *	the remaining threads from this futex, and requeue them on futex2.
+ *	The caller can limit the number of threads being requeued by
+ *	encoding an integral numerical value in the position usually used
+ *	for the timeout pointer.
+ *
+ *	The call returns the number of blocked threads that were woken up
+ *	or requeued.
+ *
+ * FUTEX_REQUEUE
+ *
+ *	 Identical to FUTEX_CMP_REQUEUE except that it does not use val2.
+ *	 This command has been declared broken and obsolete, but we still
+ *	 need to support it.
+ *
+ * FUTEX_FD
+ *
+ *	Return a file descriptor, which can be used to refer to the futex.
+ *	This operation was broken by design, and was blessedly removed in
+ *	Linux 2.6.26 ("because it was inherently racy"); it should go without
+ *	saying that we don't support this operation.
+ */
+
+/*
+ * This structure is used to track all the threads currently waiting on a
+ * futex.  There is one fwaiter_t for each blocked thread.  We store all
+ * fwaiter_t's in a hash structure, indexed by the memid_t of the integer
+ * containing the futex's value.
+ *
+ * At the moment, all fwaiter_t's for a single futex are simply dumped into
+ * the hash bucket.  If futex contention ever becomes a hot path, we can
+ * chain a single futex's waiters together.
+ */
+typedef struct fwaiter {
+	memid_t		fw_memid;	/* memid of the user-space futex */
+	kcondvar_t	fw_cv;		/* cond var */
+	struct fwaiter	*fw_next;	/* hash queue */
+	struct fwaiter	*fw_prev;	/* hash queue */
+	uint32_t	fw_bits;	/* bits waiting on */
+	volatile int	fw_woken;
+} fwaiter_t;
+
+/*
+ * The structure of the robust_list, as set with the set_robust_list() system
+ * call.  See lx_futex_robust_exit(), below, for details.
+ */
+typedef struct futex_robust_list {
+	uintptr_t	frl_head;	/* list of robust locks held */
+	uint64_t	frl_offset;	/* offset of lock word within a lock */
+	uintptr_t	frl_pending;	/* pending operation */
+} futex_robust_list_t;
+
+#if defined(_SYSCALL32_IMPL)
+
+#pragma pack(4)
+typedef struct futex_robust_list32 {
+	uint32_t	frl_head;	/* list of robust locks held */
+	uint32_t	frl_offset;	/* offset of lock word within a lock */
+	uint32_t	frl_pending;	/* pending operation */
+} futex_robust_list32_t;
+#pragma pack()
+
+#endif
+
+#define	MEMID_COPY(s, d) \
+	{ (d)->val[0] = (s)->val[0]; (d)->val[1] = (s)->val[1]; }
+#define	MEMID_EQUAL(s, d) \
+	((d)->val[0] == (s)->val[0] && (d)->val[1] == (s)->val[1])
+
+/*
+ * Because collisions on this hash table can be a source of negative
+ * scalability, we make it pretty large: 4,096 entries -- 64K.  If this
+ * size is found to be insufficient, the size should be made dynamic.
+ * (Making it dynamic will be delicate because the per-chain locking will
+ * necessitate memory retiring or similar; see the 2008 ACM Queue article
+ * "Real-world concurrency" for details on this technique.)
+ */
+#define	HASH_SHIFT_SZ	12
+#define	HASH_SIZE	(1 << HASH_SHIFT_SZ)
+#define	HASH_FUNC(id)						\
+	((((uintptr_t)((id)->val[1]) >> 3) +			\
+	((uintptr_t)((id)->val[1]) >> (3 + HASH_SHIFT_SZ)) +		\
+	((uintptr_t)((id)->val[1]) >> (3 + 2 * HASH_SHIFT_SZ)) +	\
+	((uintptr_t)((id)->val[0]) >> 3) +				\
+	((uintptr_t)((id)->val[0]) >> (3 + HASH_SHIFT_SZ)) +		\
+	((uintptr_t)((id)->val[0]) >> (3 + 2 * HASH_SHIFT_SZ))) &	\
+	(HASH_SIZE - 1))
+
+/*
+ * We place the per-chain lock next to the pointer to the chain itself.
+ * When compared to an array of orthogonal locks, this reduces false sharing
+ * (though adjacent entries can still be falsely shared -- just not as many),
+ * while having the additional bonus of increasing locality.
+ */
+typedef struct futex_hash {
+	kmutex_t fh_lock;
+	fwaiter_t *fh_waiters;
+} futex_hash_t;
+
+static futex_hash_t futex_hash[HASH_SIZE];
+
+static void
+futex_hashin(fwaiter_t *fwp)
+{
+	int index;
+
+	index = HASH_FUNC(&fwp->fw_memid);
+	ASSERT(MUTEX_HELD(&futex_hash[index].fh_lock));
+
+	fwp->fw_prev = NULL;
+	fwp->fw_next = futex_hash[index].fh_waiters;
+	if (fwp->fw_next)
+		fwp->fw_next->fw_prev = fwp;
+	futex_hash[index].fh_waiters = fwp;
+}
+
+static void
+futex_hashout(fwaiter_t *fwp)
+{
+	int index;
+
+	index = HASH_FUNC(&fwp->fw_memid);
+	ASSERT(MUTEX_HELD(&futex_hash[index].fh_lock));
+
+	if (fwp->fw_prev)
+		fwp->fw_prev->fw_next = fwp->fw_next;
+	if (fwp->fw_next)
+		fwp->fw_next->fw_prev = fwp->fw_prev;
+	if (futex_hash[index].fh_waiters == fwp)
+		futex_hash[index].fh_waiters = fwp->fw_next;
+
+	fwp->fw_prev = NULL;
+	fwp->fw_next = NULL;
+}
+
+/*
+ * Go to sleep until somebody does a WAKE operation on this futex, we get a
+ * signal, or the timeout expires.
+ */
+static int
+futex_wait(memid_t *memid, caddr_t addr,
+    int val, timespec_t *timeout, uint32_t bits)
+{
+	kthread_t *t = curthread;
+	int err, ret;
+	int32_t curval;
+	fwaiter_t fw;
+	int index;
+
+	/*
+	 * The LMS_USER_LOCK micro state becomes valid if we sleep; otherwise
+	 * our time will accrue against LMS_SYSTEM.  Use of this micro state
+	 * is modelled on lwp_mutex_timedlock(), a native analogue of
+	 * futex_wait().
+	 */
+	(void) new_mstate(t, LMS_USER_LOCK);
+
+	fw.fw_woken = 0;
+	fw.fw_bits = bits;
+
+	MEMID_COPY(memid, &fw.fw_memid);
+	cv_init(&fw.fw_cv, NULL, CV_DEFAULT, NULL);
+
+	index = HASH_FUNC(&fw.fw_memid);
+	mutex_enter(&futex_hash[index].fh_lock);
+
+	if (fuword32(addr, (uint32_t *)&curval)) {
+		err = set_errno(EFAULT);
+		goto out;
+	}
+	if (curval != val) {
+		err = set_errno(EWOULDBLOCK);
+		goto out;
+	}
+
+	futex_hashin(&fw);
+
+	err = 0;
+	while ((fw.fw_woken == 0) && (err == 0)) {
+		ret = cv_waituntil_sig(&fw.fw_cv, &futex_hash[index].fh_lock,
+		    timeout, timechanged);
+		if (ret < 0) {
+			err = set_errno(ETIMEDOUT);
+		} else if (ret == 0) {
+			/*
+			 * According to signal(7), a futex(2) call with the
+			 * FUTEX_WAIT operation is restartable.
+			 */
+			ttolxlwp(t)->br_syscall_restart = B_TRUE;
+			err = set_errno(EINTR);
+		}
+	}
+
+	/*
+	 * The futex is normally hashed out in wakeup.  If we timed out or
+	 * got a signal, we need to hash it out here instead.
+	 */
+	if (fw.fw_woken == 0)
+		futex_hashout(&fw);
+
+out:
+	mutex_exit(&futex_hash[index].fh_lock);
+
+	return (err);
+}
+
+/*
+ * Wake up to wake_threads threads that are blocked on the futex at memid.
+ */
+static int
+futex_wake(memid_t *memid, int wake_threads, uint32_t mask)
+{
+	fwaiter_t *fwp, *next;
+	int index;
+	int ret = 0;
+
+	index = HASH_FUNC(memid);
+
+	mutex_enter(&futex_hash[index].fh_lock);
+
+	for (fwp = futex_hash[index].fh_waiters;
+	    fwp != NULL && ret < wake_threads; fwp = next) {
+		next = fwp->fw_next;
+		if (MEMID_EQUAL(&fwp->fw_memid, memid) &&
+		    (fwp->fw_bits & mask)) {
+			futex_hashout(fwp);
+			fwp->fw_woken = 1;
+			cv_signal(&fwp->fw_cv);
+			ret++;
+		}
+	}
+
+	mutex_exit(&futex_hash[index].fh_lock);
+
+	return (ret);
+}
+
+static int
+futex_wake_op_execute(int32_t *addr, int32_t val3)
+{
+	int32_t op = FUTEX_OP_OP(val3);
+	int32_t cmp = FUTEX_OP_CMP(val3);
+	int32_t cmparg = FUTEX_OP_CMPARG(val3);
+	int32_t oparg, oldval, newval;
+	label_t ljb;
+	int rval;
+
+	if ((uintptr_t)addr >= KERNELBASE)
+		return (set_errno(EFAULT));
+
+	if (on_fault(&ljb))
+		return (set_errno(EFAULT));
+
+	oparg = FUTEX_OP_OPARG(val3);
+
+	do {
+		oldval = *addr;
+		newval = oparg;
+
+		switch (op) {
+		case FUTEX_OP_SET:
+			break;
+
+		case FUTEX_OP_ADD:
+			newval += oparg;
+			break;
+
+		case FUTEX_OP_OR:
+			newval |= oparg;
+			break;
+
+		case FUTEX_OP_ANDN:
+			newval &= ~oparg;
+			break;
+
+		case FUTEX_OP_XOR:
+			newval ^= oparg;
+			break;
+
+		default:
+			no_fault();
+			return (set_errno(EINVAL));
+		}
+	} while (atomic_cas_32((uint32_t *)addr, oldval, newval) != oldval);
+
+	no_fault();
+
+	switch (cmp) {
+	case FUTEX_OP_CMP_EQ:
+		rval = (oldval == cmparg);
+		break;
+
+	case FUTEX_OP_CMP_NE:
+		rval = (oldval != cmparg);
+		break;
+
+	case FUTEX_OP_CMP_LT:
+		rval = (oldval < cmparg);
+		break;
+
+	case FUTEX_OP_CMP_LE:
+		rval = (oldval <= cmparg);
+		break;
+
+	case FUTEX_OP_CMP_GT:
+		rval = (oldval > cmparg);
+		break;
+
+	case FUTEX_OP_CMP_GE:
+		rval = (oldval >= cmparg);
+		break;
+
+	default:
+		return (set_errno(EINVAL));
+	}
+
+	return (rval);
+}
+
+static int
+futex_wake_op(memid_t *memid, caddr_t addr2, memid_t *memid2,
+    int wake_threads, int wake_threads2, int val3)
+{
+	kmutex_t *l1, *l2;
+	int ret = 0, ret2 = 0, wake;
+	fwaiter_t *fwp, *next;
+	int index1, index2;
+
+	index1 = HASH_FUNC(memid);
+	index2 = HASH_FUNC(memid2);
+
+	if (index1 == index2) {
+		l1 = &futex_hash[index1].fh_lock;
+		l2 = NULL;
+	} else if (index1 < index2) {
+		l1 = &futex_hash[index1].fh_lock;
+		l2 = &futex_hash[index2].fh_lock;
+	} else {
+		l1 = &futex_hash[index2].fh_lock;
+		l2 = &futex_hash[index1].fh_lock;
+	}
+
+	mutex_enter(l1);
+	if (l2 != NULL)
+		mutex_enter(l2);
+
+	/* LINTED: alignment */
+	if ((wake = futex_wake_op_execute((int32_t *)addr2, val3)) < 0)
+		goto out;
+
+	for (fwp = futex_hash[index1].fh_waiters; fwp != NULL; fwp = next) {
+		next = fwp->fw_next;
+		if (!MEMID_EQUAL(&fwp->fw_memid, memid))
+			continue;
+
+		futex_hashout(fwp);
+		fwp->fw_woken = 1;
+		cv_signal(&fwp->fw_cv);
+		if (++ret >= wake_threads) {
+			break;
+		}
+	}
+
+	if (!wake)
+		goto out;
+
+	for (fwp = futex_hash[index2].fh_waiters; fwp != NULL; fwp = next) {
+		next = fwp->fw_next;
+		if (!MEMID_EQUAL(&fwp->fw_memid, memid2))
+			continue;
+
+		futex_hashout(fwp);
+		fwp->fw_woken = 1;
+		cv_signal(&fwp->fw_cv);
+		if (++ret2 >= wake_threads2) {
+			break;
+		}
+	}
+
+	ret += ret2;
+out:
+	if (l2 != NULL)
+		mutex_exit(l2);
+	mutex_exit(l1);
+
+	return (ret);
+}
+
+/*
+ * Wake up to wake_threads waiting on the futex at memid.  If there are
+ * more than that many threads waiting, requeue the remaining threads on
+ * the futex at requeue_memid.
+ */
+static int
+futex_requeue(memid_t *memid, memid_t *requeue_memid, int wake_threads,
+	ulong_t requeue_threads, caddr_t addr, int *cmpval)
+{
+	fwaiter_t *fwp, *next;
+	int index1, index2;
+	int ret = 0;
+	int32_t curval;
+	kmutex_t *l1, *l2;
+
+	/*
+	 * To ensure that we don't miss a wakeup if the value of cmpval
+	 * changes, we need to grab locks on both the original and new hash
+	 * buckets.  To avoid deadlock, we always grab the lower-indexed
+	 * lock first.
+	 */
+	index1 = HASH_FUNC(memid);
+	index2 = HASH_FUNC(requeue_memid);
+
+	if (index1 == index2) {
+		l1 = &futex_hash[index1].fh_lock;
+		l2 = NULL;
+	} else if (index1 < index2) {
+		l1 = &futex_hash[index1].fh_lock;
+		l2 = &futex_hash[index2].fh_lock;
+	} else {
+		l1 = &futex_hash[index2].fh_lock;
+		l2 = &futex_hash[index1].fh_lock;
+	}
+
+	mutex_enter(l1);
+	if (l2 != NULL)
+		mutex_enter(l2);
+
+	if (cmpval != NULL) {
+		if (fuword32(addr, (uint32_t *)&curval)) {
+			ret = -EFAULT;
+			goto out;
+		}
+		if (curval != *cmpval) {
+			ret = -EAGAIN;
+			goto out;
+		}
+	}
+
+	for (fwp = futex_hash[index1].fh_waiters; fwp != NULL; fwp = next) {
+		next = fwp->fw_next;
+		if (!MEMID_EQUAL(&fwp->fw_memid, memid))
+			continue;
+
+		futex_hashout(fwp);
+		if (ret++ < wake_threads) {
+			fwp->fw_woken = 1;
+			cv_signal(&fwp->fw_cv);
+		} else {
+			MEMID_COPY(requeue_memid, &fwp->fw_memid);
+			futex_hashin(fwp);
+
+			if ((ret - wake_threads) >= requeue_threads)
+				break;
+		}
+	}
+
+out:
+	if (l2 != NULL)
+		mutex_exit(l2);
+	mutex_exit(l1);
+
+	if (ret < 0)
+		return (set_errno(-ret));
+	return (ret);
+}
+
+/*
+ * Copy in the relative timeout provided by the application and convert it
+ * to an absolute timeout.  Sadly, this is complicated by the different
+ * timeout of semantics of FUTEX_WAIT vs. FUTEX_WAIT_BITSET.  (Yes, you read
+ * that correctly; FUTEX_WAIT and FUTEX_WAIT_BITSET have different timeout
+ * semantics; see the block comment at the top of the file for commentary
+ * on this inanity.)
+ */
+static int
+get_timeout(void *lx_timeout, timestruc_t *timeout, int cmd, int clock)
+{
+	timestruc_t now;
+
+	if (get_udatamodel() == DATAMODEL_NATIVE) {
+		if (copyin(lx_timeout, timeout, sizeof (timestruc_t)))
+			return (EFAULT);
+	}
+#ifdef _SYSCALL32_IMPL
+	else {
+		timestruc32_t timeout32;
+		if (copyin(lx_timeout, &timeout32, sizeof (timestruc32_t)))
+			return (EFAULT);
+		timeout->tv_sec = (time_t)timeout32.tv_sec;
+		timeout->tv_nsec = timeout32.tv_nsec;
+	}
+#endif
+	if (itimerspecfix(timeout))
+		return (EINVAL);
+
+	if (cmd == FUTEX_WAIT) {
+		/*
+		 * We've been given a relative time; add it to the current
+		 * time to derive an absolute time.
+		 */
+		gethrestime(&now);
+		timespecadd(timeout, &now);
+	} else {
+		/*
+		 * This is a FUTEX_WAIT_BITSET operation, which (1) specifies
+		 * the timeout as an absolute rather than a relative timeout
+		 * and (2) allows for different clock types to be specified.
+		 * If the clock is CLOCK_REALTIME, we actually have nothing
+		 * to do -- but if this is CLOCK_MONOTONIC, we need to convert
+		 * our absolute time back into a relative time and then add
+		 * it to our current hrestime to get an absolute CLOCK_REALTIME
+		 * timeout.
+		 */
+		if (clock == CLOCK_MONOTONIC) {
+			/*
+			 * Get our current time, and subtract it from our
+			 * timeout to get the relative value.
+			 */
+			hrt2ts(gethrtime(), &now);
+			timespecsub(timeout, &now);
+
+			/*
+			 * If our timeout is in the past, set it to be 0.
+			 */
+			if (timeout->tv_sec < 0) {
+				timeout->tv_sec = 0;
+				timeout->tv_nsec = 0;
+			}
+
+			/*
+			 * Add the relative time back into the current time.
+			 */
+			gethrestime(&now);
+			timespecadd(timeout, &now);
+		}
+	}
+
+	return (0);
+}
+
+long
+lx_futex(uintptr_t addr, int op, int val, uintptr_t lx_timeout,
+	uintptr_t addr2, int val3)
+{
+	struct as *as = curproc->p_as;
+	memid_t memid, memid2;
+	timestruc_t timeout;
+	timestruc_t *tptr = NULL;
+	int val2 = NULL;
+	int rval = 0;
+	int cmd = op & FUTEX_CMD_MASK;
+	int private = op & FUTEX_PRIVATE_FLAG;
+	char dmsg[32];
+
+	/* must be aligned on int boundary */
+	if (addr & 0x3)
+		return (set_errno(EINVAL));
+
+	/* Sanity check the futex command */
+	if (cmd < 0 || cmd > FUTEX_MAX_CMD)
+		return (set_errno(EINVAL));
+
+	if (cmd == FUTEX_FD) {
+		/*
+		 * FUTEX_FD was sentenced to death for grievous crimes of
+		 * semantics against humanity; it has been ripped out of Linux
+		 * and will never be supported by us.
+		 */
+		(void) snprintf(dmsg, sizeof (dmsg), "futex 0x%x", cmd);
+		lx_unsupported(dmsg);
+		return (set_errno(ENOSYS));
+	}
+
+	switch (cmd) {
+	case FUTEX_LOCK_PI:
+	case FUTEX_UNLOCK_PI:
+	case FUTEX_TRYLOCK_PI:
+	case FUTEX_WAIT_REQUEUE_PI:
+	case FUTEX_CMP_REQUEUE_PI:
+		/*
+		 * These are operations that we don't currently support, but
+		 * may well need to in the future.  For now, callers need to
+		 * deal with these being missing -- but if and as that changes,
+		 * they may well need to be implemented.
+		 */
+		(void) snprintf(dmsg, sizeof (dmsg), "futex 0x%x", cmd);
+		lx_unsupported(dmsg);
+		return (set_errno(ENOSYS));
+	}
+
+	if ((op & FUTEX_CLOCK_REALTIME) && cmd != FUTEX_WAIT_BITSET) {
+		/*
+		 * Linux only allows FUTEX_CLOCK_REALTIME to be set on the
+		 * FUTEX_WAIT_BITSET and FUTEX_WAIT_REQUEUE_PI commands.
+		 */
+		return (set_errno(ENOSYS));
+	}
+
+	/* Copy in the timeout structure from userspace. */
+	if ((cmd == FUTEX_WAIT || cmd == FUTEX_WAIT_BITSET) &&
+	    lx_timeout != NULL) {
+		rval = get_timeout((timespec_t *)lx_timeout, &timeout, cmd,
+		    op & FUTEX_CLOCK_REALTIME ? CLOCK_REALTIME :
+		    CLOCK_MONOTONIC);
+
+		if (rval != 0)
+			return (set_errno(rval));
+		tptr = &timeout;
+	}
+
+	switch (cmd) {
+	case FUTEX_REQUEUE:
+	case FUTEX_CMP_REQUEUE:
+	case FUTEX_WAKE_OP:
+		/*
+		 * lx_timeout is nominally a pointer to a userspace address.
+		 * For several commands, however, it actually contains
+		 * an additional integer parameter.  This is horrible, and
+		 * the people who did this to us should be sorry.
+		 */
+		val2 = (int)lx_timeout;
+	}
+
+	/*
+	 * Translate the process-specific, user-space futex virtual
+	 * address(es) to a universal memid.  If the private bit is set, we
+	 * can just use our as plus the virtual address, saving quite a bit
+	 * of effort.
+	 */
+	if (private) {
+		memid.val[0] = (uintptr_t)as;
+		memid.val[1] = (uintptr_t)addr;
+	} else {
+		rval = as_getmemid(as, (void *)addr, &memid);
+		if (rval != 0)
+			return (set_errno(rval));
+	}
+
+	if (cmd == FUTEX_REQUEUE || cmd == FUTEX_CMP_REQUEUE ||
+	    cmd == FUTEX_WAKE_OP) {
+		if (addr2 & 0x3)
+			return (set_errno(EINVAL));
+
+		if (private) {
+			memid2.val[0] = (uintptr_t)as;
+			memid2.val[1] = (uintptr_t)addr2;
+		} else {
+			rval = as_getmemid(as, (void *)addr2, &memid2);
+			if (rval)
+				return (set_errno(rval));
+		}
+	}
+
+	switch (cmd) {
+	case FUTEX_WAIT:
+		rval = futex_wait(&memid, (void *)addr, val,
+		    tptr, FUTEX_BITSET_MATCH_ANY);
+		break;
+
+	case FUTEX_WAIT_BITSET:
+		rval = futex_wait(&memid, (void *)addr, val, tptr, val3);
+		break;
+
+	case FUTEX_WAKE:
+		rval = futex_wake(&memid, val, FUTEX_BITSET_MATCH_ANY);
+		break;
+
+	case FUTEX_WAKE_BITSET:
+		rval = futex_wake(&memid, val, val3);
+		break;
+
+	case FUTEX_WAKE_OP:
+		rval = futex_wake_op(&memid, (void *)addr2, &memid2,
+		    val, val2, val3);
+		break;
+
+	case FUTEX_CMP_REQUEUE:
+	case FUTEX_REQUEUE:
+		rval = futex_requeue(&memid, &memid2, val,
+		    val2, (void *)addr2, &val3);
+
+		break;
+	}
+
+	return (rval);
+}
+
+/*
+ * Does the dirty work of actually dropping a held robust lock in the event
+ * of the untimely death of the owner; see lx_futex_robust_exit(), below.
+ */
+static void
+lx_futex_robust_drop(uintptr_t addr, uint32_t tid)
+{
+	memid_t memid;
+	uint32_t oldval, newval;
+
+	VERIFY(addr + sizeof (uint32_t) < KERNELBASE);
+
+	do {
+		fuword32_noerr((void *)addr, &oldval);
+
+		if ((oldval & FUTEX_TID_MASK) != tid)
+			return;
+
+		newval = (oldval & FUTEX_WAITERS) | FUTEX_OWNER_DIED;
+	} while (atomic_cas_32((uint32_t *)addr, oldval, newval) != oldval);
+
+	/*
+	 * We have now denoted that this lock's owner is dead; we need to
+	 * wake any waiters.
+	 */
+	if (as_getmemid(curproc->p_as, (void *)addr, &memid) != 0)
+		return;
+
+	(void) futex_wake(&memid, 1, FUTEX_BITSET_MATCH_ANY);
+}
+
+/*
+ * Called when a thread is exiting.  The role of the kernel is very clearly
+ * spelled out in the Linux design document entitled robust-futex-ABI.txt:
+ * we must (carefully!) iterate over the list of held locks pointed to by
+ * the robust list head; for each lock, we'll check to see if the calling
+ * (exiting) thread is the owner, and if so, denote that the lock is dead
+ * and wake any waiters.  (The "pending" field of the head points to a lock
+ * that is in transition; it should be dropped if held.)  If there are any
+ * errors through here at all (including memory operations), we abort the
+ * entire operation.
+ */
+void
+lx_futex_robust_exit(uintptr_t addr, uint32_t tid)
+{
+	futex_robust_list_t list;
+	uintptr_t entry, next;
+	model_t model = get_udatamodel();
+	int length = 0;
+	label_t ljb;
+
+	if (on_fault(&ljb))
+		return;
+
+	if (addr + sizeof (futex_robust_list_t) >= KERNELBASE)
+		goto out;
+
+	if (model == DATAMODEL_NATIVE) {
+		copyin_noerr((void *)addr, &list, sizeof (list));
+	}
+#if defined(_SYSCALL32_IMPL)
+	else {
+		futex_robust_list32_t list32;
+
+		copyin_noerr((void *)addr, &list32, sizeof (list32));
+		list.frl_head = list32.frl_head;
+		list.frl_offset = list32.frl_offset;
+		list.frl_pending = list32.frl_pending;
+	}
+#endif
+
+	/*
+	 * Strip off the PI bit, if any.
+	 */
+	entry = list.frl_head & ~FUTEX_ROBUST_LOCK_PI;
+
+	while (entry != addr && length++ < FUTEX_ROBUST_LIST_LIMIT) {
+		if (entry + list.frl_offset + sizeof (uint32_t) >= KERNELBASE)
+			goto out;
+
+		if (model == DATAMODEL_NATIVE) {
+			fulword_noerr((void *)entry, &next);
+		}
+#if defined(_SYSCALL32_IMPL)
+		else {
+			uint32_t next32;
+			fuword32_noerr((void *)entry, &next32);
+			next = next32;
+		}
+#endif
+
+		/*
+		 * Drop the robust mutex -- but only if our pending lock didn't
+		 * somehow sneak on there.
+		 */
+		if (entry != list.frl_pending)
+			lx_futex_robust_drop(entry + list.frl_offset, tid);
+
+		entry = next & ~FUTEX_LOCK_PI;
+	}
+
+	/*
+	 * Finally, drop the pending lock if there is one.
+	 */
+	if (list.frl_pending != NULL && list.frl_pending +
+	    list.frl_offset + sizeof (uint32_t) < KERNELBASE)
+		lx_futex_robust_drop(list.frl_pending + list.frl_offset, tid);
+
+out:
+	no_fault();
+}
+
+long
+lx_set_robust_list(void *listp, size_t len)
+{
+	proc_t *p = curproc;
+	klwp_t *lwp = ttolwp(curthread);
+	struct lx_lwp_data *lwpd = lwptolxlwp(lwp);
+
+	if (get_udatamodel() == DATAMODEL_NATIVE) {
+		if (len != sizeof (futex_robust_list_t))
+			return (set_errno(EINVAL));
+	}
+#if defined(_SYSCALL32_IMPL)
+	else {
+		if (len != sizeof (futex_robust_list32_t))
+			return (set_errno(EINVAL));
+	}
+#endif
+
+	/*
+	 * To assure that we are serialized with respect to any racing call
+	 * to lx_get_robust_list(), we lock ourselves to set the value.  (Note
+	 * that sprunlock() drops p_lock.)
+	 */
+	mutex_enter(&p->p_lock);
+	sprlock_proc(p);
+	lwpd->br_robust_list = listp;
+	sprunlock(p);
+
+	return (0);
+}
+
+long
+lx_get_robust_list(pid_t pid, void **listp, size_t *lenp)
+{
+	model_t model = get_udatamodel();
+	pid_t rpid;
+	id_t rtid;
+	proc_t *rproc;
+	klwp_t *rlwp;
+	lx_lwp_data_t *rlwpd;
+	kthread_t *rthr;
+	void *list;
+	int err = 0;
+
+	if (pid == 0) {
+		/*
+		 * A pid of 0 denotes the current thread; we lock the current
+		 * process even though it isn't strictly necessary (we can't
+		 * race with set_robust_list() because a thread may only set
+		 * its robust list on itself).
+		 */
+		rproc = curproc;
+		rlwpd = lwptolxlwp(ttolwp(curthread));
+		mutex_enter(&curproc->p_lock);
+		sprlock_proc(rproc);
+	} else {
+		if (lx_lpid_to_spair(pid, &rpid, &rtid) != 0 ||
+		    (rproc = sprlock(rpid)) == NULL) {
+			/*
+			 * We couldn't find the specified process.
+			 */
+			return (set_errno(ESRCH));
+		}
+
+		if (rproc->p_model != model ||
+		    (rthr = idtot(rproc, rtid)) == NULL ||
+		    (rlwp = ttolwp(rthr)) == NULL ||
+		    (rlwpd = lwptolxlwp(rlwp)) == NULL) {
+			/*
+			 * The target process does not match our data model, or
+			 * we couldn't find the LWP, or the target process is
+			 * not branded.
+			 */
+			err = ESRCH;
+			goto out;
+		}
+	}
+
+	if (curproc != rproc &&
+	    priv_proc_cred_perm(curproc->p_cred, rproc, NULL, VREAD) != 0) {
+		/*
+		 * We don't have the permission to examine the target.
+		 */
+		err = EPERM;
+		goto out;
+	}
+
+	list = rlwpd->br_robust_list;
+
+out:
+	sprunlock(rproc);
+
+	if (err != 0)
+		return (set_errno(err));
+
+	if (model == DATAMODEL_NATIVE) {
+		if (sulword(listp, (uintptr_t)list) != 0)
+			return (set_errno(EFAULT));
+
+		if (sulword(lenp, sizeof (futex_robust_list_t)) != 0)
+			return (set_errno(EFAULT));
+	}
+#if defined(_SYSCALL32_IMPL)
+	else {
+		if (suword32(listp, (uint32_t)(uintptr_t)list) != 0)
+			return (set_errno(EFAULT));
+
+		if (suword32(lenp, sizeof (futex_robust_list32_t)) != 0)
+			return (set_errno(EFAULT));
+	}
+#endif
+
+	return (0);
+}
+
+void
+lx_futex_init(void)
+{
+	int i;
+
+	for (i = 0; i < HASH_SIZE; i++)
+		mutex_init(&futex_hash[i].fh_lock, NULL, MUTEX_DEFAULT, NULL);
+}
+
+int
+lx_futex_fini(void)
+{
+	int i, err;
+
+	err = 0;
+	for (i = 0; (err == 0) && (i < HASH_SIZE); i++) {
+		mutex_enter(&futex_hash[i].fh_lock);
+		if (futex_hash[i].fh_waiters != NULL)
+			err = EBUSY;
+		mutex_exit(&futex_hash[i].fh_lock);
+	}
+	return (err);
+}
--- /dev/null	Wed Oct  5 00:32:08 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_getcwd.c	Wed Oct  5 00:32:08 2016
@@ -0,0 +1,50 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#include <sys/systm.h>
+#include <sys/types.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/pathname.h>
+
+/*
+ * getcwd() - Linux syscall semantics are slightly different; we need to return
+ * the length of the pathname copied (+ 1 for the terminating NULL byte.)
+ */
+long
+lx_getcwd(char *buf, int size)
+{
+	int len;
+	int error;
+	vnode_t *vp;
+	char path[MAXPATHLEN + 1];
+
+	vp = PTOU(curproc)->u_cdir;
+	VN_HOLD(vp);
+	if ((error = vnodetopath(NULL, vp, path, sizeof (path), CRED())) != 0) {
+		VN_RELE(vp);
+		return (set_errno(error));
+	}
+	VN_RELE(vp);
+
+	len = strlen(path) + 1;
+	if (len > size)
+		return (set_errno(ERANGE));
+
+	if (copyout(path, buf, len) != 0)
+		return (set_errno(EFAULT));
+
+	return (len);
+}
--- /dev/null	Wed Oct  5 00:32:08 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_getdents.c	Wed Oct  5 00:32:08 2016
@@ -0,0 +1,354 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#include <sys/systm.h>
+#include <sys/filio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/inttypes.h>
+#include <sys/vnode.h>
+#include <sys/dirent.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/sysmacros.h>
+#include <sys/kmem.h>
+#include <sys/sunddi.h>
+
+#include <sys/lx_types.h>
+#include <sys/lx_misc.h>
+
+#define	LX_NAMEMAX	256
+
+#define	LX_GETDENTS_MAX_BUFSZ	65536
+
+/*
+ * Because the Linux dirent has an extra field (d_type), it's possible that
+ * each entry will be 8 bytes larger (and aligned to 8 bytes) due to padding.
+ * To prevent overrun during translation, the illumos-native buffer is sized
+ * pessimistically.
+ */
+#define	LTOS_GETDENTS_BUFSZ(bufsz, datasz)	\
+	(((bufsz) / (((datasz) + 15) & ~7)) * sizeof (struct dirent))
+
+/*
+ * Record must be long enough to house d_name string, null terminator and
+ * d_type field.  It's then padded to nearest 8-byte boundary
+ */
+#define	LX_RECLEN(l, t)	\
+	((offsetof(t, d_name) + 2 + (l) + 7) & ~7)
+
+/*
+ * Bytes after d_name string until d_reclen should be zeroed.
+ * Includes zero-terminating d_name
+ */
+#define	LX_ZEROLEN(l, t)	\
+	(LX_RECLEN(l, t) -	\
+	((offsetof(t, d_name) + (l))))
+
+/* The output format of getdents differs if the caller is 32 or 64 bit. */
+struct lx_dirent_32 {
+	uint32_t	d_ino;
+	int32_t		d_off;
+	ushort_t	d_reclen;
+	char		d_name[1];
+	uchar_t		d_type;
+};
+
+struct lx_dirent_64 {
+	uint64_t	d_ino;
+	int64_t		d_off;
+	ushort_t	d_reclen;
+	char		d_name[1];
+	uchar_t		d_type;
+};
+
+static long
+lx_getdents_common(int fd, caddr_t uptr, size_t count,
+    unsigned int lx_size, int (*outcb)(caddr_t, caddr_t, int))
+{
+	vnode_t *vp;
+	file_t *fp;
+	struct uio auio;
+	struct iovec aiov;
+	int error, at_eof;
+	int sbufsz, lbufsz, bufsz;
+	void *lbuf, *sbuf;
+	size_t outb = 0;
+
+	if (count < lx_size) {
+		return (set_errno(EINVAL));
+	}
+	if ((fp = getf(fd)) == NULL) {
+		return (set_errno(EBADF));
+	}
+	vp = fp->f_vnode;
+	if (vp->v_type != VDIR) {
+		releasef(fd);
+		return (set_errno(ENOTDIR));
+	}
+	if (!(fp->f_flag & FREAD)) {
+		releasef(fd);
+		return (set_errno(EBADF));
+	}
+
+	if (count > LX_GETDENTS_MAX_BUFSZ) {
+		/*
+		 * If the target buffer passed to us is huge, keep the
+		 * translation buffers moderate in size.  Iteration will be
+		 * used to fill the request.
+		 */
+		lbufsz = LX_GETDENTS_MAX_BUFSZ;
+		sbufsz = LTOS_GETDENTS_BUFSZ(LX_GETDENTS_MAX_BUFSZ, lx_size);
+	} else if (count < (lx_size + MAXPATHLEN)) {
+		/*
+		 * If the target buffer is tiny, allocate a Linux-format buffer
+		 * big enough to hold at least one max-length row while keeping
+		 * the illumos-format buffer pesimistic in size.
+		 *
+		 * Assuming the buffer is truely tiny, it's likely that the
+		 * result will not fit and an EINVAL will be tossed.
+		 */
+		lbufsz = (lx_size + MAXPATHLEN);
+		sbufsz = MAX((LTOS_GETDENTS_BUFSZ(count, lx_size)),
+		    sizeof (struct dirent));
+	} else {
+		lbufsz = count;
+		sbufsz = LTOS_GETDENTS_BUFSZ(count, lx_size);
+	}
+	bufsz = sbufsz;
+	lbuf = kmem_alloc(lbufsz, KM_SLEEP);
+	sbuf = kmem_alloc(sbufsz, KM_SLEEP);
+
+	aiov.iov_base = sbuf;
+	aiov.iov_len = sbufsz;
+	auio.uio_iov = &aiov;
+	auio.uio_iovcnt = 1;
+	auio.uio_loffset = fp->f_offset;
+	auio.uio_segflg = UIO_SYSSPACE;
+	auio.uio_resid = sbufsz;
+	auio.uio_fmode = 0;
+	auio.uio_extflg = UIO_COPY_CACHED;
+
+	/*
+	 * Since we use a conservative buffer allocation for the differing
+	 * struct sizing and Linux places fewer limits on getdents buffers in
+	 * general, there's a chance we'll undershoot on the record count.
+	 * When this happens, we can simply repeat the READDIR operation until
+	 * the available records are exhausted or we've filled the user buffer.
+	 */
+	do {
+		int res;
+
+		(void) VOP_RWLOCK(vp, V_WRITELOCK_FALSE, NULL);
+		error = VOP_READDIR(vp, &auio, fp->f_cred, &at_eof, NULL, 0);
+		VOP_RWUNLOCK(vp, V_WRITELOCK_FALSE, NULL);
+		if (error != 0 || auio.uio_resid == sbufsz) {
+			break;
+		}
+		res = outcb(sbuf, lbuf, bufsz - auio.uio_resid);
+		VERIFY(res <= lbufsz);
+		if (res == 0) {
+			/* no records to copyout from this batch */
+			break;
+		} else if (res > count) {
+			/*
+			 * For very small buffer sizes, it's possible that a
+			 * single record is too large due to a long filename.
+			 */
+			error = EINVAL;
+			break;
+		}
+
+		VERIFY(outb + res <= count);
+		if (copyout(lbuf, (void *)(uptr + outb), res) != 0) {
+			error = EFAULT;
+			break;
+		}
+		outb += res;
+
+		/*
+		 * We undershot the request buffer.
+		 * Reset for another READDIR, taking care not to overshoot.
+		 */
+		bufsz = MIN(sbufsz, LTOS_GETDENTS_BUFSZ(count - outb, lx_size));
+		auio.uio_resid = bufsz;
+		aiov.iov_len = bufsz;
+		aiov.iov_base = sbuf;
+
+		/*
+		 * Continued progress is allowed only if EOF has not been
+		 * reached and there is enough remaining buffer space to hold
+		 * an entry with a max-length filename.
+		 */
+	} while (at_eof == 0 && (count - outb) >= (lx_size + MAXPATHLEN));
+
+	kmem_free(lbuf, lbufsz);
+	kmem_free(sbuf, sbufsz);
+
+	if (error) {
+		releasef(fd);
+		return (set_errno(error));
+	}
+
+	fp->f_offset = auio.uio_loffset;
+	releasef(fd);
+	return (outb);
+}
+
+
+static int
+lx_getdents_format32(caddr_t sbuf, caddr_t lbuf, int len)
+{
+	struct dirent *sd;
+	struct lx_dirent_32 *ld;
+	int namelen;
+	int size = 0;
+
+	while (len > 0) {
+		/* LINTED: alignment */
+		sd = (struct dirent *)sbuf;
+		/* LINTED: alignment */
+		ld = (struct lx_dirent_32 *)lbuf;
+		namelen = MIN(strlen(sd->d_name), LX_NAMEMAX - 1);
+
+		ld->d_ino = sd->d_ino;
+		ld->d_off = sd->d_off;
+		(void) strncpy(ld->d_name, sd->d_name, namelen);
+		ld->d_name[namelen] = 0;
+		ld->d_reclen = (ushort_t)LX_RECLEN(namelen,
+		    struct lx_dirent_32);
+		/* Zero out any alignment padding and d_type */
+		bzero(ld->d_name + namelen,
+		    LX_ZEROLEN(namelen, struct lx_dirent_32));
+
+		len -= sd->d_reclen;
+		size += ld->d_reclen;
+		sbuf += sd->d_reclen;
+		lbuf += ld->d_reclen;
+	}
+	return (size);
+}
+
+static int
+lx_getdents_format64(caddr_t sbuf, caddr_t lbuf, int len)
+{
+	struct dirent *sd;
+	struct lx_dirent_64 *ld;
+	int namelen;
+	int size = 0;
+
+	while (len > 0) {
+		/* LINTED: alignment */
+		sd = (struct dirent *)sbuf;
+		/* LINTED: alignment */
+		ld = (struct lx_dirent_64 *)lbuf;
+		namelen = MIN(strlen(sd->d_name), LX_NAMEMAX - 1);
+
+		ld->d_ino = sd->d_ino;
+		ld->d_off = sd->d_off;
+		(void) strncpy(ld->d_name, sd->d_name, namelen);
+		ld->d_name[namelen] = 0;
+		ld->d_reclen = (ushort_t)LX_RECLEN(namelen,
+		    struct lx_dirent_64);
+		/* Zero out any alignment padding and d_type */
+		bzero(ld->d_name + namelen,
+		    LX_ZEROLEN(namelen, struct lx_dirent_64));
+
+		len -= sd->d_reclen;
+		size += ld->d_reclen;
+		sbuf += sd->d_reclen;
+		lbuf += ld->d_reclen;
+	}
+	return (size);
+}
+
+long
+lx_getdents_32(int fd, caddr_t buf, size_t count)
+{
+	return (lx_getdents_common(fd, buf, count,
+	    sizeof (struct lx_dirent_32), lx_getdents_format32));
+}
+
+long
+lx_getdents_64(int fd, caddr_t buf, size_t count)
+{
+	return (lx_getdents_common(fd, buf, count,
+	    sizeof (struct lx_dirent_64), lx_getdents_format64));
+}
+
+struct lx_dirent64 {
+	uint64_t	d_ino;
+	int64_t		d_off;
+	ushort_t	d_reclen;
+	uchar_t		d_type;
+	char		d_name[1];
+};
+
+#define	LX_RECLEN64(namelen)	\
+	((offsetof(struct lx_dirent64, d_name) + 1 + (namelen) + 7) & ~7)
+
+#define	LX_ZEROLEN64(namelen)	\
+	(LX_RECLEN64(namelen) -	\
+	((offsetof(struct lx_dirent64, d_name) + (namelen))))
+
+static int
+lx_getdents64_format(caddr_t sbuf, caddr_t lbuf, int len)
+{
+	struct dirent *sd;
+	struct lx_dirent64 *ld;
+	int namelen;
+	int size = 0;
+
+	while (len > 0) {
+		/* LINTED: alignment */
+		sd = (struct dirent *)sbuf;
+		/* LINTED: alignment */
+		ld = (struct lx_dirent64 *)lbuf;
+		namelen = MIN(strlen(sd->d_name), LX_NAMEMAX - 1);
+
+		ld->d_ino = sd->d_ino;
+		ld->d_off = sd->d_off;
+		ld->d_type = 0;
+		(void) strncpy(ld->d_name, sd->d_name, namelen);
+		ld->d_name[namelen] = 0;
+		ld->d_reclen = (ushort_t)LX_RECLEN64(namelen);
+		/* Zero out any alignment padding */
+		bzero(ld->d_name + namelen, LX_ZEROLEN64(namelen));
+
+		len -= sd->d_reclen;
+		size += ld->d_reclen;
+		sbuf += sd->d_reclen;
+		lbuf += ld->d_reclen;
+	}
+	return (size);
+}
+
+
+long
+lx_getdents64(int fd, caddr_t buf, size_t count)
+{
+	return (lx_getdents_common(fd, buf, count,
+	    sizeof (struct lx_dirent64), lx_getdents64_format));
+}
--- /dev/null	Wed Oct  5 00:32:08 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_getpid.c	Wed Oct  5 00:32:08 2016
@@ -0,0 +1,79 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * Copyright 2015 Joyent, Inc.
+ */
+
+#include <sys/zone.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/thread.h>
+#include <sys/cpuvar.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+
+/*
+ * return the pid
+ */
+long
+lx_getpid(void)
+{
+	lx_lwp_data_t *lwpd = ttolxlwp(curthread);
+	long rv;
+
+	if (curproc->p_pid == curproc->p_zone->zone_proc_initpid) {
+		rv = 1;
+	} else {
+		VERIFY(lwpd != NULL);
+
+		if (lwpd->br_lx_thunk_pid != 0) {
+			rv = lwpd->br_lx_thunk_pid;
+		} else {
+			rv = lwpd->br_tgid;
+		}
+	}
+
+	return (rv);
+}
+
+/*
+ * return the parent pid
+ */
+long
+lx_getppid(void)
+{
+	return (lx_lwp_ppid(ttolwp(curthread), NULL, NULL));
+}
+
+/*
+ * return the thread id
+ */
+long
+lx_gettid(void)
+{
+	lx_lwp_data_t *lwpd = ttolxlwp(curthread);
+
+	return (lwpd->br_pid);
+}
--- /dev/null	Wed Oct  5 00:32:08 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_getrandom.c	Wed Oct  5 00:32:08 2016
@@ -0,0 +1,33 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+
+/*
+ * From "uts/common/syscall/getrandom.c":
+ */
+extern int getrandom(void *, size_t, int);
+
+long
+lx_getrandom(void *bufp, size_t buflen, int flags)
+{
+	/*
+	 * According to signal(7), calls to getrandom(2) are restartable.
+	 */
+	ttolxlwp(curthread)->br_syscall_restart = B_TRUE;
+
+	return (getrandom(bufp, buflen, flags));
+}
--- /dev/null	Wed Oct  5 00:32:08 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_id.c	Wed Oct  5 00:32:08 2016
@@ -0,0 +1,509 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/zone.h>
+#include <sys/cred.h>
+#include <sys/cred_impl.h>
+#include <sys/policy.h>
+#include <sys/lx_types.h>
+
+#define	LX_NGROUPS_MAX	32
+
+/* From usr/src/uts/common/syscall/gid.c & uid.c */
+extern int setgid(gid_t);
+extern int setregid(gid_t, gid_t);
+extern int setreuid(uid_t, uid_t);
+extern int setuid(uid_t);
+
+/* From usr/src/uts/common/syscall/groups.c */
+extern int setgroups(int, gid_t *);
+
+long
+lx_getegid(void)
+{
+	return (crgetgid(CRED()));
+}
+
+long
+lx_getegid16(void)
+{
+	return ((int)LX_GID32_TO_GID16(crgetgid(CRED())));
+}
+
+long
+lx_geteuid(void)
+{
+	return (crgetuid(CRED()));
+}
+
+long
+lx_geteuid16(void)
+{
+	return ((int)LX_UID32_TO_UID16(crgetuid(CRED())));
+}
+
+long
+lx_getgid(void)
+{
+	return (crgetrgid(CRED()));
+}
+
+long
+lx_getgid16(void)
+{
+	return ((int)LX_GID32_TO_GID16(crgetrgid(CRED())));
+}
+
+long
+lx_getuid(void)
+{
+	return (crgetruid(CRED()));
+}
+
+long
+lx_getuid16(void)
+{
+	return ((int)LX_UID32_TO_UID16(crgetruid(CRED())));
+}
+
+long
+lx_setgid(gid_t gid)
+{
+	return (setgid(gid));
+}
+
+long
+lx_setgid16(lx_gid16_t gid)
+{
+	return (setgid(LX_GID16_TO_GID32(gid)));
+}
+
+long
+lx_setregid(gid_t rgid, gid_t egid)
+{
+	return (setregid(rgid, egid));
+}
+
+long
+lx_setregid16(lx_gid16_t rgid, lx_gid16_t egid)
+{
+	return (setregid(LX_UID16_TO_UID32(rgid), LX_UID16_TO_UID32(egid)));
+}
+
+long
+lx_setreuid(uid_t ruid, uid_t euid)
+{
+	return (setreuid(ruid, euid));
+}
+
+long
+lx_setreuid16(lx_uid16_t ruid, lx_uid16_t euid)
+{
+	return (setreuid(LX_UID16_TO_UID32(ruid), LX_UID16_TO_UID32(euid)));
+}
+
+long
+lx_setuid(uid_t uid)
+{
+	return (setuid(uid));
+}
+
+long
+lx_setuid16(lx_uid16_t uid)
+{
+	return (setuid(LX_UID16_TO_UID32(uid)));
+}
+
+/*
+ * This function is based on setreuid in common/syscall/uid.c and exists
+ * because illumos does not have a way to explicitly set the saved uid (suid)
+ * from any other system call.
+ */
+long
+lx_setresuid(lx_uid_t ruid, lx_uid_t euid, lx_uid_t suid)
+{
+	proc_t	*p;
+	int	error = 0;
+	int	do_nocd = 0;
+	int	uidchge = 0;
+	uid_t	oldruid = ruid;
+	cred_t	*cr, *newcr;
+	zoneid_t zoneid = getzoneid();
+
+	if ((ruid != -1 && (ruid > MAXUID)) ||
+	    (euid != -1 && (euid > MAXUID)) ||
+	    (suid != -1 && (suid > MAXUID))) {
+		error = EINVAL;
+		goto done;
+	}
+
+	/*
+	 * Need to pre-allocate the new cred structure before grabbing
+	 * the p_crlock mutex.
+	 */
+	newcr = cralloc();
+
+	p = ttoproc(curthread);
+
+retry:
+	mutex_enter(&p->p_crlock);
+	cr = p->p_cred;
+
+	if (ruid != -1 &&
+	    ruid != cr->cr_ruid && ruid != cr->cr_uid &&
+	    ruid != cr->cr_suid && secpolicy_allow_setid(cr, ruid, B_FALSE)) {
+		error = EPERM;
+	} else if (euid != -1 &&
+	    euid != cr->cr_ruid && euid != cr->cr_uid &&
+	    euid != cr->cr_suid && secpolicy_allow_setid(cr, euid, B_FALSE)) {
+		error = EPERM;
+	} else if (suid != -1 &&
+	    suid != cr->cr_ruid && suid != cr->cr_uid &&
+	    suid != cr->cr_suid && secpolicy_allow_setid(cr, suid, B_FALSE)) {
+		error = EPERM;
+	} else {
+		if (!uidchge && ruid != -1 && cr->cr_ruid != ruid) {
+			/*
+			 * The ruid of the process is going to change. In order
+			 * to avoid a race condition involving the
+			 * process count associated with the newly given ruid,
+			 * we increment the count before assigning the
+			 * credential to the process.
+			 * To do that, we'll have to take pidlock, so we first
+			 * release p_crlock.
+			 */
+			mutex_exit(&p->p_crlock);
+			uidchge = 1;
+			mutex_enter(&pidlock);
+			upcount_inc(ruid, zoneid);
+			mutex_exit(&pidlock);
+			/*
+			 * As we released p_crlock we can't rely on the cr
+			 * we read. So retry the whole thing.
+			 */
+			goto retry;
+		}
+		crhold(cr);
+		crcopy_to(cr, newcr);
+		p->p_cred = newcr;
+
+		if (euid != -1)
+			newcr->cr_uid = euid;
+		if (suid != -1)
+			newcr->cr_suid = suid;
+		if (ruid != -1) {
+			oldruid = newcr->cr_ruid;
+			newcr->cr_ruid = ruid;
+			ASSERT(ruid != oldruid ? uidchge : 1);
+		}
+
+		/*
+		 * A process that gives up its privilege
+		 * must be marked to produce no core dump.
+		 */
+		if ((cr->cr_uid != newcr->cr_uid ||
+		    cr->cr_ruid != newcr->cr_ruid ||
+		    cr->cr_suid != newcr->cr_suid))
+			do_nocd = 1;
+
+		crfree(cr);
+	}
+	mutex_exit(&p->p_crlock);
+
+	/*
+	 * We decrement the number of processes associated with the oldruid
+	 * to match the increment above, even if the ruid of the process
+	 * did not change or an error occurred (oldruid == uid).
+	 */
+	if (uidchge) {
+		ASSERT(oldruid != -1 && ruid != -1);
+		mutex_enter(&pidlock);
+		upcount_dec(oldruid, zoneid);
+		mutex_exit(&pidlock);
+	}
+
+	if (error == 0) {
+		if (do_nocd) {
+			mutex_enter(&p->p_lock);
+			p->p_flag |= SNOCD;
+			mutex_exit(&p->p_lock);
+		}
+		crset(p, newcr);	/* broadcast to process threads */
+		goto done;
+	}
+	crfree(newcr);
+done:
+	if (error)
+		return (set_errno(error));
+	else
+		return (0);
+}
+
+long
+lx_setresuid16(lx_uid16_t ruid16, lx_uid16_t euid16, lx_uid16_t suid16)
+{
+	long	rval;
+
+	rval = lx_setresuid(
+	    LX_UID16_TO_UID32(ruid16),
+	    LX_UID16_TO_UID32(euid16),
+	    LX_UID16_TO_UID32(suid16));
+
+	return (rval);
+}
+
+/*
+ * This function is based on setregid in common/syscall/gid.c
+ */
+long
+lx_setresgid(lx_gid_t rgid, lx_gid_t egid, lx_gid_t sgid)
+{
+	proc_t	*p;
+	int	error = 0;
+	int	do_nocd = 0;
+	cred_t	*cr, *newcr;
+
+	if ((rgid != -1 && (rgid > MAXUID)) ||
+	    (egid != -1 && (egid > MAXUID)) ||
+	    (sgid != -1 && (sgid > MAXUID))) {
+		error = EINVAL;
+		goto done;
+	}
+
+	/*
+	 * Need to pre-allocate the new cred structure before grabbing
+	 * the p_crlock mutex.
+	 */
+	newcr = cralloc();
+
+	p = ttoproc(curthread);
+	mutex_enter(&p->p_crlock);
+	cr = p->p_cred;
+
+	if (rgid != -1 &&
+	    rgid != cr->cr_rgid && rgid != cr->cr_gid &&
+	    rgid != cr->cr_sgid && secpolicy_allow_setid(cr, -1, B_FALSE)) {
+		error = EPERM;
+	} else if (egid != -1 &&
+	    egid != cr->cr_rgid && egid != cr->cr_gid &&
+	    egid != cr->cr_sgid && secpolicy_allow_setid(cr, -1, B_FALSE)) {
+		error = EPERM;
+	} else if (sgid != -1 &&
+	    sgid != cr->cr_rgid && sgid != cr->cr_gid &&
+	    sgid != cr->cr_sgid && secpolicy_allow_setid(cr, -1, B_FALSE)) {
+		error = EPERM;
+	} else {
+		crhold(cr);
+		crcopy_to(cr, newcr);
+		p->p_cred = newcr;
+
+		if (egid != -1)
+			newcr->cr_gid = egid;
+		if (sgid != -1)
+			newcr->cr_sgid = sgid;
+		if (rgid != -1)
+			newcr->cr_rgid = rgid;
+
+		/*
+		 * A process that gives up its privilege
+		 * must be marked to produce no core dump.
+		 */
+		if ((cr->cr_gid != newcr->cr_gid ||
+		    cr->cr_rgid != newcr->cr_rgid ||
+		    cr->cr_sgid != newcr->cr_sgid))
+			do_nocd = 1;
+
+		crfree(cr);
+	}
+	mutex_exit(&p->p_crlock);
+
+	if (error == 0) {
+		if (do_nocd) {
+			mutex_enter(&p->p_lock);
+			p->p_flag |= SNOCD;
+			mutex_exit(&p->p_lock);
+		}
+		crset(p, newcr);	/* broadcast to process threads */
+		goto done;
+	}
+	crfree(newcr);
+done:
+	if (error)
+		return (set_errno(error));
+	else
+		return (0);
+}
+
+long
+lx_setresgid16(lx_gid16_t rgid16, lx_gid16_t egid16, lx_gid16_t sgid16)
+{
+	long	rval;
+
+	rval = lx_setresgid(
+	    LX_GID16_TO_GID32(rgid16),
+	    LX_GID16_TO_GID32(egid16),
+	    LX_GID16_TO_GID32(sgid16));
+
+	return (rval);
+}
+
+/*
+ * Linux defines NGROUPS_MAX to be 32, but on illumos it is only 16. We employ
+ * the terrible hack below so that tests may proceed, if only on DEBUG kernels.
+ */
+int
+lx_helper_setgroups(int ngroups, gid_t *grouplist)
+{
+#ifdef DEBUG
+	if (ngroups > ngroups_max && ngroups <= LX_NGROUPS_MAX)
+		ngroups = ngroups_max;
+#endif /* DEBUG */
+
+	return (setgroups(ngroups, grouplist));
+}
+
+long
+lx_getresuid(lx_uid_t *ruid, lx_uid_t *euid, lx_uid_t *suid)
+{
+	lx_uid_t lx_ruid, lx_euid, lx_suid;
+	cred_t *cr = CRED();
+
+	lx_ruid = (lx_uid_t)crgetruid(cr);
+	lx_euid = (lx_uid_t)crgetuid(cr);
+	lx_suid = (lx_uid_t)crgetsuid(cr);
+
+	if (copyout(&lx_ruid, (void *)ruid, sizeof (lx_uid_t)) != 0)
+		return (set_errno(EFAULT));
+	if (copyout(&lx_euid, (void *)euid, sizeof (lx_uid_t)) != 0)
+		return (set_errno(EFAULT));
+	if (copyout(&lx_suid, (void *)suid, sizeof (lx_uid_t)) != 0)
+		return (set_errno(EFAULT));
+
+	return (0);
+}
+
+long
+lx_getresuid16(lx_uid16_t *ruid16, lx_uid16_t *euid16, lx_uid16_t *suid16)
+{
+	lx_uid16_t lx_ruid16, lx_euid16, lx_suid16;
+	cred_t *cr = CRED();
+
+	lx_ruid16 = LX_UID32_TO_UID16((lx_uid_t)crgetruid(cr));
+	lx_euid16 = LX_UID32_TO_UID16((lx_uid_t)crgetuid(cr));
+	lx_suid16 = LX_UID32_TO_UID16((lx_uid_t)crgetsuid(cr));
+
+	if (copyout(&lx_ruid16, (void *)ruid16, sizeof (lx_uid16_t)) != 0)
+		return (set_errno(EFAULT));
+	if (copyout(&lx_euid16, (void *)euid16, sizeof (lx_uid16_t)) != 0)
+		return (set_errno(EFAULT));
+	if (copyout(&lx_suid16, (void *)suid16, sizeof (lx_uid16_t)) != 0)
+		return (set_errno(EFAULT));
+
+	return (0);
+}
+
+long
+lx_getresgid(lx_gid_t *rgid, lx_gid_t *egid, lx_gid_t *sgid)
+{
+	lx_gid_t lx_rgid, lx_egid, lx_sgid;
+	cred_t *cr = CRED();
+
+	lx_rgid = (lx_gid_t)crgetrgid(cr);
+	lx_egid = (lx_gid_t)crgetgid(cr);
+	lx_sgid = (lx_gid_t)crgetsgid(cr);
+
+	if (copyout(&lx_rgid, (void *)rgid, sizeof (lx_gid_t)) != 0)
+		return (set_errno(EFAULT));
+	if (copyout(&lx_egid, (void *)egid, sizeof (lx_gid_t)) != 0)
+		return (set_errno(EFAULT));
+	if (copyout(&lx_sgid, (void *)sgid, sizeof (lx_gid_t)) != 0)
+		return (set_errno(EFAULT));
+
+	return (0);
+}
+
+long
+lx_getresgid16(lx_gid16_t *rgid16, lx_gid16_t *egid16, lx_gid16_t *sgid16)
+{
+	lx_gid16_t lx_rgid16, lx_egid16, lx_sgid16;
+	cred_t *cr = CRED();
+
+	lx_rgid16 = LX_GID32_TO_GID16((lx_gid_t)crgetrgid(cr));
+	lx_egid16 = LX_GID32_TO_GID16((lx_gid_t)crgetgid(cr));
+	lx_sgid16 = LX_GID32_TO_GID16((lx_gid_t)crgetsgid(cr));
+
+	if (copyout(&lx_rgid16, (void *)rgid16, sizeof (lx_gid16_t)) != 0)
+		return (set_errno(EFAULT));
+	if (copyout(&lx_egid16, (void *)egid16, sizeof (lx_gid16_t)) != 0)
+		return (set_errno(EFAULT));
+	if (copyout(&lx_sgid16, (void *)sgid16, sizeof (lx_gid16_t)) != 0)
+		return (set_errno(EFAULT));
+
+	return (0);
+}
+
+/*
+ * The lx brand cannot support the setfs[ug]id16/setfs[ug]id calls as that
+ * would require significant rework of the illumos privilege mechanisms, so
+ * instead return the current effective [ug]id.
+ *
+ * In Linux, fsids track effective IDs, so returning the effective IDs works
+ * as a substitute; returning the current value also denotes failure of the
+ * call if the caller had specified something different.  We don't need to
+ * worry about setting error codes because the Linux calls don't set any.
+ */
+/*ARGSUSED*/
+long
+lx_setfsuid16(uid_t fsuid16)
+{
+	return ((int)LX_UID32_TO_UID16(crgetuid(CRED())));
+}
+
+/*ARGSUSED*/
+long
+lx_setfsgid16(gid_t fsgid16)
+{
+	return ((int)LX_GID32_TO_GID16(crgetgid(CRED())));
+}
+
+/*ARGSUSED*/
+long
+lx_setfsuid(uid_t fsuid)
+{
+	return (crgetuid(CRED()));
+}
+
+/*ARGSUSED*/
+long
+lx_setfsgid(gid_t fsgid)
+{
+	return (crgetgid(CRED()));
+}
--- /dev/null	Wed Oct  5 00:32:08 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_ioctl.c	Wed Oct  5 00:32:08 2016
@@ -0,0 +1,1769 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#include <sys/errno.h>
+#include <sys/systm.h>
+#include <sys/file.h>
+#include <sys/filio.h>
+#include <sys/vnode.h>
+#include <sys/fcntl.h>
+#include <sys/termio.h>
+#include <sys/termios.h>
+#include <sys/ptyvar.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <sys/sockio.h>
+#include <sys/stropts.h>
+#include <sys/ptms.h>
+#include <sys/cred.h>
+#include <sys/cred_impl.h>
+#include <sys/sysmacros.h>
+#include <sys/lx_misc.h>
+#include <sys/lx_ptm.h>
+#include <sys/sunddi.h>
+#include <sys/thread.h>
+#include <sys/proc.h>
+#include <sys/session.h>
+#include <sys/kmem.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <net/if_arp.h>
+#include <sys/ioccom.h>
+#include <sys/dtrace.h>
+#include <sys/ethernet.h>
+#include <sys/dlpi.h>
+#include <sys/lx_autofs.h>
+#include <sys/netstack.h>
+#include <inet/ip.h>
+#include <inet/ip_if.h>
+#include <sys/dkio.h>
+#include <sys/sdt.h>
+
+/*
+ * Linux ioctl types
+ */
+#define	LX_IOC_TYPE_HD		0x03
+#define	LX_IOC_TYPE_BLK		0x12
+#define	LX_IOC_TYPE_FD		0x54
+#define	LX_IOC_TYPE_DTRACE	0x68
+#define	LX_IOC_TYPE_SOCK	0x89
+#define	LX_IOC_TYPE_AUTOFS	0x93
+
+/*
+ * Supported ioctls
+ */
+#define	LX_HDIO_GETGEO		0x0301
+#define	LX_BLKGETSIZE		0x1260
+#define	LX_BLKSSZGET		0x1268
+#define	LX_BLKGETSIZE64		0x80081272
+#define	LX_TCGETS		0x5401
+#define	LX_TCSETS		0x5402
+#define	LX_TCSETSW		0x5403
+#define	LX_TCSETSF		0x5404
+#define	LX_TCGETA		0x5405
+#define	LX_TCSETA		0x5406
+#define	LX_TCSETAW		0x5407
+#define	LX_TCSETAF		0x5408
+#define	LX_TCSBRK		0x5409
+#define	LX_TCXONC		0x540a
+#define	LX_TCFLSH		0x540b
+#define	LX_TIOCEXCL		0x540c
+#define	LX_TIOCNXCL		0x540d
+#define	LX_TIOCSCTTY		0x540e
+#define	LX_TIOCGPGRP		0x540f
+#define	LX_TIOCSPGRP		0x5410
+#define	LX_TIOCOUTQ		0x5411
+#define	LX_TIOCSTI		0x5412
+#define	LX_TIOCGWINSZ		0x5413
+#define	LX_TIOCSWINSZ		0x5414
+#define	LX_TIOCMGET		0x5415
+#define	LX_TIOCMBIS		0x5416
+#define	LX_TIOCMBIC		0x5417
+#define	LX_TIOCMSET		0x5418
+#define	LX_TIOCGSOFTCAR		0x5419
+#define	LX_TIOCSSOFTCAR		0x541a
+#define	LX_FIONREAD		0x541b
+#define	LX_TIOCPKT		0x5420
+#define	LX_FIONBIO		0x5421
+#define	LX_TIOCNOTTY		0x5422
+#define	LX_TIOCSETD		0x5423
+#define	LX_TIOCGETD		0x5424
+#define	LX_TCSBRKP		0x5425
+#define	LX_TIOCGSID		0x5429
+#define	LX_TIOCGPTN		0x80045430
+#define	LX_TIOCSPTLCK		0x40045431
+#define	LX_FIONCLEX		0x5450
+#define	LX_FIOCLEX		0x5451
+#define	LX_FIOASYNC		0x5452
+#define	LX_FIOSETOWN		0x8901
+#define	LX_SIOCSPGRP		0x8902
+#define	LX_FIOGETOWN		0x8903
+#define	LX_SIOCGPGRP		0x8904
+#define	LX_SIOCATMARK		0x8905
+#define	LX_SIOCGSTAMP		0x8906
+#define	LX_SIOCADDRT		0x890b
+#define	LX_SIOCDELRT		0x890c
+#define	LX_SIOCRTMSG		0x890d
+#define	LX_SIOCGIFNAME		0x8910
+#define	LX_SIOCSIFLINK		0x8911
+#define	LX_SIOCGIFCONF		0x8912
+#define	LX_SIOCGIFFLAGS		0x8913
+#define	LX_SIOCSIFFLAGS		0x8914
+#define	LX_SIOCGIFADDR		0x8915
+#define	LX_SIOCSIFADDR		0x8916
+#define	LX_SIOCGIFDSTADDR	0x8917
+#define	LX_SIOCSIFDSTADDR	0x8918
+#define	LX_SIOCGIFBRDADDR	0x8919
+#define	LX_SIOCSIFBRDADDR	0x891a
+#define	LX_SIOCGIFNETMASK	0x891b
+#define	LX_SIOCSIFNETMASK	0x891c
+#define	LX_SIOCGIFMETRIC	0x891d
+#define	LX_SIOCSIFMETRIC	0x891e
+#define	LX_SIOCGIFMEM		0x891f
+#define	LX_SIOCSIFMEM		0x8920
+#define	LX_SIOCGIFMTU		0x8921
+#define	LX_SIOCSIFMTU		0x8922
+#define	LX_SIOCSIFNAME		0x8923
+#define	LX_SIOCSIFHWADDR	0x8924
+#define	LX_SIOCGIFENCAP		0x8925
+#define	LX_SIOCSIFENCAP		0x8926
+#define	LX_SIOCGIFHWADDR	0x8927
+#define	LX_SIOCGIFSLAVE		0x8929
+#define	LX_SIOCSIFSLAVE		0x8930
+#define	LX_SIOCADDMULTI		0x8931
+#define	LX_SIOCDELMULTI		0x8932
+#define	LX_SIOCGIFINDEX		0x8933
+#define	LX_SIOCSIFPFLAGS	0x8934
+#define	LX_SIOCGIFPFLAGS	0x8935
+#define	LX_SIOCDIFADDR		0x8936
+#define	LX_SIOCSIFHWBROADCAST	0x8937
+#define	LX_SIOCGIFCOUNT		0x8938
+#define	LX_SIOCGIFBR		0x8940
+#define	LX_SIOCSIFBR		0x8941
+#define	LX_SIOCGIFTXQLEN	0x8942
+#define	LX_SIOCSIFTXQLEN	0x8943
+#define	LX_SIOCETHTOOL		0x8946
+#define	LX_SIOCGMIIPHY		0x8947
+#define	LX_SIOCGMIIREG		0x8948
+#define	LX_SIOCSMIIREG		0x8949
+#define	LX_SIOCWANDEV		0x894a
+#define	LX_SIOCOUTQNSD		0x894b
+#define	LX_SIOCDARP		0x8953
+#define	LX_SIOCGARP		0x8954
+#define	LX_SIOCSARP		0x8955
+#define	LX_SIOCDRARP		0x8960
+#define	LX_SIOCGRARP		0x8961
+#define	LX_SIOCSRARP		0x8962
+#define	LX_SIOCGIFMAP		0x8970
+#define	LX_SIOCSIFMAP		0x8971
+#define	LX_SIOCADDDLCI		0x8980
+#define	LX_SIOCDELDLCI		0x8981
+#define	LX_SIOCGIFVLAN		0x8982
+#define	LX_SIOCSIFVLAN		0x8983
+#define	LX_SIOCBONDENSLAVE	0x8990
+#define	LX_SIOCBONDRELEASE	0x8991
+#define	LX_SIOCBONDSETHWADDR	0x8992
+#define	LX_SIOCBONDSLAVEINFOQUERY 0x8993
+#define	LX_SIOCBONDINFOQUERY	0x8994
+#define	LX_SIOCBONDCHANGEACTIVE	0x8995
+#define	LX_SIOCBRADDBR		0x89a0
+#define	LX_SIOCBRDELBR		0x89a1
+#define	LX_SIOCBRADDIF		0x89a2
+#define	LX_SIOCBRDELIF		0x89a3
+#define	LX_SIOCSHWTSTAMP	0x89b0
+#define	LX_SIOCGHWTSTAMP	0x89b1
+#define	LX_SIOCDEVPRIVATE	0x89f0
+#define	LX_SIOCPROTOPRIVATE	0x89e0
+
+#define	FLUSER(fp)	fp->f_flag | get_udatamodel()
+#define	FLFAKE(fp)	fp->f_flag | FKIOCTL
+
+/*
+ * LX_NCC must be different from LX_NCCS since while the termio and termios
+ * structures may look similar they are fundamentally different sizes and
+ * have different members.
+ */
+#define	LX_NCC	8
+#define	LX_NCCS	19
+
+struct lx_termio {
+	unsigned short c_iflag;		/* input mode flags */
+	unsigned short c_oflag;		/* output mode flags */
+	unsigned short c_cflag;		/* control mode flags */
+	unsigned short c_lflag;		/* local mode flags */
+	unsigned char c_line;		/* line discipline */
+	unsigned char c_cc[LX_NCC];	/* control characters */
+};
+
+struct lx_termios {
+	uint32_t c_iflag;		/* input mode flags */
+	uint32_t c_oflag;		/* output mode flags */
+	uint32_t c_cflag;		/* control mode flags */
+	uint32_t c_lflag;		/* local mode flags */
+	unsigned char c_line;		/* line discipline */
+	unsigned char c_cc[LX_NCCS];	/* control characters */
+};
+
+/*
+ * c_cc characters which are valid for lx_termio and lx_termios
+ */
+#define	LX_VINTR	0
+#define	LX_VQUIT	1
+#define	LX_VERASE	2
+#define	LX_VKILL	3
+#define	LX_VEOF		4
+#define	LX_VTIME	5
+#define	LX_VMIN		6
+#define	LX_VSWTC	7
+
+/*
+ * c_cc characters which are valid for lx_termios
+ */
+#define	LX_VSTART	8
+#define	LX_VSTOP	9
+#define	LX_VSUSP	10
+#define	LX_VEOL		11
+#define	LX_VREPRINT	12
+#define	LX_VDISCARD	13
+#define	LX_VWERASE	14
+#define	LX_VLNEXT	15
+#define	LX_VEOL2	16
+
+/*
+ * Defaults needed for SunOS to Linux format conversion.
+ * See INIT_C_CC in linux-stable/include/asm-generic/termios.h
+ */
+#define	LX_DEF_VTIME	0
+#define	LX_DEF_VMIN	1
+#define	LX_DEF_VEOF	'\004'
+#define	LX_DEF_VEOL	0
+
+/* VSD key for lx_cc information */
+static uint_t lx_ioctl_vsd = 0;
+
+extern int lx_lpid_to_spair(pid_t l_pid, pid_t *s_pid, id_t *s_tid);
+
+/* Terminal helpers */
+
+static void
+l2s_termios(struct lx_termios *l_tios, struct termios *s_tios)
+{
+	ASSERT((l_tios != NULL) && (s_tios != NULL));
+
+	bzero(s_tios, sizeof (*s_tios));
+
+	s_tios->c_iflag = l_tios->c_iflag;
+	s_tios->c_oflag = l_tios->c_oflag;
+	s_tios->c_cflag = l_tios->c_cflag;
+	s_tios->c_lflag = l_tios->c_lflag;
+
+	if (s_tios->c_lflag & ICANON) {
+		s_tios->c_cc[VEOF] = l_tios->c_cc[LX_VEOF];
+		s_tios->c_cc[VEOL] = l_tios->c_cc[LX_VEOL];
+	} else {
+		s_tios->c_cc[VMIN] = l_tios->c_cc[LX_VMIN];
+		s_tios->c_cc[VTIME] = l_tios->c_cc[LX_VTIME];
+	}
+
+	s_tios->c_cc[VEOL2] = l_tios->c_cc[LX_VEOL2];
+	s_tios->c_cc[VERASE] = l_tios->c_cc[LX_VERASE];
+	s_tios->c_cc[VKILL] = l_tios->c_cc[LX_VKILL];
+	s_tios->c_cc[VREPRINT] = l_tios->c_cc[LX_VREPRINT];
+	s_tios->c_cc[VLNEXT] = l_tios->c_cc[LX_VLNEXT];
+	s_tios->c_cc[VWERASE] = l_tios->c_cc[LX_VWERASE];
+	s_tios->c_cc[VINTR] = l_tios->c_cc[LX_VINTR];
+	s_tios->c_cc[VQUIT] = l_tios->c_cc[LX_VQUIT];
+	s_tios->c_cc[VSWTCH] = l_tios->c_cc[LX_VSWTC];
+	s_tios->c_cc[VSTART] = l_tios->c_cc[LX_VSTART];
+	s_tios->c_cc[VSTOP] = l_tios->c_cc[LX_VSTOP];
+	s_tios->c_cc[VSUSP] = l_tios->c_cc[LX_VSUSP];
+	s_tios->c_cc[VDISCARD] = l_tios->c_cc[LX_VDISCARD];
+}
+
+static void
+l2s_termio(struct lx_termio *l_tio, struct termio *s_tio)
+{
+	ASSERT((l_tio != NULL) && (s_tio != NULL));
+
+	bzero(s_tio, sizeof (*s_tio));
+
+	s_tio->c_iflag = l_tio->c_iflag;
+	s_tio->c_oflag = l_tio->c_oflag;
+	s_tio->c_cflag = l_tio->c_cflag;
+	s_tio->c_lflag = l_tio->c_lflag;
+
+	if (s_tio->c_lflag & ICANON) {
+		s_tio->c_cc[VEOF] = l_tio->c_cc[LX_VEOF];
+	} else {
+		s_tio->c_cc[VMIN] = l_tio->c_cc[LX_VMIN];
+		s_tio->c_cc[VTIME] = l_tio->c_cc[LX_VTIME];
+	}
+
+	s_tio->c_cc[VINTR] = l_tio->c_cc[LX_VINTR];
+	s_tio->c_cc[VQUIT] = l_tio->c_cc[LX_VQUIT];
+	s_tio->c_cc[VERASE] = l_tio->c_cc[LX_VERASE];
+	s_tio->c_cc[VKILL] = l_tio->c_cc[LX_VKILL];
+	s_tio->c_cc[VSWTCH] = l_tio->c_cc[LX_VSWTC];
+}
+
+static void
+termios2lx_cc(struct lx_termios *l_tios, struct lx_cc *lio)
+{
+	ASSERT((l_tios != NULL) && (lio != NULL));
+
+	bzero(lio, sizeof (*lio));
+
+	lio->veof = l_tios->c_cc[LX_VEOF];
+	lio->veol = l_tios->c_cc[LX_VEOL];
+	lio->vmin = l_tios->c_cc[LX_VMIN];
+	lio->vtime = l_tios->c_cc[LX_VTIME];
+}
+
+static void
+termio2lx_cc(struct lx_termio *l_tio, struct lx_cc *lio)
+{
+	ASSERT((l_tio != NULL) && (lio != NULL));
+
+	bzero(lio, sizeof (*lio));
+
+	lio->veof = l_tio->c_cc[LX_VEOF];
+	lio->veol = 0;
+	lio->vmin = l_tio->c_cc[LX_VMIN];
+	lio->vtime = l_tio->c_cc[LX_VTIME];
+}
+
+static void
+s2l_termios(struct termios *s_tios, struct lx_termios *l_tios)
+{
+	ASSERT((s_tios != NULL) && (l_tios != NULL));
+
+	bzero(l_tios, sizeof (*l_tios));
+
+	l_tios->c_iflag = s_tios->c_iflag;
+	l_tios->c_oflag = s_tios->c_oflag;
+	l_tios->c_cflag = s_tios->c_cflag;
+	l_tios->c_lflag = s_tios->c_lflag;
+
+	/*
+	 * Since use of the VMIN/VTIME and VEOF/VEOL control characters is
+	 * mutually exclusive (determined by ICANON), SunOS aliases them in the
+	 * c_cc field in termio/termios.  Linux does not perform this aliasing,
+	 * so it expects that the default values are present regardless of
+	 * ICANON status.
+	 *
+	 * These defaults can be overridden later by any values stored via the
+	 * lx_cc mechanism.
+	 */
+	if (s_tios->c_lflag & ICANON) {
+		l_tios->c_cc[LX_VEOF] = s_tios->c_cc[VEOF];
+		l_tios->c_cc[LX_VEOL] = s_tios->c_cc[VEOL];
+		l_tios->c_cc[LX_VTIME] = LX_DEF_VTIME;
+		l_tios->c_cc[LX_VMIN] = LX_DEF_VMIN;
+
+	} else {
+		l_tios->c_cc[LX_VMIN] = s_tios->c_cc[VMIN];
+		l_tios->c_cc[LX_VTIME] = s_tios->c_cc[VTIME];
+		l_tios->c_cc[LX_VEOF] = LX_DEF_VEOF;
+		l_tios->c_cc[LX_VEOL] = LX_DEF_VEOL;
+	}
+
+	l_tios->c_cc[LX_VEOL2] = s_tios->c_cc[VEOL2];
+	l_tios->c_cc[LX_VERASE] = s_tios->c_cc[VERASE];
+	l_tios->c_cc[LX_VKILL] = s_tios->c_cc[VKILL];
+	l_tios->c_cc[LX_VREPRINT] = s_tios->c_cc[VREPRINT];
+	l_tios->c_cc[LX_VLNEXT] = s_tios->c_cc[VLNEXT];
+	l_tios->c_cc[LX_VWERASE] = s_tios->c_cc[VWERASE];
+	l_tios->c_cc[LX_VINTR] = s_tios->c_cc[VINTR];
+	l_tios->c_cc[LX_VQUIT] = s_tios->c_cc[VQUIT];
+	l_tios->c_cc[LX_VSWTC] = s_tios->c_cc[VSWTCH];
+	l_tios->c_cc[LX_VSTART] = s_tios->c_cc[VSTART];
+	l_tios->c_cc[LX_VSTOP] = s_tios->c_cc[VSTOP];
+	l_tios->c_cc[LX_VSUSP] = s_tios->c_cc[VSUSP];
+	l_tios->c_cc[LX_VDISCARD] = s_tios->c_cc[VDISCARD];
+}
+
+static void
+s2l_termio(struct termio *s_tio, struct lx_termio *l_tio)
+{
+	ASSERT((s_tio != NULL) && (l_tio != NULL));
+
+	bzero(l_tio, sizeof (*l_tio));
+
+	l_tio->c_iflag = s_tio->c_iflag;
+	l_tio->c_oflag = s_tio->c_oflag;
+	l_tio->c_cflag = s_tio->c_cflag;
+	l_tio->c_lflag = s_tio->c_lflag;
+
+	if (s_tio->c_lflag & ICANON) {
+		l_tio->c_cc[LX_VEOF] = s_tio->c_cc[VEOF];
+		l_tio->c_cc[LX_VTIME] = LX_DEF_VTIME;
+		l_tio->c_cc[LX_VMIN] = LX_DEF_VMIN;
+	} else {
+		l_tio->c_cc[LX_VMIN] = s_tio->c_cc[VMIN];
+		l_tio->c_cc[LX_VTIME] = s_tio->c_cc[VTIME];
+		l_tio->c_cc[LX_VEOF] = LX_DEF_VEOF;
+	}
+
+	l_tio->c_cc[LX_VINTR] = s_tio->c_cc[VINTR];
+	l_tio->c_cc[LX_VQUIT] = s_tio->c_cc[VQUIT];
+	l_tio->c_cc[LX_VERASE] = s_tio->c_cc[VERASE];
+	l_tio->c_cc[LX_VKILL] = s_tio->c_cc[VKILL];
+	l_tio->c_cc[LX_VSWTC] = s_tio->c_cc[VSWTCH];
+}
+
+static void
+set_lx_cc(vnode_t *vp, struct lx_cc *lio)
+{
+	struct lx_cc *cur;
+	/*
+	 * Linux expects that the termio/termios control characters are
+	 * preserved more strictly than illumos supports.  In order to preserve
+	 * the illusion that the characters are maintained, they are stored as
+	 * vnode-specific data.
+	 */
+	mutex_enter(&vp->v_vsd_lock);
+	cur = (struct lx_cc *)vsd_get(vp, lx_ioctl_vsd);
+	if (cur == NULL) {
+		cur = kmem_alloc(sizeof (struct lx_cc), KM_SLEEP);
+		bcopy(lio, cur, sizeof (struct lx_cc));
+		(void) vsd_set(vp, lx_ioctl_vsd, cur);
+	} else {
+		bcopy(lio, cur, sizeof (struct lx_cc));
+	}
+	mutex_exit(&vp->v_vsd_lock);
+}
+
+static int
+get_lx_cc(vnode_t *vp, struct lx_cc *lio)
+{
+	struct lx_cc *cur;
+	int rv = 1;
+	mutex_enter(&vp->v_vsd_lock);
+	cur = (struct lx_cc *)vsd_get(vp, lx_ioctl_vsd);
+	if (cur != NULL) {
+		bcopy(cur, lio, sizeof (*lio));
+		rv = 0;
+	}
+	mutex_exit(&vp->v_vsd_lock);
+	return (rv);
+}
+
+/* Socket helpers */
+
+typedef struct lx_ifreq32 {
+	char	ifr_name[IFNAMSIZ];
+	union {
+		struct	sockaddr ifru_addr;
+	} ifr_ifrn;
+} lx_ifreq32_t;
+
+typedef struct lx_ifreq64 {
+	char	ifr_name[IFNAMSIZ];
+	union {
+		struct	sockaddr ifru_addr;
+		/* pad this out to the Linux size */
+		uint64_t	ifmap[3];
+	} ifr_ifrn;
+} lx_ifreq64_t;
+
+typedef struct lx_ifconf32 {
+	int32_t	if_len;
+	caddr32_t if_buf;
+} lx_ifconf32_t;
+
+typedef struct lx_ifconf64 {
+	int32_t	if_len;
+	caddr_t if_buf;
+} lx_ifconf64_t;
+
+
+/* Generic translators */
+
+/* ARGSUSED */
+static int
+ict_pass(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+	int error = 0;
+	int rv;
+
+	error = VOP_IOCTL(fp->f_vnode, cmd, arg, FLUSER(fp), fp->f_cred, &rv,
+	    NULL);
+	return ((error != 0) ? set_errno(error) : 0);
+}
+
+/* ARGSUSED */
+static int
+ict_fionbio(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+	vnode_t *vp;
+	int32_t iflag, flags;
+	int error;
+
+	if (copyin((caddr_t)arg, &iflag, sizeof (iflag)))
+		return (set_errno(EFAULT));
+
+	mutex_enter(&fp->f_tlock);
+	vp = fp->f_vnode;
+	flags = fp->f_flag;
+	/* Linux sets NONBLOCK instead of FIONBIO */
+	if (iflag)
+		flags |= FNONBLOCK;
+	else
+		flags &= ~FNONBLOCK;
+	/* push the flag down */
+	error = VOP_SETFL(vp, fp->f_flag, flags, fp->f_cred, NULL);
+	fp->f_flag = flags;
+	mutex_exit(&fp->f_tlock);
+	return ((error != 0) ? set_errno(error) : 0);
+}
+
+/* ARGSUSED */
+static int
+ict_fionread(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+	vnode_t *vp;
+	struct vattr vattr;
+	int error = 0;
+	int rv;
+	/*
+	 * offset is int32_t because that is what FIONREAD is defined in terms
+	 * of.  We cap at INT_MAX as in other cases for this ioctl.
+	 */
+	int32_t offset;
+
+	vp = fp->f_vnode;
+
+	if (vp->v_type == VREG || vp->v_type == VDIR) {
+		vattr.va_mask = AT_SIZE;
+		error = VOP_GETATTR(vp, &vattr, 0, fp->f_cred, NULL);
+		if (error != 0)
+			return (set_errno(error));
+		offset = MIN(vattr.va_size - fp->f_offset, INT_MAX);
+		if (copyout(&offset, (caddr_t)arg, sizeof (offset)))
+			return (set_errno(EFAULT));
+	} else {
+		error = VOP_IOCTL(vp, FIONREAD, arg, FLUSER(fp), fp->f_cred,
+		    &rv, NULL);
+		if (error)
+			return (set_errno(error));
+	}
+	return (0);
+}
+
+/*
+ * hard disk-related translators
+ *
+ * Note that the normal disk ioctls only work for VCHR devices. See spec_ioctl
+ * which will return ENOTTY for a VBLK device. However, fdisk, etc. expect to
+ * work with block devices.
+ *
+ * We expect a zvol to be the primary block device we're interacting with and
+ * we use the zone's lxzd_vdisks list to handle zvols specifically.
+ */
+
+typedef struct lx_hd_geom {
+	unsigned char heads;
+	unsigned char sectors;
+	unsigned short cylinders;
+	unsigned long start;
+} lx_hd_geom_t;
+
+static lx_virt_disk_t *
+lx_lookup_zvol(lx_zone_data_t *lxzd, dev_t dev)
+{
+	lx_virt_disk_t *vd;
+
+	vd = list_head(lxzd->lxzd_vdisks);
+	while (vd != NULL) {
+		if (vd->lxvd_type == LXVD_ZVOL && vd->lxvd_real_dev == dev)
+			return (vd);
+		vd = list_next(lxzd->lxzd_vdisks, vd);
+	}
+
+	return (NULL);
+}
+
+/*
+ * See zvol_ioctl() which always fails for DKIOCGGEOM. The geometry for a
+ * zvol (or really any modern disk) is made up, so we do that here as well.
+ */
+/* ARGSUSED */
+static int
+ict_hdgetgeo(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+	lx_hd_geom_t lx_geom;
+	lx_zone_data_t *lxzd;
+
+	if (fp->f_vnode->v_type != VCHR && fp->f_vnode->v_type != VBLK)
+		return (set_errno(EINVAL));
+
+	lxzd = ztolxzd(curproc->p_zone);
+	ASSERT(lxzd != NULL);
+	ASSERT(lxzd->lxzd_vdisks != NULL);
+
+	if (getmajor(fp->f_vnode->v_rdev) == getmajor(lxzd->lxzd_zfs_dev)) {
+		lx_virt_disk_t *vd;
+
+		vd = lx_lookup_zvol(lxzd, fp->f_vnode->v_rdev);
+		if (vd == NULL) {
+			/* should only happen if new zvol */
+			bzero(&lx_geom, sizeof (lx_geom));
+		} else {
+			diskaddr_t tot;
+
+			tot = vd->lxvd_volsize / vd->lxvd_blksize;
+
+			/*
+			 * Since the 'sectors' value is only one byte we make
+			 * up heads/cylinder values to get things to fit.
+			 * We roundup the number of heads to ensure we don't
+			 * overflow the sectors due to truncation.
+			 */
+			lx_geom.heads = lx_geom.cylinders = (tot / 0xff) + 1;
+			lx_geom.sectors = tot / lx_geom.heads;
+			lx_geom.start = 0;
+		}
+	} else {
+		int res, rv;
+		struct dk_geom geom;
+
+		res = VOP_IOCTL(fp->f_vnode, DKIOCGGEOM, (intptr_t)&geom,
+		    fp->f_flag | FKIOCTL, fp->f_cred, &rv, NULL);
+		if (res > 0)
+			return (set_errno(res));
+
+		lx_geom.heads = geom.dkg_nhead;
+		lx_geom.sectors = geom.dkg_nsect;
+		lx_geom.cylinders = geom.dkg_ncyl;
+		lx_geom.start = 0;
+	}
+
+	if (copyout(&lx_geom, (caddr_t)arg, sizeof (lx_geom)))
+		return (set_errno(EFAULT));
+	return (0);
+}
+
+/*
+ * Per the Linux sd(4) man page, get the number of sectors. The linux/fs.h
+ * header says its 512 byte blocks.
+ */
+/* ARGSUSED */
+static int
+ict_blkgetsize(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+	diskaddr_t tot;
+	lx_zone_data_t *lxzd;
+
+	if (fp->f_vnode->v_type != VCHR && fp->f_vnode->v_type != VBLK)
+		return (set_errno(EINVAL));
+
+	lxzd = ztolxzd(curproc->p_zone);
+	ASSERT(lxzd != NULL);
+	ASSERT(lxzd->lxzd_vdisks != NULL);
+
+	if (getmajor(fp->f_vnode->v_rdev) == getmajor(lxzd->lxzd_zfs_dev)) {
+		lx_virt_disk_t *vd;
+
+		vd = lx_lookup_zvol(lxzd, fp->f_vnode->v_rdev);
+		if (vd == NULL) {
+			/* should only happen if new zvol */
+			tot = 0;
+		} else {
+			tot = vd->lxvd_volsize / 512;
+		}
+	} else {
+		int res, rv;
+		struct dk_minfo minfo;
+
+		res = VOP_IOCTL(fp->f_vnode, DKIOCGMEDIAINFO, (intptr_t)&minfo,
+		    fp->f_flag | FKIOCTL, fp->f_cred, &rv, NULL);
+		if (res > 0)
+			return (set_errno(res));
+
+		tot = minfo.dki_capacity;
+		if (minfo.dki_lbsize > 512) {
+			uint_t bsize = minfo.dki_lbsize / 512;
+
+			tot *= bsize;
+		}
+	}
+
+	if (copyout(&tot, (caddr_t)arg, sizeof (long)))
+		return (set_errno(EFAULT));
+	return (0);
+}
+
+/*
+ * Get the sector size (i.e. the logical block size).
+ */
+/* ARGSUSED */
+static int
+ict_blkgetssize(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+	uint_t bsize;
+	lx_zone_data_t *lxzd;
+
+	if (fp->f_vnode->v_type != VCHR && fp->f_vnode->v_type != VBLK)
+		return (set_errno(EINVAL));
+
+	lxzd = ztolxzd(curproc->p_zone);
+	ASSERT(lxzd != NULL);
+	ASSERT(lxzd->lxzd_vdisks != NULL);
+
+	if (getmajor(fp->f_vnode->v_rdev) == getmajor(lxzd->lxzd_zfs_dev)) {
+		lx_virt_disk_t *vd;
+
+		vd = lx_lookup_zvol(lxzd, fp->f_vnode->v_rdev);
+		if (vd == NULL) {
+			/* should only happen if new zvol */
+			bsize = 0;
+		} else {
+			bsize = (uint_t)vd->lxvd_blksize;
+		}
+	} else {
+		int res, rv;
+		struct dk_minfo minfo;
+
+		res = VOP_IOCTL(fp->f_vnode, DKIOCGMEDIAINFO, (intptr_t)&minfo,
+		    fp->f_flag | FKIOCTL, fp->f_cred, &rv, NULL);
+		if (res > 0)
+			return (set_errno(res));
+
+		bsize = (uint_t)minfo.dki_lbsize;
+	}
+
+	if (copyout(&bsize, (caddr_t)arg, sizeof (bsize)))
+		return (set_errno(EFAULT));
+	return (0);
+}
+
+/*
+ * Get the size. The linux/fs.h header says its in bytes.
+ */
+/* ARGSUSED */
+static int
+ict_blkgetsize64(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+	uint64_t tot;
+	lx_zone_data_t *lxzd;
+
+	if (fp->f_vnode->v_type != VCHR && fp->f_vnode->v_type != VBLK)
+		return (set_errno(EINVAL));
+
+	lxzd = ztolxzd(curproc->p_zone);
+	ASSERT(lxzd != NULL);
+	ASSERT(lxzd->lxzd_vdisks != NULL);
+
+	if (getmajor(fp->f_vnode->v_rdev) == getmajor(lxzd->lxzd_zfs_dev)) {
+		lx_virt_disk_t *vd;
+
+		vd = lx_lookup_zvol(lxzd, fp->f_vnode->v_rdev);
+		if (vd == NULL) {
+			/* should only happen if new zvol */
+			tot = 0;
+		} else {
+			tot = vd->lxvd_volsize;
+		}
+	} else {
+		int res, rv;
+		struct dk_minfo minfo;
+
+		res = VOP_IOCTL(fp->f_vnode, DKIOCGMEDIAINFO, (intptr_t)&minfo,
+		    fp->f_flag | FKIOCTL, fp->f_cred, &rv, NULL);
+		if (res > 0)
+			return (set_errno(res));
+
+		tot = minfo.dki_capacity * minfo.dki_lbsize;
+	}
+
+	if (copyout(&tot, (caddr_t)arg, sizeof (uint64_t)))
+		return (set_errno(EFAULT));
+	return (0);
+}
+
+/* ARGSUSED */
+/* Terminal-related translators */
+
+/* ARGSUSED */
+static int
+ict_tcsets(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+	struct lx_termios	l_tios;
+	struct termios		s_tios;
+	struct lx_cc		lio;
+	int			error, rv;
+
+	ASSERT(cmd == TCSETS || cmd == TCSETSW || cmd == TCSETSF);
+
+	if (copyin((struct lx_termios *)arg, &l_tios, sizeof (l_tios)) != 0)
+		return (set_errno(EFAULT));
+	termios2lx_cc(&l_tios, &lio);
+	l2s_termios(&l_tios, &s_tios);
+
+	error = VOP_IOCTL(fp->f_vnode, cmd, (intptr_t)&s_tios,
+	    FLFAKE(fp), fp->f_cred, &rv, NULL);
+	if (error)
+		return (set_errno(error));
+	/* preserve lx_cc */
+	set_lx_cc(fp->f_vnode, &lio);
+
+	return (0);
+}
+
+/* ARGSUSED */
+static int
+ict_tcseta(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+	struct lx_termio	l_tio;
+	struct termio		s_tio;
+	struct lx_cc		lio;
+	int			error, rv;
+
+	ASSERT(cmd == TCSETA || cmd == TCSETAW || cmd == TCSETAF);
+
+	if (copyin((struct lx_termio *)arg, &l_tio, sizeof (l_tio)) != 0)
+		return (set_errno(EFAULT));
+	l2s_termio(&l_tio, &s_tio);
+	termio2lx_cc(&l_tio, &lio);
+
+	error = VOP_IOCTL(fp->f_vnode, cmd, (intptr_t)&s_tio,
+	    FLFAKE(fp), fp->f_cred, &rv, NULL);
+	if (error)
+		return (set_errno(error));
+	/* preserve lx_cc */
+	set_lx_cc(fp->f_vnode, &lio);
+
+	return (0);
+}
+
+/* ARGSUSED */
+static int
+ict_tcgets_ptm(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+	struct lx_termios	l_tios;
+	struct termios		s_tios, *s_tiosd;
+	uint_t			s_tiosl;
+
+	/* get termios defaults */
+	if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, ddi_root_node(),
+	    DDI_PROP_NOTPROM, "ttymodes", (uchar_t **)&s_tiosd,
+	    &s_tiosl) != DDI_SUCCESS)
+		return (EIO);
+	ASSERT(s_tiosl == sizeof (*s_tiosd));
+	bcopy(s_tiosd, &s_tios, sizeof (s_tios));
+	ddi_prop_free(s_tiosd);
+
+	/* Now munge the data to how Linux wants it. */
+	s2l_termios(&s_tios, &l_tios);
+	if (copyout(&l_tios, (struct lx_termios *)arg, sizeof (l_tios)) != 0)
+		return (set_errno(EFAULT));
+
+	return (0);
+}
+
+/* ARGSUSED */
+static int
+ict_tcgets_native(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+	struct lx_termios	l_tios;
+	struct termios		s_tios;
+	struct lx_cc		lio;
+	int			error, rv;
+
+	error = VOP_IOCTL(fp->f_vnode, cmd, (intptr_t)&s_tios,
+	    FLFAKE(fp), fp->f_cred, &rv, NULL);
+	if (error)
+		return (set_errno(error));
+
+	/* Now munge the data to how Linux wants it. */
+	s2l_termios(&s_tios, &l_tios);
+
+	/* return preserved lx_cc */
+	if (get_lx_cc(fp->f_vnode, &lio) == 0) {
+		l_tios.c_cc[LX_VEOF] = lio.veof;
+		l_tios.c_cc[LX_VEOL] = lio.veol;
+		l_tios.c_cc[LX_VMIN] = lio.vmin;
+		l_tios.c_cc[LX_VTIME] = lio.vtime;
+	}
+
+	if (copyout(&l_tios, (struct lx_termios *)arg, sizeof (l_tios)) != 0)
+		return (set_errno(EFAULT));
+
+	return (0);
+}
+
+/* ARGSUSED */
+static int
+ict_tcgets(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+	if (getmajor(fp->f_vnode->v_rdev) == ddi_name_to_major(LX_PTM_DRV))
+		return (ict_tcgets_ptm(fp, cmd, arg, lxcmd));
+	else
+		return (ict_tcgets_native(fp, cmd, arg, lxcmd));
+}
+
+/* ARGSUSED */
+static int
+ict_tcgeta(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+	struct lx_termio	l_tio;
+	struct termio		s_tio;
+	struct lx_cc		lio;
+	int			error, rv;
+
+	error = VOP_IOCTL(fp->f_vnode, cmd, (intptr_t)&s_tio,
+	    FLFAKE(fp), fp->f_cred, &rv, NULL);
+	if (error)
+		return (set_errno(error));
+
+	s2l_termio(&s_tio, &l_tio);
+	/* return preserved lx_cc */
+	if (get_lx_cc(fp->f_vnode, &lio) == 0) {
+		l_tio.c_cc[LX_VEOF] = lio.veof;
+		l_tio.c_cc[LX_VMIN] = lio.vmin;
+		l_tio.c_cc[LX_VTIME] = lio.vtime;
+	}
+
+	if (copyout(&l_tio, (struct lx_termios *)arg, sizeof (l_tio)) != 0)
+		return (set_errno(EFAULT));
+
+	return (0);
+}
+
+/* ARGSUSED */
+static int
+ict_tiocspgrp(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+	pid_t lpid, spid, tid;
+	int error, rv;
+
+	/* Converting to the illumos pid is necessary */
+	if (copyin((pid_t *)arg, &lpid, sizeof (lpid)) < 0)
+		return (set_errno(EFAULT));
+	if (lx_lpid_to_spair(lpid, &spid, &tid) < 0)
+		return (set_errno(EPERM));
+
+	error = VOP_IOCTL(fp->f_vnode, cmd, (intptr_t)&spid,
+	    fp->f_flag |FKIOCTL, fp->f_cred, &rv, NULL);
+	return ((error != 0) ? set_errno(error) : 0);
+}
+
+/* ARGSUSED */
+static int
+ict_tcsbrkp(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+	int rv, error;
+	/* use null duration to emulate TCSBRKP */
+	int dur = 0;
+	error = VOP_IOCTL(fp->f_vnode, TCSBRK, (intptr_t)&dur,
+	    FLFAKE(fp), fp->f_cred, &rv, NULL);
+	return ((error != 0) ? set_errno(error) : 0);
+}
+
+/* ARGSUSED */
+static int
+ict_tiocgpgrp(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+	pid_t	spgrp;
+	int	error, rv;
+
+	error = VOP_IOCTL(fp->f_vnode, cmd, (intptr_t)&spgrp, FLFAKE(fp),
+	    fp->f_cred, &rv, NULL);
+	if (error == 0) {
+		if (spgrp == curproc->p_zone->zone_proc_initpid) {
+			spgrp = 1;
+		}
+		if (copyout(&spgrp, (caddr_t)arg, sizeof (spgrp))) {
+			return (set_errno(EFAULT));
+		}
+	}
+	return ((error != 0) ? set_errno(error) : 0);
+}
+
+/* ARGSUSED */
+static int
+ict_sptlock(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+	struct strioctl istr;
+	int error, rv;
+
+	istr.ic_cmd = UNLKPT;
+	istr.ic_len = 0;
+	istr.ic_timout = 0;
+	istr.ic_dp = NULL;
+	error = VOP_IOCTL(fp->f_vnode, I_STR, (intptr_t)&istr,
+	    fp->f_flag |FKIOCTL, fp->f_cred, &rv, NULL);
+	/*
+	 * The success/fail return values are different between Linux
+	 * and illumos.   Linux expects 0 or -1.  Illumos can return
+	 * positive number on success.
+	 */
+	return ((error != 0) ? set_errno(error) : 0);
+}
+
+/* ARGSUSED */
+static int
+ict_gptn(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+	struct strioctl istr;
+	cred_t *cr;
+	pt_own_t pto;
+	int error, rv;
+	int ptyno;
+
+	/* This operation is only valid for the lx_ptm device. */
+	if (getmajor(fp->f_vnode->v_rdev) != ddi_name_to_major(LX_PTM_DRV))
+		return (set_errno(ENOTTY));
+
+	cr = CRED();
+	pto.pto_ruid = cr->cr_uid;
+	pto.pto_rgid = cr->cr_gid;
+
+	istr.ic_cmd = OWNERPT;
+	istr.ic_len = sizeof (pto);
+	istr.ic_timout = 0;
+	istr.ic_dp = (char *)&pto;
+	error = VOP_IOCTL(fp->f_vnode, I_STR, (intptr_t)&istr,
+	    FLFAKE(fp), fp->f_cred, &rv, NULL);
+
+	if (error)
+		return (set_errno((error == ENOTTY) ? error: EACCES));
+
+	ptyno = getminor(fp->f_vnode->v_rdev) - 1;
+	if (copyout(&ptyno, (caddr_t)arg, sizeof (ptyno)))
+		return (set_errno(EFAULT));
+
+	return (0);
+}
+
+/* ARGSUSED */
+static int
+ict_tiocgwinsz(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+	int error, rv;
+
+	error = VOP_IOCTL(fp->f_vnode, cmd, arg, FLUSER(fp), fp->f_cred, &rv,
+	    NULL);
+
+	/*
+	 * A few Linux libc's (e.g. musl) have chosen to implement isatty()
+	 * using the TIOCGWINSZ ioctl. Some apps also do the same thing
+	 * directly. On Linux that ioctl will return a size of 0x0 for dumb
+	 * terminals but on illumos see the handling for TIOCGWINSZ in ptem's
+	 * ptioc(). We fail if the winsize is all zeros. To emulate the Linux
+	 * behavior use the native ioctl check that we do for isatty and return
+	 * a size of 0x0 if that succeeds.
+	 */
+	if (error == EINVAL) {
+		int err;
+		struct termio s_tio;
+
+		err = VOP_IOCTL(fp->f_vnode, TCGETA, (intptr_t)&s_tio,
+		    FLFAKE(fp), fp->f_cred, &rv, NULL);
+
+		if (err == 0) {
+			struct winsize w;
+
+			bzero(&w, sizeof (w));
+			if (copyout(&w, (struct winsize *)arg, sizeof (w)) != 0)
+				return (set_errno(EFAULT));
+			return (0);
+		}
+	}
+
+	if (error != 0)
+		return (set_errno(error));
+
+	return (0);
+}
+
+/* ARGSUSED */
+static int
+ict_tiocsctty(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+	pid_t	ttysid, mysid;
+	int	error, rv;
+	proc_t *p = curproc;
+
+	/* getsid */
+	mutex_enter(&p->p_splock);
+	mysid = p->p_sessp->s_sid;
+	mutex_exit(&p->p_splock);
+
+	/*
+	 * Report success if we already control the tty.
+	 * If no one controls it, TIOCSCTTY will change that later.
+	 */
+	error = VOP_IOCTL(fp->f_vnode, TIOCGSID, (intptr_t)&ttysid,
+	    FLFAKE(fp), fp->f_cred, &rv, NULL);
+	if (error == 0 && ttysid == mysid)
+		return (0);
+
+	/*
+	 * Need to make sure we're a session leader, otherwise the
+	 * TIOCSCTTY ioctl will fail.
+	 */
+	mutex_enter(&pidlock);
+	if (p->p_sessp->s_sidp != p->p_pidp && !pgmembers(p->p_pid)) {
+		mutex_exit(&pidlock);
+		sess_create();
+	} else {
+		mutex_exit(&pidlock);
+	}
+
+	error = VOP_IOCTL(fp->f_vnode, cmd, 0, FLUSER(fp),
+	    fp->f_cred, &rv, NULL);
+	return ((error != 0) ? set_errno(error) : 0);
+}
+
+/* Socket-related translators */
+
+/* ARGSUSED */
+static int
+ict_siocatmark(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+	vnode_t *vp = fp->f_vnode;
+	int error, rv;
+	/*
+	 * Linux expects a SIOCATMARK of a UDP socket to return ENOTTY, while
+	 * Illumos allows it. Linux prior to 2.6.39 returned EINVAL for this.
+	 */
+	if (vp->v_type != VSOCK || VTOSO(vp)->so_type != SOCK_STREAM)
+		return (set_errno(ENOTTY));
+
+	error = VOP_IOCTL(fp->f_vnode, cmd, arg, FLUSER(fp), fp->f_cred, &rv,
+	    NULL);
+	if (error)
+		return (set_errno(error));
+
+	return (0);
+}
+
+static int
+ict_if_ioctl(vnode_t *vn, int cmd, intptr_t arg, int flags, cred_t *cred)
+{
+	int error, rv;
+	lx_zone_data_t *lxzd = ztolxzd(curproc->p_zone);
+	ksocket_t ks;
+
+	ASSERT(lxzd != NULL);
+
+	/*
+	 * For ioctls of this type, we are strict about address family
+	 * whereas Linux is lenient.  This strictness can be avoided by using
+	 * an internal AF_INET ksocket, which we use if the family is anything
+	 * but AF_PACKET.
+	 */
+	if (vn->v_type == VSOCK && VTOSO(vn)->so_family == AF_PACKET)
+		return (VOP_IOCTL(vn, cmd, arg, flags, cred, &rv, NULL));
+
+	mutex_enter(&lxzd->lxzd_lock);
+	ks = lxzd->lxzd_ioctl_sock;
+	if (ks == NULL) {
+		/*
+		 * Linux is not at all picky about address family when it comes
+		 * to supporting interface-related ioctls. To mimic this
+		 * behavior, we'll attempt those ioctls against a ksocket
+		 * configured for that purpose.
+		 */
+		(void) ksocket_socket(&lxzd->lxzd_ioctl_sock, AF_INET,
+		    SOCK_DGRAM, 0, 0, curproc->p_zone->zone_kcred);
+		ks = lxzd->lxzd_ioctl_sock;
+	}
+	mutex_exit(&lxzd->lxzd_lock);
+
+	if (ks != NULL) {
+		error = ksocket_ioctl(ks, cmd, arg, &rv, cred);
+	} else {
+		error = VOP_IOCTL(vn, cmd, arg, flags, cred, &rv, NULL);
+	}
+
+	return (error);
+}
+
+static int
+ict_sioghwaddr(file_t *fp, struct lifreq *lreq)
+{
+	struct sockaddr_dl *sdl = (struct sockaddr_dl *)&lreq->lifr_addr;
+	struct sockaddr hwaddr;
+	int error, size;
+
+	error = ict_if_ioctl(fp->f_vnode, SIOCGLIFHWADDR, (intptr_t)lreq,
+	    FLFAKE(fp), fp->f_cred);
+
+	if (error == EADDRNOTAVAIL &&
+	    strncmp(lreq->lifr_name, "lo", 2) == 0) {
+		/* Emulate success on suspected loopbacks */
+		sdl->sdl_type = DL_LOOP;
+		sdl->sdl_alen = ETHERADDRL;
+		bzero(LLADDR(sdl), sdl->sdl_alen);
+		error = 0;
+	}
+
+	if (error == 0) {
+		bzero(&hwaddr, sizeof (hwaddr));
+		lx_stol_hwaddr(sdl, &hwaddr, &size);
+		bcopy(&hwaddr, &lreq->lifr_addr,
+		    size + sizeof (sdl->sdl_family));
+	}
+
+	return (error);
+}
+
+/* ARGSUSED */
+static int
+ict_siocgifname(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+	struct ifreq	req;
+	int		len;
+	char		name[LIFNAMSIZ];
+	netstack_t *ns;
+	ip_stack_t *ipst;
+	phyint_t *phyi;
+
+	if (fp->f_vnode->v_type != VSOCK) {
+		return (set_errno(EINVAL));
+	}
+
+	len = (curproc->p_model == DATAMODEL_LP64) ? sizeof (lx_ifreq64_t) :
+	    sizeof (lx_ifreq32_t);
+	if (copyin((struct ifreq *)arg, &req, len) != 0) {
+		return (set_errno(EFAULT));
+	}
+
+	/*
+	 * Since Linux calls this ioctl on all sorts of sockets, perform the
+	 * interface name lookup manually.
+	 */
+	if ((ns = netstack_get_current()) == NULL) {
+		return (set_errno(EINVAL));
+	}
+	ipst = ns->netstack_ip;
+
+	rw_enter(&ipst->ips_ill_g_lock, RW_READER);
+	phyi = avl_find(&ipst->ips_phyint_g_list->phyint_list_avl_by_index,
+	    (void *) &req.ifr_index, NULL);
+	if (phyi != NULL) {
+		(void) strncpy(name, phyi->phyint_name, LIFNAMSIZ);
+		lx_ifname_convert(name, LX_IF_FROMNATIVE);
+	} else {
+		name[0] = '\0';
+	}
+
+	rw_exit(&ipst->ips_ill_g_lock);
+	netstack_rele(ns);
+
+	if (strlen(name) != 0) {
+		/* Truncate for ifreq and copyout */
+		(void) strncpy(req.ifr_name, name, IFNAMSIZ);
+		if (copyout(&req, (struct ifreq *)arg, len) != 0) {
+			return (set_errno(EFAULT));
+		}
+		return (0);
+	}
+
+	return (set_errno(EINVAL));
+}
+
+/* ARGSUSED */
+static int
+ict_siolifreq(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+	struct ifreq	req;
+	struct lifreq	lreq;
+	int		error, len;
+
+	/* Convert from Linux ifreq to illumos lifreq */
+	if (curproc->p_model == DATAMODEL_LP64)
+		len = sizeof (lx_ifreq64_t);
+	else
+		len = sizeof (lx_ifreq32_t);
+	if (copyin((struct ifreq *)arg, &req, len) != 0)
+		return (set_errno(EFAULT));
+	bzero(&lreq, sizeof (lreq));
+	(void) strncpy(lreq.lifr_name, req.ifr_name, IFNAMSIZ);
+	bcopy(&req.ifr_ifru, &lreq.lifr_lifru, len - IFNAMSIZ);
+	lx_ifname_convert(lreq.lifr_name, LX_IF_TONATIVE);
+
+	switch (cmd) {
+	case SIOCGIFADDR:
+	case SIOCSIFADDR:
+	case SIOCGIFDSTADDR:
+	case SIOCSIFDSTADDR:
+	case SIOCGIFBRDADDR:
+	case SIOCSIFBRDADDR:
+	case SIOCGIFNETMASK:
+	case SIOCSIFNETMASK:
+	case SIOCGIFMETRIC:
+	case SIOCSIFMETRIC:
+	case SIOCGIFMTU:
+	case SIOCSIFMTU:
+		/*
+		 * Convert cmd from SIO*IF* to SIO*LIF*.
+		 * This is needed since Linux allows ifreq operations on ipv6
+		 * sockets where illumos does not.
+		 */
+		cmd = ((cmd & IOC_INOUT) |
+		    _IOW('i', ((cmd & 0xff) + 100), struct lifreq));
+		error = ict_if_ioctl(fp->f_vnode, cmd, (intptr_t)&lreq,
+		    FLFAKE(fp), fp->f_cred);
+		break;
+	case SIOCGIFINDEX:
+		cmd = SIOCGLIFINDEX;
+		error = ict_if_ioctl(fp->f_vnode, cmd, (intptr_t)&lreq,
+		    FLFAKE(fp), fp->f_cred);
+		break;
+	case SIOCGIFFLAGS:
+		cmd = SIOCGLIFFLAGS;
+		error = ict_if_ioctl(fp->f_vnode, cmd, (intptr_t)&lreq,
+		    FLFAKE(fp), fp->f_cred);
+		if (error == 0)
+			lx_ifflags_convert(&lreq.lifr_flags, LX_IF_FROMNATIVE);
+		break;
+	case SIOCSIFFLAGS:
+		cmd = SIOCSLIFFLAGS;
+		lx_ifflags_convert(&lreq.lifr_flags, LX_IF_TONATIVE);
+		error = ict_if_ioctl(fp->f_vnode, cmd, (intptr_t)&lreq,
+		    FLFAKE(fp), fp->f_cred);
+		break;
+	case SIOCGIFHWADDR:
+		error = ict_sioghwaddr(fp, &lreq);
+		break;
+	case LX_SIOCGIFTXQLEN:
+		/*
+		 * Illumos lacks the notion of txqlen.  Confirm the provided
+		 * interface is valid with SIOCGLIFINDEX and return a fake
+		 * txqlen of 1.  Loopback devices will report txqlen of 0.
+		 */
+		if (strncmp(lreq.lifr_name, "lo", 2) == 0) {
+			lreq.lifr_index = 0;
+			error = 0;
+			break;
+		}
+		cmd = SIOCGLIFINDEX;
+		error = ict_if_ioctl(fp->f_vnode, cmd, (intptr_t)&lreq,
+		    FLFAKE(fp), fp->f_cred);
+		if (error == 0) {
+			/* lifr_index aliases to the qlen field */
+			lreq.lifr_index = 1;
+		}
+		break;
+	case LX_SIOCSIFHWADDR:
+		/*
+		 * We're not going to support SIOCSIFHWADDR, but we need to be
+		 * able to check the result of the copyin first to see if the
+		 * command should have returned EFAULT.
+		 */
+	default:
+		error = EINVAL;
+	}
+
+	if (error != 0)
+		return (set_errno(error));
+
+	/* Convert back to a Linux ifreq */
+	lx_ifname_convert(lreq.lifr_name, LX_IF_FROMNATIVE);
+	bzero(&req, sizeof (req));
+	(void) strncpy(req.ifr_name, lreq.lifr_name, IFNAMSIZ);
+	bcopy(&lreq.lifr_lifru, &req.ifr_ifru, len - IFNAMSIZ);
+
+	if (copyout(&req, (struct lifreq *)arg, len) != 0)
+		return (set_errno(EFAULT));
+
+	return (0);
+}
+
+/* ARGSUSED */
+static int
+ict_siocgifconf32(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+	lx_ifconf32_t	conf;
+	lx_ifreq32_t	*oreq;
+	struct ifconf	sconf;
+	int		ifcount, error, i, buf_len;
+
+	if (copyin((lx_ifconf32_t *)arg, &conf, sizeof (conf)) != 0)
+		return (set_errno(EFAULT));
+
+	/* They want to know how many interfaces there are. */
+	if (conf.if_len <= 0 || conf.if_buf == NULL) {
+		error = ict_if_ioctl(fp->f_vnode, SIOCGIFNUM,
+		    (intptr_t)&ifcount, FLFAKE(fp), fp->f_cred);
+		if (error != 0)
+			return (set_errno(error));
+
+		conf.if_len = ifcount * sizeof (lx_ifreq32_t);
+
+		if (copyout(&conf, (lx_ifconf32_t *)arg, sizeof (conf)) != 0)
+			return (set_errno(EFAULT));
+		return (0);
+	} else {
+		ifcount = conf.if_len / sizeof (lx_ifreq32_t);
+	}
+
+	/* Get interface configuration list. */
+	sconf.ifc_len = ifcount * sizeof (struct ifreq);
+	sconf.ifc_req = (struct ifreq *)kmem_alloc(sconf.ifc_len, KM_SLEEP);
+
+	error = ict_if_ioctl(fp->f_vnode, cmd, (intptr_t)&sconf, FLFAKE(fp),
+	    fp->f_cred);
+	if (error != 0) {
+		kmem_free(sconf.ifc_req, ifcount * sizeof (struct ifreq));
+		return (set_errno(error));
+	}
+
+	/* Convert data to Linux format & rename interfaces */
+	buf_len = ifcount * sizeof (lx_ifreq32_t);
+	oreq = (lx_ifreq32_t *)kmem_alloc(buf_len, KM_SLEEP);
+	for (i = 0; i < sconf.ifc_len / sizeof (struct ifreq); i++) {
+		bcopy(&sconf.ifc_req[i], oreq + i, sizeof (lx_ifreq32_t));
+		lx_ifname_convert(oreq[i].ifr_name, LX_IF_FROMNATIVE);
+	}
+	conf.if_len = i * sizeof (*oreq);
+	kmem_free(sconf.ifc_req, ifcount * sizeof (struct ifreq));
+
+	error = 0;
+	if (copyout(oreq, (caddr_t)(uintptr_t)conf.if_buf, conf.if_len) != 0 ||
+	    copyout(&conf, (lx_ifconf32_t *)arg, sizeof (conf)) != 0)
+		error = set_errno(EFAULT);
+
+	kmem_free(oreq, buf_len);
+	return (error);
+}
+
+/* ARGSUSED */
+static int
+ict_siocgifconf64(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+	lx_ifconf64_t	conf;
+	lx_ifreq64_t	*oreq;
+	struct ifconf	sconf;
+	int		ifcount, error, i, buf_len;
+
+	if (copyin((lx_ifconf64_t *)arg, &conf, sizeof (conf)) != 0)
+		return (set_errno(EFAULT));
+
+	/* They want to know how many interfaces there are. */
+	if (conf.if_len <= 0 || conf.if_buf == NULL) {
+		error = ict_if_ioctl(fp->f_vnode, SIOCGIFNUM,
+		    (intptr_t)&ifcount, FLFAKE(fp), fp->f_cred);
+		if (error != 0)
+			return (set_errno(error));
+
+		conf.if_len = ifcount * sizeof (lx_ifreq64_t);
+
+		if (copyout(&conf, (lx_ifconf64_t *)arg, sizeof (conf)) != 0)
+			return (set_errno(EFAULT));
+		return (0);
+	} else {
+		ifcount = conf.if_len / sizeof (lx_ifreq64_t);
+	}
+
+	/* Get interface configuration list. */
+	sconf.ifc_len = ifcount * sizeof (struct ifreq);
+	sconf.ifc_req = (struct ifreq *)kmem_alloc(sconf.ifc_len, KM_SLEEP);
+
+	error = ict_if_ioctl(fp->f_vnode, cmd, (intptr_t)&sconf, FLFAKE(fp),
+	    fp->f_cred);
+	if (error != 0) {
+		kmem_free(sconf.ifc_req, ifcount * sizeof (struct ifreq));
+		return (set_errno(error));
+	}
+
+	/* Convert data to Linux format & rename interfaces */
+	buf_len = ifcount * sizeof (lx_ifreq64_t);
+	oreq = (lx_ifreq64_t *)kmem_alloc(buf_len, KM_SLEEP);
+	for (i = 0; i < sconf.ifc_len / sizeof (struct ifreq); i++) {
+		bcopy(&sconf.ifc_req[i], oreq + i, sizeof (lx_ifreq64_t));
+		lx_ifname_convert(oreq[i].ifr_name, LX_IF_FROMNATIVE);
+	}
+	conf.if_len = i * sizeof (*oreq);
+	kmem_free(sconf.ifc_req, ifcount * sizeof (struct ifreq));
+
+	error = 0;
+	if (copyout(oreq, (caddr_t)(uintptr_t)conf.if_buf, conf.if_len) != 0 ||
+	    copyout(&conf, (lx_ifconf64_t *)arg, sizeof (conf)) != 0)
+		error = set_errno(EFAULT);
+
+	kmem_free(oreq, buf_len);
+	return (error);
+}
+
+/* ARGSUSED */
+static int
+ict_siocgifconf(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+	if (curproc->p_model == DATAMODEL_LP64)
+		return (ict_siocgifconf64(fp, cmd, arg, lxcmd));
+	else
+		return (ict_siocgifconf32(fp, cmd, arg, lxcmd));
+}
+
+/*
+ * Unfortunately some of the autofs ioctls want to return a positive integer
+ * result which does not indicate an error. To minimize disruption in the
+ * rest of the code, we'll treat a positive return as an errno and a negative
+ * return as the non-error return (which we then negate).
+ */
+/* ARGSUSED */
+static int
+ict_autofs(file_t *fp, int cmd, intptr_t arg, int lxcmd)
+{
+	int res = 0;
+	int rv;
+
+	res = VOP_IOCTL(fp->f_vnode, cmd, arg, FLUSER(fp), fp->f_cred, &rv,
+	    NULL);
+	if (res > 0)
+		return (set_errno(res));
+	if (res == 0)
+		return (0);
+	return (-res);
+}
+
+/* Structure used to define an ioctl translator. */
+typedef struct lx_ioc_cmd_translator {
+	int	lict_lxcmd;
+	int	lict_cmd;
+	int	(*lict_func)(file_t *fp, int cmd, intptr_t arg, int lxcmd);
+} lx_ioc_cmd_translator_t;
+
+#define	LX_IOC_CMD_TRANSLATOR_PASS(ioc_cmd_sym)				\
+	{ (int)LX_##ioc_cmd_sym, (int)ioc_cmd_sym, ict_pass },
+
+#define	LX_IOC_CMD_TRANSLATOR_FILTER(ioc_cmd_sym, ioct_handler)		\
+	{ (int)LX_##ioc_cmd_sym, (int)ioc_cmd_sym, ioct_handler },
+
+#define	LX_IOC_CMD_TRANSLATOR_CUSTOM(ioc_cmd_sym, ioct_handler)		\
+	{ (int)ioc_cmd_sym, (int)ioc_cmd_sym, ioct_handler },
+
+#define	LX_IOC_CMD_TRANSLATOR_PTHRU(ioc_cmd_sym)			\
+	{ (int)ioc_cmd_sym, (int)ioc_cmd_sym, ict_pass },
+
+#define	LX_IOC_CMD_TRANSLATOR_END					\
+	{0, 0, NULL}
+
+static lx_ioc_cmd_translator_t lx_ioc_xlate_fd[] = {
+	LX_IOC_CMD_TRANSLATOR_FILTER(FIONBIO,	ict_fionbio)
+	LX_IOC_CMD_TRANSLATOR_FILTER(FIONREAD,	ict_fionread)
+	LX_IOC_CMD_TRANSLATOR_PASS(FIOASYNC)
+
+	/* streams related */
+	LX_IOC_CMD_TRANSLATOR_PASS(TCXONC)
+	LX_IOC_CMD_TRANSLATOR_PASS(TCFLSH)
+	LX_IOC_CMD_TRANSLATOR_PASS(TIOCEXCL)
+	LX_IOC_CMD_TRANSLATOR_PASS(TIOCNXCL)
+	LX_IOC_CMD_TRANSLATOR_PASS(TIOCSTI)
+	LX_IOC_CMD_TRANSLATOR_PASS(TIOCSWINSZ)
+	LX_IOC_CMD_TRANSLATOR_PASS(TIOCMBIS)
+	LX_IOC_CMD_TRANSLATOR_PASS(TIOCMBIC)
+	LX_IOC_CMD_TRANSLATOR_PASS(TIOCMSET)
+	LX_IOC_CMD_TRANSLATOR_PASS(TIOCSETD)
+	LX_IOC_CMD_TRANSLATOR_PASS(TCSBRK)
+
+	/* terminal related */
+	LX_IOC_CMD_TRANSLATOR_PASS(TIOCGETD)
+	LX_IOC_CMD_TRANSLATOR_PASS(TIOCGSID)
+	LX_IOC_CMD_TRANSLATOR_PASS(TIOCNOTTY)
+	LX_IOC_CMD_TRANSLATOR_PASS(TIOCPKT)
+
+	LX_IOC_CMD_TRANSLATOR_FILTER(TCSETS,		ict_tcsets)
+	LX_IOC_CMD_TRANSLATOR_FILTER(TCSETSW,		ict_tcsets)
+	LX_IOC_CMD_TRANSLATOR_FILTER(TCSETSF,		ict_tcsets)
+	LX_IOC_CMD_TRANSLATOR_FILTER(TCSETA,		ict_tcseta)
+	LX_IOC_CMD_TRANSLATOR_FILTER(TCSETAW,		ict_tcseta)
+	LX_IOC_CMD_TRANSLATOR_FILTER(TCSETAF,		ict_tcseta)
+	LX_IOC_CMD_TRANSLATOR_FILTER(TCGETS,		ict_tcgets)
+	LX_IOC_CMD_TRANSLATOR_FILTER(TCGETA,		ict_tcgeta)
+	LX_IOC_CMD_TRANSLATOR_FILTER(TIOCGWINSZ,	ict_tiocgwinsz)
+	LX_IOC_CMD_TRANSLATOR_CUSTOM(LX_TCSBRKP,	ict_tcsbrkp)
+	LX_IOC_CMD_TRANSLATOR_FILTER(TIOCSPGRP,		ict_tiocspgrp)
+	LX_IOC_CMD_TRANSLATOR_FILTER(TIOCGPGRP,		ict_tiocgpgrp)
+	LX_IOC_CMD_TRANSLATOR_CUSTOM(LX_TIOCSPTLCK,	ict_sptlock)
+	LX_IOC_CMD_TRANSLATOR_CUSTOM(LX_TIOCGPTN,	ict_gptn)
+	LX_IOC_CMD_TRANSLATOR_FILTER(TIOCSCTTY,		ict_tiocsctty)
+
+	LX_IOC_CMD_TRANSLATOR_END
+};
+
+static lx_ioc_cmd_translator_t lx_ioc_xlate_socket[] = {
+	LX_IOC_CMD_TRANSLATOR_PASS(FIOGETOWN)
+
+	LX_IOC_CMD_TRANSLATOR_PASS(SIOCSPGRP)
+	LX_IOC_CMD_TRANSLATOR_PASS(SIOCGPGRP)
+	LX_IOC_CMD_TRANSLATOR_PASS(SIOCGSTAMP)
+	LX_IOC_CMD_TRANSLATOR_FILTER(SIOCATMARK,	ict_siocatmark)
+	LX_IOC_CMD_TRANSLATOR_FILTER(SIOCGIFFLAGS,	ict_siolifreq)
+	LX_IOC_CMD_TRANSLATOR_FILTER(SIOCSIFFLAGS,	ict_siolifreq)
+	LX_IOC_CMD_TRANSLATOR_FILTER(SIOCGIFADDR,	ict_siolifreq)
+	LX_IOC_CMD_TRANSLATOR_FILTER(SIOCSIFADDR,	ict_siolifreq)
+	LX_IOC_CMD_TRANSLATOR_FILTER(SIOCGIFDSTADDR,	ict_siolifreq)
+	LX_IOC_CMD_TRANSLATOR_FILTER(SIOCSIFDSTADDR,	ict_siolifreq)
+	LX_IOC_CMD_TRANSLATOR_FILTER(SIOCGIFBRDADDR,	ict_siolifreq)
+	LX_IOC_CMD_TRANSLATOR_FILTER(SIOCSIFBRDADDR,	ict_siolifreq)
+	LX_IOC_CMD_TRANSLATOR_FILTER(SIOCGIFNETMASK,	ict_siolifreq)
+	LX_IOC_CMD_TRANSLATOR_FILTER(SIOCSIFNETMASK,	ict_siolifreq)
+	LX_IOC_CMD_TRANSLATOR_FILTER(SIOCGIFMETRIC,	ict_siolifreq)
+	LX_IOC_CMD_TRANSLATOR_FILTER(SIOCSIFMETRIC,	ict_siolifreq)
+	LX_IOC_CMD_TRANSLATOR_FILTER(SIOCGIFMTU,	ict_siolifreq)
+	LX_IOC_CMD_TRANSLATOR_FILTER(SIOCSIFMTU,	ict_siolifreq)
+	LX_IOC_CMD_TRANSLATOR_FILTER(SIOCGIFHWADDR,	ict_siolifreq)
+	LX_IOC_CMD_TRANSLATOR_CUSTOM(LX_SIOCSIFHWADDR,	ict_siolifreq)
+	LX_IOC_CMD_TRANSLATOR_FILTER(SIOCGIFINDEX,	ict_siolifreq)
+	LX_IOC_CMD_TRANSLATOR_CUSTOM(LX_SIOCGIFTXQLEN,	ict_siolifreq)
+	LX_IOC_CMD_TRANSLATOR_FILTER(SIOCGIFCONF,	ict_siocgifconf)
+	LX_IOC_CMD_TRANSLATOR_CUSTOM(LX_SIOCGIFNAME,	ict_siocgifname)
+
+	LX_IOC_CMD_TRANSLATOR_END
+};
+
+static lx_ioc_cmd_translator_t lx_ioc_xlate_dtrace[] = {
+	LX_IOC_CMD_TRANSLATOR_PTHRU(DTRACEHIOC_ADD)
+	LX_IOC_CMD_TRANSLATOR_PTHRU(DTRACEHIOC_REMOVE)
+	LX_IOC_CMD_TRANSLATOR_PTHRU(DTRACEHIOC_ADDDOF)
+
+	LX_IOC_CMD_TRANSLATOR_END
+};
+
+static lx_ioc_cmd_translator_t lx_ioc_xlate_autofs[] = {
+	LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_IOC_READY)
+	LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_IOC_FAIL)
+	LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_IOC_CATATONIC)
+	LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_IOC_PROTOVER)
+	LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_IOC_SETTIMEOUT)
+	LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_IOC_EXPIRE)
+	LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_IOC_EXPIRE_MULTI)
+	LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_IOC_PROTOSUBVER)
+	LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_IOC_ASKUMOUNT)
+
+	LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_VERSION_CMD)
+	LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_PROTOVER_CMD)
+	LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_PROTOSUBVER_CMD)
+	LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_OPENMOUNT_CMD)
+	LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_CLOSEMOUNT_CMD)
+	LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_READY_CMD)
+	LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_FAIL_CMD)
+	LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_SETPIPEFD_CMD)
+	LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_CATATONIC_CMD)
+	LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_TIMEOUT_CMD)
+	LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_REQUESTER_CMD)
+	LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_EXPIRE_CMD)
+	LX_IOC_CMD_TRANSLATOR_PTHRU(LX_AUTOFS_DEV_IOC_ASKUMOUNT_CMD)
+	LX_IOC_CMD_TRANSLATOR_CUSTOM(LX_AUTOFS_DEV_IOC_ISMOUNTPOINT_CMD,
+	    ict_autofs)
+
+	LX_IOC_CMD_TRANSLATOR_END
+};
+
+static lx_ioc_cmd_translator_t lx_ioc_xlate_hd[] = {
+	LX_IOC_CMD_TRANSLATOR_CUSTOM(LX_HDIO_GETGEO, ict_hdgetgeo)
+
+	LX_IOC_CMD_TRANSLATOR_END
+};
+
+static lx_ioc_cmd_translator_t lx_ioc_xlate_blk[] = {
+	LX_IOC_CMD_TRANSLATOR_CUSTOM(LX_BLKGETSIZE, ict_blkgetsize)
+	LX_IOC_CMD_TRANSLATOR_CUSTOM(LX_BLKSSZGET, ict_blkgetssize)
+	LX_IOC_CMD_TRANSLATOR_CUSTOM(LX_BLKGETSIZE64, ict_blkgetsize64)
+
+	LX_IOC_CMD_TRANSLATOR_END
+};
+
+static void
+lx_ioctl_vsd_free(void *data)
+{
+	kmem_free(data, sizeof (struct lx_cc));
+}
+
+void
+lx_ioctl_init()
+{
+	vsd_create(&lx_ioctl_vsd, lx_ioctl_vsd_free);
+}
+
+void
+lx_ioctl_fini()
+{
+	vsd_destroy(&lx_ioctl_vsd);
+}
+
+long
+lx_ioctl(int fdes, int cmd, intptr_t arg)
+{
+	file_t *fp;
+	int res = 0, error = ENOTTY;
+	lx_ioc_cmd_translator_t *ict = NULL;
+
+	if (cmd == LX_FIOCLEX || cmd == LX_FIONCLEX) {
+		res = f_setfd_error(fdes, (cmd == LX_FIOCLEX) ? FD_CLOEXEC : 0);
+		return ((res != 0) ? set_errno(res) : 0);
+	}
+
+	if ((fp = getf(fdes)) == NULL)
+		return (set_errno(EBADF));
+
+	switch ((cmd & 0xff00) >> 8) {
+	case LX_IOC_TYPE_FD:
+		ict = lx_ioc_xlate_fd;
+		break;
+
+	case LX_IOC_TYPE_DTRACE:
+		ict = lx_ioc_xlate_dtrace;
+		break;
+
+	case LX_IOC_TYPE_SOCK:
+		ict = lx_ioc_xlate_socket;
+		error = EOPNOTSUPP;
+		break;
+
+	case LX_IOC_TYPE_AUTOFS:
+		ict = lx_ioc_xlate_autofs;
+		break;
+
+	case LX_IOC_TYPE_BLK:
+		ict = lx_ioc_xlate_blk;
+		break;
+
+	case LX_IOC_TYPE_HD:
+		ict = lx_ioc_xlate_hd;
+		break;
+
+	default:
+		releasef(fdes);
+		return (set_errno(ENOTTY));
+	}
+
+	/*
+	 * Today, none of the ioctls supported by the emulation possess
+	 * overlapping cmd values.  Because of that, no type interrogation of
+	 * the fd is done before executing specific ioctl emulation.  It's
+	 * assumed that the vnode-specific logic called by the emulation
+	 * function will reject ioctl commands not supported by the fd.
+	 */
+	VERIFY(ict != NULL);
+	while (ict->lict_func != NULL) {
+		if (ict->lict_lxcmd == cmd)
+			break;
+		ict++;
+	}
+	if (ict->lict_func == NULL) {
+		releasef(fdes);
+		return (set_errno(error));
+	}
+
+	res = ict->lict_func(fp, ict->lict_cmd, arg, ict->lict_lxcmd);
+	releasef(fdes);
+	return (res);
+}
--- /dev/null	Wed Oct  5 00:32:08 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_ioprio.c	Wed Oct  5 00:32:08 2016
@@ -0,0 +1,66 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+#include <sys/errno.h>
+#include <sys/systm.h>
+#include <sys/lx_brand.h>
+
+/* 'which' values. */
+#define	LX_IOPRIO_WHO_PROCESS	1
+#define	LX_IOPRIO_WHO_PGRP	2
+#define	LX_IOPRIO_WHO_USER	3
+
+/*
+ * The possible values for the class. We report best effort (BE) as the class
+ * in use.
+ */
+#define	LX_IOPRIO_CLASS_RT	1
+#define	LX_IOPRIO_CLASS_BE	2
+#define	LX_IOPRIO_CLASS_IDLE	3
+
+/* Macro to determine the class from the input mask */
+#define	LX_IOPRIO_PRIO_CLASS(m)	((m) >> 13)
+
+/* ARGSUSED */
+long
+lx_ioprio_get(int which, int who)
+{
+	if (which < LX_IOPRIO_WHO_PROCESS || which > LX_IOPRIO_WHO_USER)
+		return (set_errno(EINVAL));
+
+	return (LX_IOPRIO_CLASS_BE);
+}
+
+/*
+ * We allow setting any valid class, even though it's ignored.
+ * We ignore the 'who' parameter which means that we're not searching for
+ * the specified target in order to return a specific errno in the case that
+ * the target does not exist.
+ */
+/* ARGSUSED */
+long
+lx_ioprio_set(int which, int who, int mask)
+{
+	int class;
+
+	if (which < LX_IOPRIO_WHO_PROCESS || which > LX_IOPRIO_WHO_USER)
+		return (set_errno(EINVAL));
+
+	class = LX_IOPRIO_PRIO_CLASS(mask);
+	if (class < LX_IOPRIO_CLASS_RT || class > LX_IOPRIO_CLASS_IDLE)
+		return (set_errno(EINVAL));
+
+	return (0);
+}
--- /dev/null	Wed Oct  5 00:32:08 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_kill.c	Wed Oct  5 00:32:08 2016
@@ -0,0 +1,402 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/zone.h>
+#include <sys/thread.h>
+#include <sys/signal.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <lx_signum.h>
+#include <sys/contract/process_impl.h>
+
+extern int kill(pid_t, int);
+
+/*
+ * Check if it is legal to send this signal to the init process.  Linux
+ * kill(2) semantics dictate that no _unhandled_ signal may be sent to pid
+ * 1.
+ */
+static int
+init_sig_check(int sig, pid_t pid)
+{
+	proc_t *p;
+	int rv = 0;
+
+	mutex_enter(&pidlock);
+
+	if (((p = prfind(pid)) == NULL) || (p->p_stat == SIDL))
+		rv = ESRCH;
+	else if (sig && (sigismember(&cantmask, sig) ||
+	    (PTOU(p)->u_signal[sig-1] == SIG_DFL) ||
+	    (PTOU(p)->u_signal[sig-1] == SIG_IGN)))
+		rv = EPERM;
+
+	mutex_exit(&pidlock);
+
+	return (rv);
+}
+
+static long
+lx_thrkill(pid_t tgid, pid_t pid, int lx_sig, boolean_t tgkill)
+{
+	kthread_t *t;
+	proc_t *pp, *cp = curproc;
+	pid_t initpid;
+	sigqueue_t *sqp;
+	int tid = 1;	/* default tid */
+	int sig, rv;
+
+	/*
+	 * Unlike kill(2), Linux tkill(2) doesn't allow signals to
+	 * be sent to process IDs <= 0 as it doesn't overlay any special
+	 * semantics on the pid.
+	 */
+	if ((pid <= 0) || ((lx_sig < 0) || (lx_sig > LX_NSIG)) ||
+	    ((sig = ltos_signo[lx_sig]) < 0))
+		return (set_errno(EINVAL));
+
+	/*
+	 * If the Linux pid is 1, translate the pid to the actual init
+	 * pid for the zone.  Note that Linux dictates that no unhandled
+	 * signals may be sent to init, so check for that, too.
+	 *
+	 * Otherwise, extract the tid and real pid from the Linux pid.
+	 */
+	initpid = cp->p_zone->zone_proc_initpid;
+	if (pid == 1)
+		pid = initpid;
+	if ((pid == initpid) && ((rv = init_sig_check(sig, pid)) != 0))
+		return (set_errno(rv));
+	else if (lx_lpid_to_spair(pid, &pid, &tid) < 0)
+		return (set_errno(ESRCH));
+
+	if (tgkill && tgid != pid)
+		return (set_errno(ESRCH));
+
+	sqp = kmem_zalloc(sizeof (sigqueue_t), KM_SLEEP);
+
+	/*
+	 * Find the process for the passed pid...
+	 */
+	mutex_enter(&pidlock);
+	if (((pp = prfind(pid)) == NULL) || (pp->p_stat == SIDL)) {
+		mutex_exit(&pidlock);
+		rv = set_errno(ESRCH);
+		goto free_and_exit;
+	}
+	mutex_enter(&pp->p_lock);
+	mutex_exit(&pidlock);
+
+	/*
+	 * Deny permission to send the signal if either of the following
+	 * is true:
+	 *
+	 *	+ The signal is SIGCONT and the target pid is not in the same
+	 *	  session as the sender
+	 *
+	 *	+ prochasprocperm() shows the user lacks sufficient permission
+	 *	  to send the signal to the target pid
+	 */
+	if (((sig == SIGCONT) && (pp->p_sessp != cp->p_sessp)) ||
+	    (!prochasprocperm(pp, cp, CRED()))) {
+		mutex_exit(&pp->p_lock);
+		rv = set_errno(EPERM);
+		goto free_and_exit;
+	}
+
+	/* check for the tid */
+	if ((t = idtot(pp, tid)) == NULL) {
+		mutex_exit(&pp->p_lock);
+		rv = set_errno(ESRCH);
+		goto free_and_exit;
+	}
+
+	/* a signal of 0 means just check for the existence of the thread */
+	if (lx_sig == 0) {
+		mutex_exit(&pp->p_lock);
+		rv = 0;
+		goto free_and_exit;
+	}
+
+	sqp->sq_info.si_signo = sig;
+	sqp->sq_info.si_code = SI_LWP;
+	sqp->sq_info.si_pid = cp->p_pid;
+	sqp->sq_info.si_zoneid = getzoneid();
+	sqp->sq_info.si_uid = crgetruid(CRED());
+	sigaddqa(pp, t, sqp);
+
+	mutex_exit(&pp->p_lock);
+
+	return (0);
+
+free_and_exit:
+	kmem_free(sqp, sizeof (sigqueue_t));
+	return (rv);
+}
+
+long
+lx_tgkill(pid_t tgid, pid_t pid, int lx_sig)
+{
+	return (lx_thrkill(tgid, pid, lx_sig, B_TRUE));
+}
+
+long
+lx_tkill(pid_t pid, int lx_sig)
+{
+	return (lx_thrkill(0, pid, lx_sig, B_FALSE));
+}
+
+long
+lx_kill(pid_t lx_pid, int lx_sig)
+{
+	pid_t s_pid, initpid;
+	sigsend_t v;
+	zone_t *zone = curproc->p_zone;
+	struct proc *p;
+	int err, sig, nfound;
+
+	if ((lx_sig < 0) || (lx_sig > LX_NSIG) ||
+	    ((sig = ltos_signo[lx_sig]) < 0))
+		return (set_errno(EINVAL));
+
+	/*
+	 * Since some linux apps rely on init(1M) having PID 1, we
+	 * transparently translate 1 to the real init(1M)'s pid.  We then
+	 * check to be sure that it is legal for this process to send this
+	 * signal to init(1M).
+	 */
+	initpid = zone->zone_proc_initpid;
+	if (lx_pid == 1) {
+		s_pid = initpid;
+	} else if (lx_pid == 0 || lx_pid == -1) {
+		s_pid = 0;
+	} else if (lx_pid > 0) {
+		if (lx_lpid_to_spair(lx_pid, &s_pid, NULL) != 0) {
+			/*
+			 * If we didn't find this pid that means it doesn't
+			 * exist in this zone.
+			 */
+			return (set_errno(ESRCH));
+		}
+	} else {
+		ASSERT(lx_pid < 0);
+		if (lx_lpid_to_spair(-lx_pid, &s_pid, NULL) != 0) {
+			/*
+			 * If we didn't find this pid it means that the
+			 * process group leader doesn't exist in this zone.
+			 * In this case assuming that the Linux pid is
+			 * the same as the Solaris pid will get us the
+			 * correct behavior.
+			 */
+			s_pid = -lx_pid;
+		}
+	}
+
+	if ((s_pid == initpid) && ((err = init_sig_check(sig, s_pid)) != 0))
+		return (set_errno(err));
+
+	/*
+	 * For individual processes, kill() semantics are the same between
+	 * Solaris and Linux.
+	 */
+	if (lx_pid >= 0)
+		return (kill(s_pid, sig));
+
+	/*
+	 * In Solaris, sending a signal to -pid means "send a signal to
+	 * everyone in process group pid."  In Linux it means "send a
+	 * signal to everyone in the group other than init."  Sending a
+	 * signal to -1 means "send a signal to every process except init
+	 * and myself."
+	 */
+
+	bzero(&v, sizeof (v));
+	v.sig = sig;
+	v.checkperm = 1;
+	v.sicode = SI_USER;
+	err = 0;
+
+	mutex_enter(&pidlock);
+
+	p = (lx_pid == -1) ? practive : pgfind(s_pid);
+	nfound = 0;
+	while (err == 0 && p != NULL) {
+		if ((p->p_zone == zone) && (p->p_stat != SIDL) &&
+		    (p->p_pid != initpid) && (lx_pid < -1 || p != curproc)) {
+			nfound++;
+			err = sigsendproc(p, &v);
+		}
+
+		p = (lx_pid == -1) ? p->p_next : p->p_pglink;
+	}
+	mutex_exit(&pidlock);
+
+	/*
+	 * If we found no processes, we'll return ESRCH -- but unlike our
+	 * native kill(2), we do not return EPERM if processes are found but
+	 * we did not have permission to send any of them a signal.
+	 */
+	if (nfound == 0)
+		err = ESRCH;
+
+	return (err ? set_errno(err) : 0);
+}
+
+/*
+ * This handles the unusual case where the user sends a non-queueable signal
+ * through rt_sigqueueinfo. Signals sent with codes that indicate they are
+ * queuable are sent through the sigqueue syscall via the user level function
+ * lx_rt_sigqueueinfo().
+ */
+int
+lx_helper_rt_sigqueueinfo(pid_t tgid, int sig, siginfo_t *uinfo)
+{
+	proc_t *target_proc;
+	pid_t s_pid;
+	zone_t *zone = curproc->p_zone;
+	sigsend_t send;
+	int err;
+	siginfo_t kinfo;
+
+	if (copyin(uinfo, &kinfo, sizeof (siginfo_t)) != 0)
+		return (set_errno(EFAULT));
+	/* Unlike in lx_kill, this process id must be exact, no negatives. */
+	if (tgid == 0)
+		return (set_errno(ESRCH));
+	if (tgid < 0)
+		return (set_errno(EINVAL));
+	/*
+	 * Translate init directly, otherwise use the convenient utility
+	 * function to translate. Since we're sending to the whole group, we
+	 * only need the solaris pid, and not the lwp id.
+	 */
+	if (tgid == 1) {
+		s_pid = zone->zone_proc_initpid;
+	} else {
+		if (lx_lpid_to_spair(tgid, &s_pid, NULL) != 0) {
+			/*
+			 * If we didn't find this pid that means it doesn't
+			 * exist in this zone.
+			 */
+			return (set_errno(ESRCH));
+		}
+	}
+	/*
+	 * We shouldn't have queuable signals here, those are sent elsewhere by
+	 * the usermode handler for this emulated call.
+	 */
+	if (!SI_CANQUEUE(kinfo.si_code)) {
+		return (set_errno(EINVAL));
+	}
+	/* Since our signal shouldn't queue, we just call sigsendproc(). */
+	bzero(&send, sizeof (send));
+	send.sig = sig;
+	send.checkperm = 1;
+	send.sicode = kinfo.si_code;
+	send.value = kinfo.si_value;
+
+	mutex_enter(&pidlock);
+	target_proc = prfind(s_pid);
+	err = 0;
+	if (target_proc != NULL) {
+		err = sigsendproc(target_proc, &send);
+		if (err == 0 && send.perm == 0)
+			err = EPERM;
+	} else {
+		err = ESRCH;
+	}
+	mutex_exit(&pidlock);
+
+	return (err ? set_errno(err) : 0);
+}
+
+/*
+ * Unlike the above function, this handles all system calls to rt_tgsigqueue
+ * regardless of si_code.
+ */
+int
+lx_helper_rt_tgsigqueueinfo(pid_t tgid, pid_t tid, int sig, siginfo_t *uinfo)
+{
+	id_t s_tid;
+	pid_t s_pid;
+	proc_t *target_proc;
+	sigqueue_t *sqp;
+	kthread_t *t;
+	siginfo_t kinfo;
+
+	if (copyin(uinfo, &kinfo, sizeof (siginfo_t)) != 0)
+		return (set_errno(EFAULT));
+	if (lx_lpid_to_spair(tid, &s_pid, &s_tid) != 0)
+		return (set_errno(ESRCH));
+	/*
+	 * For group leaders, solaris pid == linux pid, so the solaris leader
+	 * pid should be the same as the tgid but since the tgid comes in via
+	 * the syscall we need to check for an invalid value.
+	 */
+	if (s_pid != tgid)
+		return (set_errno(EINVAL));
+
+	mutex_enter(&pidlock);
+	target_proc = prfind(s_pid);
+	if (target_proc != NULL)
+		mutex_enter(&target_proc->p_lock);
+	mutex_exit(&pidlock);
+
+	if (target_proc == NULL) {
+		return (set_errno(ESRCH));
+	}
+	if (sig < 0 || sig >= NSIG)
+		return (set_errno(EINVAL));
+
+	/*
+	 * Some code adapted from lwp_kill, duplicated here because we do some
+	 * customization to the sq_info field of sqp.
+	 */
+	if ((t = idtot(target_proc, s_tid)) == NULL) {
+		mutex_exit(&target_proc->p_lock);
+		return (set_errno(ESRCH));
+	}
+	/* Just checking for existence of the process, not sending a signal. */
+	if (sig == 0) {
+		mutex_exit(&target_proc->p_lock);
+		return (0);
+	}
+	sqp = kmem_zalloc(sizeof (sigqueue_t), KM_SLEEP);
+	sqp->sq_info.si_signo = sig;
+	sqp->sq_info.si_code = kinfo.si_code;
+	sqp->sq_info.si_pid = target_proc->p_pid;
+	sqp->sq_info.si_ctid = PRCTID(target_proc);
+	sqp->sq_info.si_zoneid = getzoneid();
+	sqp->sq_info.si_uid = crgetruid(CRED());
+	sigaddqa(target_proc, t, sqp);
+	mutex_exit(&target_proc->p_lock);
+	return (0);
+}
--- /dev/null	Wed Oct  5 00:32:09 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_link.c	Wed Oct  5 00:32:09 2016
@@ -0,0 +1,194 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#include <sys/fcntl.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/vnode.h>
+#include <sys/systm.h>
+#include <sys/lx_fcntl.h>
+#include <sys/lx_misc.h>
+
+#define	LX_LINK_ALLOWED	(LX_AT_SYMLINK_FOLLOW | LX_AT_EMPTY_PATH)
+
+/* From "uts/common/syscall/stat.c" */
+extern int cstatat_getvp(int, char *, int, vnode_t **, cred_t **);
+/* From uts/common/syscall/unlink.c */
+extern int unlinkat(int, char *, int);
+/* From uts/common/syscall/symlink.c */
+extern int symlinkat(char *, int, char *);
+/* From uts/common/syscall/readlink.c */
+extern ssize_t readlinkat(int, char *, char *, size_t);
+
+static long
+lx_link_common(int ffd, char *from, int tfd, char *to, int flags)
+{
+	int error;
+	vnode_t *fsvp = NULL, *tsvp = NULL;
+	enum symfollow follow = NO_FOLLOW;
+
+	if ((flags & ~LX_LINK_ALLOWED) != 0) {
+		return (set_errno(EINVAL));
+	}
+	if ((flags & LX_AT_EMPTY_PATH) == 0) {
+		char c;
+
+		/*
+		 * Check that both 'from' and 'to' names are non-empty if
+		 * AT_EMPTY_PATH is not set.
+		 */
+		if (copyin(from, &c, sizeof (c)) != 0) {
+			return (set_errno(EFAULT));
+		} else if (c == '\0') {
+			return (set_errno(ENOENT));
+		}
+		if (copyin(to, &c, sizeof (c)) != 0) {
+			return (set_errno(EFAULT));
+		} else if (c == '\0') {
+			return (set_errno(ENOENT));
+		}
+
+		/*
+		 * XXX: When our support for LX capabilities improves, ENOENT
+		 * should be thrown when a process lacking CAP_DAC_READ_SEARCH
+		 * attempts to use the AT_EMPTY_PATH flag.
+		 */
+	}
+	if ((flags & LX_AT_SYMLINK_FOLLOW) != 0) {
+		follow = FOLLOW;
+	}
+
+	if ((error = fgetstartvp(ffd, from, &fsvp)) != 0) {
+		goto out;
+	}
+	if ((error = fgetstartvp(tfd, to, &tsvp)) != 0) {
+		goto out;
+	}
+	error = vn_linkat(fsvp, from, follow, tsvp, to, UIO_USERSPACE);
+
+out:
+	if (fsvp != NULL) {
+		VN_RELE(fsvp);
+	}
+	if (tsvp != NULL) {
+		VN_RELE(tsvp);
+	}
+	if (error) {
+		return (set_errno(error));
+	}
+	return (0);
+}
+
+long
+lx_link(char *from, char *to)
+{
+	return (lx_link_common(AT_FDCWD, from, AT_FDCWD, to, 0));
+}
+
+long
+lx_linkat(int ffd, char *from, int tfd, char *to, int flags)
+{
+	ffd = (ffd == LX_AT_FDCWD) ? AT_FDCWD : ffd;
+	tfd = (tfd == LX_AT_FDCWD) ? AT_FDCWD : tfd;
+
+	return (lx_link_common(ffd, from, tfd, to, flags));
+}
+
+static boolean_t
+lx_isdir(int atfd, char *path)
+{
+	cred_t *cr = NULL;
+	vnode_t *vp = NULL;
+	boolean_t is_dir;
+
+	if (cstatat_getvp(atfd, path, NO_FOLLOW, &vp, &cr) != 0)
+		return (B_FALSE);
+
+	is_dir = (vp->v_type == VDIR);
+	VN_RELE(vp);
+
+	return (is_dir);
+}
+
+long
+lx_unlink(char *path)
+{
+	int err;
+
+	if ((err = unlinkat(AT_FDCWD, path, 0)) == EPERM) {
+		/* On Linux, an unlink of a dir returns EISDIR, not EPERM. */
+		if (lx_isdir(AT_FDCWD, path))
+			return (set_errno(EISDIR));
+	}
+
+	return (err);
+}
+
+long
+lx_unlinkat(int atfd, char *path, int flag)
+{
+	int err;
+
+	if (atfd == LX_AT_FDCWD)
+		atfd = AT_FDCWD;
+
+	if ((flag = ltos_at_flag(flag, AT_REMOVEDIR, B_TRUE)) < 0)
+		return (set_errno(EINVAL));
+
+	err = unlinkat(atfd, path, flag);
+	if (err == EPERM && !(flag & AT_REMOVEDIR)) {
+		/* On Linux, an unlink of a dir returns EISDIR, not EPERM. */
+		if (lx_isdir(atfd, path))
+			return (set_errno(EISDIR));
+	}
+
+	return (err);
+}
+
+long
+lx_symlink(char *name1, char *name2)
+{
+	return (symlinkat(name1, AT_FDCWD, name2));
+}
+
+long
+lx_symlinkat(char *name1, int atfd, char *name2)
+{
+	if (atfd == LX_AT_FDCWD)
+		atfd = AT_FDCWD;
+
+	return (symlinkat(name1, atfd, name2));
+}
+
+long
+lx_readlink(char *path, char *buf, size_t bufsize)
+{
+	if (bufsize <= 0)
+		return (set_errno(EINVAL));
+
+	return (readlinkat(AT_FDCWD, path, buf, bufsize));
+}
+
+long
+lx_readlinkat(int atfd, char *path, char *buf, size_t bufsize)
+{
+	if (bufsize <= 0)
+		return (set_errno(EINVAL));
+
+	if (atfd == LX_AT_FDCWD)
+		atfd = AT_FDCWD;
+
+	return (readlinkat(atfd, path, buf, bufsize));
+}
--- /dev/null	Wed Oct  5 00:32:09 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_lseek.c	Wed Oct  5 00:32:09 2016
@@ -0,0 +1,81 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/thread.h>
+#include <sys/errno.h>
+#include <sys/debug.h>
+
+
+#if defined(_SYSCALL32_IMPL) || defined(_ILP32)
+
+/* from uts/common/syscalls/lseek.c */
+extern offset_t llseek32(int32_t, uint32_t, uint32_t, int);
+extern off32_t lseek32(int32_t, off32_t, int32_t);
+
+long
+lx_llseek(int fd, uint32_t off_high, uint32_t off_low, void *out, int whence)
+{
+	offset_t res;
+
+	ASSERT(get_udatamodel() == DATAMODEL_ILP32);
+	res = llseek32(fd, off_low, off_high, whence);
+	if (ttolwp(curthread)->lwp_errno == 0) {
+		if (copyout(&res, out, sizeof (offset_t)) != 0) {
+			return (set_errno(EFAULT));
+		}
+	}
+	return (ttolwp(curthread)->lwp_errno);
+}
+
+
+long
+lx_lseek32(int fd, off32_t offset, int whence)
+{
+	offset_t res;
+
+	/*
+	 * When returning EOVERFLOW for an offset which is outside the bounds
+	 * of an off32_t, Linux will still perform the actual seek before
+	 * yielding EOVERFLOW.
+	 *
+	 * In order to emulate that behavior, an llseek bound to the 64-bit
+	 * boundary is used.  The overflow can then be reported after the
+	 * successful seek.
+	 */
+	ASSERT(get_udatamodel() == DATAMODEL_ILP32);
+	res = llseek32(fd, (uint32_t)offset, 0, whence);
+	if (ttolwp(curthread)->lwp_errno == 0 && res > MAXOFF32_T) {
+		return (set_errno(EOVERFLOW));
+	}
+	return (res);
+
+}
+#endif /* defined(_SYSCALL32_IMPL) || defined(_ILP32) */
+
+#if defined(_LP64)
+
+/* from uts/common/syscalls/lseek.c */
+extern off_t lseek64(int, off_t, int);
+
+long
+lx_lseek64(int fd, off_t offset, int whence)
+{
+	ASSERT(get_udatamodel() == DATAMODEL_LP64);
+	return (lseek64(fd, offset, whence));
+}
+
+#endif /* defined(_LP64) */
--- /dev/null	Wed Oct  5 00:32:09 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_miscsys.c	Wed Oct  5 00:32:09 2016
@@ -0,0 +1,282 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#include <sys/systeminfo.h>
+#include <sys/fcntl.h>
+#include <sys/resource.h>
+#include <sys/uadmin.h>
+#include <sys/lx_misc.h>
+
+#define	LINUX_REBOOT_MAGIC1		0xfee1dead
+#define	LINUX_REBOOT_MAGIC2		672274793
+#define	LINUX_REBOOT_MAGIC2A		85072278
+#define	LINUX_REBOOT_MAGIC2B		369367448
+#define	LINUX_REBOOT_MAGIC2C		537993216
+
+#define	LINUX_REBOOT_CMD_RESTART	0x1234567
+#define	LINUX_REBOOT_CMD_HALT		0xcdef0123
+#define	LINUX_REBOOT_CMD_CAD_ON		0x89abcdef
+#define	LINUX_REBOOT_CMD_CAD_OFF	0
+#define	LINUX_REBOOT_CMD_POWER_OFF	0x4321fedc
+#define	LINUX_REBOOT_CMD_RESTART2	0xa1b2c3d4
+#define	LINUX_REBOOT_CMD_SW_SUSPEND	0xD000FCE2
+#define	LINUX_REBOOT_CMD_KEXEC		0x45584543
+
+#define	LX_RUSAGE_SELF			0
+#define	LX_RUSAGE_CHILDREN		(-1)
+#define	LX_RUSAGE_BOTH			(-2)
+#define	LX_RUSAGE_THREAD		1
+
+/* From uts/common/fs/vfs.c */
+extern void vfs_sync(int);
+/* From uts/common/os/grow.c */
+extern int mincore(caddr_t, size_t, char *);
+extern int munmap(caddr_t, size_t);
+/* From uts/common/os/session.c */
+extern int vhangup();
+/* From uts/common/syscall/alarm.c */
+extern int alarm(int);
+/* From uts/common/syscall/chdir.c */
+extern int chdir(char *);
+extern int chroot(char *);
+extern int fchdir(int);
+/* From uts/common/syscall/nice.c */
+extern int nice(int);
+/* From uts/common/syscall/open.c */
+extern int open(char *, int, int);
+/* From uts/common/syscall/pause.c */
+extern int pause();
+/* From uts/common/syscall/rusagesys.c */
+extern int rusagesys(int, void *, void *, void *, void *);
+/* From uts/common/syscall/systeminfo.c */
+extern long systeminfo(int, char *, long);
+/* From uts/common/syscall/timers.c */
+extern int getitimer(uint_t, struct itimerval *);
+/* From uts/common/syscall/time.c */
+extern int stime(time_t);
+/* From uts/common/syscall/uadmin.c */
+extern int uadmin(int, int, uintptr_t);
+
+long
+lx_alarm(int seconds)
+{
+	return (alarm(seconds));
+}
+
+long
+lx_chdir(char *path)
+{
+	return (chdir(path));
+}
+
+long
+lx_chroot(char *path)
+{
+	return (chroot(path));
+}
+
+long
+lx_creat(char *path, mode_t mode)
+{
+	return (open(path, O_WRONLY | O_CREAT | O_TRUNC, mode));
+}
+
+long
+lx_fchdir(int fd)
+{
+	return (fchdir(fd));
+}
+
+long
+lx_getitimer(int which, struct itimerval *value)
+{
+	return (getitimer(which, value));
+}
+
+/* Linux and illumos have the same rusage structures. */
+long
+lx_getrusage(int who, struct rusage *rup)
+{
+	int code;
+
+	switch (who) {
+	case LX_RUSAGE_SELF:
+		code = _RUSAGESYS_GETRUSAGE;
+		break;
+	case LX_RUSAGE_CHILDREN:
+		code = _RUSAGESYS_GETRUSAGE_CHLD;
+		break;
+	case LX_RUSAGE_THREAD:
+		code = _RUSAGESYS_GETRUSAGE_LWP;
+		break;
+	default:
+		return (set_errno(EINVAL));
+	}
+
+	return (rusagesys(code, rup, NULL, NULL, NULL));
+}
+
+long
+lx_mincore(caddr_t addr, size_t len, char *vec)
+{
+	int r;
+
+	r = mincore(addr, len, vec);
+	if (r == EINVAL) {
+		/*
+		 * LTP mincore01 expects mincore with a huge len to fail with
+		 * ENOMEM on a modern kernel, although on Linux 2.6.11 and
+		 * earlier, it will return EINVAL.
+		 */
+		if (lx_kern_release_cmp(curzone, "2.6.11") > 0 && (long)len < 0)
+			return (set_errno(ENOMEM));
+	}
+	return (r);
+}
+
+long
+lx_munmap(void *addr, size_t len)
+{
+	return (munmap(addr, len));
+}
+
+long
+lx_nice(int incr)
+{
+	return (nice(incr));
+}
+
+long
+lx_pause(void)
+{
+	return (pause());
+}
+
+/*ARGSUSED*/
+long
+lx_reboot(int magic1, int magic2, uint_t flag, uintptr_t p4)
+{
+	if (magic1 != LINUX_REBOOT_MAGIC1)
+		return (set_errno(EINVAL));
+
+	switch (magic2) {
+	case LINUX_REBOOT_MAGIC2:
+	case LINUX_REBOOT_MAGIC2A:
+	case LINUX_REBOOT_MAGIC2B:
+	case LINUX_REBOOT_MAGIC2C:
+		break;
+	default:
+		return (set_errno(EINVAL));
+	}
+
+	/*
+	 * Once we have better Linux capabilities(7) support we should check
+	 * CAP_SYS_BOOT instead.
+	 */
+	if (crgetuid(CRED()) != 0)
+		return (set_errno(EPERM));
+
+	switch (flag) {
+	case LINUX_REBOOT_CMD_CAD_ON:
+	case LINUX_REBOOT_CMD_CAD_OFF:
+		/* ignored */
+		return (0);
+
+	case LINUX_REBOOT_CMD_POWER_OFF:
+	case LINUX_REBOOT_CMD_HALT:
+		return (uadmin(A_SHUTDOWN, AD_HALT, NULL));
+
+	case LINUX_REBOOT_CMD_RESTART:
+	case LINUX_REBOOT_CMD_RESTART2:
+		/* RESTART2 may need more work */
+		return (uadmin(A_SHUTDOWN, AD_BOOT, NULL));
+
+	default:
+		return (set_errno(EINVAL));
+	}
+}
+
+long
+lx_setdomainname(char *name, long len)
+{
+	if (len < 0 || len >= LX_SYS_UTS_LN)
+		return (set_errno(EINVAL));
+
+	ttolwp(curthread)->lwp_errno = 0;
+	(void) systeminfo(SI_SET_SRPC_DOMAIN, name, len);
+	if (ttolwp(curthread)->lwp_errno != 0)
+		return (ttolwp(curthread)->lwp_errno);
+	return (0);
+}
+
+long
+lx_sethostname(char *name, size_t len)
+{
+	ttolwp(curthread)->lwp_errno = 0;
+	(void) systeminfo(SI_SET_HOSTNAME, name, len);
+	if (ttolwp(curthread)->lwp_errno != 0)
+		return (ttolwp(curthread)->lwp_errno);
+	return (0);
+}
+
+long
+lx_stime(time_t *tp)
+{
+	time_t time;
+
+	if (copyin(tp, &time, sizeof (time)) != 0)
+		return (set_errno(EFAULT));
+
+	return (stime(time));
+}
+
+long
+lx_sync(void)
+{
+	vfs_sync(0);
+	return (0);
+}
+
+/*
+ * For syslog, since there is no Linux kernel and nothing to log, we simply
+ * emulate a kernel buffer (LOG_BUF_LEN) of 0 bytes and only handle errors for
+ * bad input. All actions except 3 and 10 require CAP_SYS_ADMIN or CAP_SYSLOG
+ * so without full capabilities support, for now we just perform an euid check.
+ */
+long
+lx_syslog(int type, char *bufp, int len)
+{
+	if (type < 0 || type > 10)
+		return (set_errno(EINVAL));
+
+	if (type != 3 && type != 10 && crgetuid(CRED()) != 0)
+		return (set_errno(EPERM));
+
+	if (type >= 2 && type <= 4 && (bufp == NULL || len < 0))
+		return (set_errno(EINVAL));
+
+	if (type == 8 && (len < 1 || len > 8))
+		return (set_errno(EINVAL));
+
+	return (0);
+}
+
+long
+lx_vhangup(void)
+{
+	if (crgetuid(CRED()) != 0)
+		return (set_errno(EPERM));
+	return (vhangup());
+}
--- /dev/null	Wed Oct  5 00:32:09 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_mkdir.c	Wed Oct  5 00:32:09 2016
@@ -0,0 +1,38 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+#include <sys/fcntl.h>
+#include <sys/lx_fcntl.h>
+
+/*
+ * From "uts/common/syscall/mkdir.c":
+ */
+extern int mkdirat(int, char *, int);
+
+long
+lx_mkdirat(int fd, char *dname, int dmode)
+{
+	if (fd == LX_AT_FDCWD) {
+		fd = AT_FDCWD;
+	}
+
+	return (mkdirat(fd, dname, dmode));
+}
+
+long
+lx_mkdir(char *dname, int dmode)
+{
+	return (mkdirat(AT_FDCWD, dname, dmode));
+}
--- /dev/null	Wed Oct  5 00:32:09 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_modify_ldt.c	Wed Oct  5 00:32:09 2016
@@ -0,0 +1,121 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/segments.h>
+#include <sys/archsystm.h>
+#include <sys/proc.h>
+#include <sys/sysi86.h>
+#include <sys/cmn_err.h>
+#include <sys/lx_ldt.h>
+
+/*
+ * Read the ldt_info structure in from the Linux app, convert it to an ssd
+ * structure, and then call setdscr() to do all the heavy lifting.
+ */
+static int
+write_ldt(void *data, ulong_t count)
+{
+	user_desc_t usd;
+	struct ssd ssd;
+	struct ldt_info ldt_inf;
+	proc_t *pp = curthread->t_procp;
+	int err;
+
+	if (count != sizeof (ldt_inf))
+		return (set_errno(EINVAL));
+
+	if (copyin(data, &ldt_inf, sizeof (ldt_inf)))
+		return (set_errno(EFAULT));
+
+	if (ldt_inf.entry_number >= MAXNLDT)
+		return (set_errno(EINVAL));
+
+	LDT_INFO_TO_DESC(&ldt_inf, &usd);
+	usd_to_ssd(&usd, &ssd, SEL_LDT(ldt_inf.entry_number));
+
+	/*
+	 * Get everyone into a safe state before changing the LDT.
+	 */
+	if (!holdlwps(SHOLDFORK1))
+		return (set_errno(EINTR));
+
+	err = setdscr(&ssd);
+
+	/*
+	 * Release the hounds!
+	 */
+	mutex_enter(&pp->p_lock);
+	continuelwps(pp);
+	mutex_exit(&pp->p_lock);
+
+	return (err ? set_errno(err) : 0);
+}
+
+static int
+read_ldt(void *uptr, ulong_t count)
+{
+	proc_t *pp = curproc;
+	int bytes;
+
+	if (pp->p_ldt == NULL)
+		return (0);
+
+	bytes = (pp->p_ldtlimit + 1) * sizeof (user_desc_t);
+	if (bytes > count)
+		bytes = count;
+
+	if (copyout(pp->p_ldt, uptr, bytes))
+		return (set_errno(EFAULT));
+
+	return (bytes);
+}
+
+long
+lx_modify_ldt(int op, void *data, ulong_t count)
+{
+	int rval;
+
+	switch (op) {
+	case 0:
+		rval = read_ldt(data, count);
+		break;
+
+	case 1:
+		rval = write_ldt(data, count);
+		break;
+
+	default:
+		rval = set_errno(ENOSYS);
+		break;
+	}
+
+	return (rval);
+}
--- /dev/null	Wed Oct  5 00:32:09 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_mount.c	Wed Oct  5 00:32:09 2016
@@ -0,0 +1,657 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#include <sys/ctype.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/pathname.h>
+#include <sys/types.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_syscalls.h>
+#include <sys/lx_autofs.h>
+
+#define	tolower(x)	(((x) >= 'A' && (x) <= 'Z') ? (x) - 'A' + 'a' : (x))
+
+/*
+ * mount(2) is significantly different between Linux and illumos. One of the
+ * main differences is between the set of flags.  Some flags on Linux can be
+ * translated to an illumos equivalent, some are converted to a
+ * filesystem-specific option, while others have no equivalent whatsoever.
+ *
+ * Another big difference is that mounting NFS is fully handled in the kernel on
+ * Linux whereas on illumos a lot of preliminary work is done by the NFS mount
+ * command before calling mount(2). As a simplification, we forward NFS
+ * mount calls back out to the user-level library which does the same kind of
+ * preliminary processing that is done by the native user-level NFS mount code.
+ */
+#define	LX_MS_MGC_VAL		0xC0ED0000
+#define	LX_MS_RDONLY		0x00000001
+#define	LX_MS_NOSUID		0x00000002
+#define	LX_MS_NODEV		0x00000004
+#define	LX_MS_NOEXEC		0x00000008
+#define	LX_MS_SYNCHRONOUS	0x00000010
+#define	LX_MS_REMOUNT		0x00000020
+#define	LX_MS_MANDLOCK		0x00000040
+#define	LX_MS_NOATIME		0x00000400
+#define	LX_MS_NODIRATIME	0x00000800
+#define	LX_MS_BIND		0x00001000
+#define	LX_MS_MOVE		0x00002000
+#define	LX_MS_REC		0x00004000
+#define	LX_MS_SILENT		0x00008000
+#define	LX_MS_POSIXACL		0x00010000
+#define	LX_MS_UNBINDABLE	0x00020000
+#define	LX_MS_PRIVATE		0x00040000
+#define	LX_MS_SLAVE		0x00080000
+#define	LX_MS_SHARED		0x00100000
+#define	LX_MS_RELATIME		0x00200000
+#define	LX_MS_KERNMOUNT		0x00400000
+#define	LX_MS_I_VERSION		0x00800000
+#define	LX_MS_STRICTATIME	0x01000000
+#define	LX_MS_LAZYTIME		0x02000000
+
+/* Linux kernel-internal flags - ignored if passed in */
+#define	LX_MS_NOSEC		0x10000000
+#define	LX_MS_BORN		0x20000000
+#define	LX_MS_ACTIVE		0x40000000
+#define	LX_MS_NOUSER		0x80000000
+
+#define	LX_MS_SUPPORTED		(LX_MS_MGC_VAL | \
+				LX_MS_RDONLY | LX_MS_NOSUID | \
+				LX_MS_NODEV | LX_MS_NOEXEC | \
+				LX_MS_REMOUNT | LX_MS_NOATIME | \
+				LX_MS_BIND | LX_MS_SILENT | \
+				LX_MS_STRICTATIME | LX_MS_NOSEC | \
+				LX_MS_BORN | LX_MS_ACTIVE | LX_MS_NOUSER)
+
+/*
+ * support definitions
+ */
+typedef enum mount_opt_type {
+	MOUNT_OPT_INVALID	= 0,
+	MOUNT_OPT_NORMAL	= 1,	/* option value: none */
+	MOUNT_OPT_UINT		= 2,	/* option value: unsigned int */
+	MOUNT_OPT_PASSTHRU	= 3	/* option value: validated downstream */
+} mount_opt_type_t;
+
+typedef struct mount_opt {
+	char			*mo_name;
+	mount_opt_type_t	mo_type;
+} mount_opt_t;
+
+/* From uts/common/syscall/umount.c */
+extern int umount2(char *, int);
+
+/* From lx_chown.c */
+extern long lx_vn_chown(vnode_t *, uid_t, gid_t);
+
+/*
+ * Globals
+ */
+static mount_opt_t lofs_options[] = {
+	{ NULL,			MOUNT_OPT_INVALID }
+};
+
+static mount_opt_t lx_proc_options[] = {
+	{ NULL,			MOUNT_OPT_INVALID }
+};
+
+static mount_opt_t lx_sysfs_options[] = {
+	{ NULL,			MOUNT_OPT_INVALID }
+};
+
+static mount_opt_t lx_tmpfs_options[] = {
+	{ "size",		MOUNT_OPT_PASSTHRU },
+	{ "mode",		MOUNT_OPT_UINT },
+	{ "uid",		MOUNT_OPT_UINT },
+	{ "gid",		MOUNT_OPT_UINT },
+	{ NULL,			MOUNT_OPT_INVALID }
+};
+
+static mount_opt_t lx_autofs_options[] = {
+	{ LX_MNTOPT_FD,		MOUNT_OPT_UINT },
+	{ LX_MNTOPT_PGRP,	MOUNT_OPT_UINT },
+	{ LX_MNTOPT_MINPROTO,	MOUNT_OPT_UINT },
+	{ LX_MNTOPT_MAXPROTO,	MOUNT_OPT_UINT },
+	{ LX_MNTOPT_INDIRECT,	MOUNT_OPT_NORMAL },
+	{ LX_MNTOPT_DIRECT,	MOUNT_OPT_NORMAL },
+	{ LX_MNTOPT_OFFSET,	MOUNT_OPT_NORMAL },
+	{ NULL,			MOUNT_OPT_INVALID }
+};
+
+
+/*
+ * Check the mount options.
+ *
+ * On illumos all mount option verification is done by the user-level mount
+ * command. Invalid options are simply ignored by domount(). Thus, we check
+ * here for invalid/unsupported options.
+ */
+static int
+lx_mnt_opt_verify(char *opts, mount_opt_t *mop)
+{
+	int	opts_len = strlen(opts);
+	char	*opt, *tp;
+	int	opt_len, i;
+	boolean_t last = B_FALSE;
+
+	ASSERT((opts != NULL) && (mop != NULL));
+
+	/* If no options were specified, nothing to do. */
+	if (opts_len == 0)
+		return (0);
+
+	/* If no options are allowed, fail. */
+	if (mop[0].mo_name == NULL)
+		return (ENOTSUP);
+
+	/* Don't accept leading or trailing ','. */
+	if ((opts[0] == ',') || (opts[opts_len] == ','))
+		return (EINVAL);
+
+	/* Don't accept sequential ','. */
+	for (i = 1; i < opts_len; i++) {
+		if ((opts[i - 1] ==  ',') && (opts[i] ==  ','))
+			return (EINVAL);
+	}
+
+	/*
+	 * Verify each prop one at a time. There is no strtok in the kernel but
+	 * it's easy to tokenize the entry ourselves.
+	 */
+	opt = opts;
+	for (tp = opt; *tp != ',' && *tp != '\0'; tp++)
+		;
+	if (*tp == ',') {
+		*tp = '\0';
+	} else {
+		last = B_TRUE;
+	}
+	for (;;) {
+		opt_len = strlen(opt);
+
+		/* Check for matching option/value pair. */
+		for (i = 0; mop[i].mo_name != NULL; i++) {
+			char	*ovalue;
+			int	ovalue_len, mo_len;
+
+			/* If the options is too short don't bother comparing */
+			mo_len = strlen(mop[i].mo_name);
+			if (opt_len < mo_len) {
+				/* Keep trying to find a match. */
+				continue;
+			}
+
+			/* Compare the option to an allowed option. */
+			if (strncmp(mop[i].mo_name, opt, mo_len) != 0) {
+				/* Keep trying to find a match. */
+				continue;
+			}
+
+			if (mop[i].mo_type == MOUNT_OPT_NORMAL) {
+				/* The option doesn't take a value. */
+				if (opt_len == mo_len) {
+					/* This option is ok. */
+					break;
+				} else {
+					/* Keep trying to find a match. */
+					continue;
+				}
+			}
+
+			/* This options takes a value. */
+			if ((opt_len == mo_len) || (opt[mo_len] != '=')) {
+				/* Keep trying to find a match. */
+				continue;
+			}
+
+			/* We have an option match.  Verify option value. */
+			ovalue = &opt[mo_len] + 1;
+			ovalue_len = strlen(ovalue);
+
+			/* Value can't be zero length string. */
+			if (ovalue_len == 0) {
+				goto bad;
+			}
+
+			if (mop[i].mo_type == MOUNT_OPT_UINT) {
+				int j;
+				/* Verify that value is an unsigned int. */
+				for (j = 0; j < ovalue_len; j++) {
+					if (!ISDIGIT(ovalue[j])) {
+						goto bad;
+					}
+				}
+			} else if (mop[i].mo_type == MOUNT_OPT_PASSTHRU) {
+				/* Filesystem will do its own validation. */
+				break;
+			} else {
+				/* Unknown option type specified. */
+				goto bad;
+			}
+
+			/* The option is ok. */
+			break;
+		}
+
+		/* If there were no matches this is an unsupported option. */
+		if (mop[i].mo_name == NULL) {
+			goto bad;
+		}
+
+		/*
+		 * This option is ok, either we're done or move on to the next
+		 * option.
+		 */
+		if (last)
+			break;
+
+		*tp = ',';
+		opt = tp + 1;
+		for (tp = opt; *tp != ',' && *tp != '\0'; tp++)
+			;
+		if (*tp == ',') {
+			*tp = '\0';
+		} else {
+			last = B_TRUE;
+		}
+	};
+
+	/* We verified all the options. */
+	return (0);
+
+bad:
+	if (!last) {
+		*tp = ',';
+	}
+	return (EINVAL);
+}
+
+/*
+ * Remove an option from the string and save it in the provided buffer.
+ * The option string should have already been verified as valid.
+ * Return 0 if not present, -1 if error, and 1 if present and fine.
+ */
+static int
+lx_mnt_opt_rm(char *opts, char *rmopt, char *retstr, int retlen)
+{
+	int	opts_len = strlen(opts);
+	char	*optstart, *optend;
+	int	optlen;
+
+	ASSERT((opts != NULL) && (rmopt != NULL));
+
+	retstr[0] = '\0';
+
+	/* If no options were specified, there's no problem. */
+	if (opts_len == 0)
+		return (0);
+
+	if ((optstart = strstr(opts, rmopt)) == NULL)
+		return (0);
+
+	for (optend = optstart; *optend != ',' && *optend != '\0'; optend++)
+		;
+
+	/*LINTED*/
+	optlen = optend - optstart;
+	if (optlen >= retlen)
+		return (-1);
+	(void) strncpy(retstr, optstart, optlen);
+	retstr[optlen] = '\0';
+
+	if (*optend == ',')
+		optend++;
+
+	optlen = strlen(optend) + 1;
+	bcopy(optend, optstart, optlen);
+
+	if (*optstart == '\0' && optstart != opts) {
+		/* removed last opt and it had a preceeding opt, remove comma */
+		*(optstart - 1) = '\0';
+	}
+
+	return (1);
+}
+
+static int
+lx_mnt_opt_val(char *opt, int *valp)
+{
+	char *op, *ep;
+	long lval;
+
+	if ((op = strchr(opt, '=')) == NULL)
+		return (-1);
+
+	op++;
+	if (!ISDIGIT(*op))
+		return (-1);
+
+	if (ddi_strtoul(op, &ep, 10, (ulong_t *)&lval) != 0 || lval > INT_MAX) {
+		return (-1);
+	}
+
+	if (*ep != '\0')
+		return (-1);
+
+	*valp = (int)lval;
+	return (0);
+}
+
+static int
+lx_mnt_add_opt(char *option, char *buf, size_t buf_size)
+{
+	char *fmt_str = NULL;
+	size_t len;
+
+	ASSERT((option != NULL) && (strlen(option) > 0));
+	ASSERT((buf != NULL) && (buf_size > 0));
+
+	if (buf[0] == '\0') {
+		fmt_str = "%s";
+	} else {
+		fmt_str = ",%s";
+	}
+
+	len = strlen(buf);
+	VERIFY(len <= buf_size);
+	buf_size -= len;
+	buf += len;
+
+	if (snprintf(buf, buf_size, fmt_str, option) > (buf_size - 1))
+		return (EOVERFLOW);
+	return (0);
+}
+
+static int
+lx_mnt_copyin_arg(const char *from, char *to, size_t len)
+{
+	size_t	slen;
+	int	rv;
+
+	rv = copyinstr(from, to, len, &slen);
+	if (rv == ENAMETOOLONG || slen == len)
+		return (ENAMETOOLONG);
+	if (rv != 0)
+		return (EFAULT);
+
+	return (0);
+}
+
+long
+lx_mount(const char *sourcep, const char *targetp, const char *fstypep,
+    uint_t flags, const void *datap)
+{
+	char			fstype[16];
+	char			source[MAXPATHLEN];
+	char			target[MAXPATHLEN];
+	char			options[MAX_MNTOPT_STR];
+	int			sflags, rv;
+	struct mounta		ma, *map = &ma;
+	vfs_t			*vfsp;
+	vnode_t			*vp = NULL;
+	int			uid = -1;
+	int			gid = -1;
+
+	if ((rv = lx_mnt_copyin_arg(fstypep, fstype, sizeof (fstype))) != 0) {
+		if (rv == ENAMETOOLONG)
+			return (set_errno(ENODEV));
+		return (set_errno(rv));
+	}
+
+	/*
+	 * Vector back out to userland emulation for NFS.
+	 */
+	if (strcmp(fstype, "nfs") == 0) {
+		uintptr_t uargs[5] = {(uintptr_t)sourcep, (uintptr_t)targetp,
+		    (uintptr_t)fstypep, (uintptr_t)flags, (uintptr_t)datap};
+
+		/* The userspace emulation will do the lx_syscall_return() */
+		ttolxlwp(curthread)->br_eosys = JUSTRETURN;
+
+#if defined(_LP64)
+		if (get_udatamodel() != DATAMODEL_NATIVE) {
+			lx_emulate_user32(ttolwp(curthread), LX_SYS32_mount,
+			    uargs);
+		} else
+#endif
+		{
+			lx_emulate_user(ttolwp(curthread), LX_SYS_mount, uargs);
+		}
+		return (0);
+	}
+
+	sflags = MS_SYSSPACE | MS_OPTIONSTR;
+	options[0] = '\0';
+
+	/* Copy in parameters that are always present. */
+	if ((rv = lx_mnt_copyin_arg(sourcep, source, sizeof (source))) != 0)
+		return (set_errno(rv));
+
+	if ((rv = lx_mnt_copyin_arg(targetp, target, sizeof (target))) != 0)
+		return (set_errno(rv));
+
+	/*
+	 * While SunOS is picky about mount(2) target paths being absolute,
+	 * Linux is not so strict. In order to facilitate this looser
+	 * requirement we must lookup the full path.
+	 */
+	if (target[0] != '/') {
+		vnode_t *vp;
+
+		if ((rv = lookupnameatcred(target, UIO_SYSSPACE, FOLLOW,
+		    NULLVPP, &vp, NULL, CRED())) != 0)
+			return (set_errno(rv));
+
+		rv = vnodetopath(NULL, vp, target, MAXPATHLEN, CRED());
+		VN_RELE(vp);
+		if (rv != 0)
+			return (set_errno(rv));
+	}
+
+	/* Make sure we support the requested mount flags. */
+	if ((flags & ~LX_MS_SUPPORTED) != 0)
+		return (set_errno(ENOTSUP));
+
+	/* Copy in Linux mount options. */
+	if (datap != NULL &&
+	    (rv = lx_mnt_copyin_arg(datap, options, sizeof (options))) != 0)
+		return (set_errno(rv));
+
+	/* Do filesystem specific mount work. */
+	if (flags & LX_MS_BIND) {
+		/* If MS_BIND is set, we turn this into a lofs mount.  */
+		(void) strcpy(fstype, "lofs");
+
+		/* Verify Linux mount options. */
+		if ((rv = lx_mnt_opt_verify(options, lofs_options)) != 0)
+			return (set_errno(rv));
+	} else if (strcmp(fstype, "tmpfs") == 0) {
+		char	idstr[64];
+
+		/* Verify Linux mount options. */
+		if ((rv = lx_mnt_opt_verify(options, lx_tmpfs_options)) != 0)
+			return (set_errno(rv));
+
+		/*
+		 * Linux defaults to mode=1777 for tmpfs mounts.
+		 */
+		if (strstr(options, "mode=") == NULL) {
+			if (options[0] != '\0')
+				(void) strlcat(options, ",", sizeof (options));
+			(void) strlcat(options, "mode=1777", sizeof (options));
+		}
+
+		switch (lx_mnt_opt_rm(options, "uid=", idstr, sizeof (idstr))) {
+		case 0:
+			uid = -1;
+			break;
+		case 1:
+			if (lx_mnt_opt_val(idstr, &uid) < 0)
+				return (set_errno(EINVAL));
+			break;
+		default:
+			return (set_errno(E2BIG));
+		}
+		switch (lx_mnt_opt_rm(options, "gid=", idstr, sizeof (idstr))) {
+		case 0:
+			gid = -1;
+			break;
+		case 1:
+			if (lx_mnt_opt_val(idstr, &gid) < 0)
+				return (set_errno(EINVAL));
+			break;
+		default:
+			return (set_errno(E2BIG));
+		}
+
+		/*
+		 * Linux seems to always allow overlay mounts. We allow this
+		 * everywhere except under /dev where it interferes with device
+		 * emulation.
+		 */
+		if (strcmp(target, "/dev") != 0 &&
+		    strncmp(target, "/dev/", 5) != 0)
+			sflags |= MS_OVERLAY;
+	} else if (strcmp(fstype, "proc") == 0) {
+		/* Translate proc mount requests to lx_proc requests. */
+		(void) strcpy(fstype, "lx_proc");
+
+		/* Verify Linux mount options. */
+		if ((rv = lx_mnt_opt_verify(options, lx_proc_options)) != 0)
+			return (set_errno(rv));
+	} else if (strcmp(fstype, "sysfs") == 0) {
+		/* Translate sysfs mount requests to lx_sysfs requests. */
+		(void) strcpy(fstype, "lx_sysfs");
+
+		/* Verify Linux mount options. */
+		if ((rv = lx_mnt_opt_verify(options, lx_sysfs_options)) != 0)
+			return (set_errno(rv));
+	} else if (strcmp(fstype, "cgroup") == 0) {
+		/* Translate cgroup mount requests to lx_cgroup requests. */
+		(void) strcpy(fstype, "lx_cgroup");
+
+		/*
+		 * Currently don't verify Linux mount options since we can
+		 * have a subsystem string provided.
+		 */
+	} else if (strcmp(fstype, "autofs") == 0) {
+		/* Translate autofs mount requests to lxautofs requests. */
+		(void) strcpy(fstype, LX_AUTOFS_NAME);
+
+		/* Verify Linux mount options. */
+		if ((rv = lx_mnt_opt_verify(options, lx_autofs_options)) != 0)
+			return (set_errno(rv));
+
+		/* Linux seems to always allow overlay mounts */
+		sflags |= MS_OVERLAY;
+	} else {
+		return (set_errno(ENODEV));
+	}
+
+	/* Convert some Linux flags to illumos flags. */
+	if (flags & LX_MS_RDONLY)
+		sflags |= MS_RDONLY;
+	if (flags & LX_MS_NOSUID)
+		sflags |= MS_NOSUID;
+	if (flags & LX_MS_REMOUNT)
+		sflags |= MS_REMOUNT;
+
+	/*
+	 * Convert some Linux flags to illumos option strings.
+	 */
+	if (flags & LX_MS_STRICTATIME) {
+		/*
+		 * The "strictatime" mount option ensures that none of the
+		 * weaker atime-related mode options are in effect.
+		 */
+		flags &= ~(LX_MS_RELATIME | LX_MS_NOATIME);
+	}
+	if ((flags & LX_MS_NODEV) &&
+	    (rv = lx_mnt_add_opt("nodev", options, sizeof (options))) != 0)
+		return (set_errno(rv));
+	if ((flags & LX_MS_NOEXEC) &&
+	    (rv = lx_mnt_add_opt("noexec", options, sizeof (options))) != 0)
+		return (set_errno(rv));
+	if ((flags & LX_MS_NOATIME) &&
+	    (rv = lx_mnt_add_opt("noatime", options, sizeof (options))) != 0)
+		return (set_errno(rv));
+
+	if ((rv = lookupname(target, UIO_SYSSPACE, FOLLOW, NULLVPP, &vp)) != 0)
+		return (set_errno(rv));
+
+	/* If mounting proc over itself, just return ok */
+	if (strcmp(fstype, "lx_proc") == 0 && strcmp("lx_proc",
+	    vfssw[vp->v_vfsp->vfs_fstype].vsw_name) == 0) {
+		VN_RELE(vp);
+		return (0);
+	}
+
+	map->spec = source;
+	map->dir = target;
+	map->flags = sflags;
+	map->fstype = fstype;
+	map->dataptr = NULL;
+	map->datalen = 0;
+	map->optptr = options;
+	map->optlen = sizeof (options);
+
+	rv = domount(NULL, map, vp, CRED(), &vfsp);
+	VN_RELE(vp);
+	if (rv != 0)
+		return (set_errno(rv));
+
+	VFS_RELE(vfsp);
+	if (strcmp(fstype, "tmpfs") == 0 && (uid != -1 || gid != -1)) {
+		/* Handle tmpfs uid/gid mount options. */
+		if (lookupname(target, UIO_SYSSPACE, FOLLOW, NULLVPP,
+		    &vp) == 0) {
+			(void) lx_vn_chown(vp, (uid_t)uid, (gid_t)gid);
+			VN_RELE(vp);
+		}
+	}
+
+	return (0);
+}
+
+/*
+ * umount() is identical to illumos, though implemented on top of umount2().
+ */
+long
+lx_umount(char *path)
+{
+	return (umount2(path, 0));
+}
+
+/*
+ * The Linux umount2() system call is identical to illumos but has a different
+ * value for MNT_FORCE (the logical equivalent to MS_FORCE).
+ */
+#define	LX_MNT_FORCE	0x1
+
+long
+lx_umount2(char *path, int flg)
+{
+	int flags = 0;
+
+	if (flg & ~LX_MNT_FORCE)
+		return (set_errno(EINVAL));
+
+	if (flg & LX_MNT_FORCE)
+		flags |= MS_FORCE;
+
+	return (umount2(path, flags));
+}
--- /dev/null	Wed Oct  5 00:32:09 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_open.c	Wed Oct  5 00:32:09 2016
@@ -0,0 +1,260 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#include <sys/systm.h>
+#include <sys/fcntl.h>
+#include <sys/file.h>
+#include <sys/filio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/inttypes.h>
+#include <sys/mutex.h>
+
+#include <sys/lx_types.h>
+#include <sys/lx_fcntl.h>
+#include <sys/lx_misc.h>
+
+extern int fcntl(int, int, intptr_t);
+extern int openat(int, char *, int, int);
+extern int open(char *, int, int);
+extern int close(int);
+extern int cioctl(file_t *, int, intptr_t, int *);
+extern int lookupnameat(char *, enum uio_seg, int, vnode_t **, vnode_t **,
+    vnode_t *);
+
+
+static int
+ltos_open_flags(int input)
+{
+	int flags;
+
+	if (input & LX_O_PATH) {
+		input &= (LX_O_DIRECTORY | LX_O_NOFOLLOW | LX_O_CLOEXEC);
+	}
+
+	/* This depends on the Linux ACCMODE flags being the same as SunOS. */
+	flags = (input & LX_O_ACCMODE);
+
+	if (input & LX_O_CREAT) {
+		flags |= O_CREAT;
+	}
+
+	if (input & LX_O_EXCL)
+		flags |= O_EXCL;
+	if (input & LX_O_NOCTTY)
+		flags |= O_NOCTTY;
+	if (input & LX_O_TRUNC)
+		flags |= O_TRUNC;
+	if (input & LX_O_APPEND)
+		flags |= O_APPEND;
+	if (input & LX_O_NONBLOCK)
+		flags |= O_NONBLOCK;
+	if (input & LX_O_SYNC)
+		flags |= O_SYNC;
+	if (input & LX_O_LARGEFILE)
+		flags |= O_LARGEFILE;
+	if (input & LX_O_NOFOLLOW)
+		flags |= O_NOFOLLOW;
+	if (input & LX_O_CLOEXEC)
+		flags |= O_CLOEXEC;
+
+	/*
+	 * Linux uses the LX_O_DIRECT flag to do raw, synchronous I/O to the
+	 * device backing the fd in question.  Illumos doesn't have similar
+	 * functionality, but we can attempt to simulate it using the flags
+	 * (O_RSYNC|O_SYNC) and directio(3C).
+	 *
+	 * The LX_O_DIRECT flag also requires that the transfer size and
+	 * alignment of I/O buffers be a multiple of the logical block size for
+	 * the underlying file system, but frankly there isn't an easy way to
+	 * support that functionality without doing something like adding an
+	 * fcntl(2) flag to denote LX_O_DIRECT mode.
+	 *
+	 * Since LX_O_DIRECT is merely a performance advisory, we'll just
+	 * emulate what we can and trust that the only applications expecting
+	 * an error when performing I/O from a misaligned buffer or when
+	 * passing a transfer size is not a multiple of the underlying file
+	 * system block size will be test suites.
+	 */
+	if (input & LX_O_DIRECT)
+		flags |= (O_RSYNC|O_SYNC);
+
+	return (flags);
+}
+
+#define	LX_POSTPROCESS_OPTS	(LX_O_DIRECT | LX_O_ASYNC | LX_O_PATH)
+
+static int
+lx_open_postprocess(int fd, int fmode)
+{
+	file_t *fp;
+	int rv, error = 0;
+
+	if ((fmode & LX_POSTPROCESS_OPTS) == 0) {
+		/* Skip out early, if possible */
+		return (0);
+	}
+
+	if ((fp = getf(fd)) == NULL) {
+		/*
+		 * It is possible that this fd was closed by the time we
+		 * arrived here if some one is hammering away with close().
+		 */
+		return (EIO);
+	}
+
+	if (fmode & LX_O_DIRECT && error == 0) {
+		(void) VOP_IOCTL(fp->f_vnode, _FIODIRECTIO, DIRECTIO_ON,
+		    fp->f_flag, fp->f_cred, &rv, NULL);
+	}
+
+	if (fmode & LX_O_ASYNC && error == 0) {
+		if ((error = VOP_SETFL(fp->f_vnode, fp->f_flag, FASYNC,
+		    fp->f_cred, NULL)) == 0) {
+			mutex_enter(&fp->f_tlock);
+			fp->f_flag |= FASYNC;
+			mutex_exit(&fp->f_tlock);
+		}
+	}
+
+	if (fmode & LX_O_PATH && error == 0) {
+		/*
+		 * While the O_PATH flag has no direct analog in SunOS, it is
+		 * emulated by removing both FREAD and FWRITE from f_flag.
+		 * This causes read(2) and write(2) result in EBADF and can be
+		 * checked for in other syscalls to tigger the correct behavior
+		 * there.
+		 */
+		mutex_enter(&fp->f_tlock);
+		fp->f_flag &= ~(FREAD|FWRITE);
+		mutex_exit(&fp->f_tlock);
+	}
+
+	releasef(fd);
+	if (error != 0) {
+		(void) closeandsetf(fd, NULL);
+	}
+	return (error);
+}
+
+long
+lx_openat(int atfd, char *path, int fmode, int cmode)
+{
+	int flags, fd, error;
+	mode_t mode = 0;
+
+	if (atfd == LX_AT_FDCWD)
+		atfd = AT_FDCWD;
+
+	flags = ltos_open_flags(fmode);
+
+	/*
+	 * We use the FSEARCH flag to make sure this is a directory. We have to
+	 * explicitly add 1 to emulate the FREAD/FWRITE mapping of the OPENMODE
+	 * macro since it won't get set via OPENMODE when FSEARCH is used.
+	 */
+	if (fmode & LX_O_DIRECTORY) {
+		flags |= FSEARCH;
+		flags++;
+	}
+
+	if (flags & O_CREAT)
+		mode = (mode_t)cmode;
+
+	ttolwp(curthread)->lwp_errno = 0;
+	fd = openat(atfd, path, flags, mode);
+	if (ttolwp(curthread)->lwp_errno != 0) {
+		if ((fmode & LX_O_DIRECTORY) &&
+		    ttolwp(curthread)->lwp_errno != ENOTDIR) {
+			/*
+			 * We got an error trying to open a file as a directory.
+			 * We need to determine if we should return the original
+			 * error or ENOTDIR.
+			 */
+			vnode_t *startvp;
+			vnode_t *vp;
+			int oerror, error = 0;
+
+			oerror = ttolwp(curthread)->lwp_errno;
+
+			if (atfd == AT_FDCWD) {
+				/* regular open */
+				startvp = NULL;
+			} else {
+				char startchar;
+
+				if (copyin(path, &startchar, sizeof (char)))
+					return (set_errno(oerror));
+
+				/* if startchar is / then startfd is ignored */
+				if (startchar == '/') {
+					startvp = NULL;
+				} else {
+					file_t *startfp;
+
+					if ((startfp = getf(atfd)) == NULL)
+						return (set_errno(oerror));
+					startvp = startfp->f_vnode;
+					VN_HOLD(startvp);
+					releasef(atfd);
+				}
+			}
+
+			if (lookupnameat(path, UIO_USERSPACE,
+			    (fmode & LX_O_NOFOLLOW) ?  NO_FOLLOW : FOLLOW,
+			    NULLVPP, &vp, startvp) != 0) {
+				if (startvp != NULL)
+					VN_RELE(startvp);
+				return (set_errno(oerror));
+			}
+
+			if (startvp != NULL)
+				VN_RELE(startvp);
+
+			if (vp->v_type != VDIR)
+				error = ENOTDIR;
+
+			VN_RELE(vp);
+			if (error != 0)
+				return (set_errno(ENOTDIR));
+
+			(void) set_errno(oerror);
+		}
+		return (ttolwp(curthread)->lwp_errno);
+	}
+
+	if ((error = lx_open_postprocess(fd, fmode)) != 0) {
+		return (set_errno(error));
+	}
+	return (fd);
+}
+
+long
+lx_open(char *path, int fmode, int cmode)
+{
+	return (lx_openat(LX_AT_FDCWD, path, fmode, cmode));
+}
--- /dev/null	Wed Oct  5 00:32:09 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_personality.c	Wed Oct  5 00:32:09 2016
@@ -0,0 +1,112 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#include <sys/systm.h>
+#include <sys/mutex.h>
+#include <sys/brand.h>
+
+#include <sys/lx_brand.h>
+#include <sys/lx_impl.h>
+
+
+/*
+ * These flags are for what Linux calls "bug emulation".
+ * (Descriptions from the personality(2) Linux man page.)
+ *
+ * Flags which are currently actionable in LX:
+ * - READ_IMPLIES_EXEC (since Linux 2.6.8)
+ *   With this flag set, PROT_READ implies PROT_EXEC for mmap(2).
+ *
+ * Flags which are current accepted but ignored:
+ * - UNAME26 (since Linux 3.1)
+ *   Have uname(2) report a 2.6.40+ version number rather than a 3.x version
+ *   number.  Added as a stopgap measure to support broken applications that
+ *   could not handle the kernel version- numbering switch from 2.6.x to 3.x.
+ *
+ * - ADDR_NO_RANDOMIZE (since Linux 2.6.12)
+ *   With this flag set, disable address-space-layout randomization.
+ *
+ * - FDPIC_FUNCPTRS (since Linux 2.6.11)
+ *   User-space function pointers to signal handlers point (on certain
+ *   architectures) to descriptors.
+ *
+ * - MMAP_PAGE_ZERO (since Linux 2.4.0)
+ *   Map page 0 as read-only (to support binaries that depend on this SVr4
+ *   behavior).
+ *
+ * - ADDR_COMPAT_LAYOUT (since Linux 2.6.9)
+ *   With this flag set, provide legacy virtual address space layout.
+ *
+ * - ADDR_LIMIT_32BIT (since Linux 2.2)
+ *   Limit the address space to 32 bits.
+ *
+ * - SHORT_INODE (since Linux 2.4.0)
+ *   No effects(?).
+ *
+ * - WHOLE_SECONDS (since Linux 1.2.0)
+ *   No effects(?).
+ *
+ * - STICKY_TIMEOUTS (since Linux 1.2.0)
+ *   With this flag set, select(2), pselect(2), and ppoll(2) do not modify the
+ *   returned timeout argument when interrupted by a signal handler.
+ *
+ * - ADDR_LIMIT_3GB (since Linux 2.4.0)
+ *   With this flag set, use 0xc0000000 as the offset at which to search a
+ *   virtual memory chunk on mmap(2); otherwise use 0xffffe000.
+ */
+
+#define	LX_PER_GET	0xffffffff
+
+long
+lx_personality(unsigned int arg)
+{
+	lx_proc_data_t *lxpd = ptolxproc(curproc);
+	unsigned int result = 0;
+
+	mutex_enter(&curproc->p_lock);
+	result = lxpd->l_personality;
+
+	if (arg == LX_PER_GET) {
+		mutex_exit(&curproc->p_lock);
+		return (result);
+	}
+
+	/*
+	 * Prevent changes to the personality if the process is undergoing an
+	 * exec.  This will allow elfexec and friends to manipulate the
+	 * personality without hinderance.
+	 */
+	if ((curproc->p_flag & P_PR_EXEC) != 0) {
+		mutex_exit(&curproc->p_lock);
+		return (set_errno(EINVAL));
+	}
+
+	/*
+	 * Keep tabs when a non-Linux personality is set.  This is silently
+	 * allowed to succeed, even though the emulation required is almost
+	 * certainly missing.
+	 */
+	if ((arg & LX_PER_MASK) != LX_PER_LINUX) {
+		char buf[64];
+
+		(void) snprintf(buf, sizeof (buf), "invalid personality: %02X",
+		    arg & LX_PER_MASK);
+		lx_unsupported(buf);
+	}
+
+	lxpd->l_personality = arg;
+	mutex_exit(&curproc->p_lock);
+	return (result);
+}
--- /dev/null	Wed Oct  5 00:32:09 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_pgrp.c	Wed Oct  5 00:32:09 2016
@@ -0,0 +1,189 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#include <sys/lx_misc.h>
+
+#define	LX_INIT_PGID	1
+#define	LX_INIT_SID	1
+
+/* From uts/common/syscall/pgrpsys.c */
+extern int setpgrp(int, int, int);
+
+long
+lx_getpgrp(void)
+{
+	int pg;
+
+	/* getpgrp() */
+	pg = setpgrp(0, 0, 0);
+
+	/*
+	 * If the pgrp is that of the init process, return the value Linux
+	 * expects.
+	 */
+	if (pg == curzone->zone_proc_initpid)
+		return (LX_INIT_PGID);
+
+	return (pg);
+}
+
+long
+lx_getpgid(int pid)
+{
+	pid_t spid;
+	int tid;
+	int pg;
+
+	if (pid < 0)
+		return (set_errno(ESRCH));
+
+	/*
+	 * If the supplied pid matches that of the init process, return the pgid
+	 * Linux expects.
+	 */
+	if (pid == curzone->zone_proc_initpid)
+		return (LX_INIT_PGID);
+
+	if (pid == 0) {
+		spid = curproc->p_pid;
+	} else if (lx_lpid_to_spair(pid, &spid, &tid) < 0) {
+		return (set_errno(ESRCH));
+	}
+
+	/* getpgid() */
+	ttolwp(curthread)->lwp_errno = 0;
+	pg = setpgrp(4, spid, 0);
+	if (ttolwp(curthread)->lwp_errno != 0)
+		return (ttolwp(curthread)->lwp_errno);
+
+	/*
+	 * If the pgid is that of the init process, return the value Linux
+	 * expects.
+	 */
+	if (pg == curzone->zone_proc_initpid)
+		return (LX_INIT_PGID);
+
+	return (pg);
+}
+
+long
+lx_setpgid(pid_t pid, pid_t pgid)
+{
+	pid_t spid, spgid;
+	int tid;
+	int pg;
+	int ret;
+
+	if (pid < 0)
+		return (set_errno(ESRCH));
+
+	if (pgid < 0)
+		return (set_errno(EINVAL));
+
+	if (pid == 0) {
+		spid = curproc->p_pid;
+	} else if (lx_lpid_to_spair(pid, &spid, &tid) < 0) {
+		return (set_errno(ESRCH));
+	}
+
+	if (pgid == 0) {
+		spgid = spid;
+	} else if (lx_lpid_to_spair(pgid, &spgid, &tid) < 0) {
+		return (set_errno(ESRCH));
+	}
+
+	/* setpgid() */
+	ret = setpgrp(5, spid, spgid);
+
+	if (ret == EPERM) {
+		/*
+		 * On Linux, when calling setpgid with a desired pgid that is
+		 * equal to the current pgid of the process, no error is
+		 * emitted. This differs slightly from illumos which would
+		 * return EPERM. To emulate the Linux behavior, we check
+		 * specifically for matching pgids.
+		 */
+
+		/* getpgid() */
+		ttolwp(curthread)->lwp_errno = 0;
+		pg = setpgrp(4, spid, 0);
+		if (ttolwp(curthread)->lwp_errno == 0 && spgid == pg)
+			return (0);
+		return (set_errno(EPERM));
+	}
+
+	return (ret);
+}
+
+long
+lx_getsid(int pid)
+{
+	pid_t spid;
+	int tid;
+	int sid;
+
+	if (pid < 0)
+		return (set_errno(ESRCH));
+
+	/*
+	 * If the supplied pid matches that of the init process, return the sid
+	 * Linux expects.
+	 */
+	if (pid == curzone->zone_proc_initpid)
+		return (LX_INIT_SID);
+
+	if (pid == 0) {
+		spid = curproc->p_pid;
+	} else if (lx_lpid_to_spair(pid, &spid, &tid) < 0) {
+		return (set_errno(ESRCH));
+	}
+
+	/* getsid() */
+	ttolwp(curthread)->lwp_errno = 0;
+	sid = setpgrp(2, spid, 0);
+	if (ttolwp(curthread)->lwp_errno != 0)
+		return (ttolwp(curthread)->lwp_errno);
+
+
+	/*
+	 * If the sid is that of the init process, return the value Linux
+	 * expects.
+	 */
+	if (sid == curzone->zone_proc_initpid)
+		return (LX_INIT_SID);
+
+	return (sid);
+}
+
+long
+lx_setsid(void)
+{
+	int sid;
+
+	/* setsid() */
+	ttolwp(curthread)->lwp_errno = 0;
+	sid = setpgrp(3, 0, 0);
+	if (ttolwp(curthread)->lwp_errno != 0)
+		return (ttolwp(curthread)->lwp_errno);
+
+	/*
+	 * If the sid is that of the init process, return the value Linux
+	 * expects.
+	 */
+	if (sid == curzone->zone_proc_initpid)
+		return (LX_INIT_SID);
+
+	return (sid);
+}
--- /dev/null	Wed Oct  5 00:32:09 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_pipe.c	Wed Oct  5 00:32:09 2016
@@ -0,0 +1,200 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T. All Rights Reserved.
+ *
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2013 OmniTI Computer Consulting, Inc. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
+ */
+
+#include <sys/zone.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/thread.h>
+#include <sys/cpuvar.h>
+#include <sys/cred.h>
+#include <sys/user.h>
+#include <sys/vnode.h>
+#include <sys/file.h>
+#include <sys/stream.h>
+#include <sys/strsubr.h>
+#include <sys/errno.h>
+#include <sys/debug.h>
+#include <sys/fs/fifonode.h>
+#include <sys/fcntl.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_fcntl.h>
+
+/*
+ * Based on native pipe(2) system call, except that the pipe is half-duplex.
+ */
+static int
+lx_hd_pipe(intptr_t arg, int flags)
+{
+	vnode_t *vp1, *vp2;
+	struct file *fp1, *fp2;
+	int error = 0;
+	int flag1, flag2, iflags;
+	int fd1, fd2;
+
+	/*
+	 * Validate allowed flags.
+	 */
+	if ((flags & ~(FCLOEXEC|FNONBLOCK)) != 0) {
+		return (set_errno(EINVAL));
+	}
+	/*
+	 * Allocate and initialize two vnodes.
+	 */
+	makepipe(&vp1, &vp2);
+
+	/*
+	 * Allocate and initialize two file table entries and two
+	 * file pointers. The first file pointer is open for read and the
+	 * second is open for write.
+	 */
+	if ((error = falloc(vp1, FREAD, &fp1, &fd1)) != 0) {
+		VN_RELE(vp1);
+		VN_RELE(vp2);
+		return (set_errno(error));
+	}
+
+	if ((error = falloc(vp2, FWRITE, &fp2, &fd2)) != 0)
+		goto out2;
+
+	/*
+	 * Create two stream heads and attach to each vnode.
+	 */
+	if ((error = fifo_stropen(&vp1, FREAD, fp1->f_cred, 0, 0)) != 0)
+		goto out;
+
+	if ((error = fifo_stropen(&vp2, FWRITE, fp2->f_cred, 0, 0)) != 0) {
+		(void) VOP_CLOSE(vp1, FREAD, 1, (offset_t)0,
+		    fp1->f_cred, NULL);
+		goto out;
+	}
+
+	strmate(vp1, vp2);
+
+	VTOF(vp1)->fn_ino = VTOF(vp2)->fn_ino = fifogetid();
+
+	/*
+	 * Set the O_NONBLOCK flag if requested.
+	 */
+	if (flags & FNONBLOCK) {
+		flag1 = fp1->f_flag;
+		flag2 = fp2->f_flag;
+		iflags = flags & FNONBLOCK;
+
+		if ((error = VOP_SETFL(vp1, flag1, iflags, fp1->f_cred,
+		    NULL)) != 0) {
+			goto out_vop_close;
+		}
+		fp1->f_flag |= iflags;
+
+		if ((error = VOP_SETFL(vp2, flag2, iflags, fp2->f_cred,
+		    NULL)) != 0) {
+			goto out_vop_close;
+		}
+		fp2->f_flag |= iflags;
+	}
+
+	/*
+	 * Return the file descriptors to the user. They now
+	 * point to two different vnodes which have different
+	 * stream heads.
+	 */
+	if (copyout(&fd1, &((int *)arg)[0], sizeof (int)) ||
+	    copyout(&fd2, &((int *)arg)[1], sizeof (int))) {
+		error = EFAULT;
+		goto out_vop_close;
+	}
+
+	/*
+	 * Now fill in the entries that falloc reserved
+	 */
+	mutex_exit(&fp1->f_tlock);
+	mutex_exit(&fp2->f_tlock);
+	setf(fd1, fp1);
+	setf(fd2, fp2);
+
+	/*
+	 * Optionally set the FCLOEXEC flag
+	 */
+	if ((flags & FCLOEXEC) != 0) {
+		f_setfd(fd1, FD_CLOEXEC);
+		f_setfd(fd2, FD_CLOEXEC);
+	}
+
+	return (0);
+out_vop_close:
+	(void) VOP_CLOSE(vp1, FREAD, 1, (offset_t)0, fp1->f_cred, NULL);
+	(void) VOP_CLOSE(vp2, FWRITE, 1, (offset_t)0, fp2->f_cred, NULL);
+out:
+	setf(fd2, NULL);
+	unfalloc(fp2);
+out2:
+	setf(fd1, NULL);
+	unfalloc(fp1);
+	VN_RELE(vp1);
+	VN_RELE(vp2);
+	return (set_errno(error));
+}
+
+/*
+ * pipe(2) system call.
+ */
+long
+lx_pipe(intptr_t arg)
+{
+	return (lx_hd_pipe(arg, 0));
+}
+
+/*
+ * pipe2(2) system call.
+ */
+long
+lx_pipe2(intptr_t arg, int lxflags)
+{
+	int flags = 0;
+
+	/*
+	 * Validate allowed flags.
+	 */
+	if ((lxflags & ~(LX_O_NONBLOCK | LX_O_CLOEXEC)) != 0) {
+		return (set_errno(EINVAL));
+	}
+
+	/*
+	 * Convert from Linux flags to illumos flags.
+	 */
+	if (lxflags & LX_O_NONBLOCK) {
+		flags |= FNONBLOCK;
+	}
+	if (lxflags & LX_O_CLOEXEC) {
+		flags |= FCLOEXEC;
+	}
+
+	return (lx_hd_pipe(arg, flags));
+}
--- /dev/null	Wed Oct  5 00:32:09 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_poll.c	Wed Oct  5 00:32:09 2016
@@ -0,0 +1,762 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/thread.h>
+#include <sys/proc.h>
+#include <sys/zone.h>
+#include <sys/brand.h>
+#include <sys/sunddi.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_types.h>
+#include <sys/poll_impl.h>
+#include <sys/schedctl.h>
+#include <sys/lx_signal.h>
+
+
+/* From uts/common/syscall/poll.c */
+extern int poll_copyin(pollstate_t *, pollfd_t *, nfds_t);
+extern int poll_common(pollstate_t *, pollfd_t *, nfds_t, timespec_t *, int *);
+
+/*
+ * These events are identical between Linux and SunOS
+ */
+#define	LX_POLLIN	0x001
+#define	LX_POLLPRI	0x002
+#define	LX_POLLOUT	0x004
+#define	LX_POLLERR	0x008
+#define	LX_POLLHUP	0x010
+#define	LX_POLLNVAL	0x020
+#define	LX_POLLRDNORM	0x040
+#define	LX_POLLRDBAND	0x080
+
+#define	LX_POLL_COMMON_EVENTS (LX_POLLIN | LX_POLLPRI | LX_POLLOUT |	\
+	LX_POLLERR | LX_POLLHUP | LX_POLLNVAL | LX_POLLRDNORM | LX_POLLRDBAND)
+
+/*
+ * These events differ between Linux and SunOS
+ */
+#define	LX_POLLWRNORM	0x0100
+#define	LX_POLLWRBAND	0x0200
+#define	LX_POLLRDHUP	0x2000
+
+
+#define	LX_POLL_SUPPORTED_EVENTS	\
+	(LX_POLL_COMMON_EVENTS | LX_POLLWRNORM | LX_POLLWRBAND | LX_POLLRDHUP)
+
+
+static int
+lx_poll_copyin(pollstate_t *ps, pollfd_t *fds, nfds_t nfds, short *oldevt)
+{
+	int i, error = 0;
+	pollfd_t *pollfdp;
+
+	if ((error = poll_copyin(ps, fds, nfds)) != 0) {
+		return (error);
+	}
+	pollfdp = ps->ps_pollfd;
+
+	/* Convert the Linux events bitmask into SunOS equivalent. */
+	for (i = 0; i < nfds; i++) {
+		short lx_events = pollfdp[i].events;
+		short events;
+
+		/*
+		 * If the caller is polling for an unsupported event, we
+		 * have to bail out.
+		 */
+		if (lx_events & ~LX_POLL_SUPPORTED_EVENTS) {
+			return (ENOTSUP);
+		}
+
+		events = lx_events & LX_POLL_COMMON_EVENTS;
+		if (lx_events & LX_POLLWRNORM)
+			events |= POLLWRNORM;
+		if (lx_events & LX_POLLWRBAND)
+			events |= POLLWRBAND;
+		if (lx_events & LX_POLLRDHUP)
+			events |= POLLRDHUP;
+		pollfdp[i].events = events;
+		oldevt[i] = lx_events;
+	}
+	return (0);
+}
+
+static int
+lx_poll_copyout(pollfd_t *pollfdp, pollfd_t *fds, nfds_t nfds, short *oldevt)
+{
+	int i;
+
+	/*
+	 * Convert SunOS revents bitmask into Linux equivalent and restore
+	 * cached events field which was swizzled by lx_poll_copyin.
+	 */
+	for (i = 0; i < nfds; i++) {
+		short revents = pollfdp[i].revents;
+		short lx_revents = revents & LX_POLL_COMMON_EVENTS;
+		short orig_events = oldevt[i];
+
+		if (revents & POLLWRBAND)
+			lx_revents |= LX_POLLWRBAND;
+		if (revents & POLLRDHUP)
+			lx_revents |= LX_POLLRDHUP;
+		/*
+		 * Because POLLOUT and POLLWRNORM are native defined as the
+		 * same value, care must be taken when translating them to
+		 * Linux where they differ.
+		 */
+		if (revents & POLLOUT) {
+			if ((orig_events & LX_POLLOUT) == 0)
+				lx_revents &= ~LX_POLLOUT;
+			if (orig_events & LX_POLLWRNORM)
+				lx_revents |= LX_POLLWRNORM;
+		}
+
+		pollfdp[i].revents = lx_revents;
+		pollfdp[i].events = orig_events;
+	}
+
+	if (copyout(pollfdp, fds, sizeof (pollfd_t) * nfds) != 0)
+		return (EFAULT);
+
+	return (0);
+}
+
+static long
+lx_poll_common(pollfd_t *fds, nfds_t nfds, timespec_t *tsp, k_sigset_t *ksetp)
+{
+	kthread_t *t = curthread;
+	klwp_t *lwp = ttolwp(t);
+	proc_t *p = ttoproc(t);
+	pollstate_t *ps = NULL;
+	pollfd_t *pollfdp = NULL;
+	short *oldevt = NULL;
+	int error = 0, fdcnt = 0;
+
+	/*
+	 * Reset our signal mask, if requested.
+	 */
+	if (ksetp != NULL) {
+		mutex_enter(&p->p_lock);
+		schedctl_finish_sigblock(t);
+		lwp->lwp_sigoldmask = t->t_hold;
+		t->t_hold = *ksetp;
+		t->t_flag |= T_TOMASK;
+		/*
+		 * Call cv_reltimedwait_sig() just to check for signals.
+		 * We will return immediately with either 0 or -1.
+		 */
+		if (!cv_reltimedwait_sig(&t->t_delay_cv, &p->p_lock, 0,
+		    TR_CLOCK_TICK)) {
+			mutex_exit(&p->p_lock);
+			error = EINTR;
+			goto pollout;
+		}
+		mutex_exit(&p->p_lock);
+	}
+
+	/*
+	 * Initialize pollstate and copy in pollfd data if present.
+	 */
+	if (nfds != 0) {
+		if (nfds > p->p_fno_ctl) {
+			mutex_enter(&p->p_lock);
+			(void) rctl_action(rctlproc_legacy[RLIMIT_NOFILE],
+			    p->p_rctls, p, RCA_SAFE);
+			mutex_exit(&p->p_lock);
+			error = EINVAL;
+			goto pollout;
+		}
+
+		/*
+		 * Need to allocate memory for pollstate before anything
+		 * because the mutex and cv are created in this space
+		 */
+		ps = pollstate_create();
+		if (ps->ps_pcache == NULL)
+			ps->ps_pcache = pcache_alloc();
+
+		/*
+		 * Certain event types which are distinct on Linux are aliased
+		 * against each other on illumos.  In order properly translate
+		 * back into the Linux format, the original events of interest
+		 * are stored in 'oldevt' for use during lx_poll_copyout.
+		 */
+		oldevt = kmem_alloc(nfds * sizeof (short), KM_SLEEP);
+		if ((error = lx_poll_copyin(ps, fds, nfds, oldevt)) != 0)
+			goto pollout;
+		pollfdp = ps->ps_pollfd;
+	}
+
+	/*
+	 * Perform the actual poll.
+	 */
+	error = poll_common(ps, fds, nfds, tsp, &fdcnt);
+
+pollout:
+	/*
+	 * If we changed the signal mask but we received no signal then restore
+	 * the signal mask.  Otherwise psig() will deal with the signal mask.
+	 */
+	if (ksetp != NULL) {
+		mutex_enter(&p->p_lock);
+		if (lwp->lwp_cursig == 0) {
+			t->t_hold = lwp->lwp_sigoldmask;
+			t->t_flag &= ~T_TOMASK;
+		}
+		mutex_exit(&p->p_lock);
+	}
+
+	/*
+	 * Copy out the events and return the fdcnt to the user.
+	 */
+	if (nfds != 0 && error == 0) {
+		error = lx_poll_copyout(pollfdp, fds, nfds, oldevt);
+	}
+	if (oldevt != NULL) {
+		kmem_free(oldevt, nfds * sizeof (short));
+	}
+	if (error) {
+		return (set_errno(error));
+	}
+	return (fdcnt);
+}
+
+long
+lx_poll(pollfd_t *fds, nfds_t nfds, int timeout)
+{
+	timespec_t ts, *tsp = NULL;
+
+	if (timeout >= 0) {
+		ts.tv_sec = timeout / MILLISEC;
+		ts.tv_nsec = (timeout % MILLISEC) * MICROSEC;
+		tsp = &ts;
+	}
+
+	return (lx_poll_common(fds, nfds, tsp, NULL));
+}
+
+long
+lx_ppoll(pollfd_t *fds, nfds_t nfds, timespec_t *timeoutp, lx_sigset_t *setp)
+{
+	timespec_t ts, *tsp = NULL;
+	k_sigset_t kset, *ksetp = NULL;
+
+	/*
+	 * Copy in timeout and sigmask.
+	 */
+	if (timeoutp != NULL) {
+		if (get_udatamodel() == DATAMODEL_NATIVE) {
+			if (copyin(timeoutp, &ts, sizeof (ts)))
+				return (set_errno(EFAULT));
+		} else {
+			timespec32_t ts32;
+
+			if (copyin(timeoutp, &ts32, sizeof (ts32)))
+				return (set_errno(EFAULT));
+			TIMESPEC32_TO_TIMESPEC(&ts, &ts32)
+		}
+
+		if (itimerspecfix(&ts))
+			return (set_errno(EINVAL));
+		tsp = &ts;
+	}
+	if (setp != NULL) {
+		lx_sigset_t lset;
+
+		if (copyin(setp, &lset, sizeof (lset)))
+			return (set_errno(EFAULT));
+		lx_ltos_sigset(&lset, &kset);
+		ksetp = &kset;
+	}
+
+	return (lx_poll_common(fds, nfds, tsp, ksetp));
+}
+
+typedef struct lx_select_buf_s {
+	long		*lsb_rfds;
+	long		*lsb_wfds;
+	long		*lsb_efds;
+	unsigned int	lsb_size;
+} lx_select_buf_t;
+
+/*
+ * Size (in bytes) of buffer appropriate for fd_set copyin/copyout.
+ * Linux uses buffers of 'long' to accomplish this.
+ */
+#define	LX_FD_SET_BYTES		(sizeof (long))
+#define	LX_FD_SET_BITS		(8 * LX_FD_SET_BYTES)
+#define	LX_FD_SET_SIZE(nfds)	\
+	((((nfds) + (LX_FD_SET_BITS - 1)) / LX_FD_SET_BITS) * LX_FD_SET_BYTES)
+
+static int
+lx_select_copyin(pollstate_t *ps, lx_select_buf_t *sbuf, int nfds,
+    long *rfds, long *wfds, long *efds)
+{
+	int n;
+	long *in, *out, *ex;
+	long absent = 0;
+	pollfd_t *pfd;
+	nfds_t old_nfds;
+
+	/*
+	 * Just like pollsys and lx_poll, attempt to reuse ps_pollfd if it is
+	 * appropriately sized.  See poll_copyin for more detail.
+	 */
+	old_nfds = ps->ps_nfds;
+	if (nfds != old_nfds) {
+		kmem_free(ps->ps_pollfd, old_nfds * sizeof (pollfd_t));
+		pfd = kmem_alloc(nfds * sizeof (pollfd_t), KM_SLEEP);
+		ps->ps_pollfd = pfd;
+		ps->ps_nfds = nfds;
+	} else {
+		pfd = ps->ps_pollfd;
+	}
+
+	if (rfds != NULL) {
+		if (copyin(rfds, sbuf->lsb_rfds, sbuf->lsb_size) != 0) {
+			return (EFAULT);
+		}
+	}
+	if (wfds != NULL) {
+		if (copyin(wfds, sbuf->lsb_wfds, sbuf->lsb_size) != 0) {
+			return (EFAULT);
+		}
+	}
+	if (efds != NULL) {
+		if (copyin(efds, sbuf->lsb_efds, sbuf->lsb_size) != 0) {
+			return (EFAULT);
+		}
+	}
+
+	/*
+	 * For each fd, if any bits are set convert them into the appropriate
+	 * pollfd struct. (Derived from libc's select logic)
+	 */
+	in = (rfds != NULL) ? sbuf->lsb_rfds : &absent;
+	out = (wfds != NULL) ? sbuf->lsb_wfds : &absent;
+	ex = (efds != NULL) ? sbuf->lsb_efds : &absent;
+	for (n = 0; n < nfds; n += LX_FD_SET_BITS) {
+		unsigned long b, m, j;
+
+		b = (unsigned long)(*in | *out | *ex);
+		m = 1;
+		for (j = 0; j < LX_FD_SET_BITS; j++) {
+			int fd = n + j;
+
+			if (fd >= nfds)
+				return (0);
+			pfd->events = 0;
+			if (b & 1) {
+				pfd->fd = fd;
+				if (*in & m)
+					pfd->events |= POLLRDNORM;
+				if (*out & m)
+					pfd->events |= POLLWRNORM;
+				if (*ex & m)
+					pfd->events |= POLLRDBAND;
+			} else {
+				pfd->fd = -1;
+			}
+			pfd++;
+			b >>= 1;
+			m <<= 1;
+		}
+
+		if (rfds != NULL)
+			in++;
+		if (wfds != NULL)
+			out++;
+		if (efds != NULL)
+			ex++;
+	}
+	return (0);
+}
+
+static int
+lx_select_copyout(pollfd_t *pollfdp, lx_select_buf_t *sbuf, int nfds,
+    long *rfds, long *wfds, long *efds, int *fdcnt)
+{
+	int n;
+	pollfd_t *pfd;
+	long rv = 0;
+
+	/*
+	 * If poll did not find any fds of interest, we can just zero out the
+	 * fd_set fields for copyout.
+	 */
+	if (*fdcnt == 0) {
+		if (rfds != NULL) {
+			bzero(sbuf->lsb_rfds, sbuf->lsb_size);
+		}
+		if (wfds != NULL) {
+			bzero(sbuf->lsb_wfds, sbuf->lsb_size);
+		}
+		if (efds != NULL) {
+			bzero(sbuf->lsb_efds, sbuf->lsb_size);
+		}
+		goto copyout;
+	}
+
+	/*
+	 * For each fd, if any bits are set convert them into the appropriate
+	 * pollfd struct. (Derived from libc's select logic)
+	 */
+	pfd = pollfdp;
+	for (n = 0; n < nfds; n += LX_FD_SET_BITS) {
+		unsigned long m, j;
+		long in = 0, out = 0, ex = 0;
+
+		m = 1;
+		for (j = 0; j < LX_FD_SET_BITS; j++) {
+			if ((n + j) >= nfds)
+				break;
+			if (pfd->revents != 0) {
+				if (pfd->revents & POLLNVAL) {
+					return (EBADF);
+				}
+				if (pfd->revents & POLLRDNORM) {
+					in |= m;
+					rv++;
+				}
+				if (pfd->revents & POLLWRNORM) {
+					out |= m;
+					rv++;
+				}
+				if (pfd->revents & POLLRDBAND) {
+					ex |= m;
+					rv++;
+				}
+				/*
+				 * Only set this bit on return if we asked
+				 * about input conditions.
+				 */
+				if ((pfd->revents & (POLLHUP|POLLERR)) &&
+				    (pfd->events & POLLRDNORM)) {
+					if ((in & m) == 0) {
+						/* wasn't already set */
+						rv++;
+					}
+					in |= m;
+				}
+				/*
+				 * Only set this bit on return if we asked
+				 * about output conditions.
+				 */
+				if ((pfd->revents & (POLLHUP|POLLERR)) &&
+				    (pfd->events & POLLWRNORM)) {
+					if ((out & m) == 0) {
+						/* wasn't already set */
+						rv++;
+					}
+					out |= m;
+				}
+				/*
+				 * Only set this bit on return if we asked
+				 * about output conditions.
+				 */
+				if ((pfd->revents & (POLLHUP|POLLERR)) &&
+				    (pfd->events & POLLRDBAND)) {
+					if ((ex & m) == 0) {
+						/* wasn't already set */
+						rv++;
+					}
+					ex |= m;
+				}
+			}
+			m <<= 1;
+			pfd++;
+		}
+		if (rfds != NULL)
+			sbuf->lsb_rfds[n / LX_FD_SET_BITS] = in;
+		if (wfds != NULL)
+			sbuf->lsb_wfds[n / LX_FD_SET_BITS] = out;
+		if (efds != NULL)
+			sbuf->lsb_efds[n / LX_FD_SET_BITS] = ex;
+	}
+
+copyout:
+	if (rfds != NULL) {
+		if (copyout(sbuf->lsb_rfds, rfds, sbuf->lsb_size) != 0) {
+			return (EFAULT);
+		}
+	}
+	if (wfds != NULL) {
+		if (copyout(sbuf->lsb_wfds, wfds, sbuf->lsb_size) != 0) {
+			return (EFAULT);
+		}
+	}
+	if (efds != NULL) {
+		if (copyout(sbuf->lsb_efds, efds, sbuf->lsb_size) != 0) {
+			return (EFAULT);
+		}
+	}
+	*fdcnt = rv;
+	return (0);
+}
+
+
+static long
+lx_select_common(int nfds, long *rfds, long *wfds, long *efds,
+    timespec_t *tsp, k_sigset_t *ksetp)
+{
+	kthread_t *t = curthread;
+	klwp_t *lwp = ttolwp(t);
+	proc_t *p = ttoproc(t);
+	pollstate_t *ps = NULL;
+	pollfd_t *pollfdp = NULL, *fake_fds = NULL;
+	lx_select_buf_t sbuf = {0};
+	int error = 0, fdcnt = 0;
+
+	if (nfds < 0) {
+		return (set_errno(EINVAL));
+	}
+
+	/*
+	 * Reset our signal mask, if requested.
+	 */
+	if (ksetp != NULL) {
+		mutex_enter(&p->p_lock);
+		schedctl_finish_sigblock(t);
+		lwp->lwp_sigoldmask = t->t_hold;
+		t->t_hold = *ksetp;
+		t->t_flag |= T_TOMASK;
+		/*
+		 * Call cv_reltimedwait_sig() just to check for signals.
+		 * We will return immediately with either 0 or -1.
+		 */
+		if (!cv_reltimedwait_sig(&t->t_delay_cv, &p->p_lock, 0,
+		    TR_CLOCK_TICK)) {
+			mutex_exit(&p->p_lock);
+			error = EINTR;
+			goto out;
+		}
+		mutex_exit(&p->p_lock);
+	}
+
+	/*
+	 * Because poll caching uses the userspace pollfd_t pointer to verify
+	 * cache reuse validity, a simulated value must be supplied when
+	 * emulating Linux select(2).  The first non-NULL pointer from
+	 * rfds/wfds/efds is used for this purpose.
+	 */
+	if (rfds != NULL) {
+		fake_fds = (pollfd_t *)rfds;
+	} else if (wfds != NULL) {
+		fake_fds = (pollfd_t *)wfds;
+	} else if (efds != NULL) {
+		fake_fds = (pollfd_t *)efds;
+	} else {
+		/*
+		 * A non-zero nfds was supplied but all three fd_set pointers
+		 * were null.  Fall back to doing a simple timeout.
+		 */
+		nfds = 0;
+	}
+
+	/*
+	 * Initialize pollstate and copy in pollfd data if present.
+	 */
+	if (nfds != 0) {
+		if (nfds > p->p_fno_ctl) {
+			mutex_enter(&p->p_lock);
+			(void) rctl_action(rctlproc_legacy[RLIMIT_NOFILE],
+			    p->p_rctls, p, RCA_SAFE);
+			mutex_exit(&p->p_lock);
+			error = EINVAL;
+			goto out;
+		}
+
+		/*
+		 * Need to allocate memory for pollstate before anything
+		 * because the mutex and cv are created in this space
+		 */
+		ps = pollstate_create();
+		if (ps->ps_pcache == NULL)
+			ps->ps_pcache = pcache_alloc();
+
+		sbuf.lsb_size = LX_FD_SET_SIZE(nfds);
+		if (rfds != NULL)
+			sbuf.lsb_rfds = kmem_alloc(sbuf.lsb_size, KM_SLEEP);
+		if (wfds != NULL)
+			sbuf.lsb_wfds = kmem_alloc(sbuf.lsb_size, KM_SLEEP);
+		if (efds != NULL)
+			sbuf.lsb_efds = kmem_alloc(sbuf.lsb_size, KM_SLEEP);
+
+		error = lx_select_copyin(ps, &sbuf, nfds, rfds, wfds, efds);
+		if (error != 0) {
+			goto out;
+		}
+
+		pollfdp = ps->ps_pollfd;
+	}
+
+	/*
+	 * Perform the actual poll.
+	 */
+	error = poll_common(ps, fake_fds, (nfds_t)nfds, tsp, &fdcnt);
+
+out:
+	/*
+	 * If we changed the signal mask but we received no signal then restore
+	 * the signal mask.  Otherwise psig() will deal with the signal mask.
+	 */
+	if (ksetp != NULL) {
+		mutex_enter(&p->p_lock);
+		if (lwp->lwp_cursig == 0) {
+			t->t_hold = lwp->lwp_sigoldmask;
+			t->t_flag &= ~T_TOMASK;
+		}
+		mutex_exit(&p->p_lock);
+	}
+
+	/*
+	 * Copy out the events and return the fdcnt to the user.
+	 */
+	if (error == 0 && nfds != 0) {
+		error = lx_select_copyout(pollfdp, &sbuf, nfds, rfds, wfds,
+		    efds, &fdcnt);
+	}
+	if (sbuf.lsb_size != 0) {
+		if (sbuf.lsb_rfds != NULL)
+			kmem_free(sbuf.lsb_rfds, sbuf.lsb_size);
+		if (sbuf.lsb_wfds != NULL)
+			kmem_free(sbuf.lsb_wfds, sbuf.lsb_size);
+		if (sbuf.lsb_efds != NULL)
+			kmem_free(sbuf.lsb_efds, sbuf.lsb_size);
+	}
+	if (error) {
+		return (set_errno(error));
+	}
+	return (fdcnt);
+}
+
+long
+lx_select(int nfds, long *rfds, long *wfds, long *efds,
+    struct timeval *timeoutp)
+{
+	timespec_t ts, *tsp = NULL;
+
+	if (timeoutp != NULL) {
+		if (get_udatamodel() == DATAMODEL_NATIVE) {
+			struct timeval tv;
+
+			if (copyin(timeoutp, &tv, sizeof (tv)))
+				return (set_errno(EFAULT));
+			ts.tv_sec = tv.tv_sec;
+			ts.tv_nsec = tv.tv_usec * (NANOSEC / MICROSEC);
+		} else {
+			struct timeval32 tv32;
+
+			if (copyin(timeoutp, &tv32, sizeof (tv32)))
+				return (set_errno(EFAULT));
+			ts.tv_sec = tv32.tv_sec;
+			ts.tv_nsec = tv32.tv_usec * (NANOSEC / MICROSEC);
+		}
+
+		if (itimerspecfix(&ts))
+			return (set_errno(EINVAL));
+		tsp = &ts;
+	}
+
+	return (lx_select_common(nfds, rfds, wfds, efds, tsp, NULL));
+}
+
+
+typedef struct {
+	uintptr_t lpsa_addr;
+	unsigned long lpsa_len;
+} lx_pselect_sig_arg_t;
+
+#if defined(_LP64)
+typedef struct {
+	caddr32_t lpsa_addr;
+	uint32_t lpsa_len;
+} lx_pselect_sig_arg32_t;
+#endif /* defined(_LP64) */
+
+long
+lx_pselect(int nfds, long *rfds, long *wfds, long *efds,
+    timespec_t *timeoutp, void *setp)
+{
+	timespec_t ts, *tsp = NULL;
+	k_sigset_t kset, *ksetp = NULL;
+
+	/*
+	 * Copy in timeout and sigmask.
+	 */
+	if (timeoutp != NULL) {
+		if (get_udatamodel() == DATAMODEL_NATIVE) {
+			if (copyin(timeoutp, &ts, sizeof (ts)))
+				return (set_errno(EFAULT));
+		} else {
+			timespec32_t ts32;
+
+			if (copyin(timeoutp, &ts32, sizeof (ts32)))
+				return (set_errno(EFAULT));
+			TIMESPEC32_TO_TIMESPEC(&ts, &ts32)
+		}
+
+		if (itimerspecfix(&ts))
+			return (set_errno(EINVAL));
+		tsp = &ts;
+	}
+	if (setp != NULL) {
+		lx_sigset_t lset, *sigaddr = NULL;
+
+		if (get_udatamodel() == DATAMODEL_NATIVE) {
+			lx_pselect_sig_arg_t lpsa;
+
+			if (copyin(setp, &lpsa, sizeof (lpsa)) != 0)
+				return (set_errno(EFAULT));
+			/*
+			 * Linux forces a size to be passed only so it can
+			 * check that it's the size of a sigset_t.
+			 */
+			if (lpsa.lpsa_len != sizeof (lx_sigset_t))
+				return (set_errno(EINVAL));
+
+			sigaddr = (lx_sigset_t *)lpsa.lpsa_addr;
+		}
+#if defined(_LP64)
+		else {
+			lx_pselect_sig_arg32_t lpsa32;
+
+			if (copyin(setp, &lpsa32, sizeof (lpsa32)) != 0)
+				return (set_errno(EFAULT));
+			/*
+			 * Linux forces a size to be passed only so it can
+			 * check that it's the size of a sigset_t.
+			 */
+			if (lpsa32.lpsa_len != sizeof (lx_sigset_t))
+				return (set_errno(EINVAL));
+
+			sigaddr = (lx_sigset_t *)(uint64_t)lpsa32.lpsa_addr;
+		}
+#endif /* defined(_LP64) */
+
+		/* This is where we check if the sigset is *really* NULL. */
+		if (sigaddr != NULL) {
+			if (copyin(sigaddr, &lset, sizeof (lset)) != 0)
+				return (set_errno(EFAULT));
+
+			lx_ltos_sigset(&lset, &kset);
+			ksetp = &kset;
+		}
+	}
+
+	return (lx_select_common(nfds, rfds, wfds, efds, tsp, ksetp));
+}
--- /dev/null	Wed Oct  5 00:32:09 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_prctl.c	Wed Oct  5 00:32:09 2016
@@ -0,0 +1,211 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#include <sys/systm.h>
+#include <sys/types.h>
+#include <sys/user.h>
+#include <sys/priv.h>
+#include <sys/brand.h>
+#include <sys/cmn_err.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_impl.h>
+#include <lx_signum.h>
+
+#define	LX_PR_SET_PDEATHSIG		1
+#define	LX_PR_GET_PDEATHSIG		2
+#define	LX_PR_GET_DUMPABLE		3
+#define	LX_PR_SET_DUMPABLE		4
+#define	LX_PR_GET_UNALIGN		5
+#define	LX_PR_SET_UNALIGN		6
+#define	LX_PR_GET_KEEPCAPS		7
+#define	LX_PR_SET_KEEPCAPS		8
+#define	LX_PR_GET_FPEMU			9
+#define	LX_PR_SET_FPEMU			10
+#define	LX_PR_GET_FPEXC			11
+#define	LX_PR_SET_FPEXC			12
+#define	LX_PR_GET_TIMING		13
+#define	LX_PR_SET_TIMING		14
+#define	LX_PR_SET_NAME			15
+#define	LX_PR_GET_NAME			16
+#define	LX_PR_GET_ENDIAN		19
+#define	LX_PR_SET_ENDIAN		20
+#define	LX_PR_GET_SECCOMP		21
+#define	LX_PR_SET_SECCOMP		22
+#define	LX_PR_CAPBSET_READ		23
+#define	LX_PR_CAPBSET_DROP		24
+#define	LX_PR_GET_TSC			25
+#define	LX_PR_SET_TSC			26
+#define	LX_PR_GET_SECUREBITS		27
+#define	LX_PR_SET_SECUREBITS		28
+#define	LX_PR_SET_TIMERSLACK		29
+#define	LX_PR_GET_TIMERSLACK		30
+#define	LX_PR_TASK_PERF_EVENTS_DISABLE	31
+#define	LX_PR_TASK_PERF_EVENTS_ENABLE	32
+#define	LX_PR_MCE_KILL			33
+#define	LX_PR_MCE_KILL_GET		34
+#define	LX_PR_SET_MM			35
+#define	LX_PR_SET_CHILD_SUBREAPER	36
+#define	LX_PR_GET_CHILD_SUBREAPER	37
+#define	LX_PR_SET_NO_NEW_PRIVS		38
+#define	LX_PR_GET_NO_NEW_PRIVS		39
+#define	LX_PR_GET_TID_ADDRESS		40
+#define	LX_PR_SET_THP_DISABLE		41
+#define	LX_PR_GET_THP_DISABLE		42
+
+#define	LX_PR_SET_NAME_NAMELEN	16
+
+long
+lx_prctl(int opt, uintptr_t data)
+{
+	long err;
+	char ebuf[64];
+
+	switch (opt) {
+	case LX_PR_GET_DUMPABLE: {
+		/* Indicate that process is always dumpable */
+		return (1);
+	}
+
+	case LX_PR_SET_DUMPABLE: {
+		if (data != 0 && data != 1) {
+			return (set_errno(EINVAL));
+		}
+		/* Lie about altering process dumpability */
+		return (0);
+	}
+
+	case LX_PR_GET_SECUREBITS: {
+		/* Our bits are always 0 */
+		return (0);
+	}
+
+	case LX_PR_SET_SECUREBITS: {
+		/* Ignore setting any bits from arg2 */
+		return (0);
+	}
+
+	case LX_PR_SET_KEEPCAPS: {
+		/*
+		 * The closest illumos analog to SET_KEEPCAPS is the PRIV_AWARE
+		 * flag.  There are probably some cases where it's not exactly
+		 * the same, but this will do for a first try.
+		 */
+		if (data == 0) {
+			err = setpflags(PRIV_AWARE_RESET, 1, NULL);
+		} else {
+			err = setpflags(PRIV_AWARE, 1, NULL);
+		}
+
+		if (err != 0) {
+			return (set_errno(err));
+		}
+		return (0);
+	}
+
+	case LX_PR_SET_NAME: {
+		char name[LX_PR_SET_NAME_NAMELEN + 1];
+		proc_t *p = curproc;
+		/*
+		 * In Linux, PR_SET_NAME sets the name of the thread, not the
+		 * process.  Due to the historical quirks of Linux's asinine
+		 * thread model, this name is effectively the name of the
+		 * process (as visible via ps(1)) if the thread is the first of
+		 * its task group.  The first thread is therefore special, and
+		 * to best mimic Linux semantics (and absent a notion of
+		 * per-LWP names), we do nothing (but return success) on LWPs
+		 * other than LWP 1.
+		 */
+		if (curthread->t_tid != 1) {
+			return (0);
+		}
+		if (copyin((void *)data, name, LX_PR_SET_NAME_NAMELEN) != 0) {
+			return (set_errno(EFAULT));
+		}
+		name[LX_PR_SET_NAME_NAMELEN] = '\0';
+		mutex_enter(&p->p_lock);
+		(void) strncpy(p->p_user.u_comm, name, MAXCOMLEN + 1);
+		(void) strncpy(p->p_user.u_psargs, name, PSARGSZ);
+		mutex_exit(&p->p_lock);
+		return (0);
+	}
+
+	case LX_PR_GET_PDEATHSIG: {
+		int sig;
+		lx_proc_data_t *lxpd;
+
+		mutex_enter(&curproc->p_lock);
+		VERIFY((lxpd = ptolxproc(curproc)) != NULL);
+		sig = lxpd->l_parent_deathsig;
+		mutex_exit(&curproc->p_lock);
+
+		return (sig);
+	}
+
+	case LX_PR_SET_PDEATHSIG: {
+		int sig = lx_ltos_signo((int)data, 0);
+		proc_t *pp = NULL;
+		lx_proc_data_t *lxpd;
+
+		if (sig == 0 && data != 0) {
+			return (set_errno(EINVAL));
+		}
+
+		mutex_enter(&pidlock);
+		/* Set signal on our self */
+		mutex_enter(&curproc->p_lock);
+		VERIFY((lxpd = ptolxproc(curproc)) != NULL);
+		lxpd->l_parent_deathsig = sig;
+		pp = curproc->p_parent;
+		mutex_exit(&curproc->p_lock);
+
+		/* Configure parent to potentially signal children on death */
+		mutex_enter(&pp->p_lock);
+		if (PROC_IS_BRANDED(pp)) {
+			VERIFY((lxpd = ptolxproc(pp)) != NULL);
+			/*
+			 * Mark the parent as having children which wish to be
+			 * signaled on death of parent.
+			 */
+			lxpd->l_flags |= LX_PROC_CHILD_DEATHSIG;
+		} else {
+			/*
+			 * If the parent is not a branded process, the needed
+			 * hooks to facilitate this mechanism will not fire
+			 * when it dies. We lie about success in this case.
+			 */
+			/* EMPTY */
+		}
+		mutex_exit(&pp->p_lock);
+		mutex_exit(&pidlock);
+		return (0);
+	}
+
+	case LX_PR_CAPBSET_DROP: {
+		/*
+		 * On recent versions of Linux the login svc drops capabilities
+		 * and if that fails the svc dies and is restarted by systemd.
+		 * For now we pretend dropping capabilities succeeded.
+		 */
+		return (0);
+	}
+
+	default:
+		break;
+	}
+
+	(void) snprintf(ebuf, 64, "prctl option %d", opt);
+	lx_unsupported(ebuf);
+	return (set_errno(EINVAL));
+}
--- /dev/null	Wed Oct  5 00:32:09 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_rename.c	Wed Oct  5 00:32:09 2016
@@ -0,0 +1,39 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#include <sys/fcntl.h>
+#include <sys/lx_fcntl.h>
+
+/* From uts/common/syscall/rename.c */
+extern int rename(char *, char *);
+extern int renameat(int, char *, int, char *);
+
+long
+lx_rename(char *p1, char *p2)
+{
+	return (rename(p1, p2));
+}
+
+long
+lx_renameat(int atfd1, char *p1, int atfd2, char *p2)
+{
+	if (atfd1 == LX_AT_FDCWD)
+		atfd1 = AT_FDCWD;
+
+	if (atfd2 == LX_AT_FDCWD)
+		atfd2 = AT_FDCWD;
+
+	return (renameat(atfd1, p1, atfd2, p2));
+}
--- /dev/null	Wed Oct  5 00:32:09 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_rlimit.c	Wed Oct  5 00:32:09 2016
@@ -0,0 +1,575 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+#include <sys/systm.h>
+#include <sys/thread.h>
+#include <sys/zone.h>
+#include <sys/cpuvar.h>
+#include <sys/cmn_err.h>
+#include <sys/lx_impl.h>
+#include <sys/lx_brand.h>
+
+#define	LX_RLIMIT_CPU		0
+#define	LX_RLIMIT_FSIZE		1
+#define	LX_RLIMIT_DATA		2
+#define	LX_RLIMIT_STACK		3
+#define	LX_RLIMIT_CORE		4
+#define	LX_RLIMIT_RSS		5
+#define	LX_RLIMIT_NPROC		6
+#define	LX_RLIMIT_NOFILE	7
+#define	LX_RLIMIT_MEMLOCK	8
+#define	LX_RLIMIT_AS		9
+#define	LX_RLIMIT_LOCKS		10	/* NA limit on locks, early 2.4 only */
+#define	LX_RLIMIT_SIGPENDING	11
+#define	LX_RLIMIT_MSGQUEUE	12
+#define	LX_RLIMIT_NICE		13	/* NA ceiling for nice */
+#define	LX_RLIMIT_RTPRIO	14	/* NA ceiling on the RT priority */
+#define	LX_RLIMIT_RTTIME	15	/* NA cpu limit for RT proc. */
+
+#define	LX_RLIMIT_NLIMITS	16
+
+#define	RCTL_INFINITE(x) \
+	((x->rcv_flagaction & RCTL_LOCAL_MAXIMAL) && \
+	(x->rcv_flagaction & RCTL_GLOBAL_INFINITE))
+
+typedef struct {
+	ulong_t	rlim_cur;
+	ulong_t	rlim_max;
+} lx_rlimit_t;
+
+typedef struct {
+	uint32_t	rlim_cur;
+	uint32_t	rlim_max;
+} lx_rlimit32_t;
+
+/*
+ * Linux supports many of the same resources that we do, but on Illumos these
+ * are rctls. Instead of using rlimit, we use rctls for all of the limits.
+ * This table is used to translate Linux rlimit keys into the Illumos legacy
+ * rlimit. We then primarily use the rctl/rlimit compatability code to
+ * manage these.
+ */
+static int l_to_r[LX_RLIMIT_NLIMITS] = {
+	RLIMIT_CPU,		/* 0 CPU */
+	RLIMIT_FSIZE,		/* 1 FSIZE */
+	RLIMIT_DATA,		/* 2 DATA */
+	RLIMIT_STACK,		/* 3 STACK */
+	RLIMIT_CORE,		/* 4 CORE */
+	-1,			/* 5 RSS */
+	-1,			/* 6 NPROC */
+	RLIMIT_NOFILE,		/* 7 NOFILE */
+	-1,			/* 8 MEMLOCK */
+	RLIMIT_AS,		/* 9 AS */
+	-1,			/* 10 LOCKS */
+	-1, 			/* 11 SIGPENDING */
+	-1, 			/* 12 MSGQUEUE */
+	-1,			/* 13 NICE */
+	-1,			/* 14 RTPRIO */
+	-1			/* 15 RTTIME */
+};
+
+/*
+ * Magic value Linux uses to indicate infinity
+ */
+#define	LX_RLIM_INFINITY_N	ULONG_MAX
+
+static void
+lx_get_rctl(char *nm, struct rlimit64 *rlp64)
+{
+	rctl_hndl_t hndl;
+	rctl_val_t *oval, *nval;
+
+	rlp64->rlim_cur = RLIM_INFINITY;
+	rlp64->rlim_max = RLIM_INFINITY;
+
+	nval = kmem_alloc(sizeof (rctl_val_t), KM_SLEEP);
+	mutex_enter(&curproc->p_lock);
+
+	hndl = rctl_hndl_lookup(nm);
+	oval = NULL;
+	while ((hndl != -1) && rctl_local_get(hndl, oval, nval, curproc) == 0) {
+		oval = nval;
+		switch (nval->rcv_privilege) {
+		case RCPRIV_BASIC:
+			if (!RCTL_INFINITE(nval))
+				rlp64->rlim_cur = nval->rcv_value;
+			break;
+		case RCPRIV_PRIVILEGED:
+			if (!RCTL_INFINITE(nval))
+				rlp64->rlim_max = nval->rcv_value;
+			break;
+		}
+	}
+
+	mutex_exit(&curproc->p_lock);
+	kmem_free(nval, sizeof (rctl_val_t));
+
+	if (rlp64->rlim_cur == RLIM_INFINITY &&
+	    rlp64->rlim_max != RLIM_INFINITY)
+		rlp64->rlim_cur = rlp64->rlim_max;
+}
+
+static int
+lx_getrlimit_common(int lx_resource, uint64_t *rlim_curp, uint64_t *rlim_maxp)
+{
+	lx_proc_data_t *pd = ptolxproc(curproc);
+	int resource;
+	int64_t cur = -1;
+	boolean_t cur_inf = B_FALSE;
+	int64_t max = -1;
+	boolean_t max_inf = B_FALSE;
+	struct rlimit64 rlim64;
+
+	if (lx_resource < 0 || lx_resource >= LX_RLIMIT_NLIMITS)
+		return (EINVAL);
+
+	switch (lx_resource) {
+	case LX_RLIMIT_LOCKS:
+		rlim64.rlim_cur = pd->l_fake_limits[LX_RLFAKE_LOCKS].rlim_cur;
+		rlim64.rlim_max = pd->l_fake_limits[LX_RLFAKE_LOCKS].rlim_max;
+		break;
+
+	case LX_RLIMIT_NICE:
+		rlim64.rlim_cur = pd->l_fake_limits[LX_RLFAKE_NICE].rlim_cur;
+		rlim64.rlim_max = pd->l_fake_limits[LX_RLFAKE_NICE].rlim_max;
+		break;
+
+	case LX_RLIMIT_RTPRIO:
+		rlim64.rlim_cur = pd->l_fake_limits[LX_RLFAKE_RTPRIO].rlim_cur;
+		rlim64.rlim_max = pd->l_fake_limits[LX_RLFAKE_RTPRIO].rlim_max;
+		break;
+
+	case LX_RLIMIT_RTTIME:
+		rlim64.rlim_cur = pd->l_fake_limits[LX_RLFAKE_RTTIME].rlim_cur;
+		rlim64.rlim_max = pd->l_fake_limits[LX_RLFAKE_RTTIME].rlim_max;
+		break;
+
+	case LX_RLIMIT_RSS:
+		/* zone.max-physical-memory */
+		rlim64.rlim_cur = rlim64.rlim_max = curzone->zone_phys_mem_ctl;
+		break;
+
+	case LX_RLIMIT_NPROC:
+		/*  zone.max-lwps */
+		rlim64.rlim_cur = rlim64.rlim_max = curzone->zone_nlwps_ctl;
+		break;
+
+	case LX_RLIMIT_MEMLOCK:
+		/* zone.max-locked-memory */
+		rlim64.rlim_cur = rlim64.rlim_max =
+		    curzone->zone_locked_mem_ctl;
+		break;
+
+	case LX_RLIMIT_SIGPENDING:
+		lx_get_rctl("process.max-sigqueue-size", &rlim64);
+		break;
+
+	case LX_RLIMIT_MSGQUEUE:
+		lx_get_rctl("process.max-msg-messages", &rlim64);
+		break;
+
+	default:
+		resource = l_to_r[lx_resource];
+
+		mutex_enter(&curproc->p_lock);
+		(void) rctl_rlimit_get(rctlproc_legacy[resource], curproc,
+		    &rlim64);
+		mutex_exit(&curproc->p_lock);
+		break;
+	}
+
+
+	if (rlim64.rlim_cur == RLIM64_INFINITY) {
+		cur = LX_RLIM_INFINITY_N;
+	} else {
+		cur = rlim64.rlim_cur;
+	}
+	if (rlim64.rlim_max == RLIM64_INFINITY) {
+		max = LX_RLIM_INFINITY_N;
+	} else {
+		max = rlim64.rlim_max;
+	}
+
+	if (lx_resource == LX_RLIMIT_STACK && cur > INT_MAX) {
+		/*
+		 * Stunningly, Linux has somehow managed to confuse the concept
+		 * of a "limit" with that of a "default" -- and the value of
+		 * RLIMIT_STACK is used by NPTL as the _default_ stack size if
+		 * it isn't specified. (!!)  Even for a system that prides
+		 * itself on slapdash castles of junk, this is an amazingly
+		 * willful act of incompetence -- and one that is gleefully
+		 * confessed in the pthread_create() man page: "if the
+		 * RLIMIT_STACK soft resource limit at the time the program
+		 * started has any value other than 'unlimited', then it
+		 * determines the default stack size of new threads."  A
+		 * typical stack limit for us is 32TB; if it needs to be said,
+		 * setting the default stack size to be 32TB doesn't work so
+		 * well!  Of course, glibc dropping a deuce in its pants
+		 * becomes our problem -- so to prevent smelly accidents we
+		 * tell Linux that any stack limit over the old (32-bit) values
+		 * for infinity are just infinitely large.
+		 */
+		cur_inf = B_TRUE;
+		max_inf = B_TRUE;
+	}
+
+	if (cur_inf) {
+		*rlim_curp = LX_RLIM64_INFINITY;
+	} else {
+		*rlim_curp = cur;
+	}
+
+	if (max_inf) {
+		*rlim_maxp = LX_RLIM64_INFINITY;
+	} else {
+		*rlim_maxp = max;
+	}
+
+	return (0);
+}
+
+/*
+ * This is the 'new' getrlimit, variously called getrlimit or ugetrlimit
+ * in Linux headers and code.  The only difference between this and the old
+ * getrlimit (variously called getrlimit or old_getrlimit) is the value of
+ * RLIM_INFINITY, which is smaller for the older version.  Modern code will
+ * use this version by default.
+ */
+long
+lx_getrlimit(int resource, lx_rlimit_t *rlp)
+{
+	int rv;
+	lx_rlimit_t rl;
+	uint64_t rlim_cur, rlim_max;
+
+	rv = lx_getrlimit_common(resource, &rlim_cur, &rlim_max);
+	if (rv != 0)
+		return (set_errno(rv));
+
+	if (get_udatamodel() == DATAMODEL_NATIVE) {
+		if (rlim_cur == LX_RLIM64_INFINITY)
+			rl.rlim_cur = LX_RLIM_INFINITY_N;
+		else if (rlim_cur > LX_RLIM_INFINITY_N)
+			rl.rlim_cur = LX_RLIM_INFINITY_N;
+		else
+			rl.rlim_cur = (ulong_t)rlim_cur;
+
+		if (rlim_max == LX_RLIM64_INFINITY)
+			rl.rlim_max = LX_RLIM_INFINITY_N;
+		else if (rlim_max > LX_RLIM_INFINITY_N)
+			rl.rlim_max = LX_RLIM_INFINITY_N;
+		else
+			rl.rlim_max = (ulong_t)rlim_max;
+
+		if (copyout(&rl, rlp, sizeof (rl)) != 0)
+			return (set_errno(EFAULT));
+	}
+#ifdef _SYSCALL32_IMPL
+	else {
+		lx_rlimit32_t rl32;
+
+		if (rlim_cur > UINT_MAX)
+			rl.rlim_cur = UINT_MAX;
+		else
+			rl.rlim_cur = (ulong_t)rlim_cur;
+
+		if (rlim_max > UINT_MAX)
+			rl.rlim_max = UINT_MAX;
+		else
+			rl.rlim_max = (ulong_t)rlim_max;
+
+		rl32.rlim_cur = rl.rlim_cur;
+		rl32.rlim_max = rl.rlim_max;
+
+		if (copyout(&rl32, rlp, sizeof (rl32)) != 0)
+			return (set_errno(EFAULT));
+	}
+#endif
+
+	return (0);
+}
+
+/*
+ * This is the 'old' getrlimit, variously called getrlimit or old_getrlimit
+ * in Linux headers and code.  The only difference between this and the new
+ * getrlimit (variously called getrlimit or ugetrlimit) is the value of
+ * RLIM_INFINITY, which is smaller for the older version.
+ *
+ * This is only used for 32-bit code.
+ */
+long
+lx_oldgetrlimit(int resource, lx_rlimit_t *rlp)
+{
+	int rv;
+	lx_rlimit32_t rl32;
+	uint64_t rlim_cur, rlim_max;
+
+	rv = lx_getrlimit_common(resource, &rlim_cur, &rlim_max);
+	if (rv != 0)
+		return (set_errno(rv));
+
+	if (rlim_cur > INT_MAX)
+		rl32.rlim_cur = INT_MAX;
+	else
+		rl32.rlim_cur = (ulong_t)rlim_cur;
+
+	if (rlim_max > INT_MAX)
+		rl32.rlim_max = INT_MAX;
+	else
+		rl32.rlim_max = (ulong_t)rlim_cur;
+
+	if (copyout(&rl32, rlp, sizeof (rl32)) != 0)
+		return (set_errno(EFAULT));
+
+	return (0);
+}
+
+static int
+lx_set_rctl(char *nm, struct rlimit64 *rlp64)
+{
+	int err;
+	rctl_hndl_t hndl;
+	rctl_alloc_gp_t *gp;
+
+	gp = rctl_rlimit_set_prealloc(1);
+
+	mutex_enter(&curproc->p_lock);
+
+	hndl = rctl_hndl_lookup(nm);
+
+	/*
+	 * We're not supposed to do this but since we want all our rctls to
+	 * behave like rlimits, we take advantage of this function to set up
+	 * this way.
+	 */
+	err = rctl_rlimit_set(hndl, curproc, rlp64, gp, RCTL_LOCAL_DENY, 0,
+	    CRED());
+
+	mutex_exit(&curproc->p_lock);
+
+	rctl_prealloc_destroy(gp);
+
+	return (err);
+}
+
+static int
+lx_setrlimit_common(int lx_resource, uint64_t rlim_cur, uint64_t rlim_max)
+{
+	lx_proc_data_t *pd = ptolxproc(curproc);
+	int err;
+	int resource;
+	rctl_alloc_gp_t *gp;
+	struct rlimit64 rl64;
+
+	if (lx_resource < 0 || lx_resource >= LX_RLIMIT_NLIMITS)
+		return (EINVAL);
+
+	switch (lx_resource) {
+	case LX_RLIMIT_LOCKS:
+		pd->l_fake_limits[LX_RLFAKE_LOCKS].rlim_cur = rlim_cur;
+		pd->l_fake_limits[LX_RLFAKE_LOCKS].rlim_max = rlim_max;
+		break;
+
+	case LX_RLIMIT_NICE:
+		pd->l_fake_limits[LX_RLFAKE_NICE].rlim_cur = rlim_cur;
+		pd->l_fake_limits[LX_RLFAKE_NICE].rlim_max = rlim_max;
+		break;
+
+	case LX_RLIMIT_RTPRIO:
+		pd->l_fake_limits[LX_RLFAKE_RTPRIO].rlim_cur = rlim_cur;
+		pd->l_fake_limits[LX_RLFAKE_RTPRIO].rlim_max = rlim_max;
+		break;
+
+	case LX_RLIMIT_RTTIME:
+		pd->l_fake_limits[LX_RLFAKE_RTTIME].rlim_cur = rlim_cur;
+		pd->l_fake_limits[LX_RLFAKE_RTTIME].rlim_max = rlim_max;
+		break;
+
+	case LX_RLIMIT_RSS:
+		/*
+		 * zone.max-physical-memory
+		 * Since we're emulating the value via a zone rctl, we can't
+		 * set that from within the zone. Lie and say we set the value.
+		 */
+		break;
+
+	case LX_RLIMIT_NPROC:
+		/*
+		 * zone.max-lwps
+		 * Since we're emulating the value via a zone rctl, we can't
+		 * set that from within the zone. Lie and say we set the value.
+		 */
+		break;
+
+	case LX_RLIMIT_MEMLOCK:
+		/*
+		 * zone.max-locked-memory
+		 * Since we're emulating the value via a zone rctl, we can't
+		 * set that from within the zone. Lie and say we set the value.
+		 */
+		break;
+
+	case LX_RLIMIT_SIGPENDING:
+		/*
+		 * On Ubuntu at least, the login and sshd processes expect to
+		 * set this limit to 16k and login will fail if this fails. On
+		 * Illumos we have a system limit of 8k and normally the
+		 * privileged limit is 512. We simply pretend this works to
+		 * allow login to work.
+		 */
+		if (rlim_max > 8192)
+			return (0);
+
+		rl64.rlim_cur = rlim_cur;
+		rl64.rlim_max = rlim_max;
+		if ((err = lx_set_rctl("process.max-sigqueue-size", &rl64))
+		    != 0)
+			return (set_errno(err));
+		break;
+
+	case LX_RLIMIT_MSGQUEUE:
+		rl64.rlim_cur = rlim_cur;
+		rl64.rlim_max = rlim_max;
+		if ((err = lx_set_rctl("process.max-msg-messages", &rl64)) != 0)
+			return (set_errno(err));
+		break;
+
+	default:
+		resource = l_to_r[lx_resource];
+
+		/*
+		 * Linux limits the max number of open files to 1m and there is
+		 * a test for this.
+		 */
+		if (lx_resource == LX_RLIMIT_NOFILE && rlim_max > (1024 * 1024))
+			return (EPERM);
+
+		rl64.rlim_cur = rlim_cur;
+		rl64.rlim_max = rlim_max;
+		gp = rctl_rlimit_set_prealloc(1);
+
+		mutex_enter(&curproc->p_lock);
+		err = rctl_rlimit_set(rctlproc_legacy[resource], curproc,
+		    &rl64, gp, rctlproc_flags[resource],
+		    rctlproc_signals[resource], CRED());
+		mutex_exit(&curproc->p_lock);
+
+		rctl_prealloc_destroy(gp);
+		if (err != 0)
+			return (set_errno(err));
+		break;
+	}
+
+	return (0);
+}
+
+long
+lx_setrlimit(int resource, lx_rlimit_t *rlp)
+{
+	int rv;
+	lx_rlimit_t rl;
+	uint64_t rlim_cur, rlim_max;
+
+	if (get_udatamodel() == DATAMODEL_NATIVE) {
+		if (copyin(rlp, &rl, sizeof (rl)) != 0)
+			return (set_errno(EFAULT));
+	}
+#ifdef _SYSCALL32_IMPL
+	else {
+		lx_rlimit32_t rl32;
+
+		if (copyin(rlp, &rl32, sizeof (rl32)) != 0)
+			return (set_errno(EFAULT));
+
+		rl.rlim_cur = rl32.rlim_cur;
+		rl.rlim_max = rl32.rlim_max;
+	}
+#endif
+
+	if ((rl.rlim_max != LX_RLIM_INFINITY_N &&
+	    rl.rlim_cur == LX_RLIM_INFINITY_N) ||
+	    rl.rlim_cur > rl.rlim_max)
+		return (set_errno(EINVAL));
+
+	if (rl.rlim_cur == LX_RLIM_INFINITY_N)
+		rlim_cur = LX_RLIM64_INFINITY;
+	else
+		rlim_cur = rl.rlim_cur;
+
+	if (rl.rlim_max == LX_RLIM_INFINITY_N)
+		rlim_max = LX_RLIM64_INFINITY;
+	else
+		rlim_max = rl.rlim_max;
+
+	rv = lx_setrlimit_common(resource, rlim_cur, rlim_max);
+	if (rv != 0)
+		return (set_errno(rv));
+	return (0);
+}
+
+/*
+ * From the man page:
+ * The Linux-specific prlimit() system call combines and extends the
+ * functionality of setrlimit() and getrlimit(). It can be used to both set
+ * and get the resource limits of an arbitrary process.
+ *
+ * If pid is 0, then the call applies to the calling process.
+ */
+long
+lx_prlimit64(pid_t pid, int resource, lx_rlimit64_t *nrlp, lx_rlimit64_t *orlp)
+{
+	int rv;
+	lx_rlimit64_t nrl, orl;
+
+	if (pid != 0) {
+		/* XXX TBD if needed */
+		char buf[80];
+
+		(void) snprintf(buf, sizeof (buf),
+		    "setting prlimit %d for another process\n", resource);
+		lx_unsupported(buf);
+		return (ENOTSUP);
+	}
+
+	if (orlp != NULL) {
+		/* we first get the current limits */
+		rv = lx_getrlimit_common(resource, &orl.rlim_cur,
+		    &orl.rlim_max);
+		if (rv != 0)
+			return (set_errno(rv));
+	}
+
+	if (nrlp != NULL) {
+		if (copyin(nrlp, &nrl, sizeof (nrl)) != 0)
+			return (set_errno(EFAULT));
+
+		if ((nrl.rlim_max != LX_RLIM64_INFINITY &&
+		    nrl.rlim_cur == LX_RLIM64_INFINITY) ||
+		    nrl.rlim_cur > nrl.rlim_max)
+			return (set_errno(EINVAL));
+
+		rv = lx_setrlimit_common(resource, nrl.rlim_cur, nrl.rlim_max);
+		if (rv != 0)
+			return (set_errno(rv));
+	}
+
+	if (orlp != NULL) {
+		/* now return the original limits, if necessary */
+		if (copyout(&orl, orlp, sizeof (orl)) != 0)
+			return (set_errno(EFAULT));
+	}
+
+	return (0);
+}
--- /dev/null	Wed Oct  5 00:32:09 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_rw.c	Wed Oct  5 00:32:09 2016
@@ -0,0 +1,949 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#include <sys/errno.h>
+#include <sys/systm.h>
+#include <sys/file.h>
+#include <sys/vnode.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_types.h>
+#include <sys/nbmlock.h>
+#include <sys/limits.h>
+
+/* uts/common/syscall/rw.c */
+extern size_t copyout_max_cached;
+
+
+/* Common routines */
+
+static int
+lx_iovec_copyin(void *uiovp, int iovcnt, iovec_t *kiovp, ssize_t *count)
+{
+#ifdef _SYSCALL32_IMPL
+	/*
+	 * 32-bit callers need to have their iovec expanded, while ensuring
+	 * that they can't move more than 2Gbytes of data in a single call.
+	 */
+	if (get_udatamodel() == DATAMODEL_ILP32) {
+		struct iovec32 buf32[IOV_MAX_STACK], *aiov32 = buf32;
+		int aiov32len = 0;
+		ssize32_t total32 = 0;
+		int i;
+
+		if (iovcnt > IOV_MAX_STACK) {
+			aiov32len = iovcnt * sizeof (iovec32_t);
+			aiov32 = kmem_alloc(aiov32len, KM_SLEEP);
+		}
+
+		if (copyin(uiovp, aiov32, iovcnt * sizeof (iovec32_t))) {
+			if (aiov32len != 0) {
+				kmem_free(aiov32, aiov32len);
+			}
+			return (EFAULT);
+		}
+
+		for (i = 0; i < iovcnt; i++) {
+			ssize32_t iovlen32 = aiov32[i].iov_len;
+			total32 += iovlen32;
+			if (iovlen32 < 0 || total32 < 0) {
+				if (aiov32len != 0) {
+					kmem_free(aiov32, aiov32len);
+				}
+				return (EINVAL);
+			}
+			kiovp[i].iov_len = iovlen32;
+			kiovp[i].iov_base =
+			    (caddr_t)(uintptr_t)aiov32[i].iov_base;
+			/* Linux does a basic sanity test on the address */
+			if ((uintptr_t)kiovp[i].iov_base >= USERLIMIT32) {
+				if (aiov32len != 0) {
+					kmem_free(aiov32, aiov32len);
+				}
+				return (EFAULT);
+			}
+		}
+		*count = total32;
+
+		if (aiov32len != 0)
+			kmem_free(aiov32, aiov32len);
+	} else
+#endif
+	{
+		ssize_t total = 0;
+		int i;
+
+		if (copyin(uiovp, kiovp, iovcnt * sizeof (iovec_t)))
+			return (EFAULT);
+		for (i = 0; i < iovcnt; i++) {
+			ssize_t iovlen = kiovp[i].iov_len;
+			total += iovlen;
+			if (iovlen < 0 || total < 0) {
+				return (EINVAL);
+			}
+			/* Linux does a basic sanity test on the address */
+			if ((uintptr_t)kiovp[i].iov_base >= USERLIMIT) {
+				return (EFAULT);
+			}
+		}
+		*count = total;
+	}
+	return (0);
+}
+
+static int
+lx_read_common(file_t *fp, uio_t *uiop, size_t *nread, boolean_t positioned)
+{
+	vnode_t *vp = fp->f_vnode;
+	int error = 0, rwflag = 0, ioflag;
+	ssize_t count = uiop->uio_resid;
+	size_t rcount = 0;
+	struct cpu *cp;
+	boolean_t in_crit = B_FALSE;
+
+	/*
+	 * We have to enter the critical region before calling VOP_RWLOCK
+	 * to avoid a deadlock with ufs.
+	 */
+	if (nbl_need_check(vp)) {
+		int svmand;
+
+		nbl_start_crit(vp, RW_READER);
+		in_crit = B_TRUE;
+		error = nbl_svmand(vp, fp->f_cred, &svmand);
+		if (error != 0)
+			goto out;
+		if (nbl_conflict(vp, NBL_READ, uiop->uio_offset, count, svmand,
+		    NULL) != 0) {
+			error = EACCES;
+			goto out;
+		}
+	}
+
+	(void) VOP_RWLOCK(vp, rwflag, NULL);
+	/*
+	 * For non-positioned reads, recheck offset/count validity inside
+	 * VOP_WRLOCK to prevent filesize from changing during validation.
+	 */
+	if (!positioned) {
+		u_offset_t uoffset = (u_offset_t)(ulong_t)fp->f_offset;
+
+		if ((vp->v_type == VREG) && (uoffset >= OFFSET_MAX(fp))) {
+			struct vattr va;
+
+			va.va_mask = AT_SIZE;
+			error = VOP_GETATTR(vp, &va, 0, fp->f_cred, NULL);
+			VOP_RWUNLOCK(vp, rwflag, NULL);
+			if (error != 0)
+				goto out;
+			/* We have to return EOF if fileoff is >= file size. */
+			if (uoffset >= va.va_size)
+				goto out;
+			/*
+			 * File is greater than or equal to maxoff and
+			 * therefore we return EOVERFLOW.
+			 */
+			error = EOVERFLOW;
+			goto out;
+		}
+		if ((vp->v_type == VREG) &&
+		    (uoffset + count > OFFSET_MAX(fp))) {
+			count = (ssize_t)(OFFSET_MAX(fp) - uoffset);
+			uiop->uio_resid = count;
+		}
+		uiop->uio_offset = uoffset;
+	}
+	ioflag = uiop->uio_fmode & (FAPPEND|FSYNC|FDSYNC|FRSYNC);
+	/* If read sync is not asked for, filter sync flags */
+	if ((ioflag & FRSYNC) == 0)
+		ioflag &= ~(FSYNC|FDSYNC);
+	error = VOP_READ(vp, uiop, ioflag, fp->f_cred, NULL);
+	rcount = count - uiop->uio_resid;
+	CPU_STATS_ENTER_K();
+	cp = CPU;
+	CPU_STATS_ADDQ(cp, sys, sysread, 1);
+	CPU_STATS_ADDQ(cp, sys, readch, (ulong_t)rcount);
+	CPU_STATS_EXIT_K();
+	ttolwp(curthread)->lwp_ru.ioch += (ulong_t)rcount;
+	/* Store offset for non-positioned reads */
+	if (!positioned) {
+		if (vp->v_type == VFIFO) {
+			/* Backward compatibility */
+			fp->f_offset = rcount;
+		} else if (((fp->f_flag & FAPPEND) == 0) ||
+		    (vp->v_type != VREG) || (count != 0)) {
+			/* POSIX */
+			fp->f_offset = uiop->uio_loffset;
+		}
+	}
+	VOP_RWUNLOCK(vp, rwflag, NULL);
+
+out:
+	if (in_crit)
+		nbl_end_crit(vp);
+	*nread = rcount;
+	return (error);
+}
+
+static int
+lx_write_common(file_t *fp, uio_t *uiop, size_t *nwrite, boolean_t positioned)
+{
+	vnode_t *vp = fp->f_vnode;
+	int error = 0, rwflag = 1, ioflag;
+	ssize_t count = uiop->uio_resid;
+	size_t wcount = 0;
+	struct cpu *cp;
+	boolean_t in_crit = B_FALSE;
+
+	/*
+	 * We have to enter the critical region before calling VOP_RWLOCK
+	 * to avoid a deadlock with ufs.
+	 */
+	if (nbl_need_check(vp)) {
+		int svmand;
+
+		nbl_start_crit(vp, RW_READER);
+		in_crit = B_TRUE;
+		error = nbl_svmand(vp, fp->f_cred, &svmand);
+		if (error != 0)
+			goto out;
+		if (nbl_conflict(vp, NBL_WRITE, uiop->uio_loffset, count,
+		    svmand, NULL) != 0) {
+			error = EACCES;
+			goto out;
+		}
+	}
+
+	(void) VOP_RWLOCK(vp, rwflag, NULL);
+
+	if (!positioned) {
+		/*
+		 * For non-positioned writes, the value of fp->f_offset is
+		 * re-queried while inside VOP_RWLOCK.  This ensures that other
+		 * writes which alter the filesize will be taken into account.
+		 */
+		uiop->uio_loffset = fp->f_offset;
+		ioflag = uiop->uio_fmode & (FAPPEND|FSYNC|FDSYNC|FRSYNC);
+	} else {
+		/*
+		 * In a senseless departure from POSIX, positioned write calls
+		 * on Linux do _not_ ignore the O_APPEND flag.
+		 */
+		ioflag = uiop->uio_fmode & (FAPPEND|FSYNC|FDSYNC|FRSYNC);
+	}
+	if (vp->v_type == VREG) {
+		u_offset_t fileoff = (u_offset_t)(ulong_t)uiop->uio_loffset;
+
+		if (fileoff >= curproc->p_fsz_ctl) {
+			VOP_RWUNLOCK(vp, rwflag, NULL);
+			mutex_enter(&curproc->p_lock);
+			(void) rctl_action(rctlproc_legacy[RLIMIT_FSIZE],
+			    curproc->p_rctls, curproc, RCA_UNSAFE_SIGINFO);
+			mutex_exit(&curproc->p_lock);
+			error = EFBIG;
+			goto out;
+		}
+		if (fileoff >= OFFSET_MAX(fp)) {
+			VOP_RWUNLOCK(vp, rwflag, NULL);
+			error = EFBIG;
+			goto out;
+		}
+		if (fileoff + count > OFFSET_MAX(fp)) {
+			count = (ssize_t)(OFFSET_MAX(fp) - fileoff);
+			uiop->uio_resid = count;
+		}
+	}
+
+	error = VOP_WRITE(vp, uiop, ioflag, fp->f_cred, NULL);
+	wcount = count - uiop->uio_resid;
+	CPU_STATS_ENTER_K();
+	cp = CPU;
+	CPU_STATS_ADDQ(cp, sys, syswrite, 1);
+	CPU_STATS_ADDQ(cp, sys, writech, (ulong_t)wcount);
+	CPU_STATS_EXIT_K();
+	ttolwp(curthread)->lwp_ru.ioch += (ulong_t)wcount;
+
+	/* Store offset for non-positioned writes */
+	if (!positioned) {
+		if (vp->v_type == VFIFO) {
+			/* Backward compatibility */
+			fp->f_offset = wcount;
+		} else if (((fp->f_flag & FAPPEND) == 0) ||
+		    (vp->v_type != VREG) || (count != 0)) {
+			/* POSIX */
+			fp->f_offset = uiop->uio_loffset;
+		}
+	}
+	VOP_RWUNLOCK(vp, rwflag, NULL);
+
+out:
+	if (in_crit)
+		nbl_end_crit(vp);
+	*nwrite = wcount;
+	return (error);
+}
+
+/*
+ * The Linux routines for reading and writing data from file descriptors behave
+ * differently from their SunOS counterparts in a few key ways:
+ *
+ * - Passing an iovcnt of 0 to the vectored functions results in an error on
+ *   SunOS, but on Linux it yields return value of 0.
+ *
+ * - If any data is successfully read or written, Linux will return a success.
+ *   This is unlike SunOS which would return an error code for the entire
+ *   operation in cases where vectors had gone unprocessed.
+ *
+ * - Breaking from POSIX, Linux positioned writes (pwrite/pwritev) on Linux
+ *   will obey the O_APPEND flag if it is set on the descriptor.
+ */
+
+ssize_t
+lx_read(int fdes, void *cbuf, size_t ccount)
+{
+	struct uio auio;
+	struct iovec aiov;
+	file_t *fp;
+	ssize_t count = (ssize_t)ccount;
+	size_t nread = 0;
+	int fflag, error = 0;
+
+	if (count < 0)
+		return (set_errno(EINVAL));
+	if ((fp = getf(fdes)) == NULL)
+		return (set_errno(EBADF));
+	if (((fflag = fp->f_flag) & FREAD) == 0) {
+		error = EBADF;
+		goto out;
+	}
+	if (fp->f_vnode->v_type == VREG && count == 0) {
+		goto out;
+	}
+	if (fp->f_vnode->v_type == VDIR) {
+		error = EISDIR;
+		goto out;
+	}
+
+	aiov.iov_base = cbuf;
+	aiov.iov_len = count;
+	auio.uio_iov = &aiov;
+	auio.uio_iovcnt = 1;
+	auio.uio_loffset = fp->f_offset;
+	auio.uio_resid = count;
+	auio.uio_segflg = UIO_USERSPACE;
+	auio.uio_llimit = MAXOFFSET_T;
+	auio.uio_fmode = fflag;
+	if (count <= copyout_max_cached)
+		auio.uio_extflg = UIO_COPY_CACHED;
+	else
+		auio.uio_extflg = UIO_COPY_DEFAULT;
+
+	error = lx_read_common(fp, &auio, &nread, B_FALSE);
+
+	if (error == EINTR) {
+		if (nread != 0) {
+			error = 0;
+		} else {
+			ttolxlwp(curthread)->br_syscall_restart = B_TRUE;
+		}
+	}
+out:
+	releasef(fdes);
+	if (error != 0)
+		return (set_errno(error));
+	return ((ssize_t)nread);
+}
+
+ssize_t
+lx_write(int fdes, void *cbuf, size_t ccount)
+{
+	struct uio auio;
+	struct iovec aiov;
+	file_t *fp;
+	ssize_t count = (ssize_t)ccount;
+	size_t nwrite = 0;
+	int fflag, error = 0;
+
+	if (count < 0)
+		return (set_errno(EINVAL));
+	if ((fp = getf(fdes)) == NULL)
+		return (set_errno(EBADF));
+	if (((fflag = fp->f_flag) & FWRITE) == 0) {
+		error = EBADF;
+		goto out;
+	}
+	if (fp->f_vnode->v_type == VREG && count == 0) {
+		goto out;
+	}
+
+	aiov.iov_base = cbuf;
+	aiov.iov_len = count;
+	auio.uio_iov = &aiov;
+	auio.uio_iovcnt = 1;
+	auio.uio_loffset = fp->f_offset;
+	auio.uio_resid = count;
+	auio.uio_segflg = UIO_USERSPACE;
+	auio.uio_llimit = curproc->p_fsz_ctl;
+	auio.uio_fmode = fflag;
+	auio.uio_extflg = UIO_COPY_DEFAULT;
+
+	error = lx_write_common(fp, &auio, &nwrite, B_FALSE);
+
+	if (error == EINTR) {
+		if (nwrite != 0) {
+			error = 0;
+		} else {
+			ttolxlwp(curthread)->br_syscall_restart = B_TRUE;
+		}
+	}
+out:
+	releasef(fdes);
+	if (error != 0)
+		return (set_errno(error));
+	return (nwrite);
+}
+
+ssize_t
+lx_readv(int fdes, struct iovec *iovp, int iovcnt)
+{
+	struct uio auio;
+	struct iovec buf[IOV_MAX_STACK], *aiov = buf;
+	int aiovlen = 0;
+	file_t *fp;
+	ssize_t count;
+	size_t nread = 0;
+	int fflag, error = 0;
+
+	if (iovcnt < 0 || iovcnt > IOV_MAX) {
+		return (set_errno(EINVAL));
+	} else if (iovcnt == 0) {
+		return (0);
+	}
+
+	if (iovcnt > IOV_MAX_STACK) {
+		aiovlen = iovcnt * sizeof (iovec_t);
+		aiov = kmem_alloc(aiovlen, KM_SLEEP);
+	}
+	if ((error = lx_iovec_copyin(iovp, iovcnt, aiov, &count)) != 0) {
+		if (aiovlen != 0)
+			kmem_free(aiov, aiovlen);
+		return (set_errno(error));
+	}
+
+	if ((fp = getf(fdes)) == NULL) {
+		if (aiovlen != 0)
+			kmem_free(aiov, aiovlen);
+		return (set_errno(EBADF));
+	}
+	if (((fflag = fp->f_flag) & FREAD) == 0) {
+		error = EBADF;
+		goto out;
+	}
+	if (fp->f_vnode->v_type == VREG && count == 0) {
+		goto out;
+	}
+	if (fp->f_vnode->v_type == VDIR) {
+		error = EISDIR;
+		goto out;
+	}
+
+	auio.uio_iov = aiov;
+	auio.uio_iovcnt = iovcnt;
+	auio.uio_loffset = fp->f_offset;
+	auio.uio_resid = count;
+	auio.uio_segflg = UIO_USERSPACE;
+	auio.uio_llimit = MAXOFFSET_T;
+	auio.uio_fmode = fflag;
+	if (count <= copyout_max_cached)
+		auio.uio_extflg = UIO_COPY_CACHED;
+	else
+		auio.uio_extflg = UIO_COPY_DEFAULT;
+
+	error = lx_read_common(fp, &auio, &nread, B_FALSE);
+
+	if (error != 0) {
+		if (nread != 0) {
+			error = 0;
+		} else if (error == EINTR) {
+			ttolxlwp(curthread)->br_syscall_restart = B_TRUE;
+		}
+	}
+out:
+	releasef(fdes);
+	if (aiovlen != 0)
+		kmem_free(aiov, aiovlen);
+	if (error != 0) {
+		return (set_errno(error));
+	}
+	return (nread);
+}
+
+ssize_t
+lx_writev(int fdes, struct iovec *iovp, int iovcnt)
+{
+	struct uio auio;
+	struct iovec buf[IOV_MAX_STACK], *aiov = buf;
+	int aiovlen = 0;
+	file_t *fp;
+	ssize_t count;
+	size_t nwrite = 0;
+	int fflag, error = 0;
+
+	if (iovcnt < 0 || iovcnt > IOV_MAX) {
+		return (set_errno(EINVAL));
+	} else if (iovcnt == 0) {
+		return (0);
+	}
+
+	if (iovcnt > IOV_MAX_STACK) {
+		aiovlen = iovcnt * sizeof (iovec_t);
+		aiov = kmem_alloc(aiovlen, KM_SLEEP);
+	}
+	if ((error = lx_iovec_copyin(iovp, iovcnt, aiov, &count)) != 0) {
+		if (aiovlen != 0)
+			kmem_free(aiov, aiovlen);
+		return (set_errno(error));
+	}
+
+	if ((fp = getf(fdes)) == NULL) {
+		if (aiovlen != 0)
+			kmem_free(aiov, aiovlen);
+		return (set_errno(EBADF));
+	}
+	if (((fflag = fp->f_flag) & FWRITE) == 0) {
+		error = EBADF;
+		goto out;
+	}
+	if (fp->f_vnode->v_type == VREG && count == 0) {
+		goto out;
+	}
+
+	auio.uio_iov = aiov;
+	auio.uio_iovcnt = iovcnt;
+	auio.uio_loffset = fp->f_offset;
+	auio.uio_resid = count;
+	auio.uio_segflg = UIO_USERSPACE;
+	auio.uio_llimit = curproc->p_fsz_ctl;
+	auio.uio_fmode = fflag;
+	auio.uio_extflg = UIO_COPY_DEFAULT;
+
+	error = lx_write_common(fp, &auio, &nwrite, B_FALSE);
+
+	if (error != 0) {
+		if (nwrite != 0) {
+			error = 0;
+		} else if (error == EINTR) {
+			ttolxlwp(curthread)->br_syscall_restart = B_TRUE;
+		}
+	}
+out:
+	releasef(fdes);
+	if (aiovlen != 0)
+		kmem_free(aiov, aiovlen);
+	if (error != 0) {
+		return (set_errno(error));
+	}
+	return (nwrite);
+}
+
+ssize_t
+lx_pread(int fdes, void *cbuf, size_t ccount, off64_t offset)
+{
+	struct uio auio;
+	struct iovec aiov;
+	file_t *fp;
+	ssize_t count = (ssize_t)ccount;
+	size_t nread = 0;
+	int fflag, error = 0;
+
+	if (count < 0)
+		return (set_errno(EINVAL));
+	if ((fp = getf(fdes)) == NULL)
+		return (set_errno(EBADF));
+	if (((fflag = fp->f_flag) & FREAD) == 0) {
+		error = EBADF;
+		goto out;
+	}
+	if (fp->f_vnode->v_type == VREG) {
+		u_offset_t fileoff = (u_offset_t)offset;
+
+		if (count == 0)
+			goto out;
+		/*
+		 * Return EINVAL if an invalid offset comes to pread.
+		 * Negative offset from user will cause this error.
+		 */
+		if (fileoff > MAXOFFSET_T) {
+			error = EINVAL;
+			goto out;
+		}
+		/*
+		 * Limit offset such that we don't read or write
+		 * a file beyond the maximum offset representable in
+		 * an off_t structure.
+		 */
+		if (fileoff + count > MAXOFFSET_T)
+			count = (ssize_t)((offset_t)MAXOFFSET_T - fileoff);
+	} else if (fp->f_vnode->v_type == VFIFO) {
+		error = ESPIPE;
+		goto out;
+	} else if (fp->f_vnode->v_type == VDIR) {
+		error = EISDIR;
+		goto out;
+	}
+
+	aiov.iov_base = cbuf;
+	aiov.iov_len = count;
+	auio.uio_iov = &aiov;
+	auio.uio_iovcnt = 1;
+	auio.uio_loffset = offset;
+	auio.uio_resid = count;
+	auio.uio_segflg = UIO_USERSPACE;
+	auio.uio_llimit = MAXOFFSET_T;
+	auio.uio_fmode = fflag;
+	auio.uio_extflg = UIO_COPY_CACHED;
+
+	error = lx_read_common(fp, &auio, &nread, B_TRUE);
+
+	if (error == EINTR) {
+		if (nread != 0) {
+			error = 0;
+		} else {
+			ttolxlwp(curthread)->br_syscall_restart = B_TRUE;
+		}
+	}
+out:
+	releasef(fdes);
+	if (error) {
+		return (set_errno(error));
+	}
+	return ((ssize_t)nread);
+
+}
+
+ssize_t
+lx_pwrite(int fdes, void *cbuf, size_t ccount, off64_t offset)
+{
+	struct uio auio;
+	struct iovec aiov;
+	file_t *fp;
+	ssize_t count = (ssize_t)ccount;
+	size_t nwrite = 0;
+	int fflag, error = 0;
+
+	if (count < 0)
+		return (set_errno(EINVAL));
+	if ((fp = getf(fdes)) == NULL)
+		return (set_errno(EBADF));
+	if (((fflag = fp->f_flag) & (FWRITE)) == 0) {
+		error = EBADF;
+		goto out;
+	}
+	if (fp->f_vnode->v_type == VREG) {
+		u_offset_t fileoff = (u_offset_t)offset;
+
+		if (count == 0)
+			goto out;
+		/*
+		 * return EINVAL for offsets that cannot be
+		 * represented in an off_t.
+		 */
+		if (fileoff > MAXOFFSET_T) {
+			error = EINVAL;
+			goto out;
+		}
+		/*
+		 * Take appropriate action if we are trying to write above the
+		 * resource limit.
+		 */
+		if (fileoff >= curproc->p_fsz_ctl) {
+			mutex_enter(&curproc->p_lock);
+			(void) rctl_action(rctlproc_legacy[RLIMIT_FSIZE],
+			    curproc->p_rctls, curproc, RCA_UNSAFE_SIGINFO);
+			mutex_exit(&curproc->p_lock);
+
+			error = EFBIG;
+			goto out;
+		}
+		/*
+		 * Don't allow pwrite to cause file sizes to exceed maxoffset.
+		 */
+		if (fileoff == MAXOFFSET_T) {
+			error = EFBIG;
+			goto out;
+		}
+		if (fileoff + count > MAXOFFSET_T)
+			count = (ssize_t)((u_offset_t)MAXOFFSET_T - fileoff);
+	} else if (fp->f_vnode->v_type == VFIFO) {
+		error = ESPIPE;
+		goto out;
+	}
+
+	aiov.iov_base = cbuf;
+	aiov.iov_len = count;
+	auio.uio_iov = &aiov;
+	auio.uio_iovcnt = 1;
+	auio.uio_loffset = offset;
+	auio.uio_resid = count;
+	auio.uio_segflg = UIO_USERSPACE;
+	auio.uio_llimit = curproc->p_fsz_ctl;
+	auio.uio_fmode = fflag;
+	auio.uio_extflg = UIO_COPY_CACHED;
+
+	error = lx_write_common(fp, &auio, &nwrite, B_TRUE);
+
+	if (error == EINTR) {
+		if (nwrite != 0) {
+			error = 0;
+		} else {
+			ttolxlwp(curthread)->br_syscall_restart = B_TRUE;
+		}
+	}
+out:
+	releasef(fdes);
+	if (error) {
+		return (set_errno(error));
+	}
+	return (nwrite);
+}
+
+ssize_t
+lx_pread32(int fdes, void *cbuf, size_t ccount, uint32_t off_lo,
+    uint32_t off_hi)
+{
+	return (lx_pread(fdes, cbuf, ccount, LX_32TO64(off_lo, off_hi)));
+}
+
+ssize_t
+lx_pwrite32(int fdes, void *cbuf, size_t ccount, uint32_t off_lo,
+    uint32_t off_hi)
+{
+	return (lx_pwrite(fdes, cbuf, ccount, LX_32TO64(off_lo, off_hi)));
+}
+
+ssize_t
+lx_preadv(int fdes, void *iovp, int iovcnt, off64_t offset)
+{
+	struct uio auio;
+	struct iovec buf[IOV_MAX_STACK], *aiov = buf;
+	int aiovlen = 0;
+	file_t *fp;
+	ssize_t count;
+	size_t nread = 0;
+	int fflag, error = 0;
+
+	if (iovcnt < 0 || iovcnt > IOV_MAX) {
+		return (set_errno(EINVAL));
+	} else if (iovcnt == 0) {
+		return (0);
+	}
+
+	if (iovcnt > IOV_MAX_STACK) {
+		aiovlen = iovcnt * sizeof (iovec_t);
+		aiov = kmem_alloc(aiovlen, KM_SLEEP);
+	}
+	if ((error = lx_iovec_copyin(iovp, iovcnt, aiov, &count)) != 0) {
+		if (aiovlen != 0)
+			kmem_free(aiov, aiovlen);
+		return (set_errno(error));
+	}
+
+	if ((fp = getf(fdes)) == NULL) {
+		if (aiovlen != 0)
+			kmem_free(aiov, aiovlen);
+		return (set_errno(EBADF));
+	}
+	if (((fflag = fp->f_flag) & FREAD) == 0) {
+		error = EBADF;
+		goto out;
+	}
+	if (fp->f_vnode->v_type == VREG) {
+		u_offset_t fileoff = (u_offset_t)offset;
+
+		if (count == 0)
+			goto out;
+		/*
+		 * Return EINVAL if an invalid offset comes to pread.
+		 * Negative offset from user will cause this error.
+		 */
+		if (fileoff > MAXOFFSET_T) {
+			error = EINVAL;
+			goto out;
+		}
+		/*
+		 * Limit offset such that we don't read or write a file beyond
+		 * the maximum offset representable in an off_t structure.
+		 */
+		if (fileoff + count > MAXOFFSET_T)
+			count = (ssize_t)((offset_t)MAXOFFSET_T - fileoff);
+	} else if (fp->f_vnode->v_type == VDIR) {
+		error = EISDIR;
+		goto out;
+	} else if (fp->f_vnode->v_type == VFIFO) {
+		error = ESPIPE;
+		goto out;
+	}
+
+	auio.uio_iov = aiov;
+	auio.uio_iovcnt = iovcnt;
+	auio.uio_loffset = offset;
+	auio.uio_resid = count;
+	auio.uio_segflg = UIO_USERSPACE;
+	auio.uio_llimit = MAXOFFSET_T;
+	auio.uio_fmode = fflag;
+	if (count <= copyout_max_cached)
+		auio.uio_extflg = UIO_COPY_CACHED;
+	else
+		auio.uio_extflg = UIO_COPY_DEFAULT;
+
+	error = lx_read_common(fp, &auio, &nread, B_TRUE);
+
+	if (error != 0) {
+		if (nread != 0) {
+			error = 0;
+		} else if (error == EINTR) {
+			ttolxlwp(curthread)->br_syscall_restart = B_TRUE;
+		}
+	}
+out:
+	releasef(fdes);
+	if (aiovlen != 0)
+		kmem_free(aiov, aiovlen);
+	if (error != 0) {
+		return (set_errno(error));
+	}
+	return (nread);
+}
+
+ssize_t
+lx_pwritev(int fdes, void *iovp, int iovcnt, off64_t offset)
+{
+	struct uio auio;
+	struct iovec buf[IOV_MAX_STACK], *aiov = buf;
+	int aiovlen = 0;
+	file_t *fp;
+	ssize_t count;
+	size_t nwrite = 0;
+	int fflag, error = 0;
+
+	if (iovcnt < 0 || iovcnt > IOV_MAX) {
+		return (set_errno(EINVAL));
+	} else if (iovcnt == 0) {
+		return (0);
+	}
+
+	if (iovcnt > IOV_MAX_STACK) {
+		aiovlen = iovcnt * sizeof (iovec_t);
+		aiov = kmem_alloc(aiovlen, KM_SLEEP);
+	}
+	if ((error = lx_iovec_copyin(iovp, iovcnt, aiov, &count)) != 0) {
+		if (aiovlen != 0)
+			kmem_free(aiov, aiovlen);
+		return (set_errno(error));
+	}
+
+	if ((fp = getf(fdes)) == NULL) {
+		if (aiovlen != 0)
+			kmem_free(aiov, aiovlen);
+		return (set_errno(EBADF));
+	}
+	if (((fflag = fp->f_flag) & FWRITE) == 0) {
+		error = EBADF;
+		goto out;
+	}
+	if (fp->f_vnode->v_type == VREG) {
+		u_offset_t fileoff = (u_offset_t)offset;
+
+		if (count == 0)
+			goto out;
+		/*
+		 * Return EINVAL if an invalid offset comes to pread.
+		 * Negative offset from user will cause this error.
+		 */
+		if (fileoff > MAXOFFSET_T) {
+			error = EINVAL;
+			goto out;
+		}
+		/*
+		 * Take appropriate action if we are trying to write above the
+		 * resource limit.
+		 */
+		if (fileoff >= curproc->p_fsz_ctl) {
+			mutex_enter(&curproc->p_lock);
+			(void) rctl_action(rctlproc_legacy[RLIMIT_FSIZE],
+			    curproc->p_rctls, curproc, RCA_UNSAFE_SIGINFO);
+			mutex_exit(&curproc->p_lock);
+
+			error = EFBIG;
+			goto out;
+		}
+		/*
+		 * Don't allow pwritev to cause file sizes to exceed maxoffset.
+		 */
+		if (fileoff == MAXOFFSET_T) {
+			error = EFBIG;
+			goto out;
+		}
+		/*
+		 * Limit offset such that we don't read or write a file beyond
+		 * the maximum offset representable in an off_t structure.
+		 */
+		if (fileoff + count > MAXOFFSET_T)
+			count = (ssize_t)((u_offset_t)MAXOFFSET_T - fileoff);
+	} else if (fp->f_vnode->v_type == VFIFO) {
+		error = ESPIPE;
+		goto out;
+	}
+
+	auio.uio_iov = aiov;
+	auio.uio_iovcnt = iovcnt;
+	auio.uio_loffset = offset;
+	auio.uio_resid = count;
+	auio.uio_segflg = UIO_USERSPACE;
+	auio.uio_llimit = curproc->p_fsz_ctl;
+	auio.uio_fmode = fflag;
+	auio.uio_extflg = UIO_COPY_DEFAULT;
+
+	error = lx_write_common(fp, &auio, &nwrite, B_TRUE);
+
+	if (error != 0) {
+		if (nwrite != 0) {
+			error = 0;
+		} else if (error == EINTR) {
+			ttolxlwp(curthread)->br_syscall_restart = B_TRUE;
+		}
+	}
+out:
+	releasef(fdes);
+	if (aiovlen != 0)
+		kmem_free(aiov, aiovlen);
+	if (error != 0) {
+		return (set_errno(error));
+	}
+	return (nwrite);
+}
+
+ssize_t
+lx_preadv32(int fdes, void *iovp, int iovcnt, uint32_t off_lo, uint32_t off_hi)
+{
+	return (lx_preadv(fdes, iovp, iovcnt, LX_32TO64(off_lo, off_hi)));
+}
+
+ssize_t
+lx_pwritev32(int fdes, void *iovp, int iovcnt, uint32_t off_lo,
+    uint32_t off_hi)
+{
+	return (lx_pwritev(fdes, iovp, iovcnt, LX_32TO64(off_lo, off_hi)));
+}
--- /dev/null	Wed Oct  5 00:32:09 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_sched.c	Wed Oct  5 00:32:09 2016
@@ -0,0 +1,524 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+/*
+ * Copyright 2015 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/cpu.h>
+#include <sys/rtpriocntl.h>
+#include <sys/tspriocntl.h>
+#include <sys/processor.h>
+#include <sys/brand.h>
+#include <sys/lx_sched.h>
+#include <sys/lx_brand.h>
+
+extern int yield();
+extern long priocntl_common(int, procset_t *, int, caddr_t, caddr_t, uio_seg_t);
+
+long
+lx_sched_yield(void)
+{
+	yield();
+
+	return (0);
+}
+
+int
+lx_sched_affinity(int cmd, uintptr_t pid, int len, uintptr_t maskp,
+    int64_t *rval)
+{
+	pid_t		s_pid;
+	id_t		s_tid;
+	kthread_t	*t = curthread;
+	lx_lwp_data_t	*lx_lwp;
+
+	if (cmd != B_GET_AFFINITY_MASK && cmd != B_SET_AFFINITY_MASK)
+		return (set_errno(EINVAL));
+
+	/*
+	 * The caller wants to know how large the mask should be.
+	 */
+	if (cmd == B_GET_AFFINITY_MASK && len == 0) {
+		*rval = sizeof (lx_affmask_t);
+		return (0);
+	}
+
+	/*
+	 * Otherwise, ensure they have a large enough mask.
+	 */
+	if (cmd == B_GET_AFFINITY_MASK && len < sizeof (lx_affmask_t)) {
+		*rval = -1;
+		return (set_errno(EINVAL));
+	}
+
+	if (pid == 0) {
+		s_pid = curproc->p_pid;
+		s_tid = curthread->t_tid;
+	} else if (lx_lpid_to_spair((pid_t)pid, &s_pid, &s_tid) == -1) {
+		return (set_errno(ESRCH));
+	}
+
+	/*
+	 * For now, we only support manipulating threads in the
+	 * same process.
+	 */
+	if (curproc->p_pid != s_pid)
+		return (set_errno(EPERM));
+
+	/*
+	 * We must hold the process lock so that the thread list
+	 * doesn't change while we're looking at it. We'll hold
+	 * the lock until we no longer reference the
+	 * corresponding lwp.
+	 */
+
+	mutex_enter(&curproc->p_lock);
+
+	do {
+		if (t->t_tid == s_tid)
+			break;
+		t = t->t_forw;
+	} while (t != curthread);
+
+	/*
+	 * If the given PID is in the current thread's process,
+	 * then we _must_ find it in the process's thread list.
+	 */
+	ASSERT(t->t_tid == s_tid);
+
+	lx_lwp = t->t_lwp->lwp_brand;
+
+	if (cmd == B_SET_AFFINITY_MASK) {
+		if (copyin_nowatch((void *)maskp, &lx_lwp->br_affinitymask,
+		    sizeof (lx_affmask_t)) != 0) {
+			mutex_exit(&curproc->p_lock);
+			return (set_errno(EFAULT));
+		}
+
+		*rval = 0;
+	} else {
+		if (copyout_nowatch(&lx_lwp->br_affinitymask, (void *)maskp,
+		    sizeof (lx_affmask_t)) != 0) {
+			mutex_exit(&curproc->p_lock);
+			return (set_errno(EFAULT));
+		}
+
+		*rval = sizeof (lx_affmask_t);
+	}
+
+	mutex_exit(&curproc->p_lock);
+	return (0);
+}
+
+long
+lx_sched_setscheduler(l_pid_t pid, int policy, struct lx_sched_param *param)
+{
+	klwp_t *lwp = ttolwp(curthread);
+	procset_t procset;
+	procset_t procset_cid;
+	pcparms_t pcparm;
+	pcinfo_t pcinfo;
+	struct lx_sched_param sched_param;
+	tsparms_t *tsp;
+	int prio, maxupri;
+	int rv;
+
+	if (pid < 0)
+		return (set_errno(ESRCH));
+
+	if ((rv = sched_setprocset(&procset, pid)))
+		return (rv);
+
+	if (copyin(param, &sched_param, sizeof (sched_param)))
+		return (set_errno(EFAULT));
+
+	prio = sched_param.lx_sched_prio;
+
+	if (policy < 0) {
+		/*
+		 * get the class id
+		 */
+		pcparm.pc_cid = PC_CLNULL;
+		(void) do_priocntlsys(PC_GETPARMS, &procset, &pcparm);
+		if (lwp->lwp_errno)
+			return (lwp->lwp_errno);
+
+		/*
+		 * get the current policy
+		 */
+		bzero(&pcinfo, sizeof (pcinfo));
+		pcinfo.pc_cid = pcparm.pc_cid;
+		(void) do_priocntlsys(PC_GETCLINFO, &procset, &pcinfo);
+		if (lwp->lwp_errno)
+			return (lwp->lwp_errno);
+
+		if (strcmp(pcinfo.pc_clname, "TS") == 0) {
+			policy = LX_SCHED_OTHER;
+		} else if (strcmp(pcinfo.pc_clname, "RT") == 0) {
+			policy = ((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs ==
+			    RT_TQINF ? LX_SCHED_FIFO : LX_SCHED_RR;
+		} else {
+			return (set_errno(EINVAL));
+		}
+	}
+
+	bzero(&pcinfo, sizeof (pcinfo));
+	bzero(&pcparm, sizeof (pcparm));
+	setprocset(&procset_cid, POP_AND, P_PID, 0, P_ALL, 0);
+	switch (policy) {
+	case LX_SCHED_FIFO:
+	case LX_SCHED_RR:
+		(void) strcpy(pcinfo.pc_clname, "RT");
+		(void) do_priocntlsys(PC_GETCID, &procset_cid, &pcinfo);
+		if (lwp->lwp_errno)
+			return (lwp->lwp_errno);
+
+		if (prio < 0 ||
+		    prio > ((rtinfo_t *)pcinfo.pc_clinfo)->rt_maxpri)
+			return (set_errno(EINVAL));
+		pcparm.pc_cid = pcinfo.pc_cid;
+		((rtparms_t *)pcparm.pc_clparms)->rt_pri = prio;
+		((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs =
+		    policy == LX_SCHED_RR ? RT_TQDEF : RT_TQINF;
+		break;
+
+	case LX_SCHED_OTHER:
+		(void) strcpy(pcinfo.pc_clname, "TS");
+		(void) do_priocntlsys(PC_GETCID, &procset_cid, &pcinfo);
+		if (lwp->lwp_errno)
+			return (lwp->lwp_errno);
+
+		maxupri = ((tsinfo_t *)pcinfo.pc_clinfo)->ts_maxupri;
+		if (prio > maxupri || prio < -maxupri)
+			return (set_errno(EINVAL));
+
+		pcparm.pc_cid = pcinfo.pc_cid;
+		tsp = (tsparms_t *)pcparm.pc_clparms;
+		tsp->ts_upri = prio;
+		tsp->ts_uprilim = TS_NOCHANGE;
+		break;
+
+	default:
+		return (set_errno(EINVAL));
+	}
+
+	/*
+	 * finally set scheduling policy and parameters
+	 */
+	(void) do_priocntlsys(PC_SETPARMS, &procset, &pcparm);
+
+	return (0);
+}
+
+long
+lx_sched_getscheduler(l_pid_t pid)
+{
+	klwp_t *lwp = ttolwp(curthread);
+	procset_t procset;
+	pcparms_t pcparm;
+	pcinfo_t pcinfo;
+	int policy;
+	int rv;
+
+	if (pid < 0)
+		return (set_errno(ESRCH));
+
+	if ((rv = sched_setprocset(&procset, pid)))
+		return (rv);
+
+	/*
+	 * get the class id
+	 */
+	pcparm.pc_cid = PC_CLNULL;
+	(void) do_priocntlsys(PC_GETPARMS, &procset, &pcparm);
+	if (lwp->lwp_errno)
+		return (lwp->lwp_errno);
+
+	/*
+	 * get the class info and identify the equivalent linux policy
+	 */
+	bzero(&pcinfo, sizeof (pcinfo));
+	pcinfo.pc_cid = pcparm.pc_cid;
+	(void) do_priocntlsys(PC_GETCLINFO, &procset, &pcinfo);
+	if (lwp->lwp_errno)
+		return (lwp->lwp_errno);
+
+	if (strcmp(pcinfo.pc_clname, "TS") == 0)
+		policy = LX_SCHED_OTHER;
+	else if (strcmp(pcinfo.pc_clname, "RT") == 0)
+		policy = ((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs ==
+		    RT_TQINF ? LX_SCHED_FIFO : LX_SCHED_RR;
+	else
+		policy = set_errno(EINVAL);
+
+	return (policy);
+}
+
+long
+lx_sched_setparam(l_pid_t pid, struct lx_sched_param *param)
+{
+	klwp_t *lwp = ttolwp(curthread);
+	procset_t procset;
+	procset_t procset_cid;
+	pcparms_t pcparm;
+	pcinfo_t pcinfo;
+	struct lx_sched_param sched_param;
+	tsparms_t *tsp;
+	int policy;
+	int prio, maxupri;
+	int rv;
+
+	if (pid < 0)
+		return (set_errno(ESRCH));
+
+	if ((rv = sched_setprocset(&procset, pid)))
+		return (rv);
+
+	if (copyin(param, &sched_param, sizeof (sched_param)))
+		return (set_errno(EFAULT));
+
+	prio = sched_param.lx_sched_prio;
+
+	/*
+	 * get the class id
+	 */
+	pcparm.pc_cid = PC_CLNULL;
+	(void) do_priocntlsys(PC_GETPARMS, &procset, &pcparm);
+	if (lwp->lwp_errno)
+		return (lwp->lwp_errno);
+
+	/*
+	 * get the current policy
+	 */
+	bzero(&pcinfo, sizeof (pcinfo));
+	pcinfo.pc_cid = pcparm.pc_cid;
+	(void) do_priocntlsys(PC_GETCLINFO, &procset, &pcinfo);
+	if (lwp->lwp_errno)
+		return (lwp->lwp_errno);
+
+	if (strcmp(pcinfo.pc_clname, "TS") == 0)
+		policy = LX_SCHED_OTHER;
+	else if (strcmp(pcinfo.pc_clname, "RT") == 0)
+		policy = ((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs ==
+		    RT_TQINF ? LX_SCHED_FIFO : LX_SCHED_RR;
+	else
+		return (set_errno(EINVAL));
+
+	bzero(&pcinfo, sizeof (pcinfo));
+	bzero(&pcparm, sizeof (pcparm));
+	setprocset(&procset_cid, POP_AND, P_PID, 0, P_ALL, 0);
+	switch (policy) {
+	case LX_SCHED_FIFO:
+	case LX_SCHED_RR:
+		(void) strcpy(pcinfo.pc_clname, "RT");
+		(void) do_priocntlsys(PC_GETCID, &procset_cid, &pcinfo);
+		if (lwp->lwp_errno)
+			return (lwp->lwp_errno);
+
+		if (prio < 0 ||
+		    prio > ((rtinfo_t *)pcinfo.pc_clinfo)->rt_maxpri)
+			return (set_errno(EINVAL));
+		pcparm.pc_cid = pcinfo.pc_cid;
+		((rtparms_t *)pcparm.pc_clparms)->rt_pri = prio;
+		((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs =
+		    policy == LX_SCHED_RR ? RT_TQDEF : RT_TQINF;
+		break;
+
+	case LX_SCHED_OTHER:
+		(void) strcpy(pcinfo.pc_clname, "TS");
+		(void) do_priocntlsys(PC_GETCID, &procset_cid, &pcinfo);
+		if (lwp->lwp_errno)
+			return (lwp->lwp_errno);
+
+		maxupri = ((tsinfo_t *)pcinfo.pc_clinfo)->ts_maxupri;
+		if (prio > maxupri || prio < -maxupri)
+			return (set_errno(EINVAL));
+
+		pcparm.pc_cid = pcinfo.pc_cid;
+		tsp = (tsparms_t *)pcparm.pc_clparms;
+		tsp->ts_upri = prio;
+		tsp->ts_uprilim = TS_NOCHANGE;
+		break;
+
+	default:
+		return (set_errno(EINVAL));
+	}
+
+	/*
+	 * finally set scheduling policy and parameters
+	 */
+	(void) do_priocntlsys(PC_SETPARMS, &procset, &pcparm);
+
+	return (0);
+}
+
+long
+lx_sched_getparam(l_pid_t pid, struct lx_sched_param *param)
+{
+	klwp_t *lwp = ttolwp(curthread);
+	struct lx_sched_param local_param;
+	procset_t procset;
+	pcparms_t pcparm;
+	pcinfo_t pcinfo;
+	tsinfo_t *tsi;
+	int prio, scale;
+	int rv;
+
+	if (pid < 0)
+		return (set_errno(ESRCH));
+
+	if ((rv = sched_setprocset(&procset, pid)))
+		return (rv);
+
+	/*
+	 * get the class id
+	 */
+	pcparm.pc_cid = PC_CLNULL;
+	(void) do_priocntlsys(PC_GETPARMS, &procset, &pcparm);
+	if (lwp->lwp_errno)
+		return (lwp->lwp_errno);
+
+	/*
+	 * get the class info and identify the equivalent linux policy
+	 */
+	bzero(&pcinfo, sizeof (pcinfo));
+	pcinfo.pc_cid = pcparm.pc_cid;
+	(void) do_priocntlsys(PC_GETCLINFO, &procset, &pcinfo);
+	if (lwp->lwp_errno)
+		return (lwp->lwp_errno);
+
+	bzero(&local_param, sizeof (local_param));
+	if (strcmp(pcinfo.pc_clname, "TS") == 0) {
+		/*
+		 * I don't know if we need to do this, coz it can't be
+		 * changed from zero anyway.....
+		 */
+		tsi = (tsinfo_t *)pcinfo.pc_clinfo;
+		prio = ((tsparms_t *)pcparm.pc_clparms)->ts_upri;
+		scale = tsi->ts_maxupri;
+		if (scale == 0)
+			local_param.lx_sched_prio = 0;
+		else
+			local_param.lx_sched_prio = -(prio * 20) / scale;
+	} else if (strcmp(pcinfo.pc_clname, "RT") == 0) {
+		local_param.lx_sched_prio =
+		    ((rtparms_t *)pcparm.pc_clparms)->rt_pri;
+	} else {
+		rv = set_errno(EINVAL);
+	}
+
+	if (rv == 0)
+		if (copyout(&local_param, param, sizeof (local_param)))
+			return (set_errno(EFAULT));
+
+	return (rv);
+}
+
+long
+lx_sched_rr_get_interval(l_pid_t pid, struct timespec *ival)
+{
+	klwp_t *lwp = ttolwp(curthread);
+	struct timespec interval;
+	procset_t procset;
+	pcparms_t pcparm;
+	pcinfo_t pcinfo;
+	int rv;
+
+	if (pid < 0)
+		return (set_errno(ESRCH));
+
+	if ((rv = sched_setprocset(&procset, pid)))
+		return (rv);
+
+	/*
+	 * get the class id
+	 */
+	pcparm.pc_cid = PC_CLNULL;
+	(void) do_priocntlsys(PC_GETPARMS, &procset, &pcparm);
+	if (lwp->lwp_errno)
+		return (lwp->lwp_errno);
+
+	/*
+	 * get the class info and identify the equivalent linux policy
+	 */
+	setprocset(&procset, POP_AND, P_PID, 0, P_ALL, 0);
+	bzero(&pcinfo, sizeof (pcinfo));
+	(void) strcpy(pcinfo.pc_clname, "RT");
+	(void) do_priocntlsys(PC_GETCID, &procset, &pcinfo);
+	if (lwp->lwp_errno)
+		return (lwp->lwp_errno);
+
+	if (pcparm.pc_cid == pcinfo.pc_cid &&
+	    ((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs != RT_TQINF) {
+		interval.tv_sec = ((rtparms_t *)pcparm.pc_clparms)->rt_tqsecs;
+		interval.tv_nsec = ((rtparms_t *)pcparm.pc_clparms)->rt_tqnsecs;
+
+		if (copyout(&interval, ival, sizeof (interval)))
+			return (set_errno(EFAULT));
+
+		return (0);
+	}
+
+	return (set_errno(EINVAL));
+}
+
+int
+sched_setprocset(procset_t *procset, l_pid_t pid)
+{
+	id_t lid, rid;
+	idtype_t lidtype, ridtype;
+
+	/*
+	 * define the target lwp
+	 */
+	if (pid == 0) {
+		ridtype = P_ALL;
+		lidtype = P_PID;
+		rid = 0;
+		lid = P_MYID;
+	} else {
+		if (lx_lpid_to_spair(pid, &pid, &lid) < 0)
+			return (set_errno(ESRCH));
+		if (pid != curproc->p_pid)
+			return (set_errno(ESRCH));
+		rid = 0;
+		ridtype = P_ALL;
+		lidtype = P_LWPID;
+	}
+	setprocset(procset, POP_AND, lidtype, lid, ridtype, rid);
+
+	return (0);
+}
+
+long
+do_priocntlsys(int cmd, procset_t *procset, void *arg)
+{
+	return (priocntl_common(PC_VERSION, procset, cmd, (caddr_t)arg, 0,
+	    UIO_SYSSPACE));
+}
--- /dev/null	Wed Oct  5 00:32:09 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_socket.c	Wed Oct  5 00:32:09 2016
@@ -0,0 +1,3966 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#include <sys/errno.h>
+#include <sys/systm.h>
+#include <sys/types.h>
+#include <sys/cmn_err.h>
+#include <sys/sockio.h>
+#include <sys/thread.h>
+#include <sys/stropts.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/kmem.h>
+#include <sys/un.h>
+#include <sys/sunddi.h>
+#include <sys/cred.h>
+#include <sys/ucred.h>
+#include <sys/model.h>
+#include <sys/brand.h>
+#include <sys/vmsystm.h>
+#include <sys/limits.h>
+#include <sys/fcntl.h>
+#include <sys/sysmacros.h>
+#include <netpacket/packet.h>
+#include <sockcommon.h>
+#include <socktpi_impl.h>
+#include <netinet/udp.h>
+#include <sys/sdt.h>
+#include <netinet/tcp.h>
+#include <netinet/igmp.h>
+#include <netinet/icmp6.h>
+#include <lx_errno.h>
+
+#include <sys/lx_brand.h>
+#include <sys/lx_socket.h>
+#include <sys/lx_types.h>
+#include <sys/lx_impl.h>
+
+/* From uts/common/fs/sockfs/socksyscalls.c */
+extern int listen(int, int, int);
+extern int shutdown(int, int, int);
+
+typedef struct lx_ucred {
+	pid_t		lxu_pid;
+	lx_uid_t	lxu_uid;
+	lx_gid_t	lxu_gid;
+} lx_ucred_t;
+
+typedef struct lx_socket_aux_data
+{
+	kmutex_t lxsad_lock;
+	enum lxsad_status_t {
+		LXSS_NONE = 0,
+		LXSS_CONNECTING,
+		LXSS_CONNECTED
+	} lxsad_status;
+	boolean_t lxsad_stream_cred;
+} lx_socket_aux_data_t;
+
+static lx_socket_aux_data_t *lx_sad_acquire(vnode_t *);
+
+/* VSD key for lx-specific socket information */
+static uint_t lx_socket_vsd = 0;
+
+/* Convenience enum to enforce translation direction */
+typedef enum lx_xlate_dir {
+	SUNOS_TO_LX,
+	LX_TO_SUNOS
+} lx_xlate_dir_t;
+
+/* enum for getpeername/getsockname handling */
+typedef enum lx_getname_type {
+	LX_GETPEERNAME,
+	LX_GETSOCKNAME
+} lx_getname_type_t;
+
+/*
+ * What follows are a series of tables we use to translate Linux constants
+ * into equivalent Illumos constants and back again.  I wish this were
+ * cleaner, more programmatic, and generally nicer.  Sadly, life is messy,
+ * and Unix networking even more so.
+ */
+static const int ltos_family[LX_AF_MAX + 1] =  {
+	AF_UNSPEC,		/* LX_AF_UNSPEC		*/
+	AF_UNIX,		/* LX_AF_UNIX		*/
+	AF_INET,		/* LX_AF_INET		*/
+	AF_NOTSUPPORTED,	/* LX_AF_AX25		*/
+	AF_NOTSUPPORTED,	/* LX_AF_IPX		*/
+	AF_NOTSUPPORTED,	/* LX_AF_APPLETALK	*/
+	AF_NOTSUPPORTED,	/* LX_AF_NETROM		*/
+	AF_NOTSUPPORTED,	/* LX_AF_BRIDGE		*/
+	AF_NOTSUPPORTED,	/* LX_AF_ATMPVC		*/
+	AF_NOTSUPPORTED,	/* LX_AF_X25		*/
+	AF_INET6,		/* LX_AF_INET6		*/
+	AF_NOTSUPPORTED,	/* LX_AF_ROSE		*/
+	AF_NOTSUPPORTED,	/* LX_AF_DECNET		*/
+	AF_NOTSUPPORTED,	/* LX_AF_NETBEUI	*/
+	AF_NOTSUPPORTED,	/* LX_AF_SECURITY	*/
+	AF_NOTSUPPORTED,	/* LX_AF_KEY		*/
+	AF_LX_NETLINK,		/* LX_AF_NETLINK	*/
+	AF_PACKET,		/* LX_AF_PACKET		*/
+	AF_NOTSUPPORTED,	/* LX_AF_ASH		*/
+	AF_NOTSUPPORTED,	/* LX_AF_ECONET		*/
+	AF_NOTSUPPORTED,	/* LX_AF_ATMSVC		*/
+	AF_NOTSUPPORTED,	/* LX_AF_RDS		*/
+	AF_NOTSUPPORTED,	/* LX_AF_SNA		*/
+	AF_NOTSUPPORTED,	/* LX_AF_IRDA		*/
+	AF_NOTSUPPORTED,	/* LX_AF_PPOX		*/
+	AF_NOTSUPPORTED,	/* LX_AF_WANPIPE	*/
+	AF_NOTSUPPORTED,	/* LX_AF_LLC		*/
+	AF_NOTSUPPORTED,	/* NONE			*/
+	AF_NOTSUPPORTED,	/* NONE			*/
+	AF_NOTSUPPORTED,	/* LX_AF_CAN		*/
+	AF_NOTSUPPORTED,	/* LX_AF_TIPC		*/
+	AF_NOTSUPPORTED,	/* LX_AF_BLUETOOTH	*/
+	AF_NOTSUPPORTED,	/* LX_AF_IUCV		*/
+	AF_NOTSUPPORTED		/* LX_AF_RXRPC		*/
+				/* LX_AF_ISDN		*/
+				/* LX_AF_PHONET		*/
+				/* LX_AF_IEEE802154	*/
+				/* LX_AF_CAIF		*/
+				/* LX_AF_ALG		*/
+				/* LX_AF_NFC		*/
+				/* LX_AF_VSOCK		*/
+};
+
+static const int stol_family[LX_AF_MAX + 1] =  {
+	AF_UNSPEC,		/* AF_UNSPEC		*/
+	AF_UNIX,		/* AF_UNIX		*/
+	AF_INET,		/* AF_INET		*/
+	AF_NOTSUPPORTED,	/* AF_IMPLINK		*/
+	AF_NOTSUPPORTED,	/* AF_PUP		*/
+	AF_NOTSUPPORTED,	/* AF_CHAOS		*/
+	AF_NOTSUPPORTED,	/* AF_NS		*/
+	AF_NOTSUPPORTED,	/* AF_NBS		*/
+	AF_NOTSUPPORTED,	/* AF_ECMA		*/
+	AF_NOTSUPPORTED,	/* AF_DATAKIT		*/
+	AF_NOTSUPPORTED,	/* AF_CCITT		*/
+	AF_NOTSUPPORTED,	/* AF_SNA		*/
+	AF_NOTSUPPORTED,	/* AF_DECNET		*/
+	AF_NOTSUPPORTED,	/* AF_DLI		*/
+	AF_NOTSUPPORTED,	/* AF_LAT		*/
+	AF_NOTSUPPORTED,	/* AF_HYLINK		*/
+	AF_NOTSUPPORTED,	/* AF_APPLETALK		*/
+	AF_NOTSUPPORTED,	/* AF_NIT		*/
+	AF_NOTSUPPORTED,	/* AF_802		*/
+	AF_NOTSUPPORTED,	/* AF_OSI		*/
+	AF_NOTSUPPORTED,	/* AF_X25		*/
+	AF_NOTSUPPORTED,	/* AF_OSINET		*/
+	AF_NOTSUPPORTED,	/* AF_GOSIP		*/
+	AF_NOTSUPPORTED,	/* AF_IPX		*/
+	AF_NOTSUPPORTED,	/* AF_ROUTE		*/
+	AF_NOTSUPPORTED,	/* AF_LINK		*/
+	LX_AF_INET6,		/* AF_INET6		*/
+	AF_NOTSUPPORTED,	/* AF_KEY		*/
+	AF_NOTSUPPORTED,	/* AF_NCA		*/
+	AF_NOTSUPPORTED,	/* AF_POLICY		*/
+	AF_NOTSUPPORTED,	/* AF_INET_OFFLOAD	*/
+	AF_NOTSUPPORTED,	/* AF_TRILL		*/
+	LX_AF_PACKET,		/* AF_PACKET		*/
+	LX_AF_NETLINK		/* AF_LX_NETLINK	*/
+};
+
+#define	LTOS_FAMILY(d) ((d) <= LX_AF_MAX ? ltos_family[(d)] : AF_INVAL)
+#define	STOL_FAMILY(d) ((d) <= LX_AF_MAX ? stol_family[(d)] : AF_INVAL)
+
+
+static const int ltos_socktype[LX_SOCK_PACKET + 1] = {
+	SOCK_NOTSUPPORTED, SOCK_STREAM, SOCK_DGRAM, SOCK_RAW,
+	SOCK_RDM, SOCK_SEQPACKET, SOCK_NOTSUPPORTED, SOCK_NOTSUPPORTED,
+	SOCK_NOTSUPPORTED, SOCK_NOTSUPPORTED, SOCK_NOTSUPPORTED
+};
+
+static const int stol_socktype[SOCK_SEQPACKET + 1] = {
+	SOCK_NOTSUPPORTED, LX_SOCK_DGRAM, LX_SOCK_STREAM, SOCK_NOTSUPPORTED,
+	LX_SOCK_RAW, LX_SOCK_RDM, LX_SOCK_SEQPACKET
+};
+
+#define	LTOS_SOCKTYPE(t)	\
+	((t) <= LX_SOCK_PACKET ? ltos_socktype[(t)] : SOCK_INVAL)
+#define	STOL_SOCKTYPE(t)	\
+	((t) <= SOCK_SEQPACKET ? stol_socktype[(t)] : SOCK_INVAL)
+
+
+/*
+ * This string is used to prefix all abstract namespace Unix sockets, ie all
+ * abstract namespace sockets are converted to regular sockets in the /tmp
+ * directory with .ABSK_ prefixed to their names.
+ */
+#define	ABST_PRFX "/tmp/.ABSK_"
+#define	ABST_PRFX_LEN (sizeof (ABST_PRFX) - 1)
+
+#define	DATAFILT	"datafilt"
+
+typedef enum {
+	lxa_none,
+	lxa_abstract,
+	lxa_devlog
+} lx_addr_type_t;
+
+static int
+ltos_pkt_proto(int protocol)
+{
+	switch (ntohs(protocol)) {
+	case LX_ETH_P_802_2:
+		return (ETH_P_802_2);
+	case LX_ETH_P_IP:
+		return (ETH_P_IP);
+	case LX_ETH_P_ARP:
+		return (ETH_P_ARP);
+	case LX_ETH_P_IPV6:
+		return (ETH_P_IPV6);
+	case LX_ETH_P_ALL:
+	case LX_ETH_P_802_3:
+		return (ETH_P_ALL);
+	default:
+		return (-1);
+	}
+}
+
+
+typedef struct lx_flag_map {
+	enum {
+		LXFM_MAP,
+		LXFM_IGNORE,
+		LXFM_UNSUP
+	} lxfm_action;
+	int lxfm_sunos_flag;
+	int lxfm_linux_flag;
+	char *lxfm_name;
+} lx_flag_map_t;
+
+static lx_flag_map_t lx_flag_map_tbl[] = {
+	{ LXFM_MAP,	MSG_OOB,		LX_MSG_OOB,		NULL },
+	{ LXFM_MAP,	MSG_PEEK,		LX_MSG_PEEK,		NULL },
+	{ LXFM_MAP,	MSG_DONTROUTE,		LX_MSG_DONTROUTE,	NULL },
+	{ LXFM_MAP,	MSG_CTRUNC,		LX_MSG_CTRUNC,		NULL },
+	{ LXFM_MAP,	MSG_TRUNC,		LX_MSG_TRUNC,		NULL },
+	{ LXFM_MAP,	MSG_DONTWAIT,		LX_MSG_DONTWAIT,	NULL },
+	{ LXFM_MAP,	MSG_EOR,		LX_MSG_EOR,		NULL },
+	{ LXFM_MAP,	MSG_WAITALL,		LX_MSG_WAITALL,		NULL },
+	/* MSG_CONFIRM is safe to ignore */
+	{ LXFM_IGNORE,	0,			LX_MSG_CONFIRM,		NULL },
+	/*
+	 * The NOSIGNAL and CMSG_CLOEXEC flags are handled by the emulation
+	 * outside of the flag-conversion routine.
+	 */
+	{ LXFM_IGNORE,	0,			LX_MSG_NOSIGNAL,	NULL },
+	{ LXFM_IGNORE,	0,			LX_MSG_CMSG_CLOEXEC,	NULL },
+	{ LXFM_UNSUP,	LX_MSG_PROXY,		0,	"MSG_PROXY" },
+	{ LXFM_UNSUP,	LX_MSG_FIN,		0,	"MSG_FIN" },
+	{ LXFM_UNSUP,	LX_MSG_SYN,		0,	"MSG_SYN" },
+	{ LXFM_UNSUP,	LX_MSG_RST,		0,	"MSG_RST" },
+	{ LXFM_UNSUP,	LX_MSG_ERRQUEUE,	0,	"MSG_ERRQUEUE" },
+	{ LXFM_UNSUP,	LX_MSG_MORE,		0,	"MSG_MORE" },
+	{ LXFM_UNSUP,	LX_MSG_WAITFORONE,	0,	"MSG_WAITFORONE" },
+	{ LXFM_UNSUP,	LX_MSG_FASTOPEN,	0,	"MSG_FASTOPEN" },
+};
+
+#define	LX_FLAG_MAP_MAX	\
+	(sizeof (lx_flag_map_tbl) / sizeof (lx_flag_map_tbl[0]))
+
+#define	LX_UNSUP_BUFSZ	64
+
+static int
+lx_xlate_sock_flags(int inflags, lx_xlate_dir_t dir)
+{
+	int i, outflags = 0;
+	char buf[LX_UNSUP_BUFSZ];
+
+	VERIFY(dir == SUNOS_TO_LX || dir == LX_TO_SUNOS);
+
+	for (i = 0; i < LX_FLAG_MAP_MAX; i++) {
+		lx_flag_map_t *map = &lx_flag_map_tbl[i];
+		int match, out;
+
+		if (dir == SUNOS_TO_LX) {
+			match = inflags & map->lxfm_sunos_flag;
+			out = map->lxfm_linux_flag;
+		} else {
+			match = inflags & map->lxfm_linux_flag;
+			out = map->lxfm_sunos_flag;
+		}
+		switch (map->lxfm_action) {
+		case LXFM_MAP:
+			if (match != 0) {
+				inflags &= ~(match);
+				outflags |= out;
+			}
+			break;
+		case LXFM_IGNORE:
+			if (match != 0) {
+				inflags &= ~(match);
+			}
+			break;
+		case LXFM_UNSUP:
+			if (match != 0) {
+				(void) snprintf(buf, LX_UNSUP_BUFSZ,
+				    "unsupported sock flag %s", map->lxfm_name);
+				lx_unsupported(buf);
+			}
+		}
+	}
+	if (inflags != 0) {
+		(void) snprintf(buf, LX_UNSUP_BUFSZ,
+		    "unsupported sock flags 0x%08x", inflags);
+		lx_unsupported(buf);
+	}
+
+	return (outflags);
+}
+
+typedef enum lx_sun_type {
+	LX_SUN_NORMAL,
+	LX_SUN_ABSTRACT,
+} lx_sun_type_t;
+
+static void
+ltos_sockaddr_ux(const struct sockaddr *inaddr, const socklen_t inlen,
+    struct sockaddr **outaddr, socklen_t *outlen, lx_sun_type_t *sun_type)
+{
+	struct sockaddr_un buf;
+	/* Calculate size of (sun_family + any padding) in sockaddr */
+	int sizediff = (sizeof (buf) - sizeof (buf.sun_path));
+	int len = inlen - sizediff;
+
+	VERIFY(len > 0);
+	VERIFY(len <= sizeof (buf.sun_path));
+	bzero(&buf, sizeof (buf));
+
+	if (inaddr->sa_data[0] == '\0') {
+		/*
+		 * Linux supports abstract Unix sockets, which are simply
+		 * sockets that do not exist on the file system.  These sockets
+		 * are denoted by beginning the path with a NULL character. To
+		 * support these, we strip out the leading NULL character and
+		 * change the path to point to a real place in /tmp directory,
+		 * by prepending ABST_PRFX and replacing all illegal characters
+		 * with * '_'.
+		 *
+		 * Since these sockets are supposed to exist outside the
+		 * filesystem, they must be cleaned up after use.  This removal
+		 * is performed during bind().
+		 */
+		int idx, odx;
+
+		/* Add our abstract prefix */
+		(void) strcpy(buf.sun_path, ABST_PRFX);
+		for (idx = 1, odx = ABST_PRFX_LEN;
+		    idx < len && odx < sizeof (buf.sun_path);
+		    idx++, odx++) {
+			char c = inaddr->sa_data[idx];
+			if (c == '\0' || c == '/') {
+				buf.sun_path[odx] = '_';
+			} else {
+				buf.sun_path[odx] = c;
+			}
+		}
+
+		/*
+		 * Since abstract socket addresses might not be NUL terminated,
+		 * we must explicitly NUL terminate the translated path.
+		 * Care is taken not to overflow the buffer.
+		 */
+		if (odx == sizeof (buf.sun_path)) {
+			buf.sun_path[odx - 1] = '\0';
+		} else {
+			buf.sun_path[odx] = '\0';
+		}
+
+		if (sun_type != NULL) {
+			*sun_type = LX_SUN_ABSTRACT;
+		}
+	} else {
+		/* Copy the address directly, minding termination */
+		(void) strncpy(buf.sun_path, inaddr->sa_data, len);
+		len = strnlen(buf.sun_path, len);
+		if (len == sizeof (buf.sun_path)) {
+			buf.sun_path[len - 1] = '\0';
+		} else {
+			VERIFY(len < sizeof (buf.sun_path));
+			buf.sun_path[len] = '\0';
+		}
+
+		if (sun_type != NULL) {
+			*sun_type = LX_SUN_NORMAL;
+		}
+	}
+	buf.sun_family = AF_UNIX;
+	*outlen = strlen(buf.sun_path) + 1 + sizediff;
+	VERIFY(*outlen <= sizeof (struct sockaddr_un));
+
+	*outaddr = kmem_alloc(*outlen, KM_SLEEP);
+	bcopy(&buf, *outaddr, *outlen);
+}
+
+/*
+ * Copy in a Linux-native socket address from userspace and convert it into
+ * illumos format.  When successful, it will allocate an appropriately sized
+ * struct to be freed by the caller.
+ */
+static long
+ltos_sockaddr_copyin(const struct sockaddr *inaddr, const socklen_t inlen,
+    struct sockaddr **outaddr, socklen_t *outlen, lx_sun_type_t *sun_type)
+{
+	sa_family_t family;
+	struct sockaddr *laddr;
+	struct sockaddr_ll *sal;
+	int proto, error = 0;
+
+	VERIFY(inaddr != NULL);
+
+	if (inlen < sizeof (sa_family_t) ||
+	    inlen > sizeof (struct sockaddr_storage)) {
+		return (EINVAL);
+	}
+	laddr = kmem_alloc(inlen, KM_SLEEP);
+	if (copyin(inaddr, laddr, inlen) != 0) {
+		kmem_free(laddr, inlen);
+		return (EFAULT);
+	}
+
+	family = LTOS_FAMILY(laddr->sa_family);
+	switch (family) {
+		case (sa_family_t)AF_NOTSUPPORTED:
+			error = EPROTONOSUPPORT;
+			break;
+
+		case (sa_family_t)AF_INVAL:
+			error = EAFNOSUPPORT;
+			break;
+
+		case AF_UNIX:
+			if (inlen < sizeof (sa_family_t) + 2 ||
+			    inlen > sizeof (struct sockaddr_un)) {
+				error = EINVAL;
+				break;
+			}
+			ltos_sockaddr_ux(laddr, inlen, outaddr, outlen,
+			    sun_type);
+
+			/* AF_UNIX bypasses the standard copy logic */
+			kmem_free(laddr, inlen);
+			return (0);
+
+		case AF_PACKET:
+			if (inlen < sizeof (struct sockaddr_ll)) {
+				error = EINVAL;
+				break;
+			}
+			*outlen = sizeof (struct sockaddr_ll);
+
+			/* sll_protocol must be translated */
+			/* LINTED: alignment */
+			sal = (struct sockaddr_ll *)laddr;
+			proto = ltos_pkt_proto(sal->sll_protocol);
+			if (proto < 0) {
+				error = EINVAL;
+			}
+			sal->sll_protocol = proto;
+			break;
+
+		case AF_INET:
+			if (inlen < sizeof (struct sockaddr)) {
+				error = EINVAL;
+				break;
+			}
+			*outlen = sizeof (struct sockaddr);
+			break;
+
+		case AF_INET6:
+			/*
+			 * The illumos sockaddr_in6 has one more 32-bit field
+			 * than the Linux version.  We simply zero that field
+			 * via kmem_zalloc.
+			 */
+			if (inlen < sizeof (lx_sockaddr_in6_t)) {
+				error = EINVAL;
+				break;
+			}
+			*outlen = sizeof (struct sockaddr_in6);
+			*outaddr = (struct sockaddr *)kmem_zalloc(*outlen,
+			    KM_SLEEP);
+			bcopy(laddr, *outaddr, sizeof (lx_sockaddr_in6_t));
+			(*outaddr)->sa_family = AF_INET6;
+			/* AF_INET6 bypasses the standard copy logic */
+			kmem_free(laddr, inlen);
+			return (0);
+
+		default:
+			*outlen = inlen;
+	}
+
+	if (error == 0) {
+		/*
+		 * For most address families, just copying into a sockaddr of
+		 * the correct size and updating sa_family is adequate.
+		 */
+		VERIFY(inlen >= *outlen);
+
+		*outaddr = (struct sockaddr *)kmem_zalloc(*outlen, KM_SLEEP);
+		bcopy(laddr, *outaddr, *outlen);
+		(*outaddr)->sa_family = family;
+	}
+	kmem_free(laddr, inlen);
+	return (error);
+}
+
+/*
+ * Convert an illumos-native socket address into Linux format and copy it out
+ * to userspace.
+ */
+static long
+stol_sockaddr_copyout(struct sockaddr *inaddr, socklen_t inlen,
+    struct sockaddr *outaddr, void *outlenp, socklen_t orig)
+{
+	socklen_t size = inlen;
+	struct sockaddr_storage buf;
+	struct sockaddr *bufaddr;
+
+	/*
+	 * Either we were passed a valid sockaddr (with length) or the length
+	 * is set to 0.
+	 */
+	VERIFY(inaddr != NULL || inlen == 0);
+
+	if (inlen == 0) {
+		goto finish;
+	}
+
+
+	switch (inaddr->sa_family) {
+	case AF_INET:
+		if (inlen != sizeof (struct sockaddr)) {
+			return (EINVAL);
+		}
+		break;
+
+	case AF_INET6:
+		if (inlen != sizeof (struct sockaddr_in6)) {
+			return (EINVAL);
+		}
+		/*
+		 * The linux sockaddr_in6 is shorter than illumos.
+		 * Truncate the extra field on the way out.
+		 */
+		size = (sizeof (lx_sockaddr_in6_t));
+		inlen = (sizeof (lx_sockaddr_in6_t));
+		break;
+
+	case AF_UNIX:
+		if (inlen > sizeof (struct sockaddr_un)) {
+			return (EINVAL);
+		}
+
+		/*
+		 * On Linux an empty AF_UNIX address is returned as NULL, which
+		 * means setting the returned length to only encompass the
+		 * address family part of the buffer. However, some code also
+		 * references the address portion of the buffer and uses it,
+		 * even though the returned length has been shortened. Thus, we
+		 * clear the buffer to ensure that the address portion is NULL.
+		 */
+		if (inaddr->sa_data[0] == '\0') {
+			bzero(&buf, sizeof (buf));
+			inlen = sizeof (inaddr->sa_family);
+		}
+		break;
+
+	case (sa_family_t)AF_NOTSUPPORTED:
+		return (EPROTONOSUPPORT);
+
+	case (sa_family_t)AF_INVAL:
+		return (EAFNOSUPPORT);
+
+	default:
+		break;
+	}
+
+	/*
+	 * The input should be smaller than sockaddr_storage, the largest
+	 * sockaddr we support.
+	 */
+	VERIFY(inlen <= sizeof (buf));
+
+	bufaddr = (struct sockaddr *)&buf;
+	bcopy(inaddr, bufaddr, inlen);
+	bufaddr->sa_family = STOL_FAMILY(bufaddr->sa_family);
+
+	/*
+	 * It is possible that userspace passed us a smaller buffer than we
+	 * hope to output.  When this is the case, we will truncate our output
+	 * to the max size of their buffer but report the true size of the
+	 * sockaddr when outputting the outlen value.
+	 */
+	size = (orig < size) ? orig : size;
+
+	if (copyout(bufaddr, outaddr, size) != 0) {
+		return (EFAULT);
+	}
+
+finish:
+#if defined(_LP64)
+	if (get_udatamodel() != DATAMODEL_NATIVE) {
+		int32_t len32 = (int32_t)inlen;
+		if (copyout(&len32, outlenp, sizeof (len32)) != 0) {
+			return (EFAULT);
+		}
+	} else
+#endif /* defined(_LP64) */
+	{
+		if (copyout(&inlen, outlenp, sizeof (inlen)) != 0) {
+			return (EFAULT);
+		}
+	}
+
+	return (0);
+}
+
+typedef struct lx_cmsg_xlate {
+	int lcx_sunos_level;
+	int lcx_sunos_type;
+	int (*lcx_stol_conv)(struct cmsghdr *, struct cmsghdr *);
+	int lcx_linux_level;
+	int lcx_linux_type;
+	int (*lcx_ltos_conv)(struct cmsghdr *, struct cmsghdr *);
+} lx_cmsg_xlate_t;
+
+static int cmsg_conv_generic(struct cmsghdr *, struct cmsghdr *);
+static int stol_conv_ucred(struct cmsghdr *, struct cmsghdr *);
+static int ltos_conv_ucred(struct cmsghdr *, struct cmsghdr *);
+static int stol_conv_recvttl(struct cmsghdr *, struct cmsghdr *);
+
+/*
+ * Table describing SunOS <-> Linux cmsg translation mappings.
+ * Certain types (IP_RECVTTL) are only converted in one direction and are
+ * indicated by one of the translation functions being set to NULL.
+ */
+static lx_cmsg_xlate_t lx_cmsg_xlate_tbl[] = {
+	{ SOL_SOCKET, SCM_RIGHTS, cmsg_conv_generic,
+	    LX_SOL_SOCKET, LX_SCM_RIGHTS, cmsg_conv_generic },
+	{ SOL_SOCKET, SCM_UCRED, stol_conv_ucred,
+	    LX_SOL_SOCKET, LX_SCM_CRED, ltos_conv_ucred },
+	{ SOL_SOCKET, SCM_TIMESTAMP, cmsg_conv_generic,
+	    LX_SOL_SOCKET, LX_SCM_TIMESTAMP, cmsg_conv_generic },
+	{ IPPROTO_IP, IP_PKTINFO, cmsg_conv_generic,
+	    LX_IPPROTO_IP, LX_IP_PKTINFO, cmsg_conv_generic },
+	{ IPPROTO_IP, IP_RECVTTL, stol_conv_recvttl,
+	    LX_IPPROTO_IP, LX_IP_TTL, NULL },
+	{ IPPROTO_IP, IP_TTL, cmsg_conv_generic,
+	    LX_IPPROTO_IP, LX_IP_TTL, cmsg_conv_generic },
+	{ IPPROTO_IPV6, IPV6_HOPLIMIT, cmsg_conv_generic,
+	    LX_IPPROTO_IPV6, LX_IPV6_HOPLIMIT, cmsg_conv_generic },
+	{ IPPROTO_IPV6, IPV6_PKTINFO, cmsg_conv_generic,
+	    LX_IPPROTO_IPV6, LX_IPV6_PKTINFO, cmsg_conv_generic }
+};
+
+#define	LX_MAX_CMSG_XLATE	\
+	(sizeof (lx_cmsg_xlate_tbl) / sizeof (lx_cmsg_xlate_tbl[0]))
+
+#if defined(_LP64)
+
+typedef struct {
+	int64_t	cmsg_len;
+	int32_t	cmsg_level;
+	int32_t	cmsg_type;
+} lx_cmsghdr64_t;
+
+/* The alignment/padding for 64bit Linux cmsghdr is not the same. */
+#define	LX_CMSG64_ALIGNMENT	8
+#define	ISALIGNED_LX_CMSG64(addr)					\
+	(((uintptr_t)(addr) & (LX_CMSG64_ALIGNMENT - 1)) == 0)
+#define	ROUNDUP_LX_CMSG64_LEN(len)					\
+	(((len) + LX_CMSG64_ALIGNMENT - 1) & ~(LX_CMSG64_ALIGNMENT - 1))
+
+#define	LX_CMSG64_IS_ALIGNED(m)			\
+	(((uintptr_t)(m) & (_CMSG_DATA_ALIGNMENT - 1)) == 0)
+#define	LX_CMSG64_DATA(c)	((unsigned char *)(((lx_cmsghdr64_t *)(c)) + 1))
+/*
+ * LX_CMSG64_VALID is closely derived from CMSG_VALID with one particularly
+ * important addition.  Since cmsg_len is 64bit, (cmsg + cmsg_len) is checked
+ * against the start address as well.  This prevents bogus inputs from wrapping
+ * around the address space.
+ */
+#define	LX_CMSG64_VALID(cmsg, start, end)				\
+	(ISALIGNED_LX_CMSG64(cmsg) &&					\
+	((uintptr_t)(cmsg) >= (uintptr_t)(start)) &&			\
+	((uintptr_t)(cmsg) < (uintptr_t)(end)) &&			\
+	((cmsg)->cmsg_len >= sizeof (lx_cmsghdr64_t)) &&		\
+	((uintptr_t)(cmsg) + (cmsg)->cmsg_len <= (uintptr_t)(end)) &&	\
+	((uintptr_t)(cmsg) + (cmsg)->cmsg_len >= (uintptr_t)(start)))
+#define	LX_CMSG64_NEXT(cmsg)				\
+	(lx_cmsghdr64_t *)((uintptr_t)(cmsg) +		\
+	    ROUNDUP_LX_CMSG64_LEN((cmsg)->cmsg_len))
+#define	LX_CMSG64_DIFF	sizeof (uint32_t)
+
+#endif /* defined(_LP64) */
+
+/*
+ * convert ucred_s to lx_ucred.
+ */
+static int
+stol_conv_ucred(struct cmsghdr *inmsg, struct cmsghdr *omsg)
+{
+	/*
+	 * Format the data correctly in the omsg buffer.
+	 */
+	if (omsg != NULL) {
+		struct ucred_s *scred;
+		prcred_t *cr;
+		lx_ucred_t lcred;
+
+		scred = (struct ucred_s *)CMSG_CONTENT(inmsg);
+		lcred.lxu_pid = scred->uc_pid;
+		/* LINTED: alignment */
+		cr = UCCRED(scred);
+		if (cr != NULL) {
+			lcred.lxu_uid = cr->pr_euid;
+			lcred.lxu_gid = cr->pr_egid;
+		} else {
+			lcred.lxu_uid = lcred.lxu_gid = 0;
+		}
+
+		bcopy(&lcred, CMSG_CONTENT(omsg), sizeof (lx_ucred_t));
+	}
+
+	return (sizeof (struct cmsghdr) + sizeof (lx_ucred_t));
+}
+
+static int
+ltos_conv_ucred(struct cmsghdr *inmsg, struct cmsghdr *omsg)
+{
+	if (omsg != NULL) {
+		struct ucred_s *uc;
+		prcred_t *pc;
+		lx_ucred_t *lcred;
+
+		uc = (struct ucred_s *)CMSG_CONTENT(omsg);
+		/* LINTED: alignment */
+		pc = (prcred_t *)((char *)uc + sizeof (struct ucred_s));
+
+		uc->uc_credoff = sizeof (struct ucred_s);
+
+		lcred = (lx_ucred_t *)CMSG_CONTENT(inmsg);
+
+		uc->uc_pid = lcred->lxu_pid;
+		pc->pr_euid = lcred->lxu_uid;
+		pc->pr_egid = lcred->lxu_gid;
+	}
+
+	return (sizeof (struct cmsghdr) + sizeof (struct ucred_s) +
+	    sizeof (prcred_t));
+
+}
+
+static int
+stol_conv_recvttl(struct cmsghdr *inmsg, struct cmsghdr *omsg)
+{
+	/*
+	 * SunOS communicates the TTL of incoming packets via IP_RECVTTL using
+	 * a uint8_t value instead of IP_TTL using an int. This conversion is
+	 * only needed in the one direction since Linux does not handle
+	 * IP_RECVTTL in the sendmsg path.
+	 */
+	if (omsg != NULL) {
+		uint8_t *inttl = (uint8_t *)CMSG_CONTENT(inmsg);
+		int *ottl = (int *)CMSG_CONTENT(omsg);
+
+		*ottl = (int)*inttl;
+	}
+
+	return (sizeof (struct cmsghdr) + sizeof (int));
+}
+
+static int
+cmsg_conv_generic(struct cmsghdr *inmsg, struct cmsghdr *omsg)
+{
+	if (omsg != NULL) {
+		size_t data_len;
+
+		data_len = inmsg->cmsg_len - sizeof (struct cmsghdr);
+		bcopy(CMSG_CONTENT(inmsg), CMSG_CONTENT(omsg), data_len);
+	}
+
+	return (inmsg->cmsg_len);
+}
+
+static int
+lx_xlate_cmsg(struct cmsghdr *inmsg, struct cmsghdr *omsg, lx_xlate_dir_t dir)
+{
+	int i;
+	int len;
+
+	VERIFY(dir == SUNOS_TO_LX || dir == LX_TO_SUNOS);
+
+	for (i = 0; i < LX_MAX_CMSG_XLATE; i++) {
+		lx_cmsg_xlate_t *xlate = &lx_cmsg_xlate_tbl[i];
+		if (dir == LX_TO_SUNOS &&
+		    inmsg->cmsg_level == xlate->lcx_linux_level &&
+		    inmsg->cmsg_type == xlate->lcx_linux_type &&
+		    xlate->lcx_ltos_conv != NULL) {
+			len = xlate->lcx_ltos_conv(inmsg, omsg);
+			if (omsg != NULL) {
+				omsg->cmsg_len = len;
+				omsg->cmsg_level = xlate->lcx_sunos_level;
+				omsg->cmsg_type = xlate->lcx_sunos_type;
+			}
+			return (len);
+		} else if (dir == SUNOS_TO_LX &&
+		    inmsg->cmsg_level == xlate->lcx_sunos_level &&
+		    inmsg->cmsg_type == xlate->lcx_sunos_type &&
+		    xlate->lcx_stol_conv != NULL) {
+			len = xlate->lcx_stol_conv(inmsg, omsg);
+			if (omsg != NULL) {
+				omsg->cmsg_len = len;
+				omsg->cmsg_level = xlate->lcx_linux_level;
+				omsg->cmsg_type = xlate->lcx_linux_type;
+			}
+			return (len);
+		}
+	}
+	/*
+	 * The Linux man page for sendmsg does not define a specific error for
+	 * unsupported cmsgs.  While it is meant to indicated bad values for
+	 * passed flags, EOPNOTSUPP appears to be the next closest choice.
+	 */
+	return (-EOPNOTSUPP);
+}
+
+static long
+ltos_cmsgs_copyin(void *addr, socklen_t inlen, void **outmsg,
+    socklen_t *outlenp)
+{
+	void *inbuf, *obuf;
+	struct cmsghdr *inmsg, *omsg;
+	int slen = 0;
+
+	if (inlen < sizeof (struct cmsghdr) || inlen > SO_MAXARGSIZE) {
+		return (EINVAL);
+	}
+
+#if defined(_LP64)
+	if (get_udatamodel() == DATAMODEL_NATIVE &&
+	    inlen < sizeof (lx_cmsghdr64_t)) {
+		/* The size requirements are more strict for 64bit. */
+		return (EINVAL);
+	}
+#endif /* defined(_LP64) */
+
+	inbuf = kmem_alloc(inlen, KM_SLEEP);
+	if (copyin(addr, inbuf, inlen) != 0) {
+		kmem_free(inbuf, inlen);
+		return (EFAULT);
+	}
+
+#if defined(_LP64)
+	if (get_udatamodel() == DATAMODEL_NATIVE) {
+		/*
+		 * Linux cmsg headers are longer than illumos under x86_64.
+		 * Convert to regular cmsgs first.
+		 */
+		lx_cmsghdr64_t *lmsg;
+		struct cmsghdr *smsg;
+		void *newbuf;
+		int len = 0;
+
+		/* Inventory the new cmsg size */
+		for (lmsg = (lx_cmsghdr64_t *)inbuf;
+		    LX_CMSG64_VALID(lmsg, inbuf, (uintptr_t)inbuf + inlen) != 0;
+		    lmsg = LX_CMSG64_NEXT(lmsg)) {
+			len += ROUNDUP_cmsglen(lmsg->cmsg_len - LX_CMSG64_DIFF);
+		}
+
+		VERIFY(len < inlen);
+		if (len == 0) {
+			/* Input was bogus, so we can give up early. */
+			kmem_free(inbuf, inlen);
+			*outmsg = NULL;
+			*outlenp = 0;
+			return (EINVAL);
+		}
+
+		newbuf = kmem_alloc(len, KM_SLEEP);
+
+		for (lmsg = (lx_cmsghdr64_t *)inbuf,
+		    smsg = (struct cmsghdr *)newbuf;
+		    LX_CMSG64_VALID(lmsg, inbuf, (uintptr_t)inbuf + inlen) != 0;
+		    lmsg = LX_CMSG64_NEXT(lmsg), smsg = CMSG_NEXT(smsg)) {
+			smsg->cmsg_level = lmsg->cmsg_level;
+			smsg->cmsg_type = lmsg->cmsg_type;
+			smsg->cmsg_len = lmsg->cmsg_len - LX_CMSG64_DIFF;
+
+			/* The above length measurement should ensure this */
+			ASSERT(CMSG_VALID(smsg, newbuf,
+			    (uintptr_t)newbuf + len));
+
+			bcopy(LX_CMSG64_DATA(lmsg), CMSG_CONTENT(smsg),
+			    smsg->cmsg_len - sizeof (*smsg));
+		}
+
+		kmem_free(inbuf, inlen);
+		inbuf = newbuf;
+		inlen = len;
+	}
+#endif /* defined(_LP64) */
+
+	/*
+	 * Now determine how much space we need for the conversion.
+	 */
+	for (inmsg = (struct cmsghdr *)inbuf;
+	    CMSG_VALID(inmsg, inbuf, (uintptr_t)inbuf + inlen) != 0;
+	    inmsg = CMSG_NEXT(inmsg)) {
+		int sz;
+
+		if ((sz = lx_xlate_cmsg(inmsg, NULL, LX_TO_SUNOS)) < 0) {
+			/* unsupported msg */
+			kmem_free(inbuf, inlen);
+			return (-sz);
+		}
+
+		slen += ROUNDUP_cmsglen(sz);
+	}
+
+	obuf = kmem_zalloc(slen, KM_SLEEP);
+
+	/*
+	 * Now do the conversion.
+	 */
+	for (inmsg = (struct cmsghdr *)inbuf, omsg = (struct cmsghdr *)obuf;
+	    CMSG_VALID(inmsg, inbuf, (uintptr_t)inbuf + inlen) != 0;
+	    inmsg = CMSG_NEXT(inmsg), omsg = CMSG_NEXT(omsg)) {
+		VERIFY(lx_xlate_cmsg(inmsg, omsg, LX_TO_SUNOS) >= 0);
+	}
+
+	kmem_free(inbuf, inlen);
+	*outmsg = obuf;
+	*outlenp = slen;
+	return (0);
+}
+
+static long
+stol_cmsgs_copyout(void *input, socklen_t inlen, void *addr,
+    void *outlenp, socklen_t orig_outlen)
+{
+	void *obuf;
+	struct cmsghdr *inmsg, *omsg;
+	int error = 0;
+	socklen_t lx_len = 0;
+#if defined(_LP64)
+	model_t model = get_udatamodel();
+#endif
+
+	if (inlen == 0) {
+		/* Simply output the zero controllen */
+		goto finish;
+	}
+
+	VERIFY(inlen > sizeof (struct cmsghdr));
+
+	/*
+	 * First determine how much space we need for the conversion and
+	 * make sure the caller has provided at least that much space to return
+	 * results.
+	 */
+	for (inmsg = (struct cmsghdr *)input;
+	    CMSG_VALID(inmsg, input, (uintptr_t)input + inlen) != 0;
+	    inmsg = CMSG_NEXT(inmsg)) {
+		int sz;
+
+		if ((sz = lx_xlate_cmsg(inmsg, NULL, SUNOS_TO_LX)) < 0) {
+			/* unsupported msg */
+			return (-sz);
+		}
+
+#if defined(_LP64)
+		if (model == DATAMODEL_NATIVE) {
+			/*
+			 * The converted 64-bit cmsgs require an additional 4
+			 * bytes of header space and must be aligned to 8 bytes
+			 * (instead of the typical 4 for x86)
+			 */
+			sz = ROUNDUP_LX_CMSG64_LEN(sz + LX_CMSG64_DIFF);
+		} else
+#endif /* defined(_LP64) */
+		{
+			/*
+			 * The converted 32-bit cmsgs do not require additional
+			 * header space or padding for Linux conversion.
+			 */
+			sz = ROUNDUP_cmsglen(sz);
+		}
+
+		/*
+		 * Unlike SunOS, Linux requires that the last cmsg be
+		 * adequately padded for alignment.
+		 */
+		lx_len += sz;
+	}
+
+	if (lx_len > orig_outlen || addr == NULL) {
+		/* This will be interpreted by the caller */
+		error = EMSGSIZE;
+		lx_len = 0;
+		goto finish;
+	}
+
+	/*
+	 * Since cmsgs are often padded to an aligned size, kmem_zalloc is
+	 * necessary to prevent leaking the contents of uninitialized memory.
+	 */
+	obuf = kmem_zalloc(lx_len, KM_SLEEP);
+
+	/*
+	 * Convert the msgs.
+	 */
+	for (inmsg = (struct cmsghdr *)input, omsg = (struct cmsghdr *)obuf;
+	    CMSG_VALID(inmsg, input, (uintptr_t)input + inlen) != 0;
+	    inmsg = CMSG_NEXT(inmsg), omsg = CMSG_NEXT(omsg)) {
+		VERIFY(lx_xlate_cmsg(inmsg, omsg, SUNOS_TO_LX) >= 0);
+	}
+
+#if defined(_LP64)
+	if (model == DATAMODEL_NATIVE) {
+		/* Linux cmsg headers are longer than illumos under x86_64. */
+		struct cmsghdr *smsg;
+		lx_cmsghdr64_t *lmsg;
+		void *newbuf;
+
+		/*
+		 * Once again, kmem_zalloc is needed to avoid leaking the
+		 * contents of uninialized memory
+		 */
+		newbuf = kmem_zalloc(lx_len, KM_SLEEP);
+		for (smsg = (struct cmsghdr *)obuf,
+		    lmsg = (lx_cmsghdr64_t *)newbuf;
+		    CMSG_VALID(smsg, obuf, (uintptr_t)obuf + inlen) != 0;
+		    smsg = CMSG_NEXT(smsg), lmsg = LX_CMSG64_NEXT(lmsg)) {
+			lmsg->cmsg_level = smsg->cmsg_level;
+			lmsg->cmsg_type = smsg->cmsg_type;
+			lmsg->cmsg_len = smsg->cmsg_len + LX_CMSG64_DIFF;
+
+			ASSERT(LX_CMSG64_VALID(lmsg, newbuf,
+			    (uintptr_t)newbuf + lx_len) != 0);
+
+			bcopy(CMSG_CONTENT(smsg), LX_CMSG64_DATA(lmsg),
+			    smsg->cmsg_len - sizeof (*smsg));
+		}
+
+		kmem_free(obuf, lx_len);
+		obuf = newbuf;
+	}
+#endif /* defined(_LP64) */
+
+	if (copyout(obuf, addr, lx_len) != 0) {
+		kmem_free(obuf, lx_len);
+		return (EFAULT);
+	}
+	kmem_free(obuf, lx_len);
+
+finish:
+	if (outlenp != NULL) {
+#if defined(_LP64)
+		if (model != DATAMODEL_NATIVE) {
+			int32_t len32 = (int32_t)lx_len;
+			if (copyout(&len32, outlenp, sizeof (len32)) != 0) {
+				return (EFAULT);
+			}
+		} else
+#endif /* defined(_LP64) */
+		{
+			if (copyout(&lx_len, outlenp, sizeof (lx_len)) != 0) {
+				return (EFAULT);
+			}
+		}
+	}
+	return (error);
+}
+
+static void
+lx_cmsg_set_cloexec(void *input, socklen_t inlen)
+{
+	struct cmsghdr *inmsg;
+
+	if (inlen == 0) {
+		return;
+	}
+
+	for (inmsg = (struct cmsghdr *)input;
+	    CMSG_VALID(inmsg, input, (uintptr_t)input + inlen) != 0;
+	    inmsg = CMSG_NEXT(inmsg)) {
+		if (inmsg->cmsg_level == SOL_SOCKET &&
+		    inmsg->cmsg_type == SCM_RIGHTS) {
+			int *fds = (int *)CMSG_CONTENT(inmsg);
+			int i, num = (int)CMSG_CONTENTLEN(inmsg) / sizeof (int);
+			for (i = 0; i < num; i++) {
+				char flags;
+				file_t *fp;
+				/* set CLOEXEC on the fd */
+				fp = getf(fds[i]);
+				VERIFY(fp != NULL);
+				flags = f_getfd(fds[i]);
+				flags |= FD_CLOEXEC;
+				f_setfd(fds[i], flags);
+				releasef(fds[i]);
+			}
+		}
+	}
+}
+
+static int
+lx_cmsg_try_ucred(sonode_t *so, struct nmsghdr *msg, socklen_t origlen)
+{
+	lx_socket_aux_data_t *sad;
+	struct cmsghdr *cmsg = NULL;
+	int msgsize;
+	cred_t *cred;
+
+	if (origlen == 0) {
+		return (0);
+	}
+	sad = lx_sad_acquire(SOTOV(so));
+	if (!sad->lxsad_stream_cred) {
+		mutex_exit(&sad->lxsad_lock);
+		return (0);
+	}
+	mutex_exit(&sad->lxsad_lock);
+
+	mutex_enter(&so->so_lock);
+	if (so->so_peercred == NULL) {
+		mutex_exit(&so->so_lock);
+		return (0);
+	}
+	crhold(cred = so->so_peercred);
+	mutex_exit(&so->so_lock);
+
+	msgsize = ucredminsize(cred) + sizeof (struct cmsghdr);
+	if (msg->msg_control == NULL) {
+		msg->msg_controllen = msgsize;
+		msg->msg_control = cmsg = kmem_zalloc(msgsize, KM_SLEEP);
+	} else {
+		/*
+		 * The so_recvmsg operation may have allocated a msg_control
+		 * buffer which precisely fits all returned cmsgs.  We must
+		 * manually verify the length of that cmsg data and reallocate
+		 * the buffer if it lacks the necessary space.
+		 */
+		uintptr_t start = (uintptr_t)msg->msg_control;
+		uintptr_t end = start + msg->msg_controllen;
+
+		ASSERT(msg->msg_controllen > 0);
+		cmsg = (struct cmsghdr *)msg->msg_control;
+		while (CMSG_VALID(cmsg, start, end) != 0) {
+			if (cmsg->cmsg_level == SOL_SOCKET &&
+			    cmsg->cmsg_type == SCM_UCRED) {
+				/*
+				 * If some later code change results in a ucred
+				 * being attached anyways, there is no need for
+				 * us to do it manually
+				 */
+				crfree(cred);
+				return (0);
+			}
+			cmsg = CMSG_NEXT(cmsg);
+		}
+		if (((uintptr_t)cmsg + msgsize) > end) {
+			socklen_t offset = (uintptr_t)cmsg - start;
+			socklen_t newsize = offset + msgsize;
+			void *newbuf;
+
+			if (newsize < msg->msg_controllen) {
+				/* size overflow, bail */
+				crfree(cred);
+				return (-1);
+			}
+			newbuf = kmem_alloc(newsize, KM_SLEEP);
+			bcopy(msg->msg_control, newbuf, msg->msg_controllen);
+			kmem_free(msg->msg_control, msg->msg_controllen);
+
+			msg->msg_control = newbuf;
+			msg->msg_controllen = newsize;
+			cmsg = (struct cmsghdr *)((uintptr_t)newbuf + offset);
+		}
+	}
+
+	cmsg->cmsg_level = SOL_SOCKET;
+	cmsg->cmsg_type = SCM_UCRED;
+	cmsg->cmsg_len = msgsize;
+	(void) cred2ucred(cred, so->so_cpid, CMSG_CONTENT(cmsg), CRED());
+	crfree(cred);
+	return (0);
+}
+
+static lx_socket_aux_data_t *
+lx_sad_acquire(vnode_t *vp)
+{
+	lx_socket_aux_data_t *cur, *created;
+
+	mutex_enter(&vp->v_vsd_lock);
+	cur = (lx_socket_aux_data_t *)vsd_get(vp, lx_socket_vsd);
+	if (cur == NULL) {
+		/* perform our allocation carefully */
+		mutex_exit(&vp->v_vsd_lock);
+
+		created = (lx_socket_aux_data_t *)kmem_zalloc(
+		    sizeof (*created), KM_SLEEP);
+
+		mutex_enter(&vp->v_vsd_lock);
+		cur = (lx_socket_aux_data_t *)vsd_get(vp, lx_socket_vsd);
+		if (cur == NULL) {
+			mutex_init(&created->lxsad_lock, NULL, MUTEX_DEFAULT,
+			    NULL);
+			(void) vsd_set(vp, lx_socket_vsd, created);
+			cur = created;
+		} else {
+			kmem_free(created, sizeof (*created));
+		}
+	}
+	mutex_exit(&vp->v_vsd_lock);
+	mutex_enter(&cur->lxsad_lock);
+	return (cur);
+}
+
+static int
+lx_convert_pkt_proto(int protocol)
+{
+	switch (ntohs(protocol)) {
+	case LX_ETH_P_802_2:
+		return (ETH_P_802_2);
+	case LX_ETH_P_IP:
+		return (ETH_P_IP);
+	case LX_ETH_P_ARP:
+		return (ETH_P_ARP);
+	case LX_ETH_P_IPV6:
+		return (ETH_P_IPV6);
+	case LX_ETH_P_ALL:
+	case LX_ETH_P_802_3:
+		return (ETH_P_ALL);
+	default:
+		return (-1);
+	}
+}
+
+static int
+lx_convert_sock_args(int in_dom, int in_type, int in_proto, int *out_dom,
+    int *out_type, int *out_options, int *out_proto)
+{
+	int domain, type, options;
+
+	if (in_dom < 0 || in_type < 0 || in_proto < 0)
+		return (EINVAL);
+
+	domain = LTOS_FAMILY(in_dom);
+	if (domain == AF_NOTSUPPORTED || domain == AF_UNSPEC)
+		return (EAFNOSUPPORT);
+	if (domain == AF_INVAL)
+		return (EINVAL);
+
+	type = LTOS_SOCKTYPE(in_type & LX_SOCK_TYPE_MASK);
+	if (type == SOCK_INVAL)
+		return (EINVAL);
+	/*
+	 * Linux does not allow the app to specify IP Protocol for raw sockets.
+	 * SunOS does, so bail out here.
+	 */
+	if (type == SOCK_NOTSUPPORTED ||
+	    (domain == AF_INET && type == SOCK_RAW && in_proto == IPPROTO_IP)) {
+		if (lx_kern_release_cmp(curzone, "2.6.15") < 0) {
+			/*
+			 * Use error appropriate for kernel version.
+			 * See lx_socket_create for more detail.
+			 */
+			return (ESOCKTNOSUPPORT);
+		}
+		return (EPROTONOSUPPORT);
+	}
+
+	options = 0;
+	in_type &= ~(LX_SOCK_TYPE_MASK);
+	if (in_type & LX_SOCK_NONBLOCK) {
+		in_type ^= LX_SOCK_NONBLOCK;
+		options |= SOCK_NONBLOCK;
+	}
+	if (in_type & LX_SOCK_CLOEXEC) {
+		in_type ^= LX_SOCK_CLOEXEC;
+		options |= SOCK_CLOEXEC;
+	}
+	if (in_type != 0) {
+		return (EINVAL);
+	}
+
+	/* Protocol definitions for PF_PACKET differ between Linux and SunOS */
+	if (domain == PF_PACKET &&
+	    (in_proto = lx_convert_pkt_proto(in_proto)) < 0)
+		return (EINVAL);
+
+	*out_dom = domain;
+	*out_type = type;
+	*out_options = options;
+	*out_proto = in_proto;
+	return (0);
+}
+
+
+static int
+lx_socket_create(int domain, int type, int protocol, int options, file_t **fpp,
+    int *fdp)
+{
+	sonode_t *so;
+	vnode_t *vp;
+	file_t *fp;
+	int err, fd;
+
+	/* logic cloned from so_socket */
+	so = socket_create(domain, type, protocol, NULL, NULL, SOCKET_SLEEP,
+	    SOV_DEFAULT, CRED(), &err);
+
+	if (so == NULL) {
+		switch (err) {
+		case EPROTOTYPE:
+		case EPROTONOSUPPORT:
+			if (lx_kern_release_cmp(curzone, "2.6.15") < 0) {
+				/*
+				 * Linux changed its socket error behavior in
+				 * versions 2.6.15 and later.  See git commit
+				 * 86c8f9d158f68538a971a47206a46a22c7479bac in
+				 * the Linux repository.
+				 *
+				 * LTP presently checks for version 2.6.16.
+				 */
+				return (ESOCKTNOSUPPORT);
+			}
+			return (EPROTONOSUPPORT);
+		default:
+			return (err);
+		}
+	}
+
+	/* Allocate a file descriptor for the socket */
+	vp = SOTOV(so);
+	if ((err = falloc(vp, FWRITE|FREAD, &fp, &fd)) != 0) {
+		(void) socket_close(so, 0, CRED());
+		socket_destroy(so);
+		return (err);
+	}
+
+	/*
+	 * Linux programs do not tolerate errors appearing from asynchronous
+	 * events (such as ICMP messages arriving).  Setting SM_DEFERERR will
+	 * prevent checking/delivery of such errors.
+	 */
+	so->so_mode |= SM_DEFERERR;
+
+	/* Now fill in the entries that falloc reserved */
+	if (options & SOCK_NONBLOCK) {
+		so->so_state |= SS_NONBLOCK;
+		fp->f_flag |= FNONBLOCK;
+	}
+	mutex_exit(&fp->f_tlock);
+	*fpp = fp;
+	*fdp = fd;
+	return (0);
+}
+
+static void
+lx_socket_destroy(file_t *fp, int fd)
+{
+	sonode_t *so = VTOSO(fp->f_vnode);
+
+	setf(fd, NULL);
+
+	mutex_enter(&fp->f_tlock);
+	unfalloc(fp);
+
+	(void) socket_close(so, 0, CRED());
+	socket_destroy(so);
+}
+
+long
+lx_socket(int domain, int type, int protocol)
+{
+	int error, options, fd = -1;
+	file_t *fp = NULL;
+
+	if ((error = lx_convert_sock_args(domain, type, protocol, &domain,
+	    &type, &options, &protocol)) != 0) {
+		return (set_errno(error));
+	}
+
+	error = lx_socket_create(domain, type, protocol, options, &fp, &fd);
+	if (error != 0) {
+		return (set_errno(error));
+	}
+
+	setf(fd, fp);
+	if ((options & SOCK_CLOEXEC) != 0) {
+		f_setfd(fd, FD_CLOEXEC);
+	}
+	return (fd);
+}
+
+long
+lx_bind(long sock, uintptr_t name, socklen_t namelen)
+{
+	struct sonode *so;
+	struct sockaddr *addr = NULL;
+	socklen_t len = 0;
+	file_t *fp;
+	int error;
+	lx_sun_type_t sun_type;
+	boolean_t not_sock = B_FALSE;
+
+	if ((so = getsonode(sock, &error, &fp)) == NULL) {
+		return (set_errno(error));
+	}
+
+	if (namelen != 0) {
+		error = ltos_sockaddr_copyin((struct sockaddr *)name, namelen,
+		    &addr, &len, &sun_type);
+		if (error != 0) {
+			releasef(sock);
+			return (set_errno(error));
+		}
+	}
+
+	if (addr != NULL && addr->sa_family == AF_UNIX) {
+		vnode_t *vp;
+
+		error = so_ux_lookup(so, (struct sockaddr_un *)addr, B_TRUE,
+		    &vp);
+		if (error == 0) {
+			/* A valid socket exists and is open at this address. */
+			VN_RELE(vp);
+		} else {
+			/* Keep track of paths which are not valid sockets. */
+			if (error == ENOTSOCK) {
+				not_sock = B_TRUE;
+			}
+
+			/*
+			 * When binding to an abstract namespace address or
+			 * /dev/log, implicit clean-up must occur if there is
+			 * not a valid socket at the specififed address.  See
+			 * ltos_sockaddr_copyin for details about why these
+			 * socket types act differently.
+			 */
+			if (sun_type == LX_SUN_ABSTRACT) {
+				(void) vn_removeat(NULL, addr->sa_data,
+				    UIO_SYSSPACE, RMFILE);
+			}
+		}
+	}
+
+	error = socket_bind(so, addr, len, _SOBIND_XPG4_2, CRED());
+
+	/*
+	 * Linux returns EADDRINUSE for attempts to bind to Unix domain
+	 * sockets that aren't sockets.
+	 */
+	if (error == EINVAL && addr != NULL && addr->sa_family == AF_UNIX &&
+	    not_sock == B_TRUE) {
+		error = EADDRINUSE;
+	}
+
+	releasef(sock);
+
+	if (addr != NULL) {
+		kmem_free(addr, len);
+	}
+
+	if (error != 0) {
+		return (set_errno(error));
+	}
+	return (0);
+}
+
+long
+lx_connect(long sock, uintptr_t name, socklen_t namelen)
+{
+	struct sonode *so;
+	struct sockaddr *addr = NULL;
+	lx_socket_aux_data_t *sad = NULL;
+	socklen_t len = 0;
+	file_t *fp;
+	int error;
+
+	if ((so = getsonode(sock, &error, &fp)) == NULL) {
+		return (set_errno(error));
+	}
+
+	/*
+	 * Ensure the name is sized appropriately before we alloc memory and
+	 * copy it in from userspace.  We need at least the address family to
+	 * make later sizing decisions.
+	 */
+	if (namelen != 0) {
+		error = ltos_sockaddr_copyin((struct sockaddr *)name, namelen,
+		    &addr, &len, NULL);
+		if (error != 0) {
+			releasef(sock);
+			return (set_errno(error));
+		}
+	}
+
+	error = socket_connect(so, addr, len, fp->f_flag,
+	    _SOCONNECT_XPG4_2, CRED());
+
+	/*
+	 * Linux connect(2) behavior is rather strange when using the
+	 * O_NONBLOCK flag.  The first call will return EINPROGRESS, as
+	 * expected.  Provided that is successful, a second call to connect
+	 * will return 0 instead of EISCONN.  Subsequent connect calls will
+	 * return EISCONN.
+	 */
+	if ((fp->f_flag & FNONBLOCK) != 0 && error != 0) {
+		sad = lx_sad_acquire(SOTOV(so));
+		if (error == EISCONN &&
+		    sad->lxsad_status == LXSS_CONNECTING) {
+			/* Report the one success */
+			sad->lxsad_status = LXSS_CONNECTED;
+			error = 0;
+		} else if (error == EINPROGRESS) {
+			sad->lxsad_status = LXSS_CONNECTING;
+		}
+		mutex_exit(&sad->lxsad_lock);
+	}
+
+	/*
+	 * When connecting to a UDP socket, configure it so that future
+	 * sendto/sendmsg operations are allowed to specify a destination
+	 * address. See the Posix spec. for sendto(2). Linux allows this while
+	 * illumos would return EISCONN if the option is not set.
+	 */
+	if (error == 0 && so->so_protocol == IPPROTO_UDP &&
+	    (so->so_family == AF_INET || so->so_family == AF_INET6)) {
+		int val = 1;
+
+		DTRACE_PROBE(lx__connect__udp);
+		(void) socket_setsockopt(so, IPPROTO_UDP, UDP_SND_TO_CONNECTED,
+		    &val, sizeof (val), CRED());
+	}
+
+	releasef(sock);
+
+	if (addr != NULL) {
+		kmem_free(addr, len);
+	}
+
+	if (error != 0) {
+		return (set_errno(error));
+	}
+	return (0);
+}
+
+/*
+ * Custom version of socket_recvmsg for error-handling overrides.
+ */
+static int
+lx_socket_recvmsg(struct sonode *so, struct nmsghdr *msg, struct uio *uiop,
+    cred_t *cr)
+{
+	int error;
+	ssize_t orig_resid = uiop->uio_resid;
+
+	/*
+	 * Do not bypass the cache when reading data, as the application
+	 * is likely to access the data shortly.
+	 */
+	uiop->uio_extflg |= UIO_COPY_CACHED;
+
+	error = SOP_RECVMSG(so, msg, uiop, cr);
+
+	switch (error) {
+	case EINTR:
+	/* EAGAIN is EWOULDBLOCK */
+	case EWOULDBLOCK:
+		/* We did a partial read */
+		if (uiop->uio_resid != orig_resid)
+			error = 0;
+		break;
+	case ENOTCONN:
+		/*
+		 * The rules are different for non-blocking sockets which are
+		 * still in the process of making a connection
+		 */
+		if ((msg->msg_flags & MSG_DONTWAIT) != 0 ||
+		    (uiop->uio_fmode & (FNONBLOCK|FNDELAY)) != 0) {
+			error = EAGAIN;
+		}
+		break;
+	default:
+		break;
+	}
+	return (error);
+}
+
+static long
+lx_recv_common(int sock, struct nmsghdr *msg, xuio_t *xuiop, int flags,
+    void *namelenp, void *controllenp, void *flagsp)
+{
+	struct sonode *so;
+	file_t *fp;
+	void *name;
+	socklen_t namelen;
+	void *control;
+	socklen_t controllen;
+	ssize_t len;
+	int error;
+	boolean_t fd_cloexec;
+	boolean_t is_peek_trunc;
+
+	if ((so = getsonode(sock, &error, &fp)) == NULL) {
+		return (set_errno(error));
+	}
+
+	fd_cloexec = ((flags & LX_MSG_CMSG_CLOEXEC) != 0);
+	flags = lx_xlate_sock_flags(flags, LX_TO_SUNOS);
+	is_peek_trunc = (flags & (MSG_PEEK|MSG_TRUNC)) == (MSG_PEEK|MSG_TRUNC);
+	len = xuiop->xu_uio.uio_resid;
+	xuiop->xu_uio.uio_fmode = fp->f_flag;
+	xuiop->xu_uio.uio_extflg = UIO_COPY_CACHED;
+
+	/*
+	 * Linux accepts MSG_TRUNC as an input flag, unlike SunOS and many
+	 * other UNIX distributions.  When combined with MSG_PEEK, it causes
+	 * recvmsg to return the size of the waiting message, regardless of
+	 * buffer size.  This behavior is commonly used with a 0-length buffer
+	 * to interrogate the size of a queued message prior to allocating a
+	 * buffer for it.
+	 *
+	 * In order to support this functionality, a custom XUIO type is used
+	 * to communicate the total message size out from the depths of sockfs.
+	 */
+	if (is_peek_trunc) {
+		xuiop->xu_uio.uio_extflg |= UIO_XUIO;
+		xuiop->xu_type = UIOTYPE_PEEKSIZE;
+		xuiop->xu_ext.xu_ps.xu_ps_set = B_FALSE;
+		xuiop->xu_ext.xu_ps.xu_ps_size = 0;
+	}
+
+	name = msg->msg_name;
+	namelen = msg->msg_namelen;
+	control = msg->msg_control;
+	controllen = msg->msg_controllen;
+
+	/*
+	 * socket_recvmsg will allocate these if needed.
+	 * NULL them out to prevent any confusion.
+	 */
+	msg->msg_name = NULL;
+	msg->msg_control = NULL;
+
+	msg->msg_flags = flags & (MSG_OOB | MSG_PEEK | MSG_WAITALL |
+	    MSG_DONTWAIT);
+	/* Default to XPG4.2 operation */
+	msg->msg_flags |= MSG_XPG4_2;
+
+	error = lx_socket_recvmsg(so, msg, (struct uio *)xuiop, CRED());
+	if (error) {
+		releasef(sock);
+		return (set_errno(error));
+	}
+	lwp_stat_update(LWP_STAT_MSGRCV, 1);
+	releasef(sock);
+
+	if (namelen != 0) {
+		error = stol_sockaddr_copyout(msg->msg_name, msg->msg_namelen,
+		    name, namelenp, namelen);
+
+		if (msg->msg_namelen != 0) {
+			kmem_free(msg->msg_name, (size_t)msg->msg_namelen);
+			msg->msg_namelen = 0;
+		}
+
+		/*
+		 * Errors during copyout of the name are not a concern to Linux
+		 * callers at this point in the syscall
+		 */
+		if (error != 0 && error != EFAULT) {
+			goto err;
+		}
+	}
+
+	if (controllen != 0) {
+		if (fd_cloexec) {
+			/*
+			 * If CLOEXEC needs to set on file descriptors passed
+			 * via SCM_RIGHTS, do so before formatting the cmsgs
+			 * for Linux.
+			 */
+			lx_cmsg_set_cloexec(msg->msg_control,
+			    msg->msg_controllen);
+		}
+		if (so->so_family == AF_UNIX &&
+		    (so->so_mode & SM_CONNREQUIRED) != 0) {
+			/*
+			 * It may be necessary to append a SCM_UCRED cmsg to
+			 * the controls if SO_PASSCRED is set on a
+			 * connection-oriented AF_UNIX socket.
+			 *
+			 * See lx_setsockopt_socket for more details.
+			 */
+			if (lx_cmsg_try_ucred(so, msg, controllen) != 0) {
+				msg->msg_flags |= MSG_CTRUNC;
+			}
+		}
+
+		error = stol_cmsgs_copyout(msg->msg_control,
+		    msg->msg_controllen, control, controllenp, controllen);
+
+		if (error != 0) {
+			/*
+			 * If there was an error during cmsg translation or
+			 * copyout, we need to clean up any FDs that are being
+			 * passed back via SCM_RIGHTS.  This prevents us from
+			 * leaking those open files.
+			 */
+			so_closefds(msg->msg_control, msg->msg_controllen, 0,
+			    0);
+
+			/*
+			 * An error during cmsg_copyout means we had
+			 * _something_ to process.
+			 */
+			VERIFY(msg->msg_controllen != 0);
+
+			kmem_free(msg->msg_control,
+			    (size_t)msg->msg_controllen);
+			msg->msg_controllen = 0;
+
+			if (error == EMSGSIZE) {
+				/* Communicate that messages were truncated */
+				msg->msg_flags |= MSG_CTRUNC;
+				error = 0;
+			} else {
+				goto err;
+			}
+		} else if (msg->msg_controllen != 0) {
+			kmem_free(msg->msg_control,
+			    (size_t)msg->msg_controllen);
+			msg->msg_controllen = 0;
+		}
+	}
+
+	if (flagsp != NULL) {
+		int flags;
+
+		/* Clear internal flag. */
+		flags = msg->msg_flags & ~MSG_XPG4_2;
+		flags = lx_xlate_sock_flags(flags, SUNOS_TO_LX);
+
+		if (copyout(&flags, flagsp, sizeof (flags) != 0)) {
+			error = EFAULT;
+			goto err;
+		}
+	}
+
+	/*
+	 * If both MSG_PEEK|MSG_TRUNC were set on the input flags and the
+	 * socket layer was able to calculate the total message size for us,
+	 * return that instead of the copied size.
+	 */
+	if (is_peek_trunc && xuiop->xu_ext.xu_ps.xu_ps_set == B_TRUE) {
+		return (xuiop->xu_ext.xu_ps.xu_ps_size);
+	}
+
+	return (len - xuiop->xu_uio.uio_resid);
+
+err:
+	if (msg->msg_controllen != 0) {
+		/* Prevent FD leakage (see above) */
+		so_closefds(msg->msg_control, msg->msg_controllen, 0, 0);
+		kmem_free(msg->msg_control, (size_t)msg->msg_controllen);
+	}
+	if (msg->msg_namelen != 0) {
+		kmem_free(msg->msg_name, (size_t)msg->msg_namelen);
+	}
+	return (set_errno(error));
+}
+
+long
+lx_recv(int sock, void *buffer, size_t len, int flags)
+{
+	struct nmsghdr smsg;
+	xuio_t xuio;
+	struct iovec uiov;
+
+	if ((ssize_t)len < 0) {
+		/*
+		 * The input len is unsigned, so limit it to SSIZE_MAX since
+		 * the return value is signed.
+		 */
+		return (set_errno(EINVAL));
+	}
+
+	uiov.iov_base = buffer;
+	uiov.iov_len = len;
+	xuio.xu_uio.uio_loffset = 0;
+	xuio.xu_uio.uio_iov = &uiov;
+	xuio.xu_uio.uio_iovcnt = 1;
+	xuio.xu_uio.uio_resid = len;
+	xuio.xu_uio.uio_segflg = UIO_USERSPACE;
+	xuio.xu_uio.uio_limit = 0;
+
+	smsg.msg_namelen = 0;
+	smsg.msg_controllen = 0;
+	smsg.msg_flags = 0;
+	return (lx_recv_common(sock, &smsg, &xuio, flags, NULL, NULL, NULL));
+}
+
+long
+lx_recvfrom(int sock, void *buffer, size_t len, int flags,
+    struct sockaddr *srcaddr, socklen_t *addrlenp)
+{
+	struct nmsghdr smsg;
+	xuio_t xuio;
+	struct iovec uiov;
+
+	if ((ssize_t)len < 0) {
+		/* Keep len reasonably limited (see lx_recv) */
+		return (set_errno(EINVAL));
+	}
+
+	uiov.iov_base = buffer;
+	uiov.iov_len = len;
+	xuio.xu_uio.uio_loffset = 0;
+	xuio.xu_uio.uio_iov = &uiov;
+	xuio.xu_uio.uio_iovcnt = 1;
+	xuio.xu_uio.uio_resid = len;
+	xuio.xu_uio.uio_segflg = UIO_USERSPACE;
+	xuio.xu_uio.uio_limit = 0;
+
+	smsg.msg_name = (char *)srcaddr;
+	if (addrlenp != NULL && srcaddr != NULL) {
+		/*
+		 * Despite addrlenp being defined as a socklen_t *, Linux
+		 * treats it internally as an int *.  Certain LTP tests depend
+		 * upon this behavior, so we must emulate it as well.
+		 */
+		int namelen;
+
+		if (copyin(addrlenp, &namelen, sizeof (namelen)) != 0) {
+			return (set_errno(EFAULT));
+		}
+		if (namelen < 0) {
+			return (set_errno(EINVAL));
+		}
+		smsg.msg_namelen = namelen;
+	} else {
+		smsg.msg_namelen = 0;
+	}
+	smsg.msg_controllen = 0;
+	smsg.msg_flags = 0;
+
+	return (lx_recv_common(sock, &smsg, &xuio, flags, addrlenp, NULL,
+	    NULL));
+}
+
+long
+lx_recvmsg(int sock, void *msg, int flags)
+{
+	struct nmsghdr smsg;
+	xuio_t xuio;
+	struct iovec luiov[IOV_MAX_STACK], *uiov;
+	int i, iovcnt, iovsize;
+	long res;
+	ssize_t len = 0;
+	void *namelenp, *controllenp, *flagsp;
+
+#if defined(_LP64)
+	if (get_udatamodel() != DATAMODEL_NATIVE) {
+		lx_msghdr32_t lmsg32;
+		if (copyin(msg, &lmsg32, sizeof (lmsg32)) != 0) {
+			return (set_errno(EFAULT));
+		}
+		smsg.msg_name = (void *)(uintptr_t)lmsg32.msg_name;
+		smsg.msg_namelen = lmsg32.msg_namelen;
+		smsg.msg_iov = (struct iovec *)(uintptr_t)lmsg32.msg_iov;
+		smsg.msg_iovlen = lmsg32.msg_iovlen;
+		smsg.msg_control = (void *)(uintptr_t)lmsg32.msg_control;
+		smsg.msg_controllen = lmsg32.msg_controllen;
+		smsg.msg_flags = lmsg32.msg_flags;
+
+		namelenp = &((lx_msghdr32_t *)msg)->msg_namelen;
+		controllenp = &((lx_msghdr32_t *)msg)->msg_controllen;
+		flagsp = &((lx_msghdr32_t *)msg)->msg_flags;
+	} else
+#endif /* defined(_LP64) */
+	{
+		lx_msghdr_t lmsg;
+		if (copyin(msg, &lmsg, sizeof (lmsg)) != 0) {
+			return (set_errno(EFAULT));
+		}
+		smsg.msg_name = lmsg.msg_name;
+		smsg.msg_namelen = lmsg.msg_namelen;
+		smsg.msg_iov = lmsg.msg_iov;
+		smsg.msg_iovlen = lmsg.msg_iovlen;
+		smsg.msg_control = lmsg.msg_control;
+		smsg.msg_controllen = lmsg.msg_controllen;
+		smsg.msg_flags = lmsg.msg_flags;
+
+		namelenp = &((lx_msghdr_t *)msg)->msg_namelen;
+		controllenp = &((lx_msghdr_t *)msg)->msg_controllen;
+		flagsp = &((lx_msghdr_t *)msg)->msg_flags;
+	}
+
+	iovcnt = smsg.msg_iovlen;
+	if (iovcnt < 0 || iovcnt > IOV_MAX) {
+		return (set_errno(EMSGSIZE));
+	}
+	if (iovcnt > IOV_MAX_STACK) {
+		iovsize = iovcnt * sizeof (struct iovec);
+		uiov = kmem_alloc(iovsize, KM_SLEEP);
+	} else if (iovcnt > 0) {
+		iovsize = 0;
+		uiov = luiov;
+	} else {
+		iovsize = 0;
+		uiov = NULL;
+		goto noiov;
+	}
+
+#if defined(_LP64)
+	if (get_udatamodel() != DATAMODEL_NATIVE) {
+		/* convert from 32bit iovec structs */
+		struct iovec32 luiov32[IOV_MAX_STACK], *uiov32;
+		ssize_t iov32size;
+		ssize32_t count32;
+
+		iov32size = iovcnt * sizeof (struct iovec32);
+		if (iovsize != 0) {
+			uiov32 = kmem_alloc(iov32size, KM_SLEEP);
+		} else {
+			uiov32 = luiov32;
+		}
+
+		if (copyin((struct iovec32 *)smsg.msg_iov, uiov32, iov32size)) {
+			if (iovsize != 0) {
+				kmem_free(uiov32, iov32size);
+				kmem_free(uiov, iovsize);
+			}
+
+			return (set_errno(EFAULT));
+		}
+
+		count32 = 0;
+		for (i = 0; i < iovcnt; i++) {
+			ssize32_t iovlen32;
+
+			iovlen32 = uiov32[i].iov_len;
+			count32 += iovlen32;
+			if (iovlen32 < 0 || count32 < 0) {
+				if (iovsize != 0) {
+					kmem_free(uiov32, iov32size);
+					kmem_free(uiov, iovsize);
+				}
+
+				return (set_errno(EINVAL));
+			}
+
+			uiov[i].iov_len = iovlen32;
+			uiov[i].iov_base =
+			    (caddr_t)(uintptr_t)uiov32[i].iov_base;
+		}
+		len = count32;
+
+		if (iovsize != 0) {
+			kmem_free(uiov32, iov32size);
+		}
+	} else
+#endif /* defined(_LP64) */
+	{
+		if (copyin(smsg.msg_iov, uiov,
+		    iovcnt * sizeof (struct iovec)) != 0) {
+			if (iovsize != 0) {
+				kmem_free(uiov, iovsize);
+			}
+			return (set_errno(EFAULT));
+		}
+
+		len = 0;
+		for (i = 0; i < iovcnt; i++) {
+			ssize_t iovlen = uiov[i].iov_len;
+			len += iovlen;
+			if (iovlen < 0 || len < 0) {
+				if (iovsize != 0) {
+					kmem_free(uiov, iovsize);
+				}
+				return (set_errno(EINVAL));
+			}
+		}
+	}
+
+noiov:
+	/* Since the iovec is passed via the uio, NULL it out in the msg */
+	smsg.msg_iov = NULL;
+
+	xuio.xu_uio.uio_loffset = 0;
+	xuio.xu_uio.uio_iov = uiov;
+	xuio.xu_uio.uio_iovcnt = iovcnt;
+	xuio.xu_uio.uio_resid = len;
+	xuio.xu_uio.uio_segflg = UIO_USERSPACE;
+	xuio.xu_uio.uio_limit = 0;
+
+	res = lx_recv_common(sock, &smsg, &xuio, flags, namelenp, controllenp,
+	    flagsp);
+
+	if (iovsize != 0) {
+		kmem_free(uiov, iovsize);
+	}
+
+	return (res);
+}
+
+/*
+ * Custom version of socket_sendmsg for error-handling overrides.
+ */
+static int
+lx_socket_sendmsg(struct sonode *so, struct nmsghdr *msg, struct uio *uiop,
+    cred_t *cr, boolean_t nosig)
+{
+	int error = 0;
+	ssize_t orig_resid = uiop->uio_resid;
+
+	/*
+	 * Do not bypass the cache if we are doing a local (AF_UNIX) write.
+	 */
+	if (so->so_family == AF_UNIX) {
+		uiop->uio_extflg |= UIO_COPY_CACHED;
+	} else {
+		uiop->uio_extflg &= ~UIO_COPY_CACHED;
+	}
+
+	error = SOP_SENDMSG(so, msg, uiop, cr);
+
+	switch (error) {
+	case EINTR:
+	case ENOMEM:
+	/* EAGAIN is EWOULDBLOCK */
+	case EWOULDBLOCK:
+		/* We did a partial send */
+		if (uiop->uio_resid != orig_resid) {
+			error = 0;
+		}
+		break;
+
+	case ENOTCONN:
+		/*
+		 * The rules are different for non-blocking sockets which are
+		 * still in the process of making a connection
+		 */
+		if ((msg->msg_flags & MSG_DONTWAIT) != 0 ||
+		    (uiop->uio_fmode & (FNONBLOCK|FNDELAY)) != 0) {
+			error = EAGAIN;
+			break;
+		}
+
+		/* Appease LTP and match behavior detailed in the man page */
+		error = EPIPE;
+		/* FALLTHROUGH */
+	case EPIPE:
+		if (nosig == B_FALSE) {
+			tsignal(curthread, SIGPIPE);
+		}
+		break;
+
+	default:
+		break;
+	}
+
+	return (error);
+}
+
+static long
+lx_send_common(int sock, struct nmsghdr *msg, struct uio *uiop, int flags)
+{
+	struct sonode *so;
+	file_t *fp;
+	struct sockaddr *name = NULL;
+	socklen_t namelen;
+	void *control = NULL;
+	socklen_t controllen;
+	ssize_t len = 0;
+	int error;
+	boolean_t nosig;
+
+	if ((so = getsonode(sock, &error, &fp)) == NULL) {
+		return (set_errno(error));
+	}
+
+	uiop->uio_fmode = fp->f_flag;
+
+	/* Allocate and copyin name and control */
+	if (msg->msg_name != NULL && msg->msg_namelen != 0) {
+		ASSERT(MUTEX_NOT_HELD(&so->so_lock));
+
+		error = ltos_sockaddr_copyin((struct sockaddr *)msg->msg_name,
+		    msg->msg_namelen, &name, &namelen, NULL);
+		if (error != 0) {
+			goto done;
+		}
+		/* copyin_name null terminates addresses for AF_UNIX */
+		msg->msg_namelen = namelen;
+		msg->msg_name = name;
+	} else {
+		msg->msg_name = name = NULL;
+		msg->msg_namelen = namelen = 0;
+	}
+
+	if (msg->msg_control != NULL && msg->msg_controllen != 0) {
+		/*
+		 * Verify that the length is not excessive to prevent
+		 * an application from consuming all of kernel memory.
+		 */
+		if (msg->msg_controllen > SO_MAXARGSIZE) {
+			error = EINVAL;
+			goto done;
+		}
+		if ((error = ltos_cmsgs_copyin(msg->msg_control,
+		    msg->msg_controllen, &control, &controllen)) != 0) {
+			goto done;
+		}
+		msg->msg_control = control;
+		msg->msg_controllen = controllen;
+	} else {
+		msg->msg_control = control = NULL;
+		msg->msg_controllen = controllen = 0;
+	}
+
+	len = uiop->uio_resid;
+	msg->msg_flags = lx_xlate_sock_flags(flags, LX_TO_SUNOS);
+	/* Default to XPG4.2 operation */
+	msg->msg_flags |= MSG_XPG4_2;
+	nosig = ((flags & LX_MSG_NOSIGNAL) != 0);
+
+	error = lx_socket_sendmsg(so, msg, uiop, CRED(), nosig);
+done:
+	if (control != NULL) {
+		kmem_free(control, controllen);
+	}
+	if (name != NULL) {
+		kmem_free(name, namelen);
+	}
+	if (error != 0) {
+		releasef(sock);
+		return (set_errno(error));
+	}
+	lwp_stat_update(LWP_STAT_MSGSND, 1);
+	releasef(sock);
+	return (len - uiop->uio_resid);
+}
+
+long
+lx_send(int sock, void *buffer, size_t len, int flags)
+{
+	struct nmsghdr smsg;
+	struct uio auio;
+	struct iovec aiov[1];
+
+	if ((ssize_t)len < 0) {
+		/* Keep len reasonably limited (see lx_recv) */
+		return (set_errno(EINVAL));
+	}
+
+	aiov[0].iov_base = buffer;
+	aiov[0].iov_len = len;
+	auio.uio_loffset = 0;
+	auio.uio_iov = aiov;
+	auio.uio_iovcnt = 1;
+	auio.uio_resid = len;
+	auio.uio_segflg = UIO_USERSPACE;
+	auio.uio_limit = 0;
+
+	smsg.msg_name = NULL;
+	smsg.msg_control = NULL;
+	return (lx_send_common(sock, &smsg, &auio, flags));
+}
+
+long
+lx_sendto(int sock, void *buffer, size_t len, int flags,
+    struct sockaddr *dstaddr, socklen_t addrlen)
+{
+	struct nmsghdr smsg;
+	struct uio auio;
+	struct iovec aiov[1];
+
+	if ((ssize_t)len < 0) {
+		/* Keep len reasonably limited (see lx_recv) */
+		return (set_errno(EINVAL));
+	}
+
+	aiov[0].iov_base = buffer;
+	aiov[0].iov_len = len;
+	auio.uio_loffset = 0;
+	auio.uio_iov = aiov;
+	auio.uio_iovcnt = 1;
+	auio.uio_resid = len;
+	auio.uio_segflg = UIO_USERSPACE;
+	auio.uio_limit = 0;
+
+	smsg.msg_name = (char *)dstaddr;
+	smsg.msg_namelen = addrlen;
+	smsg.msg_control = NULL;
+	return (lx_send_common(sock, &smsg, &auio, flags));
+}
+
+long
+lx_sendmsg(int sock, void *msg, int flags)
+{
+	struct nmsghdr smsg;
+	struct uio auio;
+	struct iovec buf[IOV_MAX_STACK], *aiov;
+	int i, iovcnt, iovsize;
+	long res;
+	ssize_t len = 0;
+
+#if defined(_LP64)
+	if (get_udatamodel() != DATAMODEL_NATIVE) {
+		lx_msghdr32_t lmsg32;
+		if (copyin(msg, &lmsg32, sizeof (lmsg32)) != 0) {
+			return (set_errno(EFAULT));
+		}
+		smsg.msg_name = (void *)(uintptr_t)lmsg32.msg_name;
+		smsg.msg_namelen = lmsg32.msg_namelen;
+		smsg.msg_iov = (struct iovec *)(uintptr_t)lmsg32.msg_iov;
+		smsg.msg_iovlen = lmsg32.msg_iovlen;
+		smsg.msg_control = (void *)(uintptr_t)lmsg32.msg_control;
+		smsg.msg_controllen = lmsg32.msg_controllen;
+		smsg.msg_flags = lmsg32.msg_flags;
+	} else
+#endif /* defined(_LP64) */
+	{
+		lx_msghdr_t lmsg;
+		if (copyin(msg, &lmsg, sizeof (lmsg)) != 0) {
+			return (set_errno(EFAULT));
+		}
+		smsg.msg_name = lmsg.msg_name;
+		smsg.msg_namelen = lmsg.msg_namelen;
+		smsg.msg_iov = lmsg.msg_iov;
+		smsg.msg_iovlen = lmsg.msg_iovlen;
+		smsg.msg_control = lmsg.msg_control;
+		smsg.msg_controllen = lmsg.msg_controllen;
+		smsg.msg_flags = lmsg.msg_flags;
+	}
+
+	iovcnt = smsg.msg_iovlen;
+	if (iovcnt <= 0 || iovcnt > IOV_MAX) {
+		return (set_errno(EMSGSIZE));
+	}
+	if (iovcnt > IOV_MAX_STACK) {
+		iovsize = iovcnt * sizeof (struct iovec);
+		aiov = kmem_alloc(iovsize, KM_SLEEP);
+	} else {
+		iovsize = 0;
+		aiov = buf;
+	}
+
+#if defined(_LP64)
+	if (get_udatamodel() != DATAMODEL_NATIVE) {
+		/* convert from 32bit iovec structs */
+		struct iovec32 buf32[IOV_MAX_STACK], *aiov32 = buf32;
+		ssize_t iov32size;
+		ssize32_t count32;
+
+		iov32size = iovcnt * sizeof (struct iovec32);
+		if (iovsize != 0) {
+			aiov32 = kmem_alloc(iov32size, KM_SLEEP);
+		}
+
+		if (copyin((struct iovec32 *)smsg.msg_iov, aiov32, iov32size)) {
+			if (iovsize != 0) {
+				kmem_free(aiov32, iov32size);
+				kmem_free(aiov, iovsize);
+			}
+
+			return (set_errno(EFAULT));
+		}
+
+		count32 = 0;
+		for (i = 0; i < iovcnt; i++) {
+			ssize32_t iovlen32;
+
+			iovlen32 = aiov32[i].iov_len;
+			count32 += iovlen32;
+			if (iovlen32 < 0 || count32 < 0) {
+				if (iovsize != 0) {
+					kmem_free(aiov32, iov32size);
+					kmem_free(aiov, iovsize);
+				}
+
+				return (set_errno(EINVAL));
+			}
+
+			aiov[i].iov_len = iovlen32;
+			aiov[i].iov_base =
+			    (caddr_t)(uintptr_t)aiov32[i].iov_base;
+		}
+		len = count32;
+
+		if (iovsize != 0) {
+			kmem_free(aiov32, iov32size);
+		}
+	} else
+#endif /* defined(_LP64) */
+	{
+		if (copyin(smsg.msg_iov, aiov,
+		    iovcnt * sizeof (struct iovec)) != 0) {
+			if (iovsize != 0) {
+				kmem_free(aiov, iovsize);
+			}
+			return (set_errno(EFAULT));
+		}
+
+		len = 0;
+		for (i = 0; i < iovcnt; i++) {
+			ssize_t iovlen = aiov[i].iov_len;
+
+			len += iovlen;
+			if (iovlen < 0 || len < 0) {
+				if (iovsize != 0) {
+					kmem_free(aiov, iovsize);
+				}
+				return (set_errno(EINVAL));
+			}
+		}
+	}
+	/* Since the iovec is passed via the uio, NULL it out in the msg */
+	smsg.msg_iov = NULL;
+
+	auio.uio_loffset = 0;
+	auio.uio_iov = aiov;
+	auio.uio_iovcnt = iovcnt;
+	auio.uio_resid = len;
+	auio.uio_segflg = UIO_USERSPACE;
+	auio.uio_limit = 0;
+
+	res = lx_send_common(sock, &smsg, &auio, flags);
+
+	if (iovsize != 0) {
+		kmem_free(aiov, iovsize);
+	}
+
+	return (res);
+}
+
+/*
+ * Linux socket option type definitions
+ *
+ * The protocol `levels` are well defined (see in.h) The option values are
+ * not so well defined. Linux often uses different values vs. Illumos
+ * although they mean the same thing. For example, IP_TOS in Linux is
+ * defined as value 1 but in Illumos it is defined as value 3. This table
+ * maps all the Protocol levels to their options and maps them between
+ * Linux and Illumos and vice versa.  Hence the reason for the complexity.
+ *
+ * For a certain subset of sockopts, Linux will implicitly truncate optval
+ * input, so long as optlen meets a minimum size.  Because illumos is strict
+ * about optlen, we must cap optlen for those options.
+ */
+
+typedef struct lx_sockopt_map {
+	const int lsm_opt;	/* Illumos-native equivalent */
+	const int lsm_lcap;	/* Cap optlen to this size. (Ignored if 0) */
+} lx_sockopt_map_t;
+
+typedef struct lx_proto_opts {
+	const lx_sockopt_map_t	*lpo_entries;	/* Linux->SunOS map entries */
+	unsigned int		lpo_max;	/* max entries in table */
+} lx_proto_opts_t;
+
+#define	OPTNOTSUP	-1	/* we don't support it */
+
+#define	PROTO_SOCKOPTS(opts)    \
+	{ (opts), sizeof ((opts)) / sizeof ((opts)[0]) }
+
+/* Shorten name so the columns can line up */
+#define	IP_MREQ_SZ	sizeof (struct ip_mreq)
+
+static const lx_sockopt_map_t ltos_ip_sockopts[LX_IP_UNICAST_IF + 1] = {
+	{ OPTNOTSUP, 0 },
+	{ IP_TOS, sizeof (int) },		/* IP_TOS		*/
+	{ IP_TTL, sizeof (int) },		/* IP_TTL		*/
+	{ IP_HDRINCL, sizeof (int) },		/* IP_HDRINCL		*/
+	{ IP_OPTIONS, 0 },			/* IP_OPTIONS		*/
+	{ OPTNOTSUP, 0 },			/* IP_ROUTER_ALERT	*/
+	{ IP_RECVOPTS, sizeof (int) },		/* IP_RECVOPTS		*/
+	{ IP_RETOPTS, sizeof (int) },		/* IP_RETOPTS		*/
+	{ IP_PKTINFO, sizeof (int) },		/* IP_PKTINFO		*/
+	{ OPTNOTSUP, 0 },			/* IP_PKTOPTIONS	*/
+	{ OPTNOTSUP, 0 },			/* IP_MTUDISCOVER	*/
+	{ OPTNOTSUP, 0 },			/* IP_RECVERR		*/
+	{ IP_RECVTTL, sizeof (int) },		/* IP_RECVTTL		*/
+	{ OPTNOTSUP, 0 },			/* IP_RECVTOS		*/
+	{ OPTNOTSUP, 0 },			/* IP_MTU		*/
+	{ OPTNOTSUP, 0 },			/* IP_FREEBIND		*/
+	{ OPTNOTSUP, 0 },			/* IP_IPSEC_POLICY	*/
+	{ OPTNOTSUP, 0 },			/* IP_XFRM_POLICY	*/
+	{ OPTNOTSUP, 0 },			/* IP_PASSSEC		*/
+	{ OPTNOTSUP, 0 },			/* IP_TRANSPARENT	*/
+	{ OPTNOTSUP, 0 },			/* IP_ORIGDSTADDR	*/
+	{ OPTNOTSUP, 0 },			/* IP_MINTTL		*/
+	{ OPTNOTSUP, 0 },			/* IP_NODEFRAG		*/
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ IP_MULTICAST_IF, sizeof (int) },	/* IP_MULTICAST_IF	*/
+	{ IP_MULTICAST_TTL, sizeof (int) },	/* IP_MULTICAST_TTL	*/
+	{ IP_MULTICAST_LOOP, sizeof (int) },	/* IP_MULTICAST_LOOP	*/
+	{ IP_ADD_MEMBERSHIP, IP_MREQ_SZ },	/* IP_ADD_MEMBERSHIP	*/
+	{ IP_DROP_MEMBERSHIP, IP_MREQ_SZ },	/* IP_DROP_MEMBERSHIP	*/
+	{ IP_UNBLOCK_SOURCE, 0 },		/* IP_UNBLOCK_SOURCE	*/
+	{ IP_BLOCK_SOURCE, 0 },			/* IP_BLOCK_SOURCE	*/
+	{ IP_ADD_SOURCE_MEMBERSHIP, 0 },	/* IP_ADD_SOURCE_MEMBERSHIP */
+	{ OPTNOTSUP, 0 },			/* IP_DROP_SOURCE_MEMBERSHIP */
+	{ OPTNOTSUP, 0 },			/* IP_MSFILTER		*/
+	{ OPTNOTSUP, 0 },			/* MCAST_JOIN_GROUP	*/
+	{ OPTNOTSUP, 0 },			/* MCAST_BLOCK_SOURCE	*/
+	{ OPTNOTSUP, 0 },			/* MCAST_UNBLOCK_SOURCE	*/
+	{ OPTNOTSUP, 0 },			/* MCAST_LEAVE_GROUP	*/
+	{ OPTNOTSUP, 0 },			/* MCAST_JOIN_SOURCE_GROUP */
+	{ OPTNOTSUP, 0 },			/* MCAST_LEAVE_SOURCE_GROUP */
+	{ OPTNOTSUP, 0 },			/* MCAST_MSFILTER	*/
+	{ OPTNOTSUP, 0 },			/* IP_MULTICAST_ALL	*/
+	{ OPTNOTSUP, 0 }			/* IP_UNICAST_IF	*/
+};
+
+static const lx_sockopt_map_t ltos_ipv6_sockopts[LX_IPV6_TCLASS + 1] = {
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },			/* IPV6_ADDRFORM	*/
+	{ OPTNOTSUP, 0 },			/* IPV6_2292PKTINFO	*/
+	{ OPTNOTSUP, 0 },			/* IPV6_2292HOPOPTS	*/
+	{ OPTNOTSUP, 0 },			/* IPV6_2292DSTOPTS	*/
+	{ OPTNOTSUP, 0 },			/* IPV6_2292RTHDR	*/
+	{ OPTNOTSUP, 0 },			/* IPV6_2292PKTOPTIONS	*/
+	{ IPV6_CHECKSUM, sizeof (int) },	/* IPV6_CHECKSUM	*/
+	{ OPTNOTSUP, 0 },			/* IPV6_2292HOPLIMIT	*/
+	{ OPTNOTSUP, 0 },			/* IPV6_NEXTHOP		*/
+	{ OPTNOTSUP, 0 },			/* IPV6_AUTHHDR		*/
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ IPV6_UNICAST_HOPS, sizeof (int) },	/* IPV6_UNICAST_HOPS	*/
+	{ IPV6_MULTICAST_IF, sizeof (int) },	/* IPV6_MULTICAST_IF	*/
+	{ IPV6_MULTICAST_HOPS, sizeof (int) },	/* IPV6_MULTICAST_HOPS	*/
+	{ IPV6_MULTICAST_LOOP, sizeof (int) },	/* IPV6_MULTICAST_LOOP	*/
+	{ OPTNOTSUP, 0 },			/* IPV6_JOIN_GROUP	*/
+	{ OPTNOTSUP, 0 },			/* IPV6_LEAVE_GROUP	*/
+	{ OPTNOTSUP, 0 },			/* IPV6_ROUTER_ALERT	*/
+	{ OPTNOTSUP, 0 },			/* IPV6_MTU_DISCOVER	*/
+	{ OPTNOTSUP, 0 },			/* IPV6_MTU		*/
+	{ OPTNOTSUP, 0 },			/* IPV6_RECVERR		*/
+	{ IPV6_V6ONLY, sizeof (int) },		/* IPV6_V6ONLY		*/
+	{ OPTNOTSUP, 0 },			/* IPV6_JOIN_ANYCAST	*/
+	{ OPTNOTSUP, 0 },			/* IPV6_LEAVE_ANYCAST	*/
+	{ OPTNOTSUP, 0 },			/* IPV6_IPSEC_POLICY	*/
+	{ OPTNOTSUP, 0 },			/* IPV6_XFRM_POLICY	*/
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ IPV6_RECVPKTINFO, sizeof (int) },	/* IPV6_RECVPKTINFO	*/
+	{ IPV6_PKTINFO, 0 },			/* IPV6_PKTINFO		*/
+	{ IPV6_RECVHOPLIMIT, sizeof (int) },	/* IPV6_RECVHOPLIMIT	*/
+	{ IPV6_HOPLIMIT, 0 },			/* IPV6_HOPLIMIT	*/
+	{ OPTNOTSUP, 0 },			/* IPV6_RECVHOPOPTS	*/
+	{ OPTNOTSUP, 0 },			/* IPV6_HOPOPTS		*/
+	{ OPTNOTSUP, 0 },			/* IPV6_RTHDRDSTOPTS	*/
+	{ OPTNOTSUP, 0 },			/* IPV6_RECVRTHDR	*/
+	{ OPTNOTSUP, 0 },			/* IPV6_RTHDR		*/
+	{ OPTNOTSUP, 0 },			/* IPV6_RECVDSTOPTS	*/
+	{ OPTNOTSUP, 0 },			/* IPV6_DSTOPTS		*/
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },			/* IPV6_RECVTCLASS	*/
+	{ IPV6_TCLASS, sizeof (int) }		/* IPV6_TCLASS		*/
+};
+
+static const lx_sockopt_map_t ltos_icmpv6_sockopts[LX_ICMP6_FILTER + 1] = {
+	{ OPTNOTSUP, 0 },
+	{ ICMP6_FILTER, 0 }	/* ICMP6_FILTER	*/
+};
+
+static const lx_sockopt_map_t ltos_tcp_sockopts[LX_TCP_NOTSENT_LOWAT + 1] = {
+	{ OPTNOTSUP, 0 },
+	{ TCP_NODELAY, sizeof (int) },		/* TCP_NODELAY		*/
+	{ TCP_MAXSEG, sizeof (int) },		/* TCP_MAXSEG		*/
+	{ TCP_CORK, sizeof (int) },		/* TCP_CORK		*/
+	{ TCP_KEEPIDLE, sizeof (int) },		/* TCP_KEEPIDLE		*/
+	{ TCP_KEEPINTVL, sizeof (int) },	/* TCP_KEEPINTVL	*/
+	{ TCP_KEEPCNT, sizeof (int) },		/* TCP_KEEPCNT		*/
+	{ OPTNOTSUP, 0 },			/* TCP_SYNCNT		*/
+	{ TCP_LINGER2, sizeof (int) },		/* TCP_LINGER2		*/
+	{ OPTNOTSUP, 0 },			/* TCP_DEFER_ACCEPT	*/
+	{ OPTNOTSUP, 0 },			/* TCP_WINDOW_CLAMP	*/
+	{ OPTNOTSUP, 0 },			/* TCP_INFO		*/
+	{ OPTNOTSUP, 0 },			/* TCP_QUICKACK		*/
+	{ OPTNOTSUP, 0 },			/* TCP_CONGESTION	*/
+	{ OPTNOTSUP, 0 },			/* TCP_MD5SIG		*/
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },			/* TCP_THIN_LINEAR_TIMEOUTS */
+	{ OPTNOTSUP, 0 },			/* TCP_THIN_DUPACK	*/
+	{ OPTNOTSUP, 0 },			/* TCP_USER_TIMEOUT	*/
+	{ OPTNOTSUP, 0 },			/* TCP_REPAIR		*/
+	{ OPTNOTSUP, 0 },			/* TCP_REPAIR_QUEUE	*/
+	{ OPTNOTSUP, 0 },			/* TCP_QUEUE_SEQ	*/
+	{ OPTNOTSUP, 0 },			/* TCP_REPAIR_OPTIONS	*/
+	{ OPTNOTSUP, 0 },			/* TCP_FASTOPEN		*/
+	{ OPTNOTSUP, 0 },			/* TCP_TIMESTAMP	*/
+	{ OPTNOTSUP, 0 }			/* TCP_NOTSENT_LOWAT	*/
+};
+
+static const lx_sockopt_map_t ltos_igmp_sockopts[IGMP_MTRACE + 1] = {
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ IGMP_MINLEN, 0 },		/* IGMP_MINLEN			*/
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ IGMP_MEMBERSHIP_QUERY, 0 },	/* IGMP_HOST_MEMBERSHIP_QUERY	*/
+	{ IGMP_V1_MEMBERSHIP_REPORT, 0 }, /* IGMP_HOST_MEMBERSHIP_REPORT */
+	{ IGMP_DVMRP, 0 },		/* IGMP_DVMRP			*/
+	{ IGMP_PIM, 0 },		/* IGMP_PIM			*/
+	{ OPTNOTSUP, 0 },		/* IGMP_TRACE			*/
+	{ IGMP_V2_MEMBERSHIP_REPORT, 0 }, /* IGMPV2_HOST_MEMBERSHIP_REPORT */
+	{ IGMP_V2_LEAVE_GROUP, 0 },	/* IGMP_HOST_LEAVE_MESSAGE	*/
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },
+	{ IGMP_MTRACE_RESP, 0 },	/* IGMP_MTRACE_RESP		*/
+	{ IGMP_MTRACE, 0 }		/* IGMP_MTRACE			*/
+};
+
+static const lx_sockopt_map_t ltos_socket_sockopts[LX_SO_BPF_EXTENSIONS + 1] = {
+	{ OPTNOTSUP, 0 },
+	{ SO_DEBUG, sizeof (int) },	/* SO_DEBUG			*/
+	{ SO_REUSEADDR, sizeof (int) },	/* SO_REUSEADDR			*/
+	{ SO_TYPE, 0 },			/* SO_TYPE			*/
+	{ SO_ERROR, 0 },		/* SO_ERROR			*/
+	{ SO_DONTROUTE, sizeof (int) },	/* SO_DONTROUTE			*/
+	{ SO_BROADCAST, sizeof (int) },	/* SO_BROADCAST			*/
+	{ SO_SNDBUF, sizeof (int) },	/* SO_SNDBUF			*/
+	{ SO_RCVBUF, sizeof (int) },	/* SO_RCVBUF			*/
+	{ SO_KEEPALIVE, sizeof (int) },	/* SO_KEEPALIVE			*/
+	{ SO_OOBINLINE, sizeof (int) },	/* SO_OOBINLINE			*/
+	{ OPTNOTSUP, 0 },		/* SO_NO_CHECK			*/
+	{ OPTNOTSUP, 0 },		/* SO_PRIORITY			*/
+	{ SO_LINGER, 0 },		/* SO_LINGER			*/
+	{ OPTNOTSUP, 0 },		/* SO_BSDCOMPAT			*/
+	{ SO_REUSEPORT, sizeof (int) },	/* SO_REUSEPORT			*/
+	{ SO_RECVUCRED, sizeof (int) },	/* SO_PASSCRED			*/
+	{ OPTNOTSUP, 0 },		/* SO_PEERCRED			*/
+	{ SO_RCVLOWAT, sizeof (int) },	/* SO_RCVLOWAT			*/
+	{ SO_SNDLOWAT, sizeof (int) },	/* SO_SNDLOWAT			*/
+	{ SO_RCVTIMEO, 0 },		/* SO_RCVTIMEO			*/
+	{ SO_SNDTIMEO, 0 },		/* SO_SNDTIMEO			*/
+	{ OPTNOTSUP, 0 },		/* SO_SECURITY_AUTHENTICATION	*/
+	{ OPTNOTSUP, 0 },		/* SO_SECURITY_ENCRYPTION_TRANSPORT */
+	{ OPTNOTSUP, 0 },		/* SO_SECURITY_ENCRYPTION_NETWORK */
+	{ OPTNOTSUP, 0 },		/* SO_BINDTODEVICE		*/
+	{ SO_ATTACH_FILTER, 0 },	/* SO_ATTACH_FILTER		*/
+	{ SO_DETACH_FILTER, 0 },	/* SO_DETACH_FILTER		*/
+	{ OPTNOTSUP, 0 },		/* SO_PEERNAME			*/
+	{ SO_TIMESTAMP, sizeof (int) },	/* SO_TIMESTAMP			*/
+	{ SO_ACCEPTCONN, 0 },		/* SO_ACCEPTCONN		*/
+	{ OPTNOTSUP, 0 },		/* SO_PEERSEC			*/
+	{ SO_SNDBUF, sizeof (int) },	/* SO_SNDBUFFORCE		*/
+	{ SO_RCVBUF, sizeof (int) },	/* SO_RCVBUFFORCE		*/
+	{ OPTNOTSUP, 0 },		/* SO_PASSSEC			*/
+	{ OPTNOTSUP, 0 },		/* SO_TIMESTAMPNS		*/
+	{ OPTNOTSUP, 0 },		/* SO_MARK			*/
+	{ OPTNOTSUP, 0 },		/* SO_TIMESTAMPING		*/
+	{ SO_PROTOTYPE, 0 },		/* SO_PROTOCOL			*/
+	{ SO_DOMAIN, 0 },		/* SO_DOMAIN			*/
+	{ OPTNOTSUP, 0 },		/* SO_RXQ_OVFL			*/
+	{ OPTNOTSUP, 0 },		/* SO_WIFI_STATUS		*/
+	{ OPTNOTSUP, 0 },		/* SO_PEEK_OFF			*/
+	{ OPTNOTSUP, 0 },		/* SO_NOFCS			*/
+	{ OPTNOTSUP, 0 },		/* SO_LOCK_FILTER		*/
+	{ OPTNOTSUP, 0 },		/* SO_SELECT_ERR_QUEUE		*/
+	{ OPTNOTSUP, 0 },		/* SO_BUSY_POLL			*/
+	{ OPTNOTSUP, 0 },		/* SO_MAX_PACING_RATE		*/
+	{ OPTNOTSUP, 0 }		/* SO_BPF_EXTENSIONS		*/
+};
+
+static const lx_sockopt_map_t ltos_raw_sockopts[LX_ICMP_FILTER + 1] = {
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 }		/* ICMP_FILTER		*/
+};
+
+static const lx_sockopt_map_t ltos_packet_sockopts[LX_PACKET_STATISTICS + 1] = {
+	{ OPTNOTSUP, 0 },
+	{ PACKET_ADD_MEMBERSHIP, 0 },	/* PACKET_ADD_MEMBERSHIP	*/
+	{ PACKET_DROP_MEMBERSHIP, 0 },	/* PACKET_DROP_MEMBERSHIP	*/
+	{ OPTNOTSUP, 0 },		/* PACKET_RECV_OUTPUT		*/
+	{ OPTNOTSUP, 0 },
+	{ OPTNOTSUP, 0 },		/* PACKET_RX_RING		*/
+	{ PACKET_STATISTICS, 0 }	/* PACKET_STATISTICS		*/
+};
+
+/* Needed for SO_ATTACH_FILTER */
+struct lx_bpf_program {
+    unsigned short bf_len;
+    caddr_t bf_insns;
+};
+
+/* Invert filter fields as Linux expects */
+#define	LX_ICMP6_FILTER_INVERT(filterp) ( \
+	((filterp)->__icmp6_filt[0] ^= 0xFFFFFFFFU), \
+	((filterp)->__icmp6_filt[1] ^= 0xFFFFFFFFU), \
+	((filterp)->__icmp6_filt[2] ^= 0xFFFFFFFFU), \
+	((filterp)->__icmp6_filt[3] ^= 0xFFFFFFFFU), \
+	((filterp)->__icmp6_filt[4] ^= 0xFFFFFFFFU), \
+	((filterp)->__icmp6_filt[5] ^= 0xFFFFFFFFU), \
+	((filterp)->__icmp6_filt[6] ^= 0xFFFFFFFFU), \
+	((filterp)->__icmp6_filt[7] ^= 0xFFFFFFFFU))
+
+static boolean_t
+lx_sockopt_lookup(lx_proto_opts_t tbl, int *optname, socklen_t *optlen)
+{
+	const lx_sockopt_map_t *entry;
+
+	if (*optname > tbl.lpo_max) {
+		return (B_FALSE);
+	}
+	entry = &tbl.lpo_entries[*optname];
+	if (entry->lsm_opt == OPTNOTSUP) {
+		return (B_FALSE);
+	}
+	*optname = entry->lsm_opt;
+	/* Truncate the optlen if needed/allowed */
+	if (entry->lsm_lcap != 0 && *optlen > entry->lsm_lcap) {
+		*optlen = entry->lsm_lcap;
+	}
+	return (B_TRUE);
+}
+
+static int
+lx_setsockopt_ip(sonode_t *so, int optname, void *optval, socklen_t optlen)
+{
+	int error;
+	int *intval = (int *)optval;
+	lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_ip_sockopts);
+
+	switch (optname) {
+	case LX_IP_RECVERR:
+		/*
+		 * Ping sets this option to receive errors on raw sockets.
+		 * Currently we just ignore it to make ping happy. From the
+		 * Linux ip.7 man page:
+		 *
+		 *   For raw sockets, IP_RECVERR enables passing of all
+		 *   received ICMP errors to the application.
+		 *
+		 * Programs known to depend upon this:
+		 * - ping
+		 * - traceroute
+		 * - mount.nfs
+		 */
+		return (0);
+
+	case LX_IP_MTU_DISCOVER: {
+		int val;
+
+		/*
+		 * We translate Linux's IP_MTU_DISCOVER into our IP_DONTFRAG,
+		 * allowing this be a byte or an integer and observing the
+		 * inverted sense of the two relative to one another (and
+		 * translating accordingly).
+		 */
+		if (optlen < sizeof (int)) {
+			val = *((uint8_t *)optval);
+		} else {
+			val = *((int *)optval);
+		}
+
+		switch (val) {
+		case LX_IP_PMTUDISC_DONT:
+			val = 1;
+			break;
+
+		case LX_IP_PMTUDISC_DO:
+		case LX_IP_PMTUDISC_WANT:
+			val = 0;
+			break;
+
+		default:
+			return (EOPNOTSUPP);
+		}
+
+		error = socket_setsockopt(so, IPPROTO_IP, IP_DONTFRAG,
+		    &val, sizeof (val), CRED());
+		return (error);
+	}
+
+	case LX_IP_MULTICAST_TTL:
+	case LX_IP_MULTICAST_LOOP:
+		/*
+		 * For IP_MULTICAST_TTL and IP_MULTICAST_LOOP, Linux defines
+		 * the option value to be an integer while we define it to be
+		 * an unsigned character.  To prevent the kernel from spitting
+		 * back an error on an illegal length, verify that the option
+		 * value is less than UCHAR_MAX before truncating optlen.
+		 */
+		if (optlen <= 0 || optlen > sizeof (int) ||
+		    *intval > UINT8_MAX) {
+			return (EINVAL);
+		}
+		optlen = sizeof (uchar_t);
+		break;
+
+	default:
+		break;
+	}
+
+	if (!lx_sockopt_lookup(sockopts_tbl, &optname, &optlen)) {
+		return (ENOPROTOOPT);
+	}
+
+	error = socket_setsockopt(so, IPPROTO_IP, optname, optval, optlen,
+	    CRED());
+	return (error);
+}
+
+static int
+lx_setsockopt_ipv6(sonode_t *so, int optname, void *optval, socklen_t optlen)
+{
+	int error;
+	lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_ipv6_sockopts);
+
+	if (optname == LX_IPV6_MTU) {
+		/*
+		 * There isn't a good translation for IPV6_MTU and certain apps
+		 * such as bind9 will bail if it cannot be set.
+		 * We just lie about the success for now.
+		 */
+		return (0);
+	}
+
+	if (!lx_sockopt_lookup(sockopts_tbl, &optname, &optlen)) {
+		return (ENOPROTOOPT);
+	}
+	error = socket_setsockopt(so, IPPROTO_IPV6, optname, optval, optlen,
+	    CRED());
+	return (error);
+}
+
+static int
+lx_setsockopt_icmpv6(sonode_t *so, int optname, void *optval, socklen_t optlen)
+{
+	int error;
+	lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_icmpv6_sockopts);
+
+	if (optname == LX_ICMP6_FILTER && optval != NULL) {
+		/*
+		 * Surprise! The input to ICMP6_FILTER on Linux is inverted
+		 * when compared to illumos.
+		 */
+		if (optlen != sizeof (icmp6_filter_t)) {
+			return (EINVAL);
+		}
+		LX_ICMP6_FILTER_INVERT((icmp6_filter_t *)optval);
+	}
+
+	if (!lx_sockopt_lookup(sockopts_tbl, &optname, &optlen)) {
+		return (ENOPROTOOPT);
+	}
+	error = socket_setsockopt(so, IPPROTO_ICMPV6, optname, optval, optlen,
+	    CRED());
+	return (error);
+}
+
+static int
+lx_setsockopt_tcp(sonode_t *so, int optname, void *optval, socklen_t optlen)
+{
+	int error;
+	lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_tcp_sockopts);
+
+	if (optname == LX_TCP_DEFER_ACCEPT) {
+		int *intval;
+		char *dfp;
+
+		/*
+		 * Emulate TCP_DEFER_ACCEPT using the datafilt(7M) socket
+		 * filter but we can't emulate the timeout aspect so treat any
+		 * non-zero value as enabling and zero as disabling.
+		 */
+		if (optlen != sizeof (int)) {
+			return (EINVAL);
+		}
+		intval = (int *)optval;
+
+		/*
+		 * socket_setsockopt asserts that the optval is aligned, so
+		 * we use kmem_alloc to ensure this.
+		 */
+		dfp = (char *)kmem_alloc(sizeof (DATAFILT), KM_SLEEP);
+		(void) strcpy(dfp, DATAFILT);
+
+		if (*intval > 0) {
+			error = socket_setsockopt(so, SOL_FILTER, FIL_ATTACH,
+			    dfp, 9, CRED());
+			if (error == EEXIST) {
+				error = 0;
+			}
+		} else {
+			error = socket_setsockopt(so, SOL_FILTER, FIL_DETACH,
+			    dfp, 9, CRED());
+			if (error == ENXIO) {
+				error = 0;
+			}
+		}
+		kmem_free(dfp, sizeof (DATAFILT));
+		return (error);
+	}
+
+	if (!lx_sockopt_lookup(sockopts_tbl, &optname, &optlen)) {
+		return (ENOPROTOOPT);
+	}
+
+	error = socket_setsockopt(so, IPPROTO_TCP, optname, optval, optlen,
+	    CRED());
+	return (error);
+}
+
+static int
+lx_setsockopt_socket(sonode_t *so, int optname, void *optval, socklen_t optlen)
+{
+	int error;
+	lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_socket_sockopts);
+	struct lx_bpf_program *lbp;
+	int *intval;
+	struct bpf_program bp;
+
+	switch (optname) {
+	case LX_SO_BSDCOMPAT:
+		/* Linux ignores this option. */
+		return (0);
+
+	case LX_SO_TIMESTAMP:
+		/*
+		 * SO_TIMESTAMP is not supported on AF_UNIX sockets but we have
+		 * some of those which apps use for logging, etc., so pretend
+		 * this worked.
+		 */
+		if (so->so_family == AF_UNIX) {
+			return (0);
+		}
+		break;
+
+	case LX_SO_ATTACH_FILTER:
+		/*
+		 * Convert bpf program struct
+		 */
+		if (optlen != sizeof (struct lx_bpf_program)) {
+			return (EINVAL);
+		}
+		lbp = (struct lx_bpf_program *)optval;
+		bp.bf_len = lbp->bf_len;
+		/* LINTED: alignment */
+		bp.bf_insns = (struct bpf_insn *)lbp->bf_insns;
+		optval = &bp;
+		break;
+
+	case LX_SO_PASSSEC:
+		/*
+		 * SO_PASSSEC is very similar to SO_PASSCRED (emulated by
+		 * SO_RECVUCRED) in that it requests that cmsgs containing
+		 * identity information be attached to recieved messages.
+		 * Instead of ucred information, security-module-specific
+		 * information such as selinux label is expected
+		 *
+		 * Since LX does not at all support selinux today, the
+		 * option is silently accepted.
+		 */
+		return (0);
+
+	case LX_SO_PASSCRED:
+		/*
+		 * In many cases, the Linux SO_PASSCRED is mapped to the SunOS
+		 * SO_RECVUCRED to enable the passing of peer credential
+		 * information via received cmsgs.  One exception is for
+		 * connection-oriented AF_UNIX sockets which do not yet support
+		 * that option.  Instead, we track the setting internally and,
+		 * when there is appropriate cmsg space, emulate the credential
+		 * passing by querying the STREAMS ioctl.
+		 */
+		if (so->so_family == AF_UNIX &&
+		    (so->so_mode & SM_CONNREQUIRED) != 0) {
+			lx_socket_aux_data_t *sad;
+
+			if (optlen != sizeof (int)) {
+				return (EINVAL);
+			}
+			intval = (int *)optval;
+			sad = lx_sad_acquire(SOTOV(so));
+			sad->lxsad_stream_cred = !(*intval == 0);
+			mutex_exit(&sad->lxsad_lock);
+			return (0);
+		}
+		break;
+	}
+
+	if (!lx_sockopt_lookup(sockopts_tbl, &optname, &optlen)) {
+		return (ENOPROTOOPT);
+	}
+
+	error = socket_setsockopt(so, SOL_SOCKET, optname, optval, optlen,
+	    CRED());
+	return (error);
+}
+
+static int
+lx_setsockopt_raw(sonode_t *so, int optname, void *optval, socklen_t optlen)
+{
+	int error;
+	lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_raw_sockopts);
+
+	switch (optname) {
+	case LX_ICMP_FILTER:
+		/*
+		 * This option is currently ignored to appease ping.
+		 */
+		return (0);
+
+	case LX_IPV6_CHECKSUM:
+		/*
+		 * Ping6 tries to set the IPV6_CHECKSUM offset in a way that
+		 * illumos won't allow.  Quietly ignore this to prevent it from
+		 * complaining.
+		 */
+		return (0);
+
+	default:
+		break;
+	}
+
+	if (!lx_sockopt_lookup(sockopts_tbl, &optname, &optlen)) {
+		return (ENOPROTOOPT);
+	}
+
+	error = socket_setsockopt(so, IPPROTO_TCP, optname, optval, optlen,
+	    CRED());
+	return (error);
+}
+
+static int
+lx_setsockopt_packet(sonode_t *so, int optname, void *optval, socklen_t optlen)
+{
+	int error;
+	lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_packet_sockopts);
+	struct packet_mreq *mr;
+
+	switch (optname) {
+	case LX_PACKET_ADD_MEMBERSHIP:
+	case LX_PACKET_DROP_MEMBERSHIP:
+		/* Convert Linux mr_type to illumos */
+		if (optlen != sizeof (struct packet_mreq)) {
+			return (EINVAL);
+		}
+		mr = (struct packet_mreq *)optval;
+		if (--mr->mr_type > PACKET_MR_ALLMULTI)
+			return (EINVAL);
+		optval = mr;
+		break;
+
+	default:
+		break;
+	}
+
+	if (!lx_sockopt_lookup(sockopts_tbl, &optname, &optlen)) {
+		return (ENOPROTOOPT);
+	}
+
+	error = socket_setsockopt(so, SOL_PACKET, optname, optval, optlen,
+	    CRED());
+	return (error);
+}
+
+static int
+lx_setsockopt_igmp(sonode_t *so, int optname, void *optval, socklen_t optlen)
+{
+	int error;
+	lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_igmp_sockopts);
+
+	if (!lx_sockopt_lookup(sockopts_tbl, &optname, &optlen)) {
+		return (ENOPROTOOPT);
+	}
+
+	error = socket_setsockopt(so, IPPROTO_IGMP, optname, optval, optlen,
+	    CRED());
+	return (error);
+}
+
+static int
+lx_getsockopt_ip(sonode_t *so, int optname, void *optval, socklen_t *optlen)
+{
+	int error = 0;
+	lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_ip_sockopts);
+
+	if (!lx_sockopt_lookup(sockopts_tbl, &optname, optlen)) {
+		return (ENOPROTOOPT);
+	}
+
+	error = socket_getsockopt(so, IPPROTO_IP, optname, optval, optlen, 0,
+	    CRED());
+	return (error);
+}
+
+static int
+lx_getsockopt_ipv6(sonode_t *so, int optname, void *optval, socklen_t *optlen)
+{
+	int error = 0;
+	lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_ipv6_sockopts);
+
+	if (!lx_sockopt_lookup(sockopts_tbl, &optname, optlen)) {
+		return (ENOPROTOOPT);
+	}
+
+	error = socket_getsockopt(so, IPPROTO_IPV6, optname, optval, optlen, 0,
+	    CRED());
+	return (error);
+}
+
+static int
+lx_getsockopt_icmpv6(sonode_t *so, int optname, void *optval,
+    socklen_t *optlen)
+{
+	int error = 0;
+	lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_icmpv6_sockopts);
+
+	if (optname == LX_ICMP6_FILTER) {
+		error = socket_getsockopt(so, IPPROTO_ICMPV6, ICMP6_FILTER,
+		    optval, optlen, 0, CRED());
+
+		/*
+		 * ICMP6_FILTER is inverted on Linux. Make it so before copying
+		 * back to caller's buffer.
+		 */
+		if (error == 0) {
+			LX_ICMP6_FILTER_INVERT((icmp6_filter_t *)optval);
+		}
+		return (error);
+	}
+
+	if (!lx_sockopt_lookup(sockopts_tbl, &optname, optlen)) {
+		return (ENOPROTOOPT);
+	}
+
+	error = socket_getsockopt(so, IPPROTO_ICMPV6, optname, optval, optlen,
+	    0, CRED());
+	return (error);
+}
+
+static int
+lx_getsockopt_tcp(sonode_t *so, int optname, void *optval, socklen_t *optlen)
+{
+	int error = 0;
+	int *intval = (int *)optval;
+	lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_tcp_sockopts);
+
+	switch (optname) {
+	case LX_TCP_CORK:
+		/*
+		 * We do not support TCP_CORK but some apps rely on it.  Rather
+		 * than return an error we just return 0.  This isn't exactly a
+		 * lie, since this option really isn't set, but it's not the
+		 * whole truth either. Fortunately, we aren't under oath.
+		 */
+		if (*optlen < sizeof (int)) {
+			error = EINVAL;
+		} else {
+			*intval = 0;
+		}
+		*optlen = sizeof (int);
+		return (error);
+
+	case LX_TCP_DEFER_ACCEPT:
+		/*
+		 * We do support TCP_DEFER_ACCEPT using the datafilt(7M) socket
+		 * filter but we don't emulate the timeout aspect so treat the
+		 * existence as 1 and absence as 0.
+		 */
+		if (*optlen < sizeof (int)) {
+			error = EINVAL;
+		} else {
+			struct fil_info fi[10];
+			int i;
+			socklen_t len = sizeof (fi);
+
+			if ((error = socket_getsockopt(so, SOL_FILTER,
+			    FIL_LIST, fi, &len, 0, CRED())) != 0) {
+				*optlen = sizeof (int);
+				return (error);
+			}
+
+			*intval = 0;
+			len = len / sizeof (struct fil_info);
+			for (i = 0; i < len; i++) {
+				if (fi[i].fi_flags == FILF_PROG &&
+				    strcmp(fi[i].fi_name, "datafilt") == 0) {
+					*intval = 1;
+					break;
+				}
+			}
+		}
+		*optlen = sizeof (int);
+		return (error);
+	default:
+		break;
+	}
+
+	if (!lx_sockopt_lookup(sockopts_tbl, &optname, optlen)) {
+		return (ENOPROTOOPT);
+	}
+
+	error = socket_getsockopt(so, IPPROTO_TCP, optname, optval, optlen, 0,
+	    CRED());
+	return (error);
+}
+
+static int
+lx_getsockopt_socket(sonode_t *so, int optname, void *optval,
+    socklen_t *optlen)
+{
+	int error = 0;
+	int *intval = (int *)optval;
+	lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_socket_sockopts);
+
+	switch (optname) {
+	case LX_SO_PASSSEC:
+		/*
+		 * Communicate value of 0 since selinux-related functionality
+		 * is not supported.
+		 */
+		if (*optlen < sizeof (int)) {
+			error = EINVAL;
+		} else {
+			*intval = 0;
+		}
+		*optlen = sizeof (int);
+		return (error);
+
+	case LX_SO_PASSCRED:
+		/*
+		 * Special handling for connection-oriented AF_UNIX sockets.
+		 * See lx_setsockopt_socket for more details.
+		 */
+		if (so->so_family == AF_UNIX &&
+		    (so->so_mode & SM_CONNREQUIRED) != 0) {
+			lx_socket_aux_data_t *sad;
+
+			if (*optlen < sizeof (int)) {
+				return (EINVAL);
+			}
+			sad = lx_sad_acquire(SOTOV(so));
+			*intval = sad->lxsad_stream_cred;
+			*optlen = sizeof (int);
+			mutex_exit(&sad->lxsad_lock);
+			return (0);
+		}
+		break;
+
+	case LX_SO_PEERCRED:
+		if (*optlen < sizeof (struct lx_ucred)) {
+			error = EINVAL;
+		} else {
+			struct lx_ucred *lcred = (struct lx_ucred *)optval;
+
+			mutex_enter(&so->so_lock);
+			if ((so->so_mode & SM_CONNREQUIRED) == 0) {
+				error = ENOTSUP;
+			} else if (so->so_peercred == NULL) {
+				error = EINVAL;
+			} else {
+				lcred->lxu_uid = crgetuid(so->so_peercred);
+				lcred->lxu_gid = crgetgid(so->so_peercred);
+				lcred->lxu_pid = so->so_cpid;
+			}
+			mutex_exit(&so->so_lock);
+		}
+		*optlen = sizeof (struct lx_ucred);
+		return (error);
+
+	default:
+		break;
+	}
+
+	if (!lx_sockopt_lookup(sockopts_tbl, &optname, optlen)) {
+		return (ENOPROTOOPT);
+	}
+
+	error = socket_getsockopt(so, SOL_SOCKET, optname, optval, optlen, 0,
+	    CRED());
+
+	if (error == 0) {
+		switch (optname) {
+		case SO_TYPE:
+			/* translate our type back to Linux */
+			*intval = STOL_SOCKTYPE(*intval);
+			break;
+
+		case SO_ERROR:
+			*intval = lx_errno(*intval, EINVAL);
+			break;
+		default:
+			break;
+		}
+	}
+	return (error);
+}
+
+static int
+lx_getsockopt_raw(sonode_t *so, int optname, void *optval, socklen_t *optlen)
+{
+	int error = 0;
+	lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_raw_sockopts);
+
+	if (!lx_sockopt_lookup(sockopts_tbl, &optname, optlen)) {
+		return (ENOPROTOOPT);
+	}
+
+	error = socket_getsockopt(so, IPPROTO_RAW, optname, optval, optlen, 0,
+	    CRED());
+	return (error);
+}
+
+static int
+lx_getsockopt_packet(sonode_t *so, int optname, void *optval,
+    socklen_t *optlen)
+{
+	int error = 0;
+	lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_packet_sockopts);
+
+	if (!lx_sockopt_lookup(sockopts_tbl, &optname, optlen)) {
+		return (ENOPROTOOPT);
+	}
+
+	error = socket_getsockopt(so, SOL_PACKET, optname, optval, optlen, 0,
+	    CRED());
+	return (error);
+}
+
+static int
+lx_getsockopt_igmp(sonode_t *so, int optname, void *optval, socklen_t *optlen)
+{
+	int error = 0;
+	lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_igmp_sockopts);
+
+	if (!lx_sockopt_lookup(sockopts_tbl, &optname, optlen)) {
+		return (ENOPROTOOPT);
+	}
+
+	error = socket_getsockopt(so, IPPROTO_IGMP, optname, optval, optlen, 0,
+	    CRED());
+	return (error);
+}
+
+long
+lx_setsockopt(int sock, int level, int optname, void *optval, socklen_t optlen)
+{
+	struct sonode *so;
+	file_t *fp;
+	int buflen = 0;
+	intptr_t stkbuf[2];
+	void *optbuf = stkbuf;
+	int error = 0;
+
+	if (optlen != 0) {
+		if (optlen > SO_MAXARGSIZE) {
+			return (set_errno(EINVAL));
+		}
+		if (optlen > sizeof (stkbuf)) {
+			buflen = optlen;
+			optbuf = kmem_alloc(optlen, KM_SLEEP);
+		} else {
+			/*
+			 * Zero the on-stack buffer to avoid poisoning smaller
+			 * optvals with stack garbage.
+			 */
+			stkbuf[0] = 0;
+			stkbuf[1] = 0;
+		}
+		if (copyin(optval, optbuf, optlen) != 0) {
+			if (buflen != 0) {
+				kmem_free(optbuf, buflen);
+			}
+			return (set_errno(EFAULT));
+		}
+	} else {
+		optbuf = NULL;
+	}
+	if ((so = getsonode(sock, &error, &fp)) == NULL) {
+		if (buflen != 0) {
+			kmem_free(optbuf, buflen);
+		}
+		return (set_errno(error));
+	}
+
+	switch (level) {
+	case LX_IPPROTO_IP:
+		error = lx_setsockopt_ip(so, optname, optbuf, optlen);
+		break;
+	case LX_IPPROTO_IPV6:
+		error = lx_setsockopt_ipv6(so, optname, optbuf, optlen);
+		break;
+	case LX_IPPROTO_ICMPV6:
+		error = lx_setsockopt_icmpv6(so, optname, optbuf, optlen);
+		break;
+	case LX_IPPROTO_TCP:
+		error = lx_setsockopt_tcp(so, optname, optbuf, optlen);
+		break;
+	case LX_SOL_SOCKET:
+		error = lx_setsockopt_socket(so, optname, optbuf, optlen);
+		break;
+	case LX_IPPROTO_RAW:
+		error = lx_setsockopt_raw(so, optname, optbuf, optlen);
+		break;
+	case LX_SOL_PACKET:
+		error = lx_setsockopt_packet(so, optname, optbuf, optlen);
+		break;
+	case LX_IPPROTO_IGMP:
+		error = lx_setsockopt_igmp(so, optname, optbuf, optlen);
+		break;
+	case LX_SOL_NETLINK:
+		/*
+		 * Since our netlink implmentation is modeled after Linux,
+		 * sockopts can be passed directly through.
+		 */
+		error = socket_setsockopt(so, LX_SOL_NETLINK, optname, optval,
+		    optlen, CRED());
+		break;
+	default:
+		error = ENOPROTOOPT;
+		break;
+	}
+
+	if (error == ENOPROTOOPT) {
+		char buf[LX_UNSUP_BUFSZ];
+
+		(void) snprintf(buf, LX_UNSUP_BUFSZ, "setsockopt(%d, %d)",
+		    level, optname);
+		lx_unsupported(buf);
+	}
+	if (buflen != 0) {
+		kmem_free(optbuf, buflen);
+	}
+	releasef(sock);
+	if (error != 0) {
+		return (set_errno(error));
+	}
+	return (0);
+}
+
+long
+lx_getsockopt(int sock, int level, int optname, void *optval,
+    socklen_t *optlenp)
+{
+	struct sonode *so;
+	file_t *fp;
+	int error = 0, buflen = 0;
+	socklen_t optlen;
+	intptr_t stkbuf[2];
+	void *optbuf = stkbuf;
+
+	if (copyin(optlenp, &optlen, sizeof (optlen)) != 0) {
+		return (set_errno(EFAULT));
+	}
+	if (optlen != 0) {
+		if (optlen > SO_MAXARGSIZE) {
+			return (set_errno(EINVAL));
+		}
+		if (optlen > sizeof (stkbuf)) {
+			buflen = optlen;
+			optbuf = kmem_zalloc(optlen, KM_SLEEP);
+		} else {
+			/* zero the on-stack buffer, just in case */
+			stkbuf[0] = 0;
+			stkbuf[1] = 0;
+		}
+	} else {
+		optbuf = NULL;
+	}
+	if ((so = getsonode(sock, &error, &fp)) == NULL) {
+		if (buflen != 0) {
+			kmem_free(optbuf, buflen);
+		}
+		return (set_errno(error));
+	}
+
+	switch (level) {
+	case LX_IPPROTO_IP:
+		error = lx_getsockopt_ip(so, optname, optbuf, &optlen);
+		break;
+	case LX_IPPROTO_IPV6:
+		error = lx_getsockopt_ipv6(so, optname, optbuf, &optlen);
+		break;
+	case LX_IPPROTO_ICMPV6:
+		error = lx_getsockopt_icmpv6(so, optname, optbuf, &optlen);
+		break;
+	case LX_IPPROTO_TCP:
+		error = lx_getsockopt_tcp(so, optname, optbuf, &optlen);
+		break;
+	case LX_SOL_SOCKET:
+		error = lx_getsockopt_socket(so, optname, optbuf, &optlen);
+		break;
+	case LX_IPPROTO_RAW:
+		error = lx_getsockopt_raw(so, optname, optbuf, &optlen);
+		break;
+	case LX_SOL_PACKET:
+		error = lx_getsockopt_packet(so, optname, optbuf, &optlen);
+		break;
+	case LX_IPPROTO_IGMP:
+		error = lx_getsockopt_igmp(so, optname, optbuf, &optlen);
+		break;
+	case LX_SOL_NETLINK:
+		/*
+		 * Since our netlink implmentation is modeled after Linux,
+		 * sockopts can be passed directly through.
+		 */
+		error = socket_getsockopt(so, LX_SOL_NETLINK, optname, optval,
+		    &optlen, 0, CRED());
+		break;
+	default:
+		error = EOPNOTSUPP;
+		break;
+	}
+
+	if (error == ENOPROTOOPT) {
+		char buf[LX_UNSUP_BUFSZ];
+
+		(void) snprintf(buf, LX_UNSUP_BUFSZ, "getsockopt(%d, %d)",
+		    level, optname);
+		lx_unsupported(buf);
+	}
+	if (copyout(&optlen, optlenp, sizeof (optlen)) != 0) {
+		error = EFAULT;
+	}
+	if (error == 0 && optlen > 0) {
+		VERIFY(optlen <= sizeof (stkbuf) || optlen <= buflen);
+		if (copyout(optbuf, optval, optlen) != 0) {
+			error = EFAULT;
+		}
+	}
+	if (buflen != 0) {
+		kmem_free(optbuf, buflen);
+	}
+	releasef(sock);
+	if (error != 0) {
+		return (set_errno(error));
+	}
+	return (0);
+}
+
+long
+lx_getname_common(lx_getname_type_t type, int sockfd, void *np, int *nlp)
+{
+	struct sockaddr_storage buf;
+	struct sockaddr *name = (struct sockaddr *)&buf;
+	socklen_t namelen, namelen_orig;
+	int err, tmp;
+	struct sonode *so;
+
+	/* We need to validate the name address up front to pass LTP. */
+	if (copyin(np, &tmp, sizeof (tmp)) != 0)
+		return (set_errno(EFAULT));
+
+	if (copyin(nlp, &namelen, sizeof (socklen_t)) != 0)
+		return (set_errno(EFAULT));
+	namelen_orig = namelen;
+
+	/* LTP can pass -1 */
+	if ((int)namelen < 0)
+		return (set_errno(EINVAL));
+
+	if ((so = getsonode(sockfd, &err, NULL)) == NULL)
+		return (set_errno(err));
+
+	bzero(&buf, sizeof (buf));
+	namelen = sizeof (struct sockaddr_storage);
+	if (type == LX_GETPEERNAME) {
+		err = socket_getpeername(so, name, &namelen, B_FALSE, CRED());
+	} else {
+		err = socket_getsockname(so, name, &namelen, CRED());
+	}
+
+	if (err == 0) {
+		ASSERT(namelen <= so->so_max_addr_len);
+		err = stol_sockaddr_copyout(name, namelen,
+		    (struct sockaddr *)np, (socklen_t *)nlp, namelen_orig);
+	}
+
+	releasef(sockfd);
+	return (err != 0 ? set_errno(err) : 0);
+}
+
+long
+lx_getpeername(int sockfd, void *np, int *nlp)
+{
+	return (lx_getname_common(LX_GETPEERNAME, sockfd, np, nlp));
+}
+
+long
+lx_getsockname(int sockfd, void *np, int *nlp)
+{
+	return (lx_getname_common(LX_GETSOCKNAME, sockfd, np, nlp));
+}
+
+static int
+lx_accept_common(int sock, struct sockaddr *name, socklen_t *nlp, int flags)
+{
+	struct sonode *so;
+	file_t *fp;
+	int error;
+	socklen_t namelen;
+	struct sonode *nso;
+	struct vnode *nvp;
+	struct file *nfp;
+	int nfd;
+	int arg;
+
+	if (flags & ~(LX_SOCK_CLOEXEC | LX_SOCK_NONBLOCK)) {
+		return (set_errno(EINVAL));
+	}
+
+	if ((so = getsonode(sock, &error, &fp)) == NULL)
+		return (set_errno(error));
+
+	if (name != NULL) {
+		/*
+		 * The Linux man page says that -1 is returned and errno is set
+		 * to EFAULT if the "name" address is bad, but it is silent on
+		 * what to set errno to if the "namelen" address is bad.
+		 * LTP expects EINVAL.
+		 *
+		 * Note that we must first check the name pointer, as the Linux
+		 * docs state nothing is copied out if the "name" pointer is
+		 * NULL. If it is NULL, we don't care about the namelen
+		 * pointer's value or about dereferencing it.
+		 */
+		if (copyin(nlp, &namelen, sizeof (namelen))) {
+			releasef(sock);
+			return (set_errno(EINVAL));
+		}
+		if (namelen == 0) {
+			name = NULL;
+		}
+	} else {
+		namelen = 0;
+	}
+
+	/*
+	 * Allocate the user fd before socket_accept() in order to
+	 * catch EMFILE errors before calling socket_accept().
+	 */
+	if ((error = falloc(NULL, FWRITE|FREAD, &nfp, &nfd)) != 0) {
+		eprintsoline(so, EMFILE);
+		releasef(sock);
+		return (set_errno(error));
+	}
+	if ((error = socket_accept(so, fp->f_flag, CRED(), &nso)) != 0) {
+		setf(nfd, NULL);
+		unfalloc(nfp);
+		releasef(sock);
+		return (set_errno(error));
+	}
+
+	nvp = SOTOV(nso);
+
+	if (namelen != 0) {
+		socklen_t addrlen = sizeof (struct sockaddr_storage);
+		struct sockaddr_storage buf;
+		struct sockaddr *addrp = (struct sockaddr *)&buf;
+
+		if ((error = socket_getpeername(nso, addrp, &addrlen, B_TRUE,
+		    CRED())) == 0) {
+			error = stol_sockaddr_copyout(addrp, addrlen,
+			    name, nlp, namelen);
+			/*
+			 * Logic might dictate that we should check if we can
+			 * write to the namelen pointer earlier so we don't
+			 * accept a pending connection only to fail the call
+			 * because we can't write the namelen value back out.
+			 * However, testing shows Linux does indeed fail the
+			 * call after accepting the connection so we must
+			 * behave in a compatible manner.
+			 */
+		} else {
+			ASSERT(error == EINVAL || error == ENOTCONN);
+			error = ECONNABORTED;
+		}
+	}
+
+	if (error != 0) {
+		setf(nfd, NULL);
+		unfalloc(nfp);
+		(void) socket_close(nso, 0, CRED());
+		socket_destroy(nso);
+		releasef(sock);
+		return (set_errno(error));
+	}
+
+	/* Fill in the entries that falloc reserved */
+	nfp->f_vnode = nvp;
+	mutex_exit(&nfp->f_tlock);
+	setf(nfd, nfp);
+
+	/* Act on LX_SOCK_CLOEXEC from flags */
+	if (flags & LX_SOCK_CLOEXEC) {
+		f_setfd(nfd, FD_CLOEXEC);
+	}
+
+	/*
+	 * In Linux, accept()ed sockets do not inherit anything set by fcntl(),
+	 * so either explicitly set the flags or filter those out.
+	 *
+	 * The VOP_SETFL code is a simplification of the F_SETFL code in
+	 * fcntl(). Ignore any errors from VOP_SETFL.
+	 */
+	arg = 0;
+	if (flags & LX_SOCK_NONBLOCK)
+		arg |= FNONBLOCK;
+
+	error = VOP_SETFL(nvp, nfp->f_flag, arg, nfp->f_cred, NULL);
+	if (error != 0) {
+		eprintsoline(so, error);
+		error = 0;
+	} else {
+		mutex_enter(&nfp->f_tlock);
+		nfp->f_flag &= ~FMASK | (FREAD|FWRITE);
+		nfp->f_flag |= arg;
+		mutex_exit(&nfp->f_tlock);
+	}
+
+	releasef(sock);
+	return (nfd);
+}
+
+long
+lx_accept(int sockfd, void *np, int *nlp)
+{
+	return (lx_accept_common(sockfd, (struct sockaddr *)np,
+	    (socklen_t *)nlp, 0));
+}
+
+long
+lx_accept4(int sockfd, void *np, int *nlp, int flags)
+{
+	return (lx_accept_common(sockfd, (struct sockaddr *)np,
+	    (socklen_t *)nlp, flags));
+}
+
+long
+lx_listen(int sockfd, int backlog)
+{
+	return (listen(sockfd, backlog, 0));
+}
+
+long
+lx_shutdown(int sockfd, int how)
+{
+	return (shutdown(sockfd, how, 0));
+}
+
+/*
+ * Connect two sockets together for a socketpair.  This is derived from
+ * so_socketpair, but forgoes the task of dealing with file descriptors.
+ */
+static int
+lx_socketpair_connect(file_t *fp1, file_t *fp2)
+{
+	sonode_t *so1, *so2;
+	sotpi_info_t *sti1, *sti2;
+	struct sockaddr_ux name;
+	int error;
+
+	so1 = VTOSO(fp1->f_vnode);
+	so2 = VTOSO(fp2->f_vnode);
+	sti1 = SOTOTPI(so1);
+	sti2 = SOTOTPI(so2);
+
+	VERIFY(so1->so_ops == &sotpi_sonodeops &&
+	    so2->so_ops == &sotpi_sonodeops);
+
+	if (so1->so_type == SOCK_DGRAM) {
+		/*
+		 * Bind both sockets and connect them with each other.
+		 */
+		error = socket_bind(so1, NULL, 0, _SOBIND_UNSPEC, CRED());
+		if (error) {
+			return (error);
+		}
+		error = socket_bind(so2, NULL, 0, _SOBIND_UNSPEC, CRED());
+		if (error) {
+			return (error);
+		}
+		name.sou_family = AF_UNIX;
+		name.sou_addr = sti2->sti_ux_laddr;
+		error = socket_connect(so1, (struct sockaddr *)&name,
+		    (socklen_t)sizeof (name), 0, _SOCONNECT_NOXLATE, CRED());
+		if (error) {
+			return (error);
+		}
+		name.sou_addr = sti1->sti_ux_laddr;
+		error = socket_connect(so2, (struct sockaddr *)&name,
+		    (socklen_t)sizeof (name), 0, _SOCONNECT_NOXLATE, CRED());
+		return (error);
+	} else {
+		sonode_t *nso;
+
+		/*
+		 * Bind both sockets, with 'so1' being a listener.  Connect
+		 * 'so2' to 'so1', doing so as nonblocking to avoid waiting for
+		 * soaccept to complete.  Accept the connection on 'so1',
+		 * replacing the socket/vnode in 'fp1' with the new connection.
+		 *
+		 * We could simply call socket_listen() here (which would do the
+		 * binding automatically) if the code didn't rely on passing
+		 * _SOBIND_NOXLATE to the TPI implementation of socket_bind().
+		 */
+		error = socket_bind(so1, NULL, 0, _SOBIND_UNSPEC|
+		    _SOBIND_NOXLATE|_SOBIND_LISTEN|_SOBIND_SOCKETPAIR, CRED());
+		if (error) {
+			return (error);
+		}
+		error = socket_bind(so2, NULL, 0, _SOBIND_UNSPEC, CRED());
+		if (error) {
+			return (error);
+		}
+
+		name.sou_family = AF_UNIX;
+		name.sou_addr = sti1->sti_ux_laddr;
+		error = socket_connect(so2,
+		    (struct sockaddr *)&name,
+		    (socklen_t)sizeof (name),
+		    FNONBLOCK, _SOCONNECT_NOXLATE, CRED());
+		if (error != 0 && error != EINPROGRESS) {
+			return (error);
+		}
+
+		error = socket_accept(so1, 0, CRED(), &nso);
+		if (error) {
+			return (error);
+		}
+
+		/* wait for so2 being SS_CONNECTED */
+		mutex_enter(&so2->so_lock);
+		error = sowaitconnected(so2, 0, 0);
+		mutex_exit(&so2->so_lock);
+		if (error != 0) {
+			(void) socket_close(nso, 0, CRED());
+			socket_destroy(nso);
+			return (error);
+		}
+
+		(void) socket_close(so1, 0, CRED());
+		socket_destroy(so1);
+		fp1->f_vnode = SOTOV(nso);
+	}
+	return (0);
+}
+
+long
+lx_socketpair(int domain, int type, int protocol, int *sv)
+{
+	int err, options, fds[2];
+	file_t *fps[2];
+
+	if ((err = lx_convert_sock_args(domain, type, protocol, &domain, &type,
+	    &options, &protocol)) != 0) {
+		return (set_errno(err));
+	}
+
+	if ((err = lx_socket_create(domain, type, protocol, options, &fps[0],
+	    &fds[0])) != 0) {
+		return (set_errno(err));
+	}
+
+	/*
+	 * While it seems silly to check the family after socket creation, this
+	 * is done to appease LTP when it tries some outlandish combinations of
+	 * domain/type/protocol.  The socket_create function is relied upon to
+	 * emit the expected errors.
+	 */
+	if (VTOSO(fps[0]->f_vnode)->so_family != AF_UNIX) {
+		lx_socket_destroy(fps[0], fds[0]);
+		return (set_errno(EOPNOTSUPP));
+	}
+
+	if ((err = lx_socket_create(domain, type, protocol, options, &fps[1],
+	    &fds[1])) != 0) {
+		lx_socket_destroy(fps[0], fds[0]);
+		return (set_errno(err));
+	}
+
+	err = lx_socketpair_connect(fps[0], fps[1]);
+	if (err != 0) {
+		lx_socket_destroy(fps[0], fds[0]);
+		lx_socket_destroy(fps[1], fds[1]);
+		return (set_errno(err));
+	}
+
+	setf(fds[0], fps[0]);
+	setf(fds[1], fps[1]);
+	if ((options & SOCK_CLOEXEC) != 0) {
+		f_setfd(fds[0], FD_CLOEXEC);
+		f_setfd(fds[1], FD_CLOEXEC);
+	}
+	if (copyout(fds, sv, sizeof (fds)) != 0) {
+		(void) closeandsetf(fds[0], NULL);
+		(void) closeandsetf(fds[1], NULL);
+		return (set_errno(EFAULT));
+	}
+	return (0);
+}
+
+
+#if defined(_SYSCALL32_IMPL)
+
+#define	LX_SYS_SOCKETCALL		102
+#define	LX_SOCKETCALL_MAX		20
+
+typedef long (*lx_sockfn_t)();
+
+static struct {
+	lx_sockfn_t s_fn;	/* Function implementing the subcommand */
+	int s_nargs;		/* Number of arguments the function takes */
+} lx_socketcall_fns[] = {
+	lx_socket,	3,	/* socket */
+	lx_bind,	3,	/* bind */
+	lx_connect,	3,	/* connect */
+	lx_listen,	2,	/* listen */
+	lx_accept,	3,	/* accept */
+	lx_getsockname,	3,	/* getsockname */
+	lx_getpeername,	3,	/* getpeername */
+	lx_socketpair,	4,	/* socketpair */
+	lx_send,	4,	/* send */
+	lx_recv,	4,	/* recv */
+	lx_sendto,	6,	/* sendto */
+	lx_recvfrom,	6,	/* recvfrom */
+	lx_shutdown,	2,	/* shutdown */
+	lx_setsockopt,	5,	/* setsockopt */
+	lx_getsockopt,	5,	/* getsockopt */
+	lx_sendmsg,	3,	/* sendmsg */
+	lx_recvmsg,	3,	/* recvmsg */
+	lx_accept4,	4,	/* accept4 */
+	NULL,		5,	/* recvmmsg */
+	NULL,		4	/* sendmmsg */
+};
+
+long
+lx_socketcall(long p1, uint32_t *p2)
+{
+	int subcmd, i;
+	unsigned long args[6] = { 0, 0, 0, 0, 0, 0 };
+
+	/* incoming subcmds are 1-indexed */
+	subcmd = (int)p1 - 1;
+
+	if (subcmd < 0 || subcmd >= LX_SOCKETCALL_MAX ||
+	    lx_socketcall_fns[subcmd].s_fn == NULL) {
+		return (set_errno(EINVAL));
+	}
+
+	/*
+	 * Copy the arguments to the subcommand in from the app's address
+	 * space, returning EFAULT if we get a bogus pointer.
+	 */
+	for (i = 0; i < lx_socketcall_fns[subcmd].s_nargs; i++) {
+		uint32_t arg;
+
+		if (copyin(&p2[i], &arg, sizeof (uint32_t)) != 0) {
+			return (set_errno(EFAULT));
+		}
+		args[i] = (unsigned long)arg;
+	}
+
+	return ((lx_socketcall_fns[subcmd].s_fn)(args[0], args[1], args[2],
+	    args[3], args[4], args[5]));
+}
+
+#endif /* defined(_SYSCALL32_IMPL) */
+
+static void
+lx_socket_vsd_free(void *data)
+{
+	lx_socket_aux_data_t *entry;
+
+	entry = (lx_socket_aux_data_t *)data;
+	mutex_destroy(&entry->lxsad_lock);
+	kmem_free(entry, sizeof (*entry));
+}
+
+void
+lx_socket_init()
+{
+	vsd_create(&lx_socket_vsd, lx_socket_vsd_free);
+}
+
+void
+lx_socket_fini()
+{
+	vsd_destroy(&lx_socket_vsd);
+}
--- /dev/null	Wed Oct  5 00:32:10 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_stat.c	Wed Oct  5 00:32:10 2016
@@ -0,0 +1,474 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/vnode.h>
+#include <sys/file.h>
+#include <sys/fcntl.h>
+#include <sys/debug.h>
+#include <sys/errno.h>
+#include <sys/model.h>
+#include <sys/mode.h>
+#include <sys/stat.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_fcntl.h>
+#include <sys/lx_types.h>
+#include <sys/lx_impl.h>
+#include <sys/brand.h>
+#include <sys/ddi.h>
+
+/* From "uts/common/syscall/stat.c" */
+extern int cstatat_getvp(int, char *, int, vnode_t **, cred_t **);
+
+typedef struct lx_timespec32 {
+	int32_t	ts_sec;
+	int32_t	ts_nsec;
+} lx_timespec32_t;
+
+typedef struct lx_timespec64 {
+	int64_t	ts_sec;
+	int64_t	ts_nsec;
+}lx_timespec64_t;
+
+struct lx_stat32 {
+	uint16_t	st_dev;
+	uint16_t	st_pad1;
+	uint32_t	st_ino;
+	uint16_t	st_mode;
+	uint16_t	st_nlink;
+	uint16_t	st_uid;
+	uint16_t	st_gid;
+	uint16_t	st_rdev;
+	uint16_t 	st_pad2;
+	uint32_t	st_size;
+	uint32_t	st_blksize;
+	uint32_t	st_blocks;
+	lx_timespec32_t	st_atime;
+	lx_timespec32_t	st_mtime;
+	lx_timespec32_t	st_ctime;
+	uint32_t	st_pad3;
+	uint32_t	st_pad4;
+};
+
+#pragma pack(4)
+struct lx_stat64_32 {
+	uint64_t	st_dev;
+	uint32_t	st_pad1;
+	uint32_t	st_small_ino;
+	uint32_t	st_mode;
+	uint32_t	st_nlink;
+	uint32_t	st_uid;
+	uint32_t	st_gid;
+	uint64_t	st_rdev;
+	uint32_t	st_pad2;
+	uint64_t	st_size;
+	uint32_t	st_blksize;
+	uint64_t	st_blocks;
+	lx_timespec32_t	st_atime;
+	lx_timespec32_t	st_mtime;
+	lx_timespec32_t	st_ctime;
+	uint64_t	st_ino;
+};
+#pragma pack()
+
+#if defined(_LP64)
+struct lx_stat64_64 {
+	uint64_t	st_dev;
+	uint64_t	st_ino;
+	uint64_t	st_nlink;	/* yes, the order really is */
+	uint32_t	st_mode;	/* different for these two */
+	uint32_t	st_uid;
+	uint32_t	st_gid;
+	uint32_t	st_pad0;
+	uint64_t	st_rdev;
+	int64_t		st_size;
+	int64_t		st_blksize;
+	int64_t		st_blocks;
+	lx_timespec64_t	st_atime;
+	lx_timespec64_t	st_mtime;
+	lx_timespec64_t	st_ctime;
+	int64_t		st_unused[3];
+};
+#endif /* defined(_LP64) */
+
+typedef enum lx_stat_fmt {
+	LXF_STAT32,
+	LXF_STAT64_32,
+	LXF_STAT64_64
+} lx_stat_fmt_t;
+
+static void
+lx_stat_xlate_dev(vattr_t *vattr)
+{
+	lx_zone_data_t *lxzd = ztolxzd(curproc->p_zone);
+	dev_t dev;
+	lx_virt_disk_t *vd;
+	boolean_t is_dev;
+
+	if (S_ISCHR(vattr->va_mode) || S_ISBLK(vattr->va_mode)) {
+		dev = vattr->va_rdev;
+		is_dev = B_TRUE;
+	} else {
+		dev = vattr->va_fsid;
+		is_dev = B_FALSE;
+	}
+
+	/*
+	 * See if this is the /dev/zfs device. If it is, the device number has
+	 * already been converted to Linux format in the lx devfs so we have
+	 * to check for that and not a native major/minor style.
+	 */
+	if (S_ISCHR(vattr->va_mode) &&
+	    LX_GETMAJOR(dev) == getmajor(lxzd->lxzd_zfs_dev) &&
+	    LX_GETMINOR(dev) == 0) {
+		/*
+		 * We use the /dev/zfs device as a placeholder for our in-zone
+		 * fabricated /dev/zfsds0 device that we're pretending / is
+		 * mounted on. lx_zone_get_zfsds has pre-allocated this
+		 * entry in the emulated device list. Reset dev so we can
+		 * properly match in the following loop.
+		 */
+		dev = curproc->p_zone->zone_rootvp->v_vfsp->vfs_dev;
+	}
+
+	/* Substitute emulated major/minor on zvols or mounted datasets. */
+	vd = list_head(lxzd->lxzd_vdisks);
+	while (vd != NULL) {
+		if (vd->lxvd_real_dev == dev) {
+			dev = vd->lxvd_emul_dev;
+			/*
+			 * We only update rdev for matching zfds/zvol devices
+			 * so that the other devices are unchanged.
+			 */
+			if (is_dev) {
+				vattr->va_rdev = LX_MAKEDEVICE(getmajor(dev),
+				    getminor(dev));
+			}
+			break;
+		}
+		vd = list_next(lxzd->lxzd_vdisks, vd);
+	}
+
+	/* Mangle st_dev into expected format */
+	vattr->va_fsid = LX_MAKEDEVICE(getmajor(dev), getminor(dev));
+}
+
+static long
+lx_stat_common(vnode_t *vp, cred_t *cr, void *outp, lx_stat_fmt_t fmt)
+{
+	vattr_t vattr;
+	mode_t mode;
+	int error;
+
+	vattr.va_mask = AT_STAT | AT_NBLOCKS | AT_BLKSIZE | AT_SIZE;
+	if ((error = VOP_GETATTR(vp, &vattr, 0, cr, NULL)) != 0) {
+		return (error);
+	}
+
+	mode = VTTOIF(vattr.va_type) | vattr.va_mode;
+	if ((mode & S_IFMT) == S_IFBLK) {
+		/* Linux seems to report a 0 st_size for all block devices */
+		vattr.va_size = 0;
+	}
+	if (vattr.va_rdev == NODEV) {
+		/* Linux leaves st_rdev zeroed when it is absent */
+		vattr.va_rdev = 0;
+	}
+
+	lx_stat_xlate_dev(&vattr);
+
+	if (fmt == LXF_STAT32) {
+		struct lx_stat32 sb;
+
+		if (vattr.va_fsid > USHRT_MAX || vattr.va_rdev > USHRT_MAX ||
+		    vattr.va_nlink > USHRT_MAX || vattr.va_size > INT_MAX) {
+			return (EOVERFLOW);
+		}
+
+		bzero(&sb, sizeof (sb));
+		sb.st_dev = vattr.va_fsid;
+		sb.st_ino = vattr.va_nodeid;
+		sb.st_mode = mode;
+		sb.st_nlink = vattr.va_nlink;
+		sb.st_uid = LX_UID32_TO_UID16(vattr.va_uid);
+		sb.st_gid = LX_GID32_TO_GID16(vattr.va_gid);
+		sb.st_rdev = vattr.va_rdev;
+		sb.st_size = vattr.va_size;
+		sb.st_blksize = vattr.va_blksize;
+		sb.st_blocks = vattr.va_nblocks;
+		sb.st_atime.ts_sec = vattr.va_atime.tv_sec;
+		sb.st_atime.ts_nsec = vattr.va_atime.tv_nsec;
+		sb.st_mtime.ts_sec = vattr.va_mtime.tv_sec;
+		sb.st_mtime.ts_nsec = vattr.va_mtime.tv_nsec;
+		sb.st_ctime.ts_sec = vattr.va_ctime.tv_sec;
+		sb.st_ctime.ts_nsec = vattr.va_ctime.tv_nsec;
+		if (copyout(&sb, outp, sizeof (sb)) != 0) {
+			return (EFAULT);
+		}
+		return (0);
+	} else if (fmt == LXF_STAT64_32) {
+		struct lx_stat64_32 sb;
+
+		bzero(&sb, sizeof (sb));
+		sb.st_dev = vattr.va_fsid;
+		sb.st_ino = vattr.va_nodeid;
+		sb.st_small_ino = (vattr.va_nodeid & UINT_MAX);
+		sb.st_mode = mode;
+		sb.st_nlink = vattr.va_nlink;
+		sb.st_uid = vattr.va_uid;
+		sb.st_gid = vattr.va_gid;
+		sb.st_rdev = vattr.va_rdev;
+		sb.st_size = vattr.va_size;
+		sb.st_blksize = vattr.va_blksize;
+		sb.st_blocks = vattr.va_nblocks;
+		sb.st_atime.ts_sec = vattr.va_atime.tv_sec;
+		sb.st_atime.ts_nsec = vattr.va_atime.tv_nsec;
+		sb.st_mtime.ts_sec = vattr.va_mtime.tv_sec;
+		sb.st_mtime.ts_nsec = vattr.va_mtime.tv_nsec;
+		sb.st_ctime.ts_sec = vattr.va_ctime.tv_sec;
+		sb.st_ctime.ts_nsec = vattr.va_ctime.tv_nsec;
+		if (copyout(&sb, outp, sizeof (sb)) != 0) {
+			return (EFAULT);
+		}
+		return (0);
+	} else if (fmt == LXF_STAT64_64) {
+#if defined(_LP64)
+		struct lx_stat64_64 sb;
+
+		bzero(&sb, sizeof (sb));
+		sb.st_dev = vattr.va_fsid;
+		sb.st_ino = vattr.va_nodeid;
+		sb.st_mode = mode;
+		sb.st_nlink = vattr.va_nlink;
+		sb.st_uid = vattr.va_uid;
+		sb.st_gid = vattr.va_gid;
+		sb.st_rdev = vattr.va_rdev;
+		sb.st_size = vattr.va_size;
+		sb.st_blksize = vattr.va_blksize;
+		sb.st_blocks = vattr.va_nblocks;
+		sb.st_atime.ts_sec = vattr.va_atime.tv_sec;
+		sb.st_atime.ts_nsec = vattr.va_atime.tv_nsec;
+		sb.st_mtime.ts_sec = vattr.va_mtime.tv_sec;
+		sb.st_mtime.ts_nsec = vattr.va_mtime.tv_nsec;
+		sb.st_ctime.ts_sec = vattr.va_ctime.tv_sec;
+		sb.st_ctime.ts_nsec = vattr.va_ctime.tv_nsec;
+		if (copyout(&sb, outp, sizeof (sb)) != 0) {
+			return (EFAULT);
+		}
+		return (0);
+#else
+		/* Invalid output format on 32-bit */
+		VERIFY(0);
+#endif
+	}
+
+	/* Invalid output format */
+	VERIFY(0);
+	return (0);
+}
+
+long
+lx_stat32(char *name, void *outp)
+{
+	vnode_t *vp = NULL;
+	cred_t *cr = NULL;
+	int error;
+
+	if ((error = cstatat_getvp(AT_FDCWD, name, FOLLOW, &vp, &cr)) != 0) {
+		return (set_errno(error));
+	}
+	error = lx_stat_common(vp, cr, outp, LXF_STAT32);
+	VN_RELE(vp);
+	crfree(cr);
+	if (error != 0) {
+		return (set_errno(error));
+	}
+	return (0);
+}
+
+long
+lx_fstat32(int fd, void *outp)
+{
+	file_t *fp;
+	int error;
+
+	if ((fp = getf(fd)) == NULL) {
+		return (set_errno(EBADF));
+	}
+	error = lx_stat_common(fp->f_vnode, fp->f_cred, outp, LXF_STAT32);
+	releasef(fd);
+	if (error != 0) {
+		return (set_errno(error));
+	}
+	return (0);
+}
+
+long
+lx_lstat32(char *name, void *outp)
+{
+	vnode_t *vp = NULL;
+	cred_t *cr = NULL;
+	int error;
+
+	if ((error = cstatat_getvp(AT_FDCWD, name, NO_FOLLOW, &vp, &cr)) != 0) {
+		return (set_errno(error));
+	}
+	error = lx_stat_common(vp, cr, outp, LXF_STAT32);
+	VN_RELE(vp);
+	crfree(cr);
+	if (error != 0) {
+		return (set_errno(error));
+	}
+	return (0);
+}
+
+long
+lx_stat64(char *name, void *outp)
+{
+	vnode_t *vp = NULL;
+	cred_t *cr = NULL;
+	model_t model = get_udatamodel();
+	int error;
+
+	if ((error = cstatat_getvp(AT_FDCWD, name, FOLLOW, &vp, &cr)) != 0) {
+		return (set_errno(error));
+	}
+	error = lx_stat_common(vp, cr, outp,
+	    (model == DATAMODEL_LP64) ? LXF_STAT64_64 : LXF_STAT64_32);
+	VN_RELE(vp);
+	crfree(cr);
+	if (error != 0) {
+		return (set_errno(error));
+	}
+	return (0);
+}
+
+long
+lx_fstat64(int fd, void *outp)
+{
+	file_t *fp;
+	model_t model = get_udatamodel();
+	int error;
+
+	if ((fp = getf(fd)) == NULL) {
+		return (set_errno(EBADF));
+	}
+	error = lx_stat_common(fp->f_vnode, fp->f_cred, outp,
+	    (model == DATAMODEL_LP64) ? LXF_STAT64_64 : LXF_STAT64_32);
+	releasef(fd);
+	if (error != 0) {
+		return (set_errno(error));
+	}
+	return (0);
+}
+
+#define	LX_FSTATAT_ALLOWED	(LX_AT_SYMLINK_NOFOLLOW | LX_AT_EMPTY_PATH | \
+    LX_AT_NO_AUTOMOUNT)
+
+long
+lx_fstatat64(int fd, char *name, void *outp, int flag)
+{
+	vnode_t *vp = NULL;
+	cred_t *cr = NULL;
+	model_t model = get_udatamodel();
+	enum symfollow follow = FOLLOW;
+	int error;
+	char c;
+
+	if (fd == LX_AT_FDCWD) {
+		fd = AT_FDCWD;
+	}
+	if ((flag & ~LX_FSTATAT_ALLOWED) != 0) {
+		return (set_errno(EINVAL));
+	}
+	if ((flag & LX_AT_NO_AUTOMOUNT) != 0) {
+		/*
+		 * While AT_NO_AUTOMOUNT is a legal flag for fstatat64, it is
+		 * not yet supported by lx_autofs.
+		 */
+		lx_unsupported("fstatat(AT_NO_AUTOMOUNT)");
+		return (set_errno(EINVAL));
+	}
+	if ((flag & LX_AT_SYMLINK_NOFOLLOW) != 0) {
+		follow = NO_FOLLOW;
+	}
+
+	if (copyin(name, &c, sizeof (c)) != 0) {
+		return (set_errno(EFAULT));
+	}
+	if (c == '\0') {
+		if ((flag & LX_AT_EMPTY_PATH) == 0) {
+			return (set_errno(ENOENT));
+		}
+
+		/*
+		 * When AT_EMPTY_PATH is set and and empty string has been
+		 * passed for the name parameter, direct the lookup against the
+		 * vnode for that fd.
+		 */
+		if (fd == AT_FDCWD) {
+			vp = PTOU(curproc)->u_cdir;
+			VN_HOLD(vp);
+			cr = CRED();
+			crhold(cr);
+		} else {
+			file_t *fp;
+
+			if ((fp = getf(fd)) == NULL) {
+				return (set_errno(EBADF));
+			}
+			vp = fp->f_vnode;
+			VN_HOLD(vp);
+			cr = fp->f_cred;
+			crhold(cr);
+			releasef(fd);
+		}
+	} else {
+		if ((error = cstatat_getvp(fd, name, follow, &vp, &cr)) != 0) {
+			return (set_errno(error));
+		}
+	}
+
+	error = lx_stat_common(vp, cr, outp,
+	    (model == DATAMODEL_LP64) ? LXF_STAT64_64 : LXF_STAT64_32);
+	VN_RELE(vp);
+	crfree(cr);
+	if (error != 0) {
+		return (set_errno(error));
+	}
+	return (0);
+}
+
+long
+lx_lstat64(char *name, void *outp)
+{
+	vnode_t *vp = NULL;
+	cred_t *cr = NULL;
+	model_t model = get_udatamodel();
+	int error;
+
+	if ((error = cstatat_getvp(AT_FDCWD, name, NO_FOLLOW, &vp, &cr)) != 0) {
+		return (set_errno(error));
+	}
+	error = lx_stat_common(vp, cr, outp,
+	    (model == DATAMODEL_LP64) ? LXF_STAT64_64 : LXF_STAT64_32);
+	VN_RELE(vp);
+	crfree(cr);
+	if (error != 0) {
+		return (set_errno(error));
+	}
+	return (0);
+}
--- /dev/null	Wed Oct  5 00:32:10 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_sync.c	Wed Oct  5 00:32:10 2016
@@ -0,0 +1,86 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+#include <sys/systm.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/vfs.h>
+#include <sys/vnode.h>
+#include <sys/lx_impl.h>
+#include <sys/lx_brand.h>
+
+long
+lx_syncfs(int fd)
+{
+	file_t *fp;
+	vfs_t *vfsp;
+
+	if ((fp = getf(fd)) == NULL)
+		return (set_errno(EBADF));
+
+	vfsp = fp->f_vnode->v_vfsp;
+	releasef(fd);
+
+	(void) (vfsp->vfs_op->vfs_sync)(vfsp, 0, CRED());
+
+	return (0);
+}
+
+#define	LX_SYNC_FILE_RANGE_WAIT_BEFORE	0x1
+#define	LX_SYNC_FILE_RANGE_WRITE	0x2
+#define	LX_SYNC_FILE_RANGE_WAIT_AFTER	0x4
+
+#define	LX_SYNC_FILE_RANGE_VALID	(LX_SYNC_FILE_RANGE_WAIT_BEFORE | \
+	LX_SYNC_FILE_RANGE_WRITE | LX_SYNC_FILE_RANGE_WAIT_AFTER)
+
+
+long
+lx_sync_file_range(int fd, off_t offset, off_t nbytes, int flags)
+{
+	file_t *fp;
+	int error, sflags = 0;
+
+	if ((flags & ~LX_SYNC_FILE_RANGE_VALID) != 0)
+		return (set_errno(EINVAL));
+	if (offset < 0 || nbytes < 0)
+		return (set_errno(EINVAL));
+
+	if ((fp = getf(fd)) == NULL)
+		return (set_errno(EBADF));
+
+	/*
+	 * Since sync_file_range is implemented in terms of VOP_PUTPAGE, both
+	 * SYNC_FILE_RANGE_WAIT flags are treated as forcing synchronous
+	 * operation.  While this differs from the Linux behavior where
+	 * BEFORE/AFTER are distinct, it achieves an adequate level of safety
+	 * since the requested data is synced out at the end of the call.
+	 */
+	if ((flags & (LX_SYNC_FILE_RANGE_WAIT_BEFORE |
+	    LX_SYNC_FILE_RANGE_WAIT_AFTER)) == 0) {
+		sflags |= B_ASYNC;
+	}
+
+	error = VOP_PUTPAGE(fp->f_vnode, offset, nbytes, sflags, CRED(), NULL);
+	if (error == ENOSYS) {
+		error = ESPIPE;
+	}
+
+	releasef(fd);
+	if (error != 0) {
+		return (set_errno(error));
+	}
+	return (0);
+}
--- /dev/null	Wed Oct  5 00:32:10 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_sysinfo.c	Wed Oct  5 00:32:10 2016
@@ -0,0 +1,218 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
+ */
+
+#include <vm/anon.h>
+#include <sys/systm.h>
+#include <sys/sysmacros.h>
+#include <sys/zone.h>
+#include <sys/time.h>
+
+typedef struct lx_sysinfo {
+	int64_t si_uptime;	/* Seconds since boot */
+	uint64_t si_loads[3];	/* 1, 5, and 15 minute avg runq length */
+	uint64_t si_totalram;	/* Total memory size */
+	uint64_t si_freeram;	/* Available memory */
+	uint64_t si_sharedram;	/* Shared memory */
+	uint64_t si_bufferram;	/* Buffer memory */
+	uint64_t si_totalswap;	/* Total swap space */
+	uint64_t si_freeswap;	/* Avail swap space */
+	uint16_t si_procs;	/* Process count */
+	uint16_t si_pad;	/* Padding */
+	uint64_t si_totalhigh;	/* High memory size */
+	uint64_t si_freehigh;	/* Avail high memory */
+	uint32_t si_mem_unit;	/* Unit size of memory fields */
+} lx_sysinfo_t;
+
+#if defined(_SYSCALL32_IMPL)
+/*
+ * 64-bit kernel view of the 32-bit usermode struct.
+ */
+#pragma pack(4)
+typedef struct lx_sysinfo32 {
+	int32_t si_uptime;	/* Seconds since boot */
+	uint32_t si_loads[3];	/* 1, 5, and 15 minute avg runq length */
+	uint32_t si_totalram;	/* Total memory size */
+	uint32_t si_freeram;	/* Available memory */
+	uint32_t si_sharedram;	/* Shared memory */
+	uint32_t si_bufferram;	/* Buffer memory */
+	uint32_t si_totalswap;	/* Total swap space */
+	uint32_t si_freeswap;	/* Avail swap space */
+	uint16_t si_procs;	/* Process count */
+	uint16_t si_pad;	/* Padding */
+	uint32_t si_totalhigh;	/* High memory size */
+	uint32_t si_freehigh;	/* Avail high memory */
+	uint32_t si_mem_unit;	/* Unit size of memory fields */
+	char __si_pad[8];
+} lx_sysinfo32_t;
+#pragma pack()
+#endif
+
+extern pgcnt_t swapfs_minfree;
+
+static void
+lx_sysinfo_common(lx_sysinfo_t *si)
+{
+	zone_t *zone = curthread->t_procp->p_zone;
+	uint64_t zphysmem, zfreemem, ztotswap, zfreeswap;
+
+	si->si_uptime = gethrestime_sec() - zone->zone_boot_time;
+
+	si->si_loads[0] = zone->zone_hp_avenrun[0];
+	si->si_loads[1] = zone->zone_hp_avenrun[1];
+	si->si_loads[2] = zone->zone_hp_avenrun[2];
+
+	/*
+	 * In linux each thread looks like a process, so we conflate the
+	 * two in this stat as well.
+	 */
+	si->si_procs = (int32_t)zone->zone_nlwps;
+
+	/*
+	 * If memory or swap limits are set on the zone, use those, otherwise
+	 * use the system values. physmem and freemem are in pages, but the
+	 * zone values are in bytes. Likewise, ani_max and ani_free are in
+	 * pages.
+	 */
+	if (zone->zone_phys_mem_ctl == UINT64_MAX) {
+		zphysmem = physmem;
+		zfreemem = freemem;
+	} else {
+		zphysmem = btop(zone->zone_phys_mem_ctl);
+		zfreemem = btop(zone->zone_phys_mem_ctl - zone->zone_phys_mem);
+	}
+
+	if (zone->zone_max_swap_ctl == UINT64_MAX) {
+		ztotswap = k_anoninfo.ani_max;
+		zfreeswap = k_anoninfo.ani_free;
+	} else {
+		/*
+		 * See the comment in swapctl for a description of how free is
+		 * calculated within a zone.
+		 */
+		rctl_qty_t used;
+		spgcnt_t avail;
+		uint64_t max;
+
+		avail = MAX((spgcnt_t)(availrmem - swapfs_minfree), 0);
+		max = k_anoninfo.ani_max + k_anoninfo.ani_mem_resv + avail;
+
+		mutex_enter(&zone->zone_mem_lock);
+		ztotswap = btop(zone->zone_max_swap_ctl);
+		used = btop(zone->zone_max_swap);
+		mutex_exit(&zone->zone_mem_lock);
+
+		zfreeswap = MIN(ztotswap, max) - used;
+	}
+
+	/*
+	 * If the maximum memory stat is less than 1^20 pages (i.e. 4GB),
+	 * then we report the result in bytes.  Otherwise we use pages.
+	 * Once we start supporting >1TB systems/zones, we'll need a third
+	 * option.
+	 */
+	if (MAX(zphysmem, ztotswap) < 1024 * 1024) {
+		si->si_totalram = ptob(zphysmem);
+		si->si_freeram = ptob(zfreemem);
+		si->si_totalswap = ptob(ztotswap);
+		si->si_freeswap = ptob(zfreeswap);
+		si->si_mem_unit = 1;
+	} else {
+		si->si_totalram = zphysmem;
+		si->si_freeram = zfreemem;
+		si->si_totalswap = ztotswap;
+		si->si_freeswap = zfreeswap;
+		si->si_mem_unit = PAGESIZE;
+	}
+	si->si_bufferram = 0;
+	si->si_sharedram = 0;
+
+	/*
+	 * These two stats refer to high physical memory.  If an
+	 * application running in a Linux zone cares about this, then
+	 * either it or we are broken.
+	 */
+	si->si_totalhigh = 0;
+	si->si_freehigh = 0;
+}
+
+long
+lx_sysinfo64(caddr_t sip)
+{
+	lx_sysinfo_t si;
+
+	bzero(&si, sizeof (si));
+	lx_sysinfo_common(&si);
+
+	if (copyout(&si, sip, sizeof (si)) != 0) {
+		return (set_errno(EFAULT));
+	}
+
+	return (0);
+}
+
+#if defined(_SYSCALL32_IMPL)
+long
+lx_sysinfo32(caddr_t sip)
+{
+	lx_sysinfo_t si;
+	lx_sysinfo32_t si32;
+	int i;
+
+	lx_sysinfo_common(&si);
+
+	/*
+	 * Convert the lx_sysinfo_t into the legacy 32-bit view:
+	 */
+	bzero(&si32, sizeof (si32));
+	si32.si_uptime = si.si_uptime;
+
+	for (i = 0; i < 3; i++) {
+		if ((si.si_loads[i]) > 0x7fffffff)
+			si32.si_loads[i] = 0x7fffffff;
+		else
+			si32.si_loads[i] = si.si_loads[i];
+	}
+
+	si32.si_procs = si.si_procs;
+	si32.si_totalram = si.si_totalram;
+	si32.si_freeram = si.si_freeram;
+	si32.si_totalswap = si.si_totalswap;
+	si32.si_freeswap = si.si_freeswap;
+	si32.si_mem_unit = si.si_mem_unit;
+
+	si32.si_bufferram = si.si_bufferram;
+	si32.si_sharedram = si.si_sharedram;
+
+	si32.si_totalhigh = si.si_totalhigh;
+	si32.si_freehigh = si.si_freehigh;
+
+	if (copyout(&si32, sip, sizeof (si32)) != 0) {
+		return (set_errno(EFAULT));
+	}
+
+	return (0);
+}
+#endif
--- /dev/null	Wed Oct  5 00:32:10 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_thread_area.c	Wed Oct  5 00:32:10 2016
@@ -0,0 +1,197 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/cpuvar.h>
+#include <sys/archsystm.h>
+#include <sys/proc.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_ldt.h>
+#include <sys/lx_misc.h>
+#include <sys/x86_archext.h>
+#include <sys/controlregs.h>
+#include <lx_syscall.h>
+
+/* ARGSUSED */
+long
+lx_arch_prctl(int code, ulong_t addr)
+{
+#if defined(__amd64)
+	klwp_t *lwp = ttolwp(curthread);
+	lx_lwp_data_t *llwp = lwptolxlwp(lwp);
+	pcb_t *pcb = &lwp->lwp_pcb;
+
+	switch (code) {
+	case LX_ARCH_GET_FS:
+		if (copyout(&llwp->br_lx_fsbase, (void *)addr,
+		    sizeof (llwp->br_lx_fsbase)) != 0) {
+			return (set_errno(EFAULT));
+		}
+		break;
+
+	case LX_ARCH_SET_FS:
+		llwp->br_lx_fsbase = addr;
+
+		kpreempt_disable();
+		if (pcb->pcb_fsbase != llwp->br_lx_fsbase) {
+			pcb->pcb_fsbase = llwp->br_lx_fsbase;
+
+			/*
+			 * Ensure we go out via update_sregs.
+			 */
+			pcb->pcb_rupdate = 1;
+		}
+		kpreempt_enable();
+		break;
+
+	case LX_ARCH_GET_GS:
+		if (copyout(&llwp->br_lx_gsbase, (void *)addr,
+		    sizeof (llwp->br_lx_gsbase)) != 0) {
+			return (set_errno(EFAULT));
+		}
+		break;
+
+	case LX_ARCH_SET_GS:
+		llwp->br_lx_gsbase = addr;
+
+		kpreempt_disable();
+		if (pcb->pcb_gsbase != llwp->br_lx_gsbase) {
+			pcb->pcb_gsbase = llwp->br_lx_gsbase;
+
+			/*
+			 * Ensure we go out via update_sregs.
+			 */
+			pcb->pcb_rupdate = 1;
+		}
+		kpreempt_enable();
+		break;
+
+	default:
+		return (set_errno(EINVAL));
+	}
+#endif
+
+	return (0);
+}
+
+long
+lx_get_thread_area(struct ldt_info *inf)
+{
+	struct lx_lwp_data *jlwp = ttolxlwp(curthread);
+	struct ldt_info ldt_inf;
+	user_desc_t *dscrp;
+	int entry;
+
+	if (fuword32(&inf->entry_number, (uint32_t *)&entry))
+		return (set_errno(EFAULT));
+
+	if (entry < GDT_TLSMIN || entry > GDT_TLSMAX)
+		return (set_errno(EINVAL));
+
+	dscrp = jlwp->br_tls + entry - GDT_TLSMIN;
+
+	/*
+	 * convert the solaris ldt to the linux format expected by the
+	 * caller
+	 */
+	DESC_TO_LDT_INFO(dscrp, &ldt_inf);
+	ldt_inf.entry_number = entry;
+
+	if (copyout(&ldt_inf, inf, sizeof (struct ldt_info)))
+		return (set_errno(EFAULT));
+
+	return (0);
+}
+
+long
+lx_set_thread_area(struct ldt_info *inf)
+{
+	struct lx_lwp_data *jlwp = ttolxlwp(curthread);
+	struct ldt_info ldt_inf;
+	user_desc_t *dscrp;
+	int entry;
+	int i;
+
+	/* Check that casts for accessing the words in user_desc are valid */
+	ASSERT(sizeof (user_desc_t) == 8);
+
+	if (copyin(inf, &ldt_inf, sizeof (ldt_inf)))
+		return (set_errno(EFAULT));
+
+	entry = ldt_inf.entry_number;
+	if (entry == -1) {
+		/*
+		 * Find an empty entry in the tls for this thread.
+		 * The casts assume each user_desc_t entry is 8 bytes.
+		 */
+		for (i = 0, dscrp = jlwp->br_tls; i < LX_TLSNUM; i++, dscrp++) {
+			if (((uint_t *)dscrp)[0] == 0 &&
+			    ((uint_t *)dscrp)[1] == 0)
+				break;
+		}
+
+		if (i < LX_TLSNUM) {
+			/*
+			 * found one
+			 */
+			entry = i + GDT_TLSMIN;
+			if (suword32(&inf->entry_number, entry))
+				return (set_errno(EFAULT));
+		} else {
+			return (set_errno(ESRCH));
+		}
+	}
+
+	if (entry < GDT_TLSMIN || entry > GDT_TLSMAX)
+		return (set_errno(EINVAL));
+
+	/*
+	 * convert the linux ldt info to standard intel descriptor
+	 */
+	dscrp = jlwp->br_tls + entry - GDT_TLSMIN;
+
+	if (LDT_INFO_EMPTY(&ldt_inf)) {
+		((uint_t *)dscrp)[0] = 0;
+		((uint_t *)dscrp)[1] = 0;
+	} else {
+		LDT_INFO_TO_DESC(&ldt_inf, dscrp);
+	}
+
+	/*
+	 * update the gdt with the new descriptor
+	 */
+	kpreempt_disable();
+
+	for (i = 0, dscrp = jlwp->br_tls; i < LX_TLSNUM; i++, dscrp++)
+		lx_set_gdt(GDT_TLSMIN + i, dscrp);
+
+	kpreempt_enable();
+
+	return (0);
+}
--- /dev/null	Wed Oct  5 00:32:10 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_timer.c	Wed Oct  5 00:32:10 2016
@@ -0,0 +1,388 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+/*
+ * The illumos kernel provides two clock backends: CLOCK_REALTIME, the
+ * adjustable system wall clock; and CLOCK_HIGHRES, the monotonically
+ * increasing time source that is not subject to drift or adjustment.  By
+ * contrast, the Linux kernel is furnished with an overabundance of narrowly
+ * differentiated clock types.
+ *
+ * Fortunately, most of the commonly used Linux clock types are either similar
+ * enough to the native clock backends that they can be directly mapped, or
+ * represent queries to the per-process and per-LWP microstate counters.
+ *
+ * CLOCK_BOOTTIME is identical to CLOCK_MONOTONIC, except that it takes into
+ * account time that the system is suspended. Since that is uninteresting to
+ * us, we treat it the same.
+ */
+
+#include <sys/time.h>
+#include <sys/systm.h>
+#include <sys/cmn_err.h>
+#include <sys/lx_impl.h>
+
+/*
+ * From "uts/common/os/timer.c":
+ */
+extern int clock_settime(clockid_t, timespec_t *);
+extern int clock_gettime(clockid_t, timespec_t *);
+extern int clock_getres(clockid_t, timespec_t *);
+extern int nanosleep(timespec_t *, timespec_t *);
+
+
+static int lx_emul_clock_getres(clockid_t, timespec_t *);
+static int lx_emul_clock_gettime(clockid_t, timespec_t *);
+static int lx_emul_clock_settime(clockid_t, timespec_t *);
+
+typedef struct lx_clock_backend {
+	clockid_t lclk_ntv_id;
+	int (*lclk_clock_getres)(clockid_t, timespec_t *);
+	int (*lclk_clock_gettime)(clockid_t, timespec_t *);
+	int (*lclk_clock_settime)(clockid_t, timespec_t *);
+} lx_clock_backend_t;
+
+/*
+ * NOTE: The Linux man pages state this structure is obsolete and is
+ * unsupported, so it is declared here for sizing purposes only.
+ */
+struct lx_timezone {
+	int tz_minuteswest;	/* minutes W of Greenwich */
+	int tz_dsttime;		/* type of dst correction */
+};
+
+/*
+ * Use the native clock_* system call implementation, but with a translated
+ * clock identifier:
+ */
+#define	NATIVE(ntv_id)							\
+	{ ntv_id, clock_getres, clock_gettime, clock_settime }
+
+/*
+ * This backend is not supported, so we provide an emulation handler:
+ */
+#define	EMUL(ntv_id)							\
+	{ ntv_id, lx_emul_clock_getres, lx_emul_clock_gettime,		\
+	    lx_emul_clock_settime }
+
+static lx_clock_backend_t lx_clock_backends[] = {
+	NATIVE(CLOCK_REALTIME),		/* LX_CLOCK_REALTIME */
+	NATIVE(CLOCK_HIGHRES),		/* LX_CLOCK_MONOTONIC */
+	EMUL(CLOCK_PROCESS_CPUTIME_ID),	/* LX_CLOCK_PROCESS_CPUTIME_ID */
+	EMUL(CLOCK_THREAD_CPUTIME_ID),	/* LX_CLOCK_THREAD_CPUTIME_ID */
+	NATIVE(CLOCK_HIGHRES),		/* LX_CLOCK_MONOTONIC_RAW */
+	NATIVE(CLOCK_REALTIME),		/* LX_CLOCK_REALTIME_COARSE */
+	NATIVE(CLOCK_HIGHRES),		/* LX_CLOCK_MONOTONIC_COARSE */
+	NATIVE(CLOCK_HIGHRES)		/* LX_CLOCK_BOOTTIME */
+};
+
+#define	LX_CLOCK_MAX \
+	(sizeof (lx_clock_backends) / sizeof (lx_clock_backends[0]))
+#define	LX_CLOCK_BACKEND(clk) \
+	((clk) < LX_CLOCK_MAX && (clk) >= 0 ? &lx_clock_backends[(clk)] : NULL)
+
+/* ARGSUSED */
+static int
+lx_emul_clock_settime(clockid_t clock, timespec_t *tp)
+{
+	return (set_errno(EINVAL));
+}
+
+static int
+lx_emul_clock_gettime(clockid_t clock, timespec_t *tp)
+{
+	timespec_t t;
+
+	switch (clock) {
+	case CLOCK_PROCESS_CPUTIME_ID: {
+		proc_t *p = ttoproc(curthread);
+		hrtime_t snsecs, unsecs;
+
+		/*
+		 * Based on getrusage() in "rusagesys.c":
+		 */
+		mutex_enter(&p->p_lock);
+		unsecs = mstate_aggr_state(p, LMS_USER);
+		snsecs = mstate_aggr_state(p, LMS_SYSTEM);
+		mutex_exit(&p->p_lock);
+
+		hrt2ts(unsecs + snsecs, &t);
+		break;
+	}
+
+	case CLOCK_THREAD_CPUTIME_ID: {
+		klwp_t *lwp = ttolwp(curthread);
+		struct mstate *ms = &lwp->lwp_mstate;
+		hrtime_t snsecs, unsecs;
+
+		/*
+		 * Based on getrusage_lwp() in "rusagesys.c":
+		 */
+		unsecs = ms->ms_acct[LMS_USER];
+		snsecs = ms->ms_acct[LMS_SYSTEM] + ms->ms_acct[LMS_TRAP];
+
+		scalehrtime(&unsecs);
+		scalehrtime(&snsecs);
+
+		hrt2ts(unsecs + snsecs, &t);
+		break;
+	}
+
+	default:
+		return (set_errno(EINVAL));
+	}
+
+#if defined(_SYSCALL32_IMPL)
+	if (get_udatamodel() != DATAMODEL_NATIVE) {
+		timespec32_t t32;
+
+		if (TIMESPEC_OVERFLOW(&t)) {
+			return (set_errno(EOVERFLOW));
+		}
+		TIMESPEC_TO_TIMESPEC32(&t32, &t);
+
+		if (copyout(&t32, tp, sizeof (t32)) != 0) {
+			return (set_errno(EFAULT));
+		}
+
+		return (0);
+	}
+#endif
+
+	if (copyout(&t, tp, sizeof (t)) != 0) {
+		return (set_errno(EFAULT));
+	}
+
+	return (0);
+}
+
+static int
+lx_emul_clock_getres(clockid_t clock, timespec_t *tp)
+{
+	timespec_t t;
+
+	if (tp == NULL) {
+		return (0);
+	}
+
+	switch (clock) {
+	case CLOCK_PROCESS_CPUTIME_ID:
+	case CLOCK_THREAD_CPUTIME_ID:
+		/*
+		 * These clock backends return microstate accounting values for
+		 * the LWP or the entire process.  The Linux kernel claims they
+		 * have nanosecond resolution; so will we.
+		 */
+		t.tv_sec = 0;
+		t.tv_nsec = 1;
+		break;
+
+	default:
+		return (set_errno(EINVAL));
+	}
+
+#if defined(_SYSCALL32_IMPL)
+	if (get_udatamodel() != DATAMODEL_NATIVE) {
+		timespec32_t t32;
+
+		if (TIMESPEC_OVERFLOW(&t)) {
+			return (set_errno(EOVERFLOW));
+		}
+		TIMESPEC_TO_TIMESPEC32(&t32, &t);
+
+		if (copyout(&t32, tp, sizeof (t32)) != 0) {
+			return (set_errno(EFAULT));
+		}
+
+		return (0);
+	}
+#endif
+
+	if (copyout(&t, tp, sizeof (t)) != 0) {
+		return (set_errno(EFAULT));
+	}
+
+	return (0);
+}
+
+static void
+lx_clock_unsupported(int clock)
+{
+	char buf[100];
+
+	(void) snprintf(buf, sizeof (buf), "unsupported clock: %d", clock);
+	lx_unsupported(buf);
+}
+
+long
+lx_clock_settime(int clock, timespec_t *tp)
+{
+	lx_clock_backend_t *backend;
+
+	if ((backend = LX_CLOCK_BACKEND(clock)) == NULL) {
+		lx_clock_unsupported(clock);
+		return (set_errno(EINVAL));
+	}
+
+	return (backend->lclk_clock_settime(backend->lclk_ntv_id, tp));
+}
+
+long
+lx_clock_gettime(int clock, timespec_t *tp)
+{
+	lx_clock_backend_t *backend;
+
+	if ((backend = LX_CLOCK_BACKEND(clock)) == NULL) {
+		lx_clock_unsupported(clock);
+		return (set_errno(EINVAL));
+	}
+
+	return (backend->lclk_clock_gettime(backend->lclk_ntv_id, tp));
+}
+
+long
+lx_clock_getres(int clock, timespec_t *tp)
+{
+	lx_clock_backend_t *backend;
+
+	if ((backend = LX_CLOCK_BACKEND(clock)) == NULL) {
+		lx_clock_unsupported(clock);
+		return (set_errno(EINVAL));
+	}
+
+	/*
+	 * It is important this check is performed after the clock
+	 * check. Both glibc and musl, in their clock_getcpuclockid(),
+	 * use clock_getres() with a NULL tp to validate a clock
+	 * value. Performing the tp check before the clock check could
+	 * indicate a valid clock to libc when it shouldn't.
+	 */
+	if (tp == NULL) {
+		return (0);
+	}
+
+	return (backend->lclk_clock_getres(backend->lclk_ntv_id, tp));
+}
+
+
+long
+lx_gettimeofday(struct timeval *tvp, struct lx_timezone *tzp)
+{
+	struct lx_timezone tz;
+
+	bzero(&tz, sizeof (tz));
+
+	/*
+	 * We want to be similar to libc which just does a fasttrap to
+	 * gethrestime and simply converts that result. We follow how uniqtime
+	 * does the conversion but we can't use that code since it does some
+	 * extra work which can cause the result to bounce around based on which
+	 * CPU we run on.
+	 */
+	if (tvp != NULL) {
+		struct timeval tv;
+		timestruc_t ts;
+		int usec, nsec;
+
+		gethrestime(&ts);
+		nsec = ts.tv_nsec;
+		usec = nsec + (nsec >> 2);
+		usec = nsec + (usec >> 1);
+		usec = nsec + (usec >> 2);
+		usec = nsec + (usec >> 4);
+		usec = nsec - (usec >> 3);
+		usec = nsec + (usec >> 2);
+		usec = nsec + (usec >> 3);
+		usec = nsec + (usec >> 4);
+		usec = nsec + (usec >> 1);
+		usec = nsec + (usec >> 6);
+		usec = usec >> 10;
+
+		tv.tv_sec = ts.tv_sec;
+		tv.tv_usec = usec;
+
+		if (get_udatamodel() == DATAMODEL_NATIVE) {
+			if (copyout(&tv, tvp, sizeof (tv)) != 0)
+				return (set_errno(EFAULT));
+		}
+#ifdef _SYSCALL32_IMPL
+		else {
+			struct timeval32 tv32;
+
+			if (TIMEVAL_OVERFLOW(&tv))
+				return (set_errno(EOVERFLOW));
+			TIMEVAL_TO_TIMEVAL32(&tv32, &tv);
+
+			if (copyout(&tv32, tvp, sizeof (tv32)))
+				return (set_errno(EFAULT));
+		}
+#endif
+	}
+
+	/*
+	 * The Linux man page states use of the second parameter is obsolete,
+	 * but gettimeofday(2) should still return EFAULT if it is set
+	 * to a bad non-NULL pointer (sigh...)
+	 */
+	if (tzp != NULL && copyout(&tz, tzp, sizeof (tz)) != 0)
+		return (set_errno(EFAULT));
+
+	return (0);
+}
+
+/*
+ * On Linux a bad buffer will set errno to EFAULT, and on Illumos the failure
+ * mode is documented as "undefined."
+ */
+long
+lx_time(time_t *tp)
+{
+	timestruc_t ts;
+	struct timeval tv;
+
+	gethrestime(&ts);
+	tv.tv_sec = ts.tv_sec;
+	tv.tv_usec = 0;
+
+	if (get_udatamodel() == DATAMODEL_NATIVE) {
+		if (tp != NULL &&
+		    copyout(&tv.tv_sec, tp, sizeof (tv.tv_sec)) != 0)
+			return (set_errno(EFAULT));
+
+		return (tv.tv_sec);
+	}
+#ifdef _SYSCALL32_IMPL
+	else {
+		struct timeval32 tv32;
+
+		if (TIMEVAL_OVERFLOW(&tv))
+			return (set_errno(EOVERFLOW));
+		TIMEVAL_TO_TIMEVAL32(&tv32, &tv);
+
+		if (tp != NULL &&
+		    copyout(&tv32.tv_sec, tp, sizeof (tv32.tv_sec)))
+			return (set_errno(EFAULT));
+
+		return (tv32.tv_sec);
+	}
+#endif /* _SYSCALL32_IMPL */
+	/* NOTREACHED */
+}
+
+long
+lx_nanosleep(timespec_t *rqtp, timespec_t *rmtp)
+{
+	return (nanosleep(rqtp, rmtp));
+}
--- /dev/null	Wed Oct  5 00:32:10 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_umask.c	Wed Oct  5 00:32:10 2016
@@ -0,0 +1,25 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+
+/* From usr/src/uts/common/syscall/umask.c */
+extern int umask(int);
+
+long
+lx_umask(mode_t cmask)
+{
+	return (umask(cmask));
+}
--- /dev/null	Wed Oct  5 00:32:10 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_uname.c	Wed Oct  5 00:32:10 2016
@@ -0,0 +1,82 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/thread.h>
+#include <sys/proc.h>
+#include <sys/zone.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_types.h>
+
+struct lx_utsname {
+	char lxu_sysname[LX_SYS_UTS_LN];
+	char lxu_nodename[LX_SYS_UTS_LN];
+	char lxu_release[LX_SYS_UTS_LN];
+	char lxu_version[LX_SYS_UTS_LN];
+	char lxu_machine[LX_SYS_UTS_LN];
+	char lxu_domainname[LX_SYS_UTS_LN];
+};
+
+long
+lx_uname(void *uptr)
+{
+	proc_t *p = curproc;
+	lx_proc_data_t *lxpd = ptolxproc(p);
+	lx_zone_data_t *lxzd = ztolxzd(p->p_zone);
+	struct lx_utsname un;
+
+	bzero(&un, sizeof (un));
+
+	(void) strlcpy(un.lxu_sysname, LX_UNAME_SYSNAME, LX_SYS_UTS_LN);
+	(void) strlcpy(un.lxu_nodename, p->p_zone->zone_nodename,
+	    LX_SYS_UTS_LN);
+
+	mutex_enter(&lxzd->lxzd_lock);
+
+	if (lxpd->l_uname_release[0] != '\0') {
+		(void) strlcpy(un.lxu_release, lxpd->l_uname_release,
+		    LX_SYS_UTS_LN);
+	} else {
+		(void) strlcpy(un.lxu_release, lxzd->lxzd_kernel_release,
+		    LX_SYS_UTS_LN);
+	}
+	if (lxpd->l_uname_version[0] != '\0') {
+		(void) strlcpy(un.lxu_version, lxpd->l_uname_version,
+		    LX_SYS_UTS_LN);
+	} else {
+		(void) strlcpy(un.lxu_version, lxzd->lxzd_kernel_version,
+		    LX_SYS_UTS_LN);
+	}
+
+	mutex_exit(&lxzd->lxzd_lock);
+
+	if (get_udatamodel() == DATAMODEL_LP64) {
+		(void) strlcpy(un.lxu_machine, LX_UNAME_MACHINE64,
+		    LX_SYS_UTS_LN);
+	} else {
+		(void) strlcpy(un.lxu_machine, LX_UNAME_MACHINE32,
+		    LX_SYS_UTS_LN);
+	}
+	(void) strlcpy(un.lxu_domainname, p->p_zone->zone_domain,
+	    LX_SYS_UTS_LN);
+
+	if (copyout(&un, uptr, sizeof (un)) != 0) {
+		return (set_errno(EFAULT));
+	}
+
+	return (0);
+}
--- /dev/null	Wed Oct  5 00:32:10 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_wait.c	Wed Oct  5 00:32:10 2016
@@ -0,0 +1,377 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
+ */
+
+/*
+ * wait() family of functions.
+ *
+ * The first minor difference between the Linux and Solaris family of wait()
+ * calls is that the values for WNOHANG and WUNTRACED are different. Thankfully,
+ * the exit status values are identical between the two implementations.
+ *
+ * Things get very different and very complicated when we introduce the Linux
+ * threading model.  Under linux, both threads and child processes are
+ * represented as processes.  However, the behavior of wait() with respect to
+ * each child varies according to the flags given to clone()
+ *
+ *	SIGCHLD 	The SIGCHLD signal should be sent on termination
+ *	CLONE_THREAD	The child shares the same thread group as the parent
+ *	CLONE_DETACHED	The parent receives no notification when the child exits
+ *
+ * The following flags control the Linux behavior w.r.t. the above attributes:
+ *
+ * 	__WALL		Wait on all children, regardless of type
+ * 	__WCLONE	Wait only on non-SIGCHLD children
+ * 	__WNOTHREAD	Don't wait on children of other threads in this group
+ *
+ * The following chart shows whether wait() returns when the child exits:
+ *
+ *                           default    __WCLONE    __WALL
+ *           no SIGCHLD		-	    X	      X
+ *              SIGCHLD		X	    -	      X
+ *
+ * The following chart shows whether wait() returns when the grandchild exits:
+ *
+ *                           default   __WNOTHREAD
+ * 	no CLONE_THREAD		-	    -
+ *         CLONE_THREAD		X	    -
+ *
+ * The CLONE_DETACHED flag is universal - when the child exits, no state is
+ * stored and wait() has no effect.
+ *
+ * XXX Support the above combination of options, or some reasonable subset that
+ *     covers at least fork() and pthread_create().
+ */
+
+#include <sys/wait.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_types.h>
+#include <sys/lx_misc.h>
+#include <lx_signum.h>
+#include <lx_errno.h>
+#include <lx_syscall.h>
+
+/*
+ * From "uts/common/os/exit.c" and "uts/common/syscall/rusagesys.c":
+ */
+extern int waitid(idtype_t, id_t, k_siginfo_t *, int);
+extern int rusagesys(int, void *, void *, void *, void *);
+
+/*
+ * Convert between Linux options and Solaris options, returning -1 if any
+ * invalid flags are found.
+ */
+#define	LX_WNOHANG	0x00000001
+#define	LX_WUNTRACED	0x00000002
+#define	LX_WSTOPPED	LX_WUNTRACED
+#define	LX_WEXITED	0x00000004
+#define	LX_WCONTINUED	0x00000008
+#define	LX_WNOWAIT	0x01000000
+
+#define	LX_WNOTHREAD	0x20000000
+#define	LX_WALL		0x40000000
+#define	LX_WCLONE	0x80000000
+
+#define	LX_P_ALL	0x0
+#define	LX_P_PID	0x1
+#define	LX_P_GID	0x2
+
+/*
+ * Split the passed waitpid/waitid options into two separate variables:
+ * those for the native illumos waitid(2), and the extra Linux-specific
+ * options we will handle in our brand-specific code.
+ */
+static int
+ltos_options(uintptr_t options, int *native_options, int *extra_options)
+{
+	int newoptions = 0;
+
+	if (((options) & ~(LX_WNOHANG | LX_WUNTRACED | LX_WEXITED |
+	    LX_WCONTINUED | LX_WNOWAIT | LX_WNOTHREAD | LX_WALL |
+	    LX_WCLONE)) != 0) {
+		return (-1);
+	}
+
+	*extra_options = options & (LX_WNOTHREAD | LX_WALL | LX_WCLONE);
+
+	if (options & LX_WNOHANG)
+		newoptions |= WNOHANG;
+	if (options & LX_WUNTRACED)
+		newoptions |= WUNTRACED;
+	if (options & LX_WEXITED)
+		newoptions |= WEXITED;
+	if (options & LX_WCONTINUED)
+		newoptions |= WCONTINUED;
+	if (options & LX_WNOWAIT)
+		newoptions |= WNOWAIT;
+
+	/*
+	 * The trapped option is implicit on Linux.
+	 */
+	newoptions |= WTRAPPED;
+
+	*native_options = newoptions;
+	return (0);
+}
+
+static int
+lx_wstat(int code, int status)
+{
+	int stat = 0;
+
+	switch (code) {
+	case CLD_EXITED:
+		stat = status << 8;
+		break;
+	case CLD_DUMPED:
+		stat = lx_stol_signo(status, SIGKILL) | WCOREFLG;
+		break;
+	case CLD_KILLED:
+		stat = lx_stol_signo(status, SIGKILL);
+		break;
+	case CLD_TRAPPED:
+	case CLD_STOPPED:
+		stat = (lx_stol_status(status, SIGKILL) << 8) | WSTOPFLG;
+		break;
+	case CLD_CONTINUED:
+		stat = WCONTFLG;
+		break;
+	}
+
+	return (stat);
+}
+
+static int
+lx_call_waitid(idtype_t idtype, id_t id, k_siginfo_t *sip, int native_options,
+    int extra_options)
+{
+	lx_lwp_data_t *lwpd = ttolxlwp(curthread);
+	int error;
+
+	/*
+	 * Our brand-specific waitid helper only understands a subset of
+	 * the possible idtypes.  Ensure we keep to that subset here:
+	 */
+	if (idtype != P_ALL && idtype != P_PID && idtype != P_PGID) {
+		return (EINVAL);
+	}
+
+	/*
+	 * Enable the return of emulated ptrace(2) stop conditions
+	 * through lx_waitid_helper, and stash the Linux-specific
+	 * extra waitid() flags.
+	 */
+	lwpd->br_waitid_emulate = B_TRUE;
+	lwpd->br_waitid_flags = extra_options;
+
+	if ((error = waitid(idtype, id, sip, native_options)) == EINTR) {
+		/*
+		 * According to signal(7), the wait4(2), waitid(2), and
+		 * waitpid(2) system calls are restartable.
+		 */
+		ttolxlwp(curthread)->br_syscall_restart = B_TRUE;
+	}
+
+	lwpd->br_waitid_emulate = B_FALSE;
+	lwpd->br_waitid_flags = 0;
+
+	return (error);
+}
+
+long
+lx_wait4(uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4)
+{
+	k_siginfo_t info = { 0 };
+	idtype_t idtype;
+	id_t id;
+	int status = 0;
+	pid_t pid = (pid_t)p1;
+	int error;
+	int native_options, extra_options;
+	int *statusp = (int *)p2;
+	void *rup = (void *)p4;
+
+	if (ltos_options(p3, &native_options, &extra_options) == -1) {
+		return (set_errno(EINVAL));
+	}
+
+	if (pid > maxpid) {
+		return (set_errno(ECHILD));
+	}
+
+	/*
+	 * While not listed as a valid return code, Linux's wait4(2) does,
+	 * in fact, get an EFAULT if either the status pointer or rusage
+	 * pointer is invalid. Since a failed waitpid should leave child
+	 * process in a state where a future wait4(2) will succeed, we
+	 * check them by copying out the values their buffers originally
+	 * contained.  (We need to do this as a failed system call should
+	 * never affect the contents of a passed buffer.)
+	 *
+	 * This will fail if the buffers in question are write-only.
+	 */
+	if (statusp != NULL) {
+		if (copyin(statusp, &status, sizeof (status)) != 0 ||
+		    copyout(&status, statusp, sizeof (status)) != 0) {
+			return (set_errno(EFAULT));
+		}
+	}
+
+	/*
+	 * Do the same check for the "struct rusage" pointer, which differs
+	 * in size for 32- and 64-bit processes.
+	 */
+	if (rup != NULL) {
+		struct rusage ru;
+		void *krup = &ru;
+		size_t rusz = sizeof (ru);
+#if defined(_SYSCALL32_IMPL)
+		struct rusage32 ru32;
+
+		if (get_udatamodel() != DATAMODEL_NATIVE) {
+			krup = &ru32;
+			rusz = sizeof (ru32);
+		}
+#endif
+
+		if (copyin(rup, krup, rusz) != 0 ||
+		    copyout(krup, rup, rusz) != 0) {
+			return (set_errno(EFAULT));
+		}
+	}
+
+	if (pid < -1) {
+		idtype = P_PGID;
+		id = -pid;
+	} else if (pid == -1) {
+		idtype = P_ALL;
+		id = 0;
+	} else if (pid == 0) {
+		idtype = P_PGID;
+		mutex_enter(&pidlock);
+		id = curproc->p_pgrp;
+		mutex_exit(&pidlock);
+	} else {
+		idtype = P_PID;
+		id = pid;
+	}
+
+	native_options |= (WEXITED | WTRAPPED);
+
+	if ((error = lx_call_waitid(idtype, id, &info, native_options,
+	    extra_options)) != 0) {
+		return (set_errno(error));
+	}
+
+	/*
+	 * If the WNOHANG flag was specified and no child was found return 0.
+	 */
+	if ((native_options & WNOHANG) && info.si_pid == 0) {
+		return (0);
+	}
+
+	status = lx_wstat(info.si_code, info.si_status);
+
+	/*
+	 * Unfortunately if this attempt to copy out either the status or the
+	 * rusage fails, the process will be in an inconsistent state as
+	 * subsequent calls to wait for the same child will fail where they
+	 * should succeed on a Linux system. This, however, is rather
+	 * unlikely since we tested the validity of both above.
+	 */
+	if (statusp != NULL) {
+		if (copyout(&status, statusp, sizeof (status)) != 0) {
+			return (set_errno(EFAULT));
+		}
+	}
+
+	if (rup != NULL) {
+		if ((error = rusagesys(_RUSAGESYS_GETRUSAGE_CHLD, rup, NULL,
+		    NULL, NULL)) != 0) {
+			return (set_errno(error));
+		}
+	}
+
+	return (info.si_pid);
+}
+
+long
+lx_waitpid(uintptr_t p1, uintptr_t p2, uintptr_t p3)
+{
+	return (lx_wait4(p1, p2, p3, NULL));
+}
+
+long
+lx_waitid(uintptr_t idtype, uintptr_t id, uintptr_t infop, uintptr_t opt)
+{
+	int error;
+	int native_options, extra_options;
+	k_siginfo_t info = { 0 };
+
+	if (ltos_options(opt, &native_options, &extra_options) == -1) {
+		return (set_errno(EINVAL));
+	}
+
+	if (((opt) & (LX_WEXITED | LX_WSTOPPED | LX_WCONTINUED)) == 0) {
+		return (set_errno(EINVAL));
+	}
+
+	switch (idtype) {
+	case LX_P_ALL:
+		idtype = P_ALL;
+		break;
+	case LX_P_PID:
+		idtype = P_PID;
+		break;
+	case LX_P_GID:
+		idtype = P_PGID;
+		break;
+	default:
+		return (set_errno(EINVAL));
+	}
+
+	if ((error = lx_call_waitid(idtype, id, &info, native_options,
+	    extra_options)) != 0) {
+		return (set_errno(error));
+	}
+
+	/*
+	 * If the WNOHANG flag was specified and no child was found return 0.
+	 */
+	if ((native_options & WNOHANG) && info.si_pid == 0) {
+		return (0);
+	}
+
+#if defined(_SYSCALL32_IMPL)
+	if (get_udatamodel() != DATAMODEL_NATIVE) {
+		return (stol_ksiginfo32_copyout(&info, (void *)infop));
+	} else
+#endif
+	{
+		return (stol_ksiginfo_copyout(&info, (void *)infop));
+	}
+}
--- /dev/null	Wed Oct  5 00:32:10 2016
+++ new/usr/src/uts/common/brand/lx/syscall/lx_xattr.c	Wed Oct  5 00:32:10 2016
@@ -0,0 +1,371 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#include <sys/errno.h>
+#include <sys/systm.h>
+#include <sys/file.h>
+#include <sys/vnode.h>
+#include <sys/pathname.h>
+
+
+#define	LX_XATTR_NAME_MAX	255
+#define	LX_XATTR_SIZE_MAX	65536
+#define	LX_XATTR_LIST_MAX	65536
+
+#define	LX_XATTR_FLAG_CREATE	0x1
+#define	LX_XATTR_FLAG_REPLACE	0x2
+#define	LX_XATTR_FLAGS_VALID	(LX_XATTR_FLAG_CREATE | LX_XATTR_FLAG_REPLACE)
+
+#define	LX_CAP_XATTR_NAME	"security.capability"
+
+/*
+ * *xattr() family of functions.
+ *
+ * These are largely unimplemented.  In most cases we return EOPNOTSUPP, rather
+ * than using NOSYS_NO_EQUIV to avoid unwanted stderr output from ls(1).
+ *
+ * Note that CRED() is used instead of f_cred in the f*xattr functions.  This
+ * is intentional as Linux does not have the same notion of per-fd credentials.
+ */
+
+/* ARGSUSED */
+static int
+lx_setxattr_common(vnode_t *vp, char *name, void *value, size_t size,
+    int flags)
+{
+	int error;
+	char name_buf[LX_XATTR_NAME_MAX + 1];
+	size_t name_len;
+
+	if ((flags & ~LX_XATTR_FLAGS_VALID) != 0) {
+		return (EINVAL);
+	}
+	error = copyinstr(name, name_buf, sizeof (name_buf), &name_len);
+	if (error == ENAMETOOLONG || name_len == sizeof (name_buf)) {
+		return (ERANGE);
+	} else if (error != 0) {
+		return (EFAULT);
+	}
+	if (size > LX_XATTR_SIZE_MAX) {
+		return (E2BIG);
+	}
+
+	/*
+	 * In order to keep package management software happy, despite lacking
+	 * support for file-based Linux capabilities via xattrs, we fake
+	 * success when root attempts a setxattr on that attribute.
+	 */
+	if (crgetuid(CRED()) == 0 &&
+	    strcmp(name_buf, LX_CAP_XATTR_NAME) == 0) {
+		return (0);
+	}
+
+
+	return (EOPNOTSUPP);
+}
+
+/* ARGSUSED */
+static int
+lx_getxattr_common(vnode_t *vp, char *name, char *value, size_t size,
+    ssize_t *osize)
+{
+	int error;
+	char name_buf[LX_XATTR_NAME_MAX + 1];
+	size_t name_len;
+
+	error = copyinstr(name, name_buf, sizeof (name_buf), &name_len);
+	if (error == ENAMETOOLONG || name_len == sizeof (name_buf)) {
+		return (ERANGE);
+	} else if (error != 0) {
+		return (EFAULT);
+	}
+
+	/*
+	 * Only parameter validation is attempted for now.
+	 */
+	return (EOPNOTSUPP);
+}
+
+/* ARGSUSED */
+static int
+lx_listxattr_common(vnode_t *vp, char *list, size_t size, ssize_t *osize)
+{
+	return (EOPNOTSUPP);
+}
+
+/* ARGSUSED */
+static int
+lx_removexattr_common(vnode_t *vp, char *name)
+{
+	int error;
+	char name_buf[LX_XATTR_NAME_MAX + 1];
+	size_t name_len;
+
+	error = copyinstr(name, name_buf, sizeof (name_buf), &name_len);
+	if (error == ENAMETOOLONG || name_len == sizeof (name_buf)) {
+		return (ERANGE);
+	} else if (error != 0) {
+		return (EFAULT);
+	}
+
+	/*
+	 * Only parameter validation is attempted for now.
+	 */
+	return (EOPNOTSUPP);
+}
+
+
+long
+lx_setxattr(char *path, char *name, void *value, size_t size, int flags)
+{
+	int error;
+	vnode_t *vp = NULL;
+
+	error = lookupname(path, UIO_USERSPACE, FOLLOW, NULLVPP, &vp);
+	if (error != 0) {
+		return (set_errno(error));
+	}
+
+	error = lx_setxattr_common(vp, name, value, size, flags);
+	VN_RELE(vp);
+
+	if (error != 0) {
+		return (set_errno(error));
+	}
+	return (0);
+}
+
+long
+lx_lsetxattr(char *path, char *name, void *value, size_t size, int flags)
+{
+	int error;
+	vnode_t *vp = NULL;
+
+	error = lookupname(path, UIO_USERSPACE, NO_FOLLOW, NULLVPP, &vp);
+	if (error != 0) {
+		return (set_errno(error));
+	}
+
+	error = lx_setxattr_common(vp, name, value, size, flags);
+	VN_RELE(vp);
+
+	if (error != 0) {
+		return (set_errno(error));
+	}
+	return (0);
+}
+
+long
+lx_fsetxattr(int fd, char *name, void *value, size_t size, int flags)
+{
+	int error;
+	file_t *fp;
+
+	if ((fp = getf(fd)) == NULL) {
+		return (set_errno(EBADF));
+	}
+
+	error = lx_setxattr_common(fp->f_vnode, name, value, size, flags);
+	releasef(fd);
+
+	if (error != 0) {
+		return (set_errno(error));
+	}
+	return (0);
+}
+
+ssize_t
+lx_getxattr(char *path, char *name, void *value, size_t size)
+{
+	int error;
+	vnode_t *vp = NULL;
+	ssize_t osize;
+
+	error = lookupname(path, UIO_USERSPACE, FOLLOW, NULLVPP, &vp);
+	if (error != 0) {
+		return (set_errno(error));
+	}
+
+	error = lx_getxattr_common(vp, name, value, size, &osize);
+	VN_RELE(vp);
+
+	if (error != 0) {
+		return (set_errno(error));
+	}
+	return (osize);
+}
+
+ssize_t
+lx_lgetxattr(char *path, char *name, void *value, size_t size)
+{
+
+	int error;
+	vnode_t *vp = NULL;
+	ssize_t osize;
+
+	error = lookupname(path, UIO_USERSPACE, NO_FOLLOW, NULLVPP, &vp);
+	if (error != 0) {
+		return (set_errno(error));
+	}
+
+	error = lx_getxattr_common(vp, name, value, size, &osize);
+	VN_RELE(vp);
+
+	if (error != 0) {
+		return (set_errno(error));
+	}
+	return (osize);
+}
+
+ssize_t
+lx_fgetxattr(int fd, char *name, void *value, size_t size)
+{
+	int error;
+	file_t *fp;
+	ssize_t osize;
+
+	if ((fp = getf(fd)) == NULL) {
+		return (set_errno(EBADF));
+	}
+
+	error = lx_getxattr_common(fp->f_vnode, name, value, size, &osize);
+	releasef(fd);
+
+	if (error != 0) {
+		return (set_errno(error));
+	}
+	return (osize);
+}
+
+ssize_t
+lx_listxattr(char *path, char *list, size_t size)
+{
+	int error;
+	vnode_t *vp = NULL;
+	ssize_t osize;
+
+	error = lookupname(path, UIO_USERSPACE, FOLLOW, NULLVPP, &vp);
+	if (error != 0) {
+		return (set_errno(error));
+	}
+
+	error = lx_listxattr_common(vp, list, size, &osize);
+	VN_RELE(vp);
+
+	if (error != 0) {
+		return (set_errno(error));
+	}
+	return (osize);
+}
+
+ssize_t
+lx_llistxattr(char *path, char *list, size_t size)
+{
+	int error;
+	vnode_t *vp = NULL;
+	ssize_t osize;
+
+	error = lookupname(path, UIO_USERSPACE, NO_FOLLOW, NULLVPP, &vp);
+	if (error != 0) {
+		return (set_errno(error));
+	}
+
+	error = lx_listxattr_common(vp, list, size, &osize);
+	VN_RELE(vp);
+
+	if (error != 0) {
+		return (set_errno(error));
+	}
+	return (osize);
+}
+
+ssize_t
+lx_flistxattr(int fd, char *list, size_t size)
+{
+	int error;
+	file_t *fp;
+	ssize_t osize;
+
+	if ((fp = getf(fd)) == NULL) {
+		return (set_errno(EBADF));
+	}
+
+	error = lx_listxattr_common(fp->f_vnode, list, size, &osize);
+	releasef(fd);
+
+	if (error != 0) {
+		return (set_errno(error));
+	}
+	return (osize);
+}
+
+int
+lx_removexattr(char *path, char *name)
+{
+	int error;
+	vnode_t *vp = NULL;
+
+	error = lookupname(path, UIO_USERSPACE, FOLLOW, NULLVPP, &vp);
+	if (error != 0) {
+		return (set_errno(error));
+	}
+
+	error = lx_removexattr_common(vp, name);
+	VN_RELE(vp);
+
+	if (error != 0) {
+		return (set_errno(error));
+	}
+	return (0);
+}
+
+int
+lx_lremovexattr(char *path, char *name)
+{
+	int error;
+	vnode_t *vp = NULL;
+
+	error = lookupname(path, UIO_USERSPACE, NO_FOLLOW, NULLVPP, &vp);
+	if (error != 0) {
+		return (set_errno(error));
+	}
+
+	error = lx_removexattr_common(vp, name);
+	VN_RELE(vp);
+
+	if (error != 0) {
+		return (set_errno(error));
+	}
+	return (0);
+}
+
+int
+lx_fremovexattr(int fd, char *name)
+{
+	int error;
+	file_t *fp;
+
+	if ((fp = getf(fd)) == NULL) {
+		return (set_errno(EBADF));
+	}
+
+	error = lx_removexattr_common(fp->f_vnode, name);
+	releasef(fd);
+
+	if (error != 0) {
+		return (set_errno(error));
+	}
+	return (0);
+}
--- /dev/null	Wed Oct  5 00:32:10 2016
+++ new/usr/src/uts/common/brand/lx/sysfs/lx_sysfs.h	Wed Oct  5 00:32:10 2016
@@ -0,0 +1,196 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#ifndef	_LXSYSFS_H
+#define	_LXSYSFS_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/*
+ * lx_sysfs.h: declarations, data structures and macros for lx_sysfs
+ */
+
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/policy.h>
+#include <sys/debug.h>
+#include <sys/dirent.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/kmem.h>
+#include <sys/pathname.h>
+#include <sys/systm.h>
+#include <sys/var.h>
+#include <sys/user.h>
+#include <sys/t_lock.h>
+#include <sys/sysmacros.h>
+#include <sys/cred.h>
+#include <sys/priv.h>
+#include <sys/vnode.h>
+#include <sys/vfs.h>
+#include <sys/statvfs.h>
+#include <sys/cmn_err.h>
+#include <sys/zone.h>
+#include <sys/uio.h>
+#include <sys/utsname.h>
+#include <sys/dnlc.h>
+#include <sys/atomic.h>
+#include <sys/sunddi.h>
+#include <sys/sunldi.h>
+#include <vm/as.h>
+#include <vm/anon.h>
+#include <sys/netstack.h>
+#include <inet/ip.h>
+#include <inet/ip_if.h>
+
+/*
+ * Convert a vnode into an lxsys_mnt_t
+ */
+#define	VTOLXSM(vp)	((lxsys_mnt_t *)(vp)->v_vfsp->vfs_data)
+
+/*
+ * convert a vnode into an lxsys_node
+ */
+#define	VTOLXS(vp)	((lxsys_node_t *)(vp)->v_data)
+
+/*
+ * convert a lxsys_node into a vnode
+ */
+#define	LXSTOV(lxsnp)	((lxsnp)->lxsys_vnode)
+
+/*
+ * convert a lxsys_node into zone for fs
+ */
+#define	LXSTOZ(lxsnp) \
+	(((lxsys_mnt_t *)(lxsnp)->lxsys_vnode->v_vfsp->vfs_data)->lxsysm_zone)
+
+#define	LXSNSIZ		256	/* max size of lx /sys file name entries */
+
+/*
+ * Pretend that a directory entry takes 16 bytes
+ */
+#define	LXSYS_SDSIZE	16
+
+/* Root sysfs lxsys_instance */
+#define	LXSYS_INST_ROOT	0
+
+/*
+ * Node/file types for lx /sys files
+ * (directories and files contained therein).
+ */
+typedef enum lxsys_nodetype {
+	LXSYS_NONE,		/* None-type to keep inodes non-zero	*/
+	LXSYS_STATIC,		/* Statically defined entries		*/
+	LXSYS_CLASS_NET,	/* /sys/class/net/<iface>		*/
+	LXSYS_DEV_NET,		/* /sys/devices/virtual/net/<iface>	*/
+	LXSYS_BLOCK,		/* /sys/block/<dev>			*/
+	LXSYS_DEV_ZFS,		/* /sys/devices/zfs/<dev>		*/
+	LXSYS_DEV_SYS_CPU,	/* /sys/devices/system/cpu/<cpu>	*/
+	LXSYS_DEV_SYS_CPUINFO,	/* /sys/devices/system/cpu/cpuN/<info>	*/
+	LXSYS_DEV_SYS_NODE,	/* /sys/devices/system/node/node0/<info> */
+	LXSYS_MAXTYPE,		/* type limit				*/
+} lxsys_nodetype_t;
+
+/*
+ * external dirent characteristics
+ */
+typedef struct {
+	unsigned int	d_idnum;
+	char		*d_name;
+} lxsys_dirent_t;
+
+typedef struct {
+	unsigned int	dl_instance;
+	lxsys_dirent_t	*dl_list;
+	int		dl_length;
+} lxsys_dirlookup_t;
+
+/*
+ * This is the lx sysfs private data object
+ * which is attached to v_data in the vnode structure
+ */
+struct lxsys_node;
+typedef struct lxsys_node lxsys_node_t;
+struct lxsys_node {
+	lxsys_nodetype_t	lxsys_type;	/* type ID of node 	*/
+	unsigned int		lxsys_instance;	/* instance ID node	*/
+	unsigned int		lxsys_endpoint;	/* endpoint ID node	*/
+	vnode_t			*lxsys_vnode;	/* vnode for the node	*/
+	vnode_t			*lxsys_parentvp; /* parent directory	*/
+	lxsys_node_t		*lxsys_next;	/* next list entry	*/
+	timestruc_t		lxsys_time;	/* creation time	*/
+	mode_t			lxsys_mode;	/* file mode bits	*/
+	uid_t			lxsys_uid;	/* file owner		*/
+	gid_t			lxsys_gid;	/* file group owner	*/
+	ino_t			lxsys_ino;	/* node id		*/
+};
+
+/*
+ * This is the lxsysfs private data object
+ * which is attached to vfs_data in the vfs structure
+ */
+typedef struct lxsys_mnt {
+	kmutex_t	lxsysm_lock;	/* protects fields		*/
+	lxsys_node_t	*lxsysm_node;	/* node at root of sys mount	*/
+	zone_t		*lxsysm_zone;	/* zone for this mount		*/
+} lxsys_mnt_t;
+
+extern vnodeops_t	*lxsys_vnodeops;
+
+typedef struct mounta	mounta_t;
+
+extern void lxsys_initnodecache();
+extern void lxsys_fininodecache();
+extern ino_t lxsys_inode(lxsys_nodetype_t, unsigned int, unsigned int);
+extern ino_t lxsys_parentinode(lxsys_node_t *);
+extern lxsys_node_t *lxsys_getnode(vnode_t *, lxsys_nodetype_t, unsigned int,
+    unsigned int);
+extern lxsys_node_t *lxsys_getnode_static(vnode_t *, unsigned int);
+extern void lxsys_freenode(lxsys_node_t *);
+
+extern netstack_t *lxsys_netstack(lxsys_node_t *);
+extern ill_t *lxsys_find_ill(ip_stack_t *, uint_t);
+
+typedef struct lxpr_uiobuf {
+	uio_t *uiop;
+	char *buffer;
+	uint32_t bufsize;
+	char *pos;
+	size_t beg;
+	int error;
+} lxsys_uiobuf_t;
+
+extern lxsys_uiobuf_t *lxsys_uiobuf_new(uio_t *);
+extern void lxsys_uiobuf_free(lxsys_uiobuf_t *);
+extern void lxsys_uiobuf_seterr(lxsys_uiobuf_t *, int);
+extern int lxsys_uiobuf_flush(lxsys_uiobuf_t *);
+extern void lxsys_uiobuf_write(lxsys_uiobuf_t *, const char *, size_t);
+extern void lxsys_uiobuf_printf(lxsys_uiobuf_t *uiobuf, const char *fmt, ...);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#ifndef islower
+#define	islower(x)	(((unsigned)(x) >= 'a') && ((unsigned)(x) <= 'z'))
+#endif
+#ifndef toupper
+#define	toupper(x)	(islower(x) ? (x) - 'a' + 'A' : (x))
+#endif
+
+#endif /* _LXSYSFS_H */
--- /dev/null	Wed Oct  5 00:32:10 2016
+++ new/usr/src/uts/common/brand/lx/sysfs/lx_syssubr.c	Wed Oct  5 00:32:10 2016
@@ -0,0 +1,452 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+/*
+ * lx_syssubr.c: Various functions for the /sys vnodeops.
+ */
+
+#include <sys/varargs.h>
+
+#include <sys/cpuvar.h>
+#include <sys/mman.h>
+#include <sys/vmsystm.h>
+#include <sys/prsystm.h>
+
+#include "lx_sysfs.h"
+
+#define	LXSYSCACHE_NAME "lxsys_cache"
+
+static int lxsys_node_constructor(void *, void *, int);
+static void lxsys_node_destructor(void *, void *);
+
+static kmem_cache_t *lxsys_node_cache;
+
+void
+lxsys_initnodecache()
+{
+	lxsys_node_cache = kmem_cache_create(LXSYSCACHE_NAME,
+	    sizeof (lxsys_node_t), 0,
+	    lxsys_node_constructor, lxsys_node_destructor, NULL, NULL, NULL, 0);
+}
+
+void
+lxsys_fininodecache()
+{
+	kmem_cache_destroy(lxsys_node_cache);
+}
+
+/* ARGSUSED */
+static int
+lxsys_node_constructor(void *buf, void *un, int kmflags)
+{
+	lxsys_node_t	*lxsnp = buf;
+	vnode_t		*vp;
+
+	vp = lxsnp->lxsys_vnode = vn_alloc(kmflags);
+	if (vp == NULL)
+		return (-1);
+
+	(void) vn_setops(vp, lxsys_vnodeops);
+	vp->v_data = lxsnp;
+
+	return (0);
+}
+
+/* ARGSUSED */
+static void
+lxsys_node_destructor(void *buf, void *un)
+{
+	lxsys_node_t	*lxsnp = buf;
+
+	vn_free(LXSTOV(lxsnp));
+}
+
+/*
+ * Calculate an inode number
+ *
+ * This takes various bits of info and munges them
+ * to give the inode number for an lxsys node
+ */
+ino_t
+lxsys_inode(lxsys_nodetype_t type, unsigned int instance,
+    unsigned int endpoint)
+{
+	/*
+	 * Sysfs Inode format:
+	 * 0000AABBBBCC
+	 *
+	 * AA - TYPE
+	 * BBBB - INSTANCE
+	 * CC - ENDPOINT
+	 */
+	ASSERT(instance <= 0xffff);
+	ASSERT(endpoint <= 0xff);
+
+	return ((ino_t)(type << 24)|(instance << 8)|endpoint);
+}
+
+/*
+ * Return inode number of parent (directory)
+ */
+ino_t
+lxsys_parentinode(lxsys_node_t *lxsnp)
+{
+	/*
+	 * If the input node is the root then the parent inode
+	 * is the mounted on inode so just return our inode number
+	 */
+	if (lxsnp->lxsys_type == LXSYS_STATIC &&
+	    lxsnp->lxsys_instance == LXSYS_INST_ROOT) {
+		return (lxsnp->lxsys_ino);
+	} else {
+		return (VTOLXS(lxsnp->lxsys_parentvp)->lxsys_ino);
+	}
+}
+
+/*
+ * Allocate a new lxsys node
+ *
+ * This also allocates the vnode associated with it
+ */
+lxsys_node_t *
+lxsys_getnode(vnode_t *dp, lxsys_nodetype_t type, unsigned int instance,
+    unsigned int endpoint)
+{
+	lxsys_node_t *lxsnp;
+	vnode_t *vp;
+	timestruc_t now;
+
+	/*
+	 * Allocate a new node. It is deallocated in vop_innactive
+	 */
+	lxsnp = kmem_cache_alloc(lxsys_node_cache, KM_SLEEP);
+
+	/*
+	 * Set defaults (may be overridden below)
+	 */
+	gethrestime(&now);
+	lxsnp->lxsys_type = type;
+	lxsnp->lxsys_instance = instance;
+	lxsnp->lxsys_endpoint = endpoint;
+	lxsnp->lxsys_next = NULL;
+	lxsnp->lxsys_parentvp = dp;
+	VN_HOLD(dp);
+
+	lxsnp->lxsys_time = now;
+	lxsnp->lxsys_uid = lxsnp->lxsys_gid = 0;
+	lxsnp->lxsys_ino = lxsys_inode(type, instance, endpoint);
+
+	/* initialize the vnode data */
+	vp = lxsnp->lxsys_vnode;
+	vn_reinit(vp);
+	vp->v_flag = VNOCACHE|VNOMAP|VNOSWAP|VNOMOUNT;
+	vp->v_vfsp = dp->v_vfsp;
+
+	/*
+	 * Default to a directory with open permissions.
+	 * Specific components will override this
+	 */
+	if (type == LXSYS_STATIC && instance == LXSYS_INST_ROOT) {
+		vp->v_flag |= VROOT;
+	}
+	vp->v_type = VDIR;
+	lxsnp->lxsys_mode = 0555;
+
+	return (lxsnp);
+}
+
+lxsys_node_t *
+lxsys_getnode_static(vnode_t *dp, unsigned int instance)
+{
+	lxsys_mnt_t *lxsm = VTOLXSM(dp);
+	lxsys_node_t *lnp, *tail = NULL;
+
+	mutex_enter(&lxsm->lxsysm_lock);
+	for (lnp = lxsm->lxsysm_node; lnp != NULL; lnp = lnp->lxsys_next) {
+		if (lnp->lxsys_instance == instance) {
+			VERIFY(lnp->lxsys_parentvp == dp);
+
+			VN_HOLD(lnp->lxsys_vnode);
+			mutex_exit(&lxsm->lxsysm_lock);
+			return (lnp);
+		} else if (lnp->lxsys_next == NULL) {
+			/* Found no match by the end of the list */
+			tail = lnp;
+			break;
+		}
+	}
+
+	tail->lxsys_next = lxsys_getnode(dp, LXSYS_STATIC, instance, 0);
+	lnp = tail->lxsys_next;
+	/* Allow mounts on static entries */
+	LXSTOV(lnp)->v_flag &= (~VNOMOUNT);
+	mutex_exit(&lxsm->lxsysm_lock);
+	return (lnp);
+}
+
+/* Clean up persistence for static lxsys_node */
+int
+lxsys_freenode_static(lxsys_node_t *lnp)
+{
+	lxsys_node_t *plnp;
+	vnode_t *vp = LXSTOV(lnp);
+	lxsys_mnt_t *lxsm = VTOLXSM(vp);
+
+	if (lnp->lxsys_instance == LXSYS_INST_ROOT) {
+		/*
+		 * The root vnode does not need special cleanup since it
+		 * anchors the list and is freed by lxsys_unmount.
+		 */
+		return (0);
+	}
+
+	mutex_enter(&lxsm->lxsysm_lock);
+
+	/*
+	 * It is possible that a different process acquired a fresh reference
+	 * to this vnode via lookup while we were waiting on the lxsysm_lock.
+	 * To avoid freeing the vnode out from under them, we will double-check
+	 * v_count and bail from the fop_inactive if it was grabbed.
+	 */
+	mutex_enter(&vp->v_lock);
+	if (vp->v_count != 1) {
+		VERIFY(vp->v_count > 0);
+
+		/* Release our hold before bailing out of lxsys_inactive */
+		vp->v_count--;
+
+		mutex_exit(&vp->v_lock);
+		mutex_exit(&lxsm->lxsysm_lock);
+		return (-1);
+	}
+	mutex_exit(&vp->v_lock);
+
+	/* search for the record pointing to lnp */
+	plnp = lxsm->lxsysm_node;
+	while (plnp != NULL && plnp->lxsys_next != lnp) {
+		plnp = plnp->lxsys_next;
+	}
+	/* entry should always be found */
+	VERIFY(plnp != NULL);
+	plnp->lxsys_next = lnp->lxsys_next;
+
+	mutex_exit(&lxsm->lxsysm_lock);
+	return (0);
+}
+
+/*
+ * Free the storage obtained from lxsys_getnode().
+ */
+void
+lxsys_freenode(lxsys_node_t *lxsnp)
+{
+	vnode_t *vp = LXSTOV(lxsnp);
+
+	VERIFY(vp != NULL);
+
+	if (lxsnp->lxsys_type == LXSYS_STATIC) {
+		if (lxsys_freenode_static(lxsnp) != 0) {
+			return;
+		}
+	}
+
+	/*
+	 * delete any association with parent vp
+	 */
+	if (lxsnp->lxsys_parentvp != NULL)
+		VN_RELE(lxsnp->lxsys_parentvp);
+
+	/*
+	 * Release the lxsysnode.
+	 */
+	kmem_cache_free(lxsys_node_cache, lxsnp);
+}
+
+/*
+ * Get the netstack associated with this lxsys mount
+ */
+netstack_t *
+lxsys_netstack(lxsys_node_t *lnp)
+{
+	zone_t *zone = VTOLXSM(LXSTOV(lnp))->lxsysm_zone;
+	netstack_t *ns = zone->zone_netstack;
+
+	VERIFY(ns != NULL);
+
+	if (ns->netstack_flags & (NSF_UNINIT|NSF_CLOSING)) {
+		ns = NULL;
+	} else {
+		netstack_hold(ns);
+	}
+
+	return (ns);
+}
+
+ill_t *
+lxsys_find_ill(ip_stack_t *ipst, uint_t ifindex)
+{
+	ill_t *ill;
+	phyint_t *phyi;
+
+	rw_enter(&ipst->ips_ill_g_lock, RW_READER);
+	phyi = avl_find(&ipst->ips_phyint_g_list->phyint_list_avl_by_index,
+	    (void *) &ifindex, NULL);
+	if (phyi != NULL) {
+		/*
+		 * Since interface information presented via /sys is not
+		 * specific to IPv4 or IPv6, an ill reference from either
+		 * protocol will be adequate.  Check both, starting with IPv4
+		 * for a valid reference to use.
+		 */
+		for (ill = phyi->phyint_illv4; ill != phyi->phyint_illv6;
+		    ill = phyi->phyint_illv6) {
+			if (ill != NULL) {
+				mutex_enter(&ill->ill_lock);
+				if (!ILL_IS_CONDEMNED(ill)) {
+					ill_refhold_locked(ill);
+					mutex_exit(&ill->ill_lock);
+					rw_exit(&ipst->ips_ill_g_lock);
+					return (ill);
+				}
+				mutex_exit(&ill->ill_lock);
+			}
+		}
+	}
+	rw_exit(&ipst->ips_ill_g_lock);
+	return (NULL);
+}
+
+
+#define	LXSYSUIOBUFSZ	4096
+
+lxsys_uiobuf_t *
+lxsys_uiobuf_new(uio_t *uiop)
+{
+	/* Allocate memory for both lxsys_uiobuf and output buffer */
+	int bufsize = LXSYSUIOBUFSZ;
+	lxsys_uiobuf_t *uiobuf =
+	    kmem_alloc(sizeof (lxsys_uiobuf_t) + bufsize, KM_SLEEP);
+
+	uiobuf->uiop = uiop;
+	uiobuf->buffer = (char *)&uiobuf[1];
+	uiobuf->bufsize = bufsize;
+	uiobuf->pos = uiobuf->buffer;
+	uiobuf->beg = 0;
+	uiobuf->error = 0;
+
+	return (uiobuf);
+}
+
+void
+lxsys_uiobuf_free(lxsys_uiobuf_t *uiobuf)
+{
+	ASSERT(uiobuf != NULL);
+	ASSERT(uiobuf->pos == uiobuf->buffer);
+
+	kmem_free(uiobuf, sizeof (lxsys_uiobuf_t) + uiobuf->bufsize);
+}
+
+void
+lxsys_uiobuf_seterr(lxsys_uiobuf_t *uiobuf, int err)
+{
+	ASSERT(uiobuf->error == 0);
+
+	uiobuf->error = err;
+}
+
+int
+lxsys_uiobuf_flush(lxsys_uiobuf_t *uiobuf)
+{
+	off_t off = uiobuf->uiop->uio_offset;
+	caddr_t uaddr = uiobuf->buffer;
+	size_t beg = uiobuf->beg;
+	size_t size = (uintptr_t)uiobuf->pos - (uintptr_t)uaddr;
+
+	if (uiobuf->error == 0 && uiobuf->uiop->uio_resid != 0) {
+		ASSERT(off >= beg);
+
+		if (beg + size > off && off >= 0)
+			uiobuf->error =
+			    uiomove(uaddr + (off - beg), size - (off - beg),
+			    UIO_READ, uiobuf->uiop);
+
+		uiobuf->beg += size;
+	}
+
+	uiobuf->pos = uaddr;
+
+	return (uiobuf->error);
+}
+
+void
+lxsys_uiobuf_write(lxsys_uiobuf_t *uiobuf, const char *buf, size_t size)
+{
+	/* While we can still carry on */
+	while (uiobuf->error == 0 && uiobuf->uiop->uio_resid != 0) {
+		uintptr_t remain = (uintptr_t)uiobuf->bufsize -
+		    ((uintptr_t)uiobuf->pos - (uintptr_t)uiobuf->buffer);
+
+		/* Enough space in buffer? */
+		if (remain >= size) {
+			bcopy(buf, uiobuf->pos, size);
+			uiobuf->pos += size;
+			return;
+		}
+
+		/* Not enough space, so copy all we can and try again */
+		bcopy(buf, uiobuf->pos, remain);
+		uiobuf->pos += remain;
+		(void) lxsys_uiobuf_flush(uiobuf);
+		buf += remain;
+		size -= remain;
+	}
+}
+
+#define	TYPBUFFSIZE 256
+
+void
+lxsys_uiobuf_printf(lxsys_uiobuf_t *uiobuf, const char *fmt, ...)
+{
+	va_list args;
+	char buff[TYPBUFFSIZE];
+	int len;
+	char *buffer;
+
+	/* Can we still do any output */
+	if (uiobuf->error != 0 || uiobuf->uiop->uio_resid == 0)
+		return;
+
+	va_start(args, fmt);
+
+	/* Try using stack allocated buffer */
+	len = vsnprintf(buff, TYPBUFFSIZE, fmt, args);
+	if (len < TYPBUFFSIZE) {
+		va_end(args);
+		lxsys_uiobuf_write(uiobuf, buff, len);
+		return;
+	}
+
+	/* Not enough space in pre-allocated buffer */
+	buffer = kmem_alloc(len + 1, KM_SLEEP);
+
+	/*
+	 * We know we allocated the correct amount of space
+	 * so no check on the return value
+	 */
+	(void) vsnprintf(buffer, len+1, fmt, args);
+	lxsys_uiobuf_write(uiobuf, buffer, len);
+	va_end(args);
+	kmem_free(buffer, len+1);
+}
--- /dev/null	Wed Oct  5 00:32:10 2016
+++ new/usr/src/uts/common/brand/lx/sysfs/lx_sysvfsops.c	Wed Oct  5 00:32:10 2016
@@ -0,0 +1,348 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+/*
+ * lxsysvfsops.c: vfs operations for lx sysfs.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/cmn_err.h>
+#include <sys/cred.h>
+#include <sys/debug.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/sysmacros.h>
+#include <sys/systm.h>
+#include <sys/var.h>
+#include <sys/vfs.h>
+#include <sys/vfs_opreg.h>
+#include <sys/vnode.h>
+#include <sys/mode.h>
+#include <sys/signal.h>
+#include <sys/user.h>
+#include <sys/mount.h>
+#include <sys/bitmap.h>
+#include <sys/kmem.h>
+#include <sys/policy.h>
+#include <sys/modctl.h>
+#include <sys/sunddi.h>
+#include <sys/sunldi.h>
+#include <sys/lx_impl.h>
+
+#include "lx_sysfs.h"
+
+/* Module level parameters */
+static int	lxsysfstype;
+static dev_t	lxsysdev;
+static kmutex_t	lxsys_mount_lock;
+
+static int lxsys_mount(vfs_t *, vnode_t *, mounta_t *, cred_t *);
+static int lxsys_unmount(vfs_t *, int, cred_t *);
+static int lxsys_root(vfs_t *, vnode_t **);
+static int lxsys_statvfs(vfs_t *, statvfs64_t *);
+static int lxsys_init(int, char *);
+
+static vfsdef_t vfw = {
+	VFSDEF_VERSION,
+	"lx_sysfs",
+	lxsys_init,
+	VSW_ZMOUNT,
+	NULL
+};
+
+/*
+ * Module linkage information for the kernel.
+ */
+extern struct mod_ops mod_fsops;
+
+static struct modlfs modlfs = {
+	&mod_fsops, "lx brand sysfs", &vfw
+};
+
+static struct modlinkage modlinkage = {
+	MODREV_1, (void *)&modlfs, NULL
+};
+
+int
+_init(void)
+{
+	return (mod_install(&modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+	int retval;
+
+	/*
+	 * attempt to unload the module
+	 */
+	if ((retval = mod_remove(&modlinkage)) != 0)
+		goto done;
+
+	/*
+	 * destroy lxsys_node cache
+	 */
+	lxsys_fininodecache();
+
+	/*
+	 * clean out the vfsops and vnodeops
+	 */
+	(void) vfs_freevfsops_by_type(lxsysfstype);
+	vn_freevnodeops(lxsys_vnodeops);
+
+	mutex_destroy(&lxsys_mount_lock);
+done:
+	return (retval);
+}
+
+static int
+lxsys_init(int fstype, char *name)
+{
+	static const fs_operation_def_t lxsys_vfsops_template[] = {
+		VFSNAME_MOUNT,		{ .vfs_mount = lxsys_mount },
+		VFSNAME_UNMOUNT,	{ .vfs_unmount = lxsys_unmount },
+		VFSNAME_ROOT,		{ .vfs_root = lxsys_root },
+		VFSNAME_STATVFS,	{ .vfs_statvfs = lxsys_statvfs },
+		NULL,			NULL
+	};
+	extern const fs_operation_def_t lxsys_vnodeops_template[];
+	int error;
+	major_t dev;
+
+	lxsysfstype = fstype;
+	ASSERT(lxsysfstype != 0);
+
+	mutex_init(&lxsys_mount_lock, NULL, MUTEX_DEFAULT, NULL);
+
+	/*
+	 * Associate VFS ops vector with this fstype.
+	 */
+	error = vfs_setfsops(fstype, lxsys_vfsops_template, NULL);
+	if (error != 0) {
+		cmn_err(CE_WARN, "lxsys_init: bad vfs ops template");
+		return (error);
+	}
+
+	/*
+	 * Set up vnode ops vector too.
+	 */
+	error = vn_make_ops(name, lxsys_vnodeops_template, &lxsys_vnodeops);
+	if (error != 0) {
+		(void) vfs_freevfsops_by_type(fstype);
+		cmn_err(CE_WARN, "lxsys_init: bad vnode ops template");
+		return (error);
+	}
+
+	/*
+	 * Assign a unique "device" number (used by stat(2)).
+	 */
+	if ((dev = getudev()) == (major_t)-1) {
+		cmn_err(CE_WARN, "lxsys_init: can't get unique device number");
+		dev = 0;
+	}
+
+	/*
+	 * Make the pseudo device
+	 */
+	lxsysdev = makedevice(dev, 0);
+
+	/*
+	 * Initialise cache for lxsys_nodes
+	 */
+	lxsys_initnodecache();
+
+	return (0);
+}
+
+static int
+lxsys_mount(vfs_t *vfsp, vnode_t *mvp, mounta_t *uap, cred_t *cr)
+{
+	lxsys_mnt_t *lxsys_mnt;
+	zone_t *zone = curproc->p_zone;
+
+	/*
+	 * must be root to mount
+	 */
+	if (secpolicy_fs_mount(cr, mvp, vfsp) != 0)
+		return (EPERM);
+
+	/*
+	 * mount point must be a directory
+	 */
+	if (mvp->v_type != VDIR)
+		return (ENOTDIR);
+
+	if (zone == global_zone) {
+		zone_t *mntzone;
+
+		mntzone = zone_find_by_path(refstr_value(vfsp->vfs_mntpt));
+		zone_rele(mntzone);
+		if (zone != mntzone)
+			return (EBUSY);
+	}
+
+	/*
+	 * Having the resource be anything but "lxsys" doesn't make sense
+	 */
+	vfs_setresource(vfsp, "lxsys", 0);
+
+	lxsys_mnt = kmem_alloc(sizeof (*lxsys_mnt), KM_SLEEP);
+
+	mutex_enter(&lxsys_mount_lock);
+
+	/*
+	 * Ensure we don't allow overlaying mounts
+	 */
+	mutex_enter(&mvp->v_lock);
+	if ((uap->flags & MS_OVERLAY) == 0 &&
+	    (mvp->v_count > 1 || (mvp->v_flag & VROOT))) {
+		mutex_exit(&mvp->v_lock);
+		mutex_exit(&lxsys_mount_lock);
+		kmem_free(lxsys_mnt, sizeof ((*lxsys_mnt)));
+		return (EBUSY);
+	}
+	mutex_exit(&mvp->v_lock);
+
+
+	mutex_init(&lxsys_mnt->lxsysm_lock, NULL, MUTEX_DEFAULT, NULL);
+	zone_hold(lxsys_mnt->lxsysm_zone = zone);
+
+	/* Arbitrarily set the parent vnode to the mounted over directory */
+	lxsys_mnt->lxsysm_node = lxsys_getnode(mvp, LXSYS_STATIC,
+	    LXSYS_INST_ROOT, 0);
+	lxsys_mnt->lxsysm_node->lxsys_next = NULL;
+
+	/* Correctly set the fs for the root node */
+	lxsys_mnt->lxsysm_node->lxsys_vnode->v_vfsp = vfsp;
+
+	vfs_make_fsid(&vfsp->vfs_fsid, lxsysdev, lxsysfstype);
+	vfsp->vfs_bsize = DEV_BSIZE;
+	vfsp->vfs_fstype = lxsysfstype;
+	vfsp->vfs_data = (caddr_t)lxsys_mnt;
+	vfsp->vfs_dev = lxsysdev;
+
+	mutex_exit(&lxsys_mount_lock);
+
+	return (0);
+}
+
+static int
+lxsys_unmount(vfs_t *vfsp, int flag, cred_t *cr)
+{
+	lxsys_mnt_t *lxsys_mnt = (lxsys_mnt_t *)vfsp->vfs_data;
+	lxsys_node_t *lnp;
+	vnode_t *vp;
+	int count;
+
+	VERIFY(lxsys_mnt != NULL);
+
+	mutex_enter(&lxsys_mount_lock);
+
+	/* must be root to unmount */
+	if (secpolicy_fs_unmount(cr, vfsp) != 0) {
+		mutex_exit(&lxsys_mount_lock);
+		return (EPERM);
+	}
+
+	/* forced unmount is not supported by this fs */
+	if (flag & MS_FORCE) {
+		mutex_exit(&lxsys_mount_lock);
+		return (ENOTSUP);
+	}
+
+	/* Ensure that no vnodes are in use on this mount point. */
+	lnp = lxsys_mnt->lxsysm_node;
+	vp = LXSTOV(lnp);
+	mutex_enter(&vp->v_lock);
+	count = vp->v_count;
+	mutex_exit(&vp->v_lock);
+	if (count > 1) {
+		mutex_exit(&lxsys_mount_lock);
+		return (EBUSY);
+	}
+
+	/*
+	 * If there are no references to the root vnode the list of persistent
+	 * static vnodes should be empty
+	 */
+	VERIFY(lnp->lxsys_next == NULL);
+
+	(void) dnlc_purge_vfsp(vfsp, 0);
+
+	lxsys_mnt->lxsysm_node = NULL;
+	lxsys_freenode(lnp);
+	zone_rele(lxsys_mnt->lxsysm_zone);
+	vfsp->vfs_data = NULL;
+	kmem_free(lxsys_mnt, sizeof (*lxsys_mnt));
+
+	mutex_exit(&lxsys_mount_lock);
+
+	return (0);
+}
+
+static int
+lxsys_root(vfs_t *vfsp, vnode_t **vpp)
+{
+	lxsys_mnt_t *lxsm = (lxsys_mnt_t *)vfsp->vfs_data;
+	vnode_t *vp;
+
+	VERIFY(lxsm != NULL);
+	VERIFY(lxsm->lxsysm_node != NULL);
+
+	vp = LXSTOV(lxsm->lxsysm_node);
+	VN_HOLD(vp);
+	*vpp = vp;
+
+	return (0);
+}
+
+static int
+lxsys_statvfs(vfs_t *vfsp, statvfs64_t *sp)
+{
+	dev32_t d32;
+
+	bzero((caddr_t)sp, sizeof (*sp));
+	sp->f_bsize	= DEV_BSIZE;
+	sp->f_frsize	= DEV_BSIZE;
+	sp->f_blocks	= (fsblkcnt64_t)0;
+	sp->f_bfree	= (fsblkcnt64_t)0;
+	sp->f_bavail	= (fsblkcnt64_t)0;
+	sp->f_files	= (fsfilcnt64_t)3;
+	sp->f_ffree	= (fsfilcnt64_t)0;	/* none */
+	sp->f_favail	= (fsfilcnt64_t)0;	/* none */
+	(void) cmpldev(&d32, vfsp->vfs_dev);
+	sp->f_fsid	= d32;
+	/* It is guaranteed that vsw_name will fit in f_basetype */
+	(void) strcpy(sp->f_basetype, vfssw[lxsysfstype].vsw_name);
+	sp->f_flag = vf_to_stf(vfsp->vfs_flag);
+	sp->f_namemax = 64;		/* quite arbitrary */
+	bzero(sp->f_fstr, sizeof (sp->f_fstr));
+
+	/* We know f_fstr is 32 chars */
+	(void) strcpy(sp->f_fstr, "/sys");
+	(void) strcpy(&sp->f_fstr[6], "/sys");
+
+	return (0);
+}
--- /dev/null	Wed Oct  5 00:32:10 2016
+++ new/usr/src/uts/common/brand/lx/sysfs/lx_sysvnops.c	Wed Oct  5 00:32:10 2016
@@ -0,0 +1,1802 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+/*
+ * lx_sysfs -- a Linux-compatible /sys for the LX brand
+ */
+
+#include <vm/seg_vn.h>
+#include <sys/sdt.h>
+#include <sys/strlog.h>
+#include <sys/stropts.h>
+#include <sys/cmn_err.h>
+#include <sys/lx_brand.h>
+#include <sys/x86_archext.h>
+#include <sys/archsystm.h>
+#include <sys/fp.h>
+#include <sys/pool_pset.h>
+#include <sys/pset.h>
+#include <sys/zone.h>
+#include <sys/pghw.h>
+#include <sys/vfs_opreg.h>
+#include <sys/param.h>
+#include <sys/utsname.h>
+#include <sys/lx_misc.h>
+#include <sys/brand.h>
+#include <sys/cred_impl.h>
+#include <sys/tihdr.h>
+#include <sys/sunddi.h>
+#include <sys/vnode.h>
+#include <sys/netstack.h>
+#include <sys/ethernet.h>
+#include <inet/ip_arp.h>
+
+#include "lx_sysfs.h"
+
+/*
+ * Pointer to the vnode ops vector for this fs.
+ * This is instantiated in lxsys_init() in lx_sysvfsops.c
+ */
+vnodeops_t *lxsys_vnodeops;
+
+static int lxsys_open(vnode_t **, int, cred_t *, caller_context_t *);
+static int lxsys_close(vnode_t *, int, int, offset_t, cred_t *,
+    caller_context_t *);
+static int lxsys_read(vnode_t *, uio_t *, int, cred_t *, caller_context_t *);
+static int lxsys_getattr(vnode_t *, vattr_t *, int, cred_t *,
+    caller_context_t *);
+static int lxsys_access(vnode_t *, int, int, cred_t *, caller_context_t *);
+static int lxsys_lookup(vnode_t *, char *, vnode_t **,
+    pathname_t *, int, vnode_t *, cred_t *, caller_context_t *, int *,
+    pathname_t *);
+static int lxsys_readdir(vnode_t *, uio_t *, cred_t *, int *,
+    caller_context_t *, int);
+static int lxsys_readlink(vnode_t *, uio_t *, cred_t *, caller_context_t *);
+static int lxsys_cmp(vnode_t *, vnode_t *, caller_context_t *);
+static int lxsys_sync(void);
+static void lxsys_inactive(vnode_t *, cred_t *, caller_context_t *);
+
+static vnode_t *lxsys_lookup_static(lxsys_node_t *, char *);
+static vnode_t *lxsys_lookup_class_netdir(lxsys_node_t *, char *);
+static vnode_t *lxsys_lookup_devices_virtual_netdir(lxsys_node_t *, char *);
+static vnode_t *lxsys_lookup_blockdir(lxsys_node_t *, char *);
+static vnode_t *lxsys_lookup_devices_zfsdir(lxsys_node_t *, char *);
+static vnode_t *lxsys_lookup_devices_syscpu(lxsys_node_t *, char *);
+static vnode_t *lxsys_lookup_devices_syscpuinfo(lxsys_node_t *, char *);
+static vnode_t *lxsys_lookup_devices_sysnode(lxsys_node_t *, char *);
+
+static int lxsys_read_static(lxsys_node_t *, lxsys_uiobuf_t *);
+static int lxsys_read_devices_virtual_net(lxsys_node_t *, lxsys_uiobuf_t *);
+static int lxsys_read_devices_zfs_block(lxsys_node_t *, lxsys_uiobuf_t *);
+static int lxsys_read_devices_sysnode(lxsys_node_t *, lxsys_uiobuf_t *);
+
+static int lxsys_readdir_devices_syscpu(lxsys_node_t *, uio_t *, int *);
+static int lxsys_readdir_devices_syscpuinfo(lxsys_node_t *, uio_t *, int *);
+static int lxsys_readdir_devices_sysnode(lxsys_node_t *, uio_t *, int *);
+static int lxsys_readdir_static(lxsys_node_t *, uio_t *, int *);
+static int lxsys_readdir_class_netdir(lxsys_node_t *, uio_t *, int *);
+static int lxsys_readdir_devices_virtual_netdir(lxsys_node_t *, uio_t *, int *);
+static int lxsys_readdir_blockdir(lxsys_node_t *, uio_t *, int *);
+static int lxsys_readdir_devices_zfsdir(lxsys_node_t *, uio_t *, int *);
+
+static int lxsys_readlink_class_net(lxsys_node_t *, char *, size_t);
+static int lxsys_readlink_block(lxsys_node_t *, char *, size_t);
+
+/*
+ * The lx /sys vnode operations vector
+ */
+const fs_operation_def_t lxsys_vnodeops_template[] = {
+	VOPNAME_OPEN,		{ .vop_open = lxsys_open },
+	VOPNAME_CLOSE,		{ .vop_close = lxsys_close },
+	VOPNAME_READ,		{ .vop_read = lxsys_read },
+	VOPNAME_GETATTR,	{ .vop_getattr = lxsys_getattr },
+	VOPNAME_ACCESS,		{ .vop_access = lxsys_access },
+	VOPNAME_LOOKUP,		{ .vop_lookup = lxsys_lookup },
+	VOPNAME_READDIR,	{ .vop_readdir = lxsys_readdir },
+	VOPNAME_READLINK,	{ .vop_readlink = lxsys_readlink },
+	VOPNAME_FSYNC,		{ .error = lxsys_sync },
+	VOPNAME_SEEK,		{ .error = lxsys_sync },
+	VOPNAME_INACTIVE,	{ .vop_inactive = lxsys_inactive },
+	VOPNAME_CMP,		{ .vop_cmp = lxsys_cmp },
+	NULL,			NULL
+};
+
+/*
+ * Sysfs Inode format:
+ * 0000AABBBBCC
+ *
+ * AA - TYPE
+ * BBBB - INSTANCE
+ * CC - ENDPOINT
+ *
+ * Where TYPE is one of:
+ * 1 - SYS_STATIC
+ * 2 - SYS_CLASS_NET
+ * 3 - SYS_DEV_NET
+ * 4 - SYS_BLOCK
+ * 5 - SYS_DEV_ZFS
+ * 6 - SYS_DEV_SYS_CPU
+ * 7 - SYS_DEV_SYS_CPUINFO
+ * 8 - SYS_DEV_SYS_NODE
+ *
+ * Static entries will have assigned INSTANCE identifiers:
+ * - 0x00: /sys
+ * - 0x01: /sys/class
+ * - 0x02: /sys/devices
+ * - 0x03: /sys/fs
+ * - 0x04: /sys/class/net
+ * - 0x05: /sys/devices/virtual
+ * - 0x06: /sys/devices/system
+ * - 0x07: /sys/fs/cgroup
+ * - 0x08: /sys/devices/virtual/net
+ * - 0x09: /sys/block
+ * - 0x0a: /sys/devices/zfs
+ * - 0x0b: /sys/devices/system/cpu
+ * - 0x0c: /sys/devices/system/cpu/kernel_max
+ * - 0x0d: /sys/devices/system/node
+ *
+ * Dynamic /sys/class/net/<interface> symlinks will use an INSTANCE derived
+ * from the corresonding ifindex.
+ *
+ * Dynamic /sys/devices/virtual/net/<interface>/<entries> directories will use
+ * an INSTANCE derived from the ifindex and statically assigned ENDPOINT IDs
+ * for the contained entries.
+ *
+ * Dynamic /sys/block/<dev> symlinks will use an INSTANCE derived from the
+ * device major and instance from records listed in kstat or zvols.
+ *
+ * Dynamic /sys/devices/zfs/<dev> directories will use an INSTANCE derived from
+ * the emulated minor number.
+ *
+ * Static/Dynamic /sys/devices/system/cpu contains a static kernel_max file
+ * and a dynamic set of cpuN subdirectories.
+ *
+ * Static/Dynamic /sys/devices/system/node/node0 currently only contains a
+ * static cpulist file, but will likely need future dynamic entries for cpuN
+ * symlinks, and perhaps other static files. By only providing 'node0' we
+ * pretend that there is only a single NUMA node available to a zone (trying to
+ * be NUMA-aware inside a zone is generally not going to work anyway).
+ */
+
+#define	LXSYS_INST_CLASSDIR			0x1
+#define	LXSYS_INST_DEVICESDIR			0x2
+#define	LXSYS_INST_FSDIR			0x3
+#define	LXSYS_INST_CLASS_NETDIR			0x4
+#define	LXSYS_INST_DEVICES_VIRTUALDIR		0x5
+#define	LXSYS_INST_DEVICES_SYSTEMDIR		0x6
+#define	LXSYS_INST_FS_CGROUPDIR			0x7
+#define	LXSYS_INST_DEVICES_VIRTUAL_NETDIR	0x8
+#define	LXSYS_INST_BLOCKDIR			0x9
+#define	LXSYS_INST_DEVICES_ZFSDIR		0xa
+#define	LXSYS_INST_DEVICES_SYSCPU		0xb
+#define	LXSYS_INST_DEV_SYSCPU_KMAX		0xc
+#define	LXSYS_INST_DEVICES_SYSNODE		0xd
+
+/*
+ * file contents of an lx /sys directory.
+ */
+static lxsys_dirent_t dirlist_root[] = {
+	{ LXSYS_INST_BLOCKDIR,		"block" },
+	{ LXSYS_INST_CLASSDIR,		"class" },
+	{ LXSYS_INST_DEVICESDIR,	"devices" },
+	{ LXSYS_INST_FSDIR,		"fs" }
+};
+static lxsys_dirent_t dirlist_class[] = {
+	{ LXSYS_INST_CLASS_NETDIR,	"net" }
+};
+static lxsys_dirent_t dirlist_fs[] = {
+	{ LXSYS_INST_FS_CGROUPDIR,	"cgroup" }
+};
+static lxsys_dirent_t dirlist_devices[] = {
+	{ LXSYS_INST_DEVICES_SYSTEMDIR,		"system" },
+	{ LXSYS_INST_DEVICES_VIRTUALDIR,	"virtual" },
+	{ LXSYS_INST_DEVICES_ZFSDIR,		"zfs" }
+};
+static lxsys_dirent_t dirlist_devices_virtual[] = {
+	{ LXSYS_INST_DEVICES_VIRTUAL_NETDIR,	"net" }
+};
+
+/*
+ * XXX: The presence of the cpu tree in sysfs triggers new behavior in various
+ * applications. The glibc code which accesses this part of the tree expects
+ * dirents to have the d_type field populated. We cannot implement the 'cpu'
+ * hierarchy until that is addressed. One such application is java, which
+ * becomes unstable due to the incorrect data from glibc.
+ */
+static lxsys_dirent_t dirlist_devices_system[] = {
+	/* { LXSYS_INST_DEVICES_SYSCPU,	"cpu" }, */
+	{ LXSYS_INST_DEVICES_SYSNODE,	"node" }
+};
+
+#define	LXSYS_ENDP_NET_ADDRESS	1
+#define	LXSYS_ENDP_NET_ADDRLEN	2
+#define	LXSYS_ENDP_NET_FLAGS	3
+#define	LXSYS_ENDP_NET_IFINDEX	4
+#define	LXSYS_ENDP_NET_MTU	5
+#define	LXSYS_ENDP_NET_TXQLEN	6
+#define	LXSYS_ENDP_NET_TYPE	7
+
+#define	LXSYS_ENDP_BLOCK_DEVICE	1
+
+#define	LXSYS_ENDP_NODE_CPULIST	1
+
+static lxsys_dirent_t dirlist_devices_virtual_net[] = {
+	{ LXSYS_ENDP_NET_ADDRESS,	"address" },
+	{ LXSYS_ENDP_NET_ADDRLEN,	"addr_len" },
+	{ LXSYS_ENDP_NET_FLAGS,		"flags" },
+	{ LXSYS_ENDP_NET_IFINDEX,	"ifindex" },
+	{ LXSYS_ENDP_NET_MTU,		"mtu" },
+	{ LXSYS_ENDP_NET_TXQLEN,	"tx_queue_len" },
+	{ LXSYS_ENDP_NET_TYPE,		"type" }
+};
+
+static lxsys_dirent_t dirlist_devices_zfs_block[] = {
+	{ LXSYS_ENDP_BLOCK_DEVICE,	"device" }
+};
+
+static lxsys_dirent_t dirlist_devices_sysnode[] = {
+	{ LXSYS_ENDP_NODE_CPULIST,	"cpulist" }
+};
+
+#define	SYSDIRLISTSZ(l)	(sizeof (l) / sizeof ((l)[0]))
+
+#define	SYSDLENT(i, l)	{ i, l, SYSDIRLISTSZ(l) }
+static lxsys_dirlookup_t lxsys_dirlookup[] = {
+	SYSDLENT(LXSYS_INST_ROOT, dirlist_root),
+	SYSDLENT(LXSYS_INST_CLASSDIR, dirlist_class),
+	SYSDLENT(LXSYS_INST_FSDIR, dirlist_fs),
+	{ LXSYS_INST_FS_CGROUPDIR, NULL, 0 },
+	SYSDLENT(LXSYS_INST_DEVICESDIR, dirlist_devices),
+	SYSDLENT(LXSYS_INST_DEVICES_SYSTEMDIR, dirlist_devices_system),
+	SYSDLENT(LXSYS_INST_DEVICES_VIRTUALDIR, dirlist_devices_virtual),
+	SYSDLENT(LXSYS_INST_DEVICES_SYSNODE, dirlist_devices_sysnode)
+};
+
+
+/*
+ * Array of lookup functions, indexed by lx /sys file type.
+ */
+static vnode_t *(*lxsys_lookup_function[LXSYS_MAXTYPE])() = {
+	NULL,					/* LXSYS_NONE		*/
+	lxsys_lookup_static,			/* LXSYS_STATIC		*/
+	lxsys_lookup_class_netdir,		/* LXSYS_CLASS_NET	*/
+	lxsys_lookup_devices_virtual_netdir,	/* LXSYS_DEV_NET	*/
+	lxsys_lookup_blockdir,			/* LXSYS_BLOCK		*/
+	lxsys_lookup_devices_zfsdir,		/* LXSYS_DEV_ZFS	*/
+	lxsys_lookup_devices_syscpu,		/* LXSYS_DEV_SYS_CPU	*/
+	lxsys_lookup_devices_syscpuinfo,	/* LXSYS_DEV_SYS_CPUINFO */
+	lxsys_lookup_devices_sysnode,		/* LXSYS_DEV_SYS_NODE	*/
+};
+
+/*
+ * Array of readdir functions, indexed by /sys file type.
+ */
+static int (*lxsys_readdir_function[LXSYS_MAXTYPE])() = {
+	NULL,					/* LXSYS_NONE		*/
+	lxsys_readdir_static,			/* LXSYS_STATIC		*/
+	lxsys_readdir_class_netdir,		/* LXSYS_CLASS_NET	*/
+	lxsys_readdir_devices_virtual_netdir,	/* LXSYS_DEV_NET	*/
+	lxsys_readdir_blockdir,			/* LXSYS_BLOCK		*/
+	lxsys_readdir_devices_zfsdir,		/* LXSYS_DEV_ZFS	*/
+	lxsys_readdir_devices_syscpu,		/* LXSYS_DEV_SYS_CPU	*/
+	lxsys_readdir_devices_syscpuinfo,	/* LXSYS_DEV_SYS_CPUINFO */
+	lxsys_readdir_devices_sysnode,		/* LXSYS_DEV_SYS_NODE	*/
+};
+
+/*
+ * Array of read functions, indexed by /sys file type.
+ */
+static int (*lxsys_read_function[LXSYS_MAXTYPE])() = {
+	NULL,					/* LXSYS_NONE		*/
+	lxsys_read_static,			/* LXSYS_STATIC		*/
+	NULL,					/* LXSYS_CLASS_NET	*/
+	lxsys_read_devices_virtual_net,		/* LXSYS_DEV_NET	*/
+	NULL,					/* LXSYS_BLOCK		*/
+	lxsys_read_devices_zfs_block,		/* LXSYS_DEV_ZFS	*/
+	NULL,					/* LXSYS_DEV_SYS_CPU	*/
+	NULL,					/* LXSYS_DEV_SYS_CPUINFO */
+	lxsys_read_devices_sysnode,		/* LXSYS_DEV_SYS_NODE	*/
+};
+
+/*
+ * Array of readlink functions, indexed by /sys file type.
+ */
+static int (*lxsys_readlink_function[LXSYS_MAXTYPE])() = {
+	NULL,					/* LXSYS_NONE		*/
+	NULL,					/* LXSYS_STATIC		*/
+	lxsys_readlink_class_net,		/* LXSYS_CLASS_NET	*/
+	NULL,					/* LXSYS_DEV_NET	*/
+	lxsys_readlink_block,			/* LXSYS_BLOCK		*/
+	NULL,					/* LXSYS_DEV_ZFS	*/
+	NULL,					/* LXSYS_DEV_SYS_CPU	*/
+	NULL,					/* LXSYS_DEV_SYS_CPUINFO */
+	NULL,					/* LXSYS_DEV_SYS_NODE	*/
+};
+
+typedef struct lxsys_cpu_info {
+	processorid_t	cpu_id;
+	processorid_t	cpu_seqid;
+} lxsys_cpu_info_t;
+
+/*
+ * lxsys_open(): Vnode operation for VOP_OPEN()
+ */
+/* ARGSUSED */
+static int
+lxsys_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct)
+{
+	/*
+	 * We only allow reading in this file system
+	 */
+	if (flag & FWRITE)
+		return (EROFS);
+
+	return (0);
+}
+
+
+/*
+ * lxsys_close(): Vnode operation for VOP_CLOSE()
+ */
+/* ARGSUSED */
+static int
+lxsys_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr,
+    caller_context_t *ct)
+{
+	return (0);
+}
+
+
+/*
+ * lxsys_read(): Vnode operation for VOP_READ()
+ * All we currently have in this fs are directories.
+ */
+/* ARGSUSED */
+static int
+lxsys_read(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr,
+    caller_context_t *ct)
+{
+	lxsys_node_t *lnp = VTOLXS(vp);
+	lxsys_nodetype_t type = lnp->lxsys_type;
+	int (*rlfunc)();
+	int error;
+	lxsys_uiobuf_t *luio;
+
+	VERIFY(type > LXSYS_NONE && type < LXSYS_MAXTYPE);
+
+	if (vp->v_type == VDIR) {
+		return (EISDIR);
+	}
+
+	rlfunc = lxsys_read_function[type];
+	if (rlfunc != NULL) {
+		luio = lxsys_uiobuf_new(uiop);
+		if ((error = rlfunc(lnp, luio)) == 0) {
+			error = lxsys_uiobuf_flush(luio);
+		}
+		lxsys_uiobuf_free(luio);
+	} else {
+		error = EIO;
+	}
+
+	return (error);
+}
+
+/*
+ * lxsys_getattr(): Vnode operation for VOP_GETATTR()
+ */
+/* ARGSUSED */
+static int
+lxsys_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
+    caller_context_t *ct)
+{
+	register lxsys_node_t *lxsnp = VTOLXS(vp);
+
+	/* Default attributes, that may be overridden below */
+	bzero(vap, sizeof (*vap));
+	vap->va_atime = vap->va_mtime = vap->va_ctime = lxsnp->lxsys_time;
+	vap->va_nlink = 1;
+	vap->va_type = vp->v_type;
+	vap->va_mode = lxsnp->lxsys_mode;
+	vap->va_fsid = vp->v_vfsp->vfs_dev;
+	vap->va_blksize = DEV_BSIZE;
+	vap->va_uid = lxsnp->lxsys_uid;
+	vap->va_gid = lxsnp->lxsys_gid;
+	vap->va_nodeid = lxsnp->lxsys_ino;
+
+	vap->va_nblocks = (fsblkcnt64_t)btod(vap->va_size);
+	return (0);
+}
+
+/*
+ * lxsys_access(): Vnode operation for VOP_ACCESS()
+ */
+/* ARGSUSED */
+static int
+lxsys_access(vnode_t *vp, int mode, int flags, cred_t *cr, caller_context_t *ct)
+{
+	lxsys_node_t *lxsnp = VTOLXS(vp);
+	int shift = 0;
+
+	/*
+	 * Although our lx sysfs is basically a read only file system, Linux
+	 * expects it to be writable so we can't just error if (mode & VWRITE).
+	 */
+
+	/* If user is root allow access regardless of permission bits */
+	if (secpolicy_proc_access(cr) == 0)
+		return (0);
+
+	/*
+	 * Access check is based on only one of owner, group, public.  If not
+	 * owner, then check group.  If not a member of the group, then check
+	 * public access.
+	 */
+	if (crgetuid(cr) != lxsnp->lxsys_uid) {
+		shift += 3;
+		if (!groupmember((uid_t)lxsnp->lxsys_gid, cr))
+			shift += 3;
+	}
+
+	mode &= ~(lxsnp->lxsys_mode << shift);
+
+	if (mode == 0)
+		return (0);
+
+	return (EACCES);
+}
+
+/*
+ * lxsys_lookup(): Vnode operation for VOP_LOOKUP()
+ */
+/* ARGSUSED */
+static int
+lxsys_lookup(vnode_t *dp, char *comp, vnode_t **vpp, pathname_t *pathp,
+    int flags, vnode_t *rdir, cred_t *cr, caller_context_t *ct,
+    int *direntflags, pathname_t *realpnp)
+{
+	lxsys_node_t *lxsnp = VTOLXS(dp);
+	lxsys_nodetype_t type = lxsnp->lxsys_type;
+	int error;
+
+	VERIFY(dp->v_type == VDIR);
+	VERIFY(type > LXSYS_NONE && type < LXSYS_MAXTYPE);
+
+	/*
+	 * restrict lookup permission to owner or root
+	 */
+	if ((error = lxsys_access(dp, VEXEC, 0, cr, ct)) != 0) {
+		return (error);
+	}
+
+	/*
+	 * Just return the parent vnode if that's where we are trying to go.
+	 */
+	if (strcmp(comp, "..") == 0) {
+		VN_HOLD(lxsnp->lxsys_parentvp);
+		*vpp = lxsnp->lxsys_parentvp;
+		return (0);
+	}
+
+	/*
+	 * Special handling for directory searches.  Note: null component name
+	 * denotes that the current directory is being searched.
+	 */
+	if ((dp->v_type == VDIR) && (*comp == '\0' || strcmp(comp, ".") == 0)) {
+		VN_HOLD(dp);
+		*vpp = dp;
+		return (0);
+	}
+
+	*vpp = (lxsys_lookup_function[type](lxsnp, comp));
+	return ((*vpp == NULL) ? ENOENT : 0);
+}
+
+static lxsys_node_t *
+lxsys_lookup_disk(lxsys_node_t *ldp, char *comp, lxsys_nodetype_t type)
+{
+	lxsys_node_t *lnp = NULL;
+	lx_zone_data_t *lxzdata;
+	lx_virt_disk_t *vd;
+
+	lxzdata = ztolxzd(curproc->p_zone);
+	if (lxzdata == NULL)
+		return (NULL);
+	ASSERT(lxzdata->lxzd_vdisks != NULL);
+
+	vd = list_head(lxzdata->lxzd_vdisks);
+	while (vd != NULL) {
+		int inst = getminor(vd->lxvd_emul_dev) & 0xffff;
+
+		if (strcmp(vd->lxvd_name, comp) == 0 && inst != 0) {
+			lnp = lxsys_getnode(ldp->lxsys_vnode, type, inst, 0);
+			break;
+		}
+
+		vd = list_next(lxzdata->lxzd_vdisks, vd);
+	}
+
+	return (lnp);
+}
+
+static vnode_t *
+lxsys_lookup_static(lxsys_node_t *ldp, char *comp)
+{
+	lxsys_dirent_t *dirent = NULL;
+	int i, len = 0;
+
+	for (i = 0; i < SYSDIRLISTSZ(lxsys_dirlookup); i++) {
+		if (ldp->lxsys_instance == lxsys_dirlookup[i].dl_instance) {
+			dirent = lxsys_dirlookup[i].dl_list;
+			len = lxsys_dirlookup[i].dl_length;
+			break;
+		}
+	}
+	if (dirent == NULL) {
+		return (NULL);
+	}
+
+	for (i = 0; i < len; i++) {
+		if (strncmp(comp, dirent[i].d_name, MAXPATHLEN) == 0) {
+			lxsys_nodetype_t node_type = ldp->lxsys_type;
+			unsigned int node_instance = 0;
+			lxsys_node_t *lnp;
+
+			switch (dirent[i].d_idnum) {
+			case LXSYS_INST_BLOCKDIR:
+				node_type = LXSYS_BLOCK;
+				break;
+			case LXSYS_INST_CLASS_NETDIR:
+				node_type = LXSYS_CLASS_NET;
+				break;
+			case LXSYS_INST_DEVICES_VIRTUAL_NETDIR:
+				node_type = LXSYS_DEV_NET;
+				break;
+			case LXSYS_INST_DEVICES_ZFSDIR:
+				node_type = LXSYS_DEV_ZFS;
+				break;
+			case LXSYS_INST_DEVICES_SYSCPU:
+				node_type = LXSYS_DEV_SYS_CPU;
+				break;
+			case LXSYS_INST_DEVICES_SYSNODE:
+				node_type = LXSYS_DEV_SYS_NODE;
+				break;
+			default:
+				/* Another static node */
+				node_instance = dirent[i].d_idnum;
+			}
+			if (node_type == LXSYS_STATIC) {
+				lnp = lxsys_getnode_static(ldp->lxsys_vnode,
+				    node_instance);
+			} else {
+				lnp = lxsys_getnode(ldp->lxsys_vnode,
+				    node_type, node_instance, 0);
+			}
+			return (lnp->lxsys_vnode);
+		}
+	}
+	return (NULL);
+}
+
+static vnode_t *
+lxsys_lookup_class_netdir(lxsys_node_t *ldp, char *comp)
+{
+	vnode_t *result = NULL;
+	lxsys_node_t *lnp;
+	netstack_t *ns;
+	ip_stack_t *ipst;
+	avl_tree_t *phytree;
+	phyint_t *phyi;
+	char ifname[LIFNAMSIZ];
+
+	if (ldp->lxsys_type != LXSYS_CLASS_NET ||
+	    ldp->lxsys_instance != 0) {
+		/* Lookups only allowed at directory level */
+		return (NULL);
+	}
+
+	(void) strncpy(ifname, comp, LIFNAMSIZ);
+	lx_ifname_convert(ifname, LX_IF_TONATIVE);
+
+	if ((ns = lxsys_netstack(ldp)) == NULL) {
+		return (NULL);
+	}
+	ipst = ns->netstack_ip;
+	rw_enter(&ipst->ips_ill_g_lock, RW_READER);
+
+	phytree = &ipst->ips_phyint_g_list->phyint_list_avl_by_name;
+	phyi = avl_find(phytree, ifname, NULL);
+	if (phyi != NULL) {
+		lnp = lxsys_getnode(ldp->lxsys_vnode, ldp->lxsys_type,
+		    phyi->phyint_ifindex, 0);
+		result = lnp->lxsys_vnode;
+		result->v_type = VLNK;
+	}
+
+	rw_exit(&ipst->ips_ill_g_lock);
+	netstack_rele(ns);
+
+	return (result);
+}
+
+static vnode_t *
+lxsys_lookup_devices_virtual_netdir(lxsys_node_t *ldp, char *comp)
+{
+	lxsys_node_t *lnp;
+
+	if (ldp->lxsys_instance == 0) {
+		/* top-level interface listing */
+		vnode_t *result = NULL;
+		netstack_t *ns;
+		ip_stack_t *ipst;
+		avl_tree_t *phytree;
+		phyint_t *phyi;
+		char ifname[LIFNAMSIZ];
+
+		(void) strncpy(ifname, comp, LIFNAMSIZ);
+		lx_ifname_convert(ifname, LX_IF_TONATIVE);
+
+		if ((ns = lxsys_netstack(ldp)) == NULL) {
+			return (NULL);
+		}
+		ipst = ns->netstack_ip;
+		rw_enter(&ipst->ips_ill_g_lock, RW_READER);
+
+		phytree = &ipst->ips_phyint_g_list->phyint_list_avl_by_name;
+		phyi = avl_find(phytree, ifname, NULL);
+		if (phyi != NULL) {
+			lnp = lxsys_getnode(ldp->lxsys_vnode, ldp->lxsys_type,
+			    phyi->phyint_ifindex, 0);
+			result = lnp->lxsys_vnode;
+		}
+
+		rw_exit(&ipst->ips_ill_g_lock);
+		netstack_rele(ns);
+
+		return (result);
+	} else if (ldp->lxsys_endpoint == 0) {
+		/* interface-level sub-item listing */
+		int i, size;
+		lxsys_dirent_t *dirent;
+
+		size = SYSDIRLISTSZ(dirlist_devices_virtual_net);
+		for (i = 0; i < size; i++) {
+			dirent = &dirlist_devices_virtual_net[i];
+			if (strncmp(comp, dirent->d_name, LXSNSIZ) == 0) {
+				lnp = lxsys_getnode(ldp->lxsys_vnode,
+				    ldp->lxsys_type, ldp->lxsys_instance,
+				    dirent->d_idnum);
+				lnp->lxsys_vnode->v_type = VREG;
+				lnp->lxsys_mode = 0444;
+				return (lnp->lxsys_vnode);
+			}
+		}
+	}
+
+	return (NULL);
+}
+
+static vnode_t *
+lxsys_lookup_blockdir(lxsys_node_t *ldp, char *comp)
+{
+	lxsys_node_t *lnp;
+
+	if (ldp->lxsys_instance == 0) {
+		/* top-level dev listing */
+		lnp = lxsys_lookup_disk(ldp, comp, LXSYS_BLOCK);
+
+		if (lnp != NULL) {
+			lnp->lxsys_vnode->v_type = VLNK;
+			return (lnp->lxsys_vnode);
+		}
+	}
+
+	return (NULL);
+}
+
+static vnode_t *
+lxsys_lookup_devices_zfsdir(lxsys_node_t *ldp, char *comp)
+{
+	lxsys_node_t *lnp;
+
+	if (ldp->lxsys_instance == 0) {
+		/* top-level dev listing */
+		lnp = lxsys_lookup_disk(ldp, comp, LXSYS_DEV_ZFS);
+
+		if (lnp != NULL) {
+			return (lnp->lxsys_vnode);
+		}
+	} else if (ldp->lxsys_endpoint == 0) {
+		/* disk-level sub-item listing */
+		int i, size;
+		lxsys_dirent_t *dirent;
+
+		/*
+		 * All of these entries currently look like regular files
+		 * but on a real Linux system some will be subdirs. This should
+		 * be fixed when we populate the directory for real.
+		 */
+		size = SYSDIRLISTSZ(dirlist_devices_zfs_block);
+		for (i = 0; i < size; i++) {
+			dirent = &dirlist_devices_zfs_block[i];
+			if (strncmp(comp, dirent->d_name, LXSNSIZ) == 0) {
+				lnp = lxsys_getnode(ldp->lxsys_vnode,
+				    ldp->lxsys_type, ldp->lxsys_instance,
+				    dirent->d_idnum);
+				lnp->lxsys_vnode->v_type = VREG;
+				lnp->lxsys_mode = 0444;
+				return (lnp->lxsys_vnode);
+			}
+		}
+	}
+
+	return (NULL);
+}
+
+static vnode_t *
+lxsys_lookup_devices_syscpu(lxsys_node_t *ldp, char *comp)
+{
+	lxsys_node_t *lnp = NULL;
+
+	if (ldp->lxsys_instance == 0) {
+		/* top-level cpu listing */
+
+		/* If fixed entry */
+		if (strcmp(comp, "kernel_max") == 0) {
+			lnp = lxsys_getnode_static(ldp->lxsys_vnode,
+			    LXSYS_INST_DEV_SYSCPU_KMAX);
+			lnp->lxsys_vnode->v_type = VREG;
+			lnp->lxsys_mode = 0444;
+		} else {
+			/* Else dynamic cpuN entry */
+			cpu_t *cp, *cpstart;
+			int pools_enabled;
+
+			mutex_enter(&cpu_lock);
+			pools_enabled = pool_pset_enabled();
+
+			cp = cpstart = CPU->cpu_part->cp_cpulist;
+			do {
+				char cpunm[16];
+
+				(void) snprintf(cpunm, sizeof (cpunm), "cpu%d",
+				    cp->cpu_seqid);
+
+				if (strcmp(comp, cpunm) == 0) {
+					lnp = lxsys_getnode(ldp->lxsys_vnode,
+					    LXSYS_DEV_SYS_CPUINFO,
+					    cp->cpu_id + 1, 0);
+					break;
+				}
+				if (pools_enabled) {
+					cp = cp->cpu_next_part;
+				} else {
+					cp = cp->cpu_next;
+				}
+			} while (cp != cpstart);
+
+			mutex_exit(&cpu_lock);
+		}
+
+		if (lnp != NULL) {
+			return (lnp->lxsys_vnode);
+		}
+	} else if (ldp->lxsys_endpoint == 0) {
+		/* cpu-level sub-item listing, currently empty */
+		/* EMPTY */
+	}
+
+	return (NULL);
+}
+
+/* ARGSUSED */
+static vnode_t *
+lxsys_lookup_devices_syscpuinfo(lxsys_node_t *ldp, char *comp)
+{
+	return (NULL);
+}
+
+static vnode_t *
+lxsys_lookup_devices_sysnode(lxsys_node_t *ldp, char *comp)
+{
+	lxsys_node_t *lnp = NULL;
+
+	if (ldp->lxsys_instance == 0) {
+		/*
+		 * The system is presently represented as a single node,
+		 * regardless of any NUMA topology which exists.
+		 * The instances are offset by 1 to account for the top level
+		 * directory occupying instance 0.
+		 */
+		if (strcmp(comp, "node0") == 0) {
+			lnp = lxsys_getnode(ldp->lxsys_vnode, ldp->lxsys_type,
+			    1, 0);
+			return (lnp->lxsys_vnode);
+		}
+	} else {
+		/* interface-level sub-item listing */
+		int i, size;
+		lxsys_dirent_t *dirent;
+
+		size = SYSDIRLISTSZ(dirlist_devices_sysnode);
+		for (i = 0; i < size; i++) {
+			dirent = &dirlist_devices_sysnode[i];
+			if (strncmp(comp, dirent->d_name, LXSNSIZ) == 0) {
+				lnp = lxsys_getnode(ldp->lxsys_vnode,
+				    ldp->lxsys_type, ldp->lxsys_instance,
+				    dirent->d_idnum);
+				lnp->lxsys_vnode->v_type = VREG;
+				lnp->lxsys_mode = 0444;
+				return (lnp->lxsys_vnode);
+			}
+		}
+	}
+
+	return (NULL);
+}
+
+static int
+lxsys_read_devices_virtual_net(lxsys_node_t *lnp, lxsys_uiobuf_t *luio)
+{
+	netstack_t *ns;
+	ill_t *ill;
+	uint_t ifindex = lnp->lxsys_instance;
+	uint8_t *addr;
+	uint64_t flags;
+	int error = 0;
+
+	if (ifindex == 0 || lnp->lxsys_endpoint == 0) {
+		return (EISDIR);
+	}
+
+	if ((ns = lxsys_netstack(lnp)) == NULL) {
+		return (EIO);
+	}
+
+	ill = lxsys_find_ill(ns->netstack_ip, ifindex);
+	if (ill == NULL) {
+		netstack_rele(ns);
+		return (EIO);
+	}
+
+	switch (lnp->lxsys_endpoint) {
+	case LXSYS_ENDP_NET_ADDRESS:
+		if (ill->ill_phys_addr_length != ETHERADDRL) {
+			lxsys_uiobuf_printf(luio, "00:00:00:00:00:00\n");
+			break;
+		}
+		addr = ill->ill_phys_addr;
+		lxsys_uiobuf_printf(luio,
+		    "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n",
+		    addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+		break;
+	case LXSYS_ENDP_NET_ADDRLEN:
+		lxsys_uiobuf_printf(luio, "%u\n",
+		    IS_LOOPBACK(ill) ? ETHERADDRL : ill->ill_phys_addr_length);
+		break;
+	case LXSYS_ENDP_NET_FLAGS:
+		flags = (ill->ill_flags | ill->ill_ipif->ipif_flags |
+		    ill->ill_phyint->phyint_flags) & 0xffff;
+		lx_ifflags_convert(&flags, LX_IF_FROMNATIVE);
+		lxsys_uiobuf_printf(luio, "0x%x\n", flags);
+		break;
+	case LXSYS_ENDP_NET_IFINDEX:
+		lxsys_uiobuf_printf(luio, "%u\n", ifindex);
+		break;
+	case LXSYS_ENDP_NET_MTU:
+		lxsys_uiobuf_printf(luio, "%u\n", ill->ill_mtu);
+		break;
+	case LXSYS_ENDP_NET_TXQLEN:
+		/* perpetuate the txqlen lie */
+		if (IS_LOOPBACK(ill)) {
+			lxsys_uiobuf_printf(luio, "0\n");
+		} else {
+			lxsys_uiobuf_printf(luio, "1\n");
+		}
+		break;
+	case LXSYS_ENDP_NET_TYPE:
+		lxsys_uiobuf_printf(luio, "%u\n",
+		    IS_LOOPBACK(ill) ? LX_ARPHRD_LOOPBACK :
+		    arp_hw_type(ill->ill_mactype));
+		break;
+	default:
+		error = EIO;
+	}
+
+	ill_refrele(ill);
+	netstack_rele(ns);
+	return (error);
+}
+
+/* ARGSUSED1 */
+static int
+lxsys_read_devices_zfs_block(lxsys_node_t *lnp, lxsys_uiobuf_t *luio)
+{
+	uint_t dskindex = lnp->lxsys_instance;
+
+	if (dskindex == 0 || lnp->lxsys_endpoint == 0) {
+		return (EISDIR);
+	}
+
+	return (EIO);
+}
+
+static int
+lxsys_read_devices_sysnode(lxsys_node_t *lnp, lxsys_uiobuf_t *luio)
+{
+	if (lnp->lxsys_instance == 1 &&
+	    lnp->lxsys_endpoint == LXSYS_ENDP_NODE_CPULIST) {
+		/* Show the range of CPUs */
+		cpu_t *cp, *cpstart;
+		int pools_enabled, maxid = -1;
+
+		mutex_enter(&cpu_lock);
+		pools_enabled = pool_pset_enabled();
+
+		cp = cpstart = CPU->cpu_part->cp_cpulist;
+		do {
+			if (cp->cpu_seqid > maxid)
+				maxid = cp->cpu_seqid;
+
+			if (pools_enabled) {
+				cp = cp->cpu_next_part;
+			} else {
+				cp = cp->cpu_next;
+			}
+		} while (cp != cpstart);
+
+		mutex_exit(&cpu_lock);
+
+		lxsys_uiobuf_printf(luio, "0-%d\n", maxid);
+		return (0);
+	}
+	return (EISDIR);
+
+}
+
+static int
+lxsys_read_static(lxsys_node_t *lnp, lxsys_uiobuf_t *luio)
+{
+	uint_t inst = lnp->lxsys_instance;
+
+	if (inst == LXSYS_INST_DEV_SYSCPU_KMAX) {
+		lxsys_uiobuf_printf(luio, "%d\n", NCPU);
+		return (0);
+	}
+
+	/* All other static nodes are directories */
+	return (EISDIR);
+}
+
+/*
+ * lxsys_readdir(): Vnode operation for VOP_READDIR()
+ */
+/* ARGSUSED */
+static int
+lxsys_readdir(vnode_t *dp, uio_t *uiop, cred_t *cr, int *eofp,
+    caller_context_t *ct, int flags)
+{
+	lxsys_node_t *lxsnp = VTOLXS(dp);
+	lxsys_nodetype_t type = lxsnp->lxsys_type;
+	ssize_t uresid;
+	off_t uoffset;
+	int error, leof;
+
+	ASSERT(dp->v_type == VDIR);
+	VERIFY(type > LXSYS_NONE && type < LXSYS_MAXTYPE);
+
+	/*
+	 * restrict readdir permission to owner or root
+	 */
+	if ((error = lxsys_access(dp, VREAD, 0, cr, ct)) != 0)
+		return (error);
+
+	uoffset = uiop->uio_offset;
+	uresid = uiop->uio_resid;
+
+	/* can't do negative reads */
+	if (uoffset < 0 || uresid <= 0)
+		return (EINVAL);
+
+	/* can't read directory entries that don't exist! */
+	if (uoffset % LXSYS_SDSIZE)
+		return (ENOENT);
+
+	/* Free lower functions from having to check eofp == NULL */
+	if (eofp == NULL) {
+		eofp = &leof;
+	}
+
+	return (lxsys_readdir_function[lxsnp->lxsys_type](lxsnp, uiop, eofp));
+}
+
+static int
+lxsys_dirent_out(dirent64_t *d, ushort_t n, struct uio *uio)
+{
+	int error;
+	off_t offset = uio->uio_offset;
+
+	/*
+	 * uiomove() updates both uiop->uio_resid and uiop->uio_offset by the
+	 * same amount.  But we want uiop->uio_offset to change in increments
+	 * of LXSYS_SDSIZE, which is different from the number of bytes being
+	 * returned to the user.  To accomplish this, we set uiop->uio_offset
+	 * separately on success, overriding what uiomove() does.
+	 */
+	d->d_off = (off64_t)(offset + LXSYS_SDSIZE);
+	d->d_reclen = n;
+	if ((error = uiomove(d, n, UIO_READ, uio)) != 0) {
+		return (error);
+	}
+	uio->uio_offset = offset + LXSYS_SDSIZE;
+	return (0);
+}
+
+/*
+ * This has the common logic for returning directory entries
+ */
+static int
+lxsys_readdir_common(lxsys_node_t *lxsnp, uio_t *uiop, int *eofp,
+    lxsys_dirent_t *dirtab, int dirtablen)
+{
+	/* bp holds one dirent64 structure */
+	longlong_t bp[DIRENT64_RECLEN(LXSNSIZ) / sizeof (longlong_t)];
+	dirent64_t *dirent = (dirent64_t *)bp;
+	ssize_t oresid;	/* save a copy for testing later */
+	ssize_t uresid;
+
+	oresid = uiop->uio_resid;
+
+	/* clear out the dirent buffer */
+	bzero(bp, sizeof (bp));
+
+	/* Satisfy user request */
+	while ((uresid = uiop->uio_resid) > 0) {
+		int dirindex;
+		off_t uoffset;
+		int reclen;
+		int error;
+
+		uoffset = uiop->uio_offset;
+		dirindex  = (uoffset / LXSYS_SDSIZE) - 2;
+
+		if (uoffset == 0) {
+
+			dirent->d_ino = lxsnp->lxsys_ino;
+			dirent->d_name[0] = '.';
+			dirent->d_name[1] = '\0';
+			reclen = DIRENT64_RECLEN(1);
+
+		} else if (uoffset == LXSYS_SDSIZE) {
+
+			dirent->d_ino = lxsys_parentinode(lxsnp);
+			dirent->d_name[0] = '.';
+			dirent->d_name[1] = '.';
+			dirent->d_name[2] = '\0';
+			reclen = DIRENT64_RECLEN(2);
+
+		} else if (dirindex >= 0 && dirindex < dirtablen) {
+
+			int slen = strlen(dirtab[dirindex].d_name);
+
+			dirent->d_ino = lxsys_inode(LXSYS_STATIC,
+			    dirtab[dirindex].d_idnum, 0);
+			(void) strcpy(dirent->d_name, dirtab[dirindex].d_name);
+			reclen = DIRENT64_RECLEN(slen);
+
+		} else {
+			/* Run out of table entries */
+			*eofp = 1;
+			return (0);
+		}
+
+		/*
+		 * If the size of the data to transfer is greater than the
+		 * user-provided buffer, we cannot continue.
+		 */
+		if (reclen > uresid) {
+			/* Error if no entries have been returned yet. */
+			if (uresid == oresid) {
+				return (EINVAL);
+			}
+			break;
+		}
+
+		if ((error = lxsys_dirent_out(dirent, reclen, uiop)) != 0) {
+			return (error);
+		}
+	}
+
+	/* Have run out of space, but could have just done last table entry */
+	*eofp = (uiop->uio_offset >= ((dirtablen+2) * LXSYS_SDSIZE)) ?  1 : 0;
+	return (0);
+}
+
+static int
+lxsys_readdir_subdir(lxsys_node_t *lxsnp, uio_t *uiop, int *eofp,
+    lxsys_dirent_t *dirtab, int dirtablen)
+{
+	/* bp holds one dirent64 structure */
+	longlong_t bp[DIRENT64_RECLEN(LXSNSIZ) / sizeof (longlong_t)];
+	dirent64_t *dirent = (dirent64_t *)bp;
+	ssize_t oresid;	/* save a copy for testing later */
+	ssize_t uresid;
+
+	VERIFY(dirtab != NULL || dirtablen == 0);
+
+	oresid = uiop->uio_resid;
+
+	/* clear out the dirent buffer */
+	bzero(bp, sizeof (bp));
+
+	/* Satisfy user request */
+	while ((uresid = uiop->uio_resid) > 0) {
+		int dirindex;
+		off_t uoffset;
+		int reclen;
+		int error;
+
+		uoffset = uiop->uio_offset;
+		dirindex  = (uoffset / LXSYS_SDSIZE) - 2;
+
+		if (uoffset == 0) {
+
+			dirent->d_ino = lxsnp->lxsys_ino;
+			dirent->d_name[0] = '.';
+			dirent->d_name[1] = '\0';
+			reclen = DIRENT64_RECLEN(1);
+
+		} else if (uoffset == LXSYS_SDSIZE) {
+
+			dirent->d_ino = lxsys_parentinode(lxsnp);
+			dirent->d_name[0] = '.';
+			dirent->d_name[1] = '.';
+			dirent->d_name[2] = '\0';
+			reclen = DIRENT64_RECLEN(2);
+
+		} else if (dirindex >= 0 && dirindex < dirtablen) {
+
+			int slen = strlen(dirtab[dirindex].d_name);
+
+			dirent->d_ino = lxsys_inode(lxsnp->lxsys_type,
+			    lxsnp->lxsys_instance, dirtab[dirindex].d_idnum);
+			(void) strcpy(dirent->d_name, dirtab[dirindex].d_name);
+			reclen = DIRENT64_RECLEN(slen);
+
+		} else {
+			/* Run out of table entries */
+			*eofp = 1;
+			return (0);
+		}
+
+		/*
+		 * If the size of the data to transfer is greater than the
+		 * user-provided buffer, we cannot continue.
+		 */
+		if (reclen > uresid) {
+			/* Error if no entries have been returned yet. */
+			if (uresid == oresid) {
+				return (EINVAL);
+			}
+			break;
+		}
+
+		if ((error = lxsys_dirent_out(dirent, reclen, uiop)) != 0) {
+			return (error);
+		}
+	}
+
+	/* Have run out of space, but could have just done last table entry */
+	*eofp = (uiop->uio_offset >= ((dirtablen+2) * LXSYS_SDSIZE)) ?  1 : 0;
+	return (0);
+}
+
+static int
+lxsys_readdir_ifaces(lxsys_node_t *ldp, struct uio *uiop, int *eofp,
+    lxsys_nodetype_t type)
+{
+	longlong_t bp[DIRENT64_RECLEN(LXSNSIZ) / sizeof (longlong_t)];
+	dirent64_t *dirent = (dirent64_t *)bp;
+	ssize_t oresid, uresid;
+	netstack_t *ns;
+	ip_stack_t *ipst;
+	avl_tree_t *phytree;
+	phyint_t *phyi;
+	int error, i;
+
+
+	/* Emit "." and ".." entries */
+	oresid = uiop->uio_resid;
+	error = lxsys_readdir_common(ldp, uiop, eofp, NULL, 0);
+	if (error != 0 || *eofp == 0) {
+		return (error);
+	}
+
+	if ((ns = lxsys_netstack(ldp)) == NULL) {
+		*eofp = 1;
+		return (0);
+	}
+	ipst = ns->netstack_ip;
+
+	rw_enter(&ipst->ips_ill_g_lock, RW_READER);
+	phytree = &ipst->ips_phyint_g_list->phyint_list_avl_by_index;
+	phyi = avl_first(phytree);
+	if (phyi == NULL) {
+		*eofp = 1;
+	}
+	bzero(bp, sizeof (bp));
+
+	/*
+	 * Skip records we have already passed with the offset.
+	 * This accounts for the two "." and ".." records already seen.
+	 */
+	for (i = (uiop->uio_offset/LXSYS_SDSIZE) - 2; i > 0; i--) {
+		if ((phyi = avl_walk(phytree, phyi, AVL_AFTER)) == NULL) {
+			*eofp = 1;
+			break;
+		}
+	}
+
+	while ((uresid = uiop->uio_resid) > 0 && phyi != NULL) {
+		uint_t ifindex;
+		int reclen;
+
+		ifindex = phyi->phyint_ifindex;
+		(void) strncpy(dirent->d_name, phyi->phyint_name, LIFNAMSIZ);
+		lx_ifname_convert(dirent->d_name, LX_IF_FROMNATIVE);
+		dirent->d_ino = lxsys_inode(type, ifindex, 0);
+		reclen = DIRENT64_RECLEN(strlen(dirent->d_name));
+
+		if (reclen > uresid) {
+			if (uresid == oresid) {
+				/* Not enough space for one record */
+				error = EINVAL;
+			}
+			break;
+		}
+		if ((error = lxsys_dirent_out(dirent, reclen, uiop)) != 0) {
+			break;
+		}
+
+		if ((phyi = avl_walk(phytree, phyi, AVL_AFTER)) == NULL) {
+			*eofp = 1;
+			break;
+		}
+	}
+
+	rw_exit(&ipst->ips_ill_g_lock);
+	netstack_rele(ns);
+	return (error);
+}
+
+static int
+lxsys_readdir_disks(lxsys_node_t *ldp, struct uio *uiop, int *eofp,
+    lxsys_nodetype_t type)
+{
+	longlong_t bp[DIRENT64_RECLEN(LXSNSIZ) / sizeof (longlong_t)];
+	dirent64_t *dirent = (dirent64_t *)bp;
+	ssize_t oresid, uresid;
+	int skip, error;
+	int reclen;
+	uint_t instance;
+	lx_zone_data_t *lxzdata;
+	lx_virt_disk_t *vd;
+
+	/* Emit "." and ".." entries */
+	oresid = uiop->uio_resid;
+	error = lxsys_readdir_common(ldp, uiop, eofp, NULL, 0);
+	if (error != 0 || *eofp == 0) {
+		return (error);
+	}
+
+	skip = (uiop->uio_offset/LXSYS_SDSIZE) - 2;
+
+	lxzdata = ztolxzd(curproc->p_zone);
+	if (lxzdata == NULL)
+		return (EINVAL);
+	ASSERT(lxzdata->lxzd_vdisks != NULL);
+
+	vd = list_head(lxzdata->lxzd_vdisks);
+	while (vd != NULL) {
+		if (skip > 0) {
+			skip--;
+			goto next;
+		}
+
+		if (strnlen(vd->lxvd_name, sizeof (vd->lxvd_name)) > LXSNSIZ)
+			goto next;
+
+		(void) strncpy(dirent->d_name, vd->lxvd_name, LXSNSIZ);
+
+		instance = getminor(vd->lxvd_emul_dev) & 0xffff;
+		if (instance == 0)
+			goto next;
+
+		dirent->d_ino = lxsys_inode(type, instance, 0);
+		reclen = DIRENT64_RECLEN(strlen(dirent->d_name));
+
+		uresid = uiop->uio_resid;
+		if (reclen > uresid) {
+			if (uresid == oresid) {
+				/* Not enough space for one record */
+				error = EINVAL;
+			}
+			break;
+		}
+		if ((error = lxsys_dirent_out(dirent, reclen, uiop)) != 0) {
+			break;
+		}
+
+next:
+		vd = list_next(lxzdata->lxzd_vdisks, vd);
+	}
+
+	/* Indicate EOF if we reached the end of the virtual disks. */
+	if (vd == NULL) {
+		*eofp = 1;
+	}
+
+	return (error);
+}
+
+
+static int
+lxsys_readdir_static(lxsys_node_t *lnp, uio_t *uiop, int *eofp)
+{
+	lxsys_dirent_t *dirent = NULL;
+	int i, len = 0;
+
+	for (i = 0; i < SYSDIRLISTSZ(lxsys_dirlookup); i++) {
+		if (lnp->lxsys_instance == lxsys_dirlookup[i].dl_instance) {
+			dirent = lxsys_dirlookup[i].dl_list;
+			len = lxsys_dirlookup[i].dl_length;
+			break;
+		}
+	}
+
+	if (dirent == NULL) {
+		return (ENOTDIR);
+	}
+
+	return (lxsys_readdir_common(lnp, uiop, eofp, dirent, len));
+}
+
+static int
+lxsys_readdir_class_netdir(lxsys_node_t *lnp, uio_t *uiop, int *eofp)
+{
+	if (lnp->lxsys_type != LXSYS_CLASS_NET ||
+	    lnp->lxsys_instance != 0) {
+		/*
+		 * Since /sys/class/net contains only symlinks, readdir
+		 * operations should not be performed anywhere except the top
+		 * level (instance == 0).
+		 */
+		return (ENOTDIR);
+	}
+
+	return (lxsys_readdir_ifaces(lnp, uiop, eofp, LXSYS_CLASS_NET));
+}
+
+static int
+lxsys_readdir_devices_virtual_netdir(lxsys_node_t *lnp, uio_t *uiop, int *eofp)
+{
+	int error;
+
+	if (lnp->lxsys_instance == 0) {
+		/* top-level interface listing */
+		error = lxsys_readdir_ifaces(lnp, uiop, eofp,
+		    LXSYS_DEV_NET);
+	} else if (lnp->lxsys_endpoint == 0) {
+		/* interface-level sub-item listing */
+		error = lxsys_readdir_subdir(lnp, uiop, eofp,
+		    dirlist_devices_virtual_net,
+		    SYSDIRLISTSZ(dirlist_devices_virtual_net));
+	} else {
+		/* there shouldn't be subdirs below this */
+		error = ENOTDIR;
+	}
+
+	return (error);
+}
+
+static int
+lxsys_readdir_blockdir(lxsys_node_t *lnp, uio_t *uiop, int *eofp)
+{
+	if (lnp->lxsys_type != LXSYS_BLOCK ||
+	    lnp->lxsys_instance != 0) {
+		/*
+		 * Since /sys/block contains only symlinks, readdir operations
+		 * should not be performed anywhere except the top level
+		 * (instance == 0).
+		 */
+		return (ENOTDIR);
+	}
+
+	return (lxsys_readdir_disks(lnp, uiop, eofp, LXSYS_BLOCK));
+}
+
+static int
+lxsys_readdir_devices_zfsdir(lxsys_node_t *lnp, uio_t *uiop, int *eofp)
+{
+	int error;
+
+	if (lnp->lxsys_instance == 0) {
+		/* top-level dev listing */
+		error = lxsys_readdir_disks(lnp, uiop, eofp,
+		    LXSYS_DEV_ZFS);
+	} else if (lnp->lxsys_endpoint == 0) {
+		/* disk-level sub-item listing */
+		error = lxsys_readdir_subdir(lnp, uiop, eofp,
+		    dirlist_devices_zfs_block,
+		    SYSDIRLISTSZ(dirlist_devices_zfs_block));
+	} else {
+		/*
+		 * Currently there shouldn't be subdirs below this but
+		 * on a real Linux system some will be subdirs. This should
+		 * be fixed when we populate the directory for real.
+		 */
+		error = ENOTDIR;
+	}
+
+	return (error);
+}
+
+static int
+lxsys_readdir_cpu(lxsys_node_t *ldp, struct uio *uiop, int *eofp)
+{
+	longlong_t bp[DIRENT64_RECLEN(LXSNSIZ) / sizeof (longlong_t)];
+	dirent64_t *dirent = (dirent64_t *)bp;
+	ssize_t oresid, uresid;
+	int skip, error;
+	int reclen;
+	cpu_t *cp, *cpstart;
+	int pools_enabled;
+	int i, cpucnt;
+	lxsys_cpu_info_t cpu_info[NCPU];
+
+	/* Emit "." and ".." entries */
+	oresid = uiop->uio_resid;
+	error = lxsys_readdir_common(ldp, uiop, eofp, NULL, 0);
+	if (error != 0 || *eofp == 0) {
+		return (error);
+	}
+
+	skip = (uiop->uio_offset/LXSYS_SDSIZE) - 2;
+
+	/* Fixed entries */
+	if (skip > 0) {
+		skip--;
+	} else {
+		(void) strncpy(dirent->d_name, "kernel_max", LXSNSIZ);
+
+		dirent->d_ino = lxsys_inode(LXSYS_STATIC,
+		    LXSYS_INST_DEV_SYSCPU_KMAX, 0);
+		reclen = DIRENT64_RECLEN(strlen(dirent->d_name));
+
+		uresid = uiop->uio_resid;
+		if (reclen > uresid) {
+			if (uresid == oresid) {
+				/* Not enough space for one record */
+				error = EINVAL;
+			}
+			goto done;
+		}
+		if ((error = lxsys_dirent_out(dirent, reclen, uiop)) != 0) {
+			goto done;
+		}
+	}
+
+	/* Collect a list of CPU info */
+	mutex_enter(&cpu_lock);
+	pools_enabled = pool_pset_enabled();
+
+	cpucnt = 0;
+	cp = cpstart = CPU->cpu_part->cp_cpulist;
+	do {
+		cpu_info[cpucnt].cpu_id = cp->cpu_id;
+		cpu_info[cpucnt++].cpu_seqid = cp->cpu_seqid;
+		ASSERT(cpucnt < NCPU);
+		if (pools_enabled) {
+			cp = cp->cpu_next_part;
+		} else {
+			cp = cp->cpu_next;
+		}
+	} while (cp != cpstart);
+
+	mutex_exit(&cpu_lock);
+
+	/* Output dynamic CPU info */
+	for (i = 0; i < cpucnt; i++) {
+		char cpunm[16];
+
+		if (skip > 0) {
+			skip--;
+			continue;
+		}
+
+		(void) snprintf(cpunm, sizeof (cpunm), "cpu%d",
+		    cpu_info[i].cpu_seqid);
+		(void) strncpy(dirent->d_name, cpunm, LXSNSIZ);
+
+		dirent->d_ino = lxsys_inode(LXSYS_DEV_SYS_CPU,
+		    cpu_info[i].cpu_id + 1, 0);
+		reclen = DIRENT64_RECLEN(strlen(dirent->d_name));
+
+		uresid = uiop->uio_resid;
+		if (reclen > uresid) {
+			if (uresid == oresid) {
+				/* Not enough space for one record */
+				error = EINVAL;
+			}
+			break;
+		}
+		if ((error = lxsys_dirent_out(dirent, reclen, uiop)) != 0) {
+			break;
+		}
+	}
+
+	/* Indicate EOF if we reached the end of the CPU list. */
+	if (i == cpucnt) {
+		*eofp = 1;
+	}
+
+done:
+	return (error);
+}
+
+static int
+lxsys_readdir_devices_syscpu(lxsys_node_t *lnp, uio_t *uiop, int *eofp)
+{
+	int error;
+
+	if (lnp->lxsys_instance == 0) {
+		/* top-level cpu listing */
+		error = lxsys_readdir_cpu(lnp, uiop, eofp);
+	} else if (lnp->lxsys_endpoint == 0) {
+		/* cpu-level sub-item listing */
+		error = lxsys_readdir_subdir(lnp, uiop, eofp, NULL, 0);
+	} else {
+		/*
+		 * Currently there shouldn't be subdirs below this but
+		 * on a real Linux system some will be subdirs. This should
+		 * be fixed when we populate the directory for real.
+		 */
+		error = ENOTDIR;
+	}
+
+	return (error);
+}
+
+static int
+lxsys_readdir_devices_syscpuinfo(lxsys_node_t *lnp, uio_t *uiop, int *eofp)
+{
+	int error;
+
+	if (lnp->lxsys_type != LXSYS_DEV_SYS_CPUINFO) {
+		/*
+		 * Since /sys/devices/system/cpu/cpuN is empty, readdir
+		 * operations should not be performed anywhere except the top
+		 * level.
+		 */
+		return (ENOTDIR);
+	}
+
+	/*
+	 * Emit "." and ".." entries
+	 * All cpuN directories are currently empty.
+	 */
+	error = lxsys_readdir_common(lnp, uiop, eofp, NULL, 0);
+	if (error != 0 || *eofp == 0) {
+		return (error);
+	}
+
+	/* Indicate EOF */
+	*eofp = 1;
+
+	return (error);
+}
+
+static int
+lxsys_readdir_devices_sysnode(lxsys_node_t *lnp, uio_t *uiop, int *eofp)
+{
+	int error;
+
+	if (lnp->lxsys_instance == 0) {
+		/* top-level node listing */
+		longlong_t bp[DIRENT64_RECLEN(LXSNSIZ) / sizeof (longlong_t)];
+		dirent64_t *dirent = (dirent64_t *)bp;
+		ssize_t oresid, uresid;
+		int reclen, skip;
+
+		/* Emit "." and ".." entries */
+		oresid = uiop->uio_resid;
+		error = lxsys_readdir_common(lnp, uiop, eofp, NULL, 0);
+		if (error != 0 || *eofp == 0) {
+			return (error);
+		}
+		skip = (uiop->uio_offset/LXSYS_SDSIZE) - 2;
+
+		/* Fixed entries */
+		if (skip > 0) {
+			skip--;
+		} else {
+			(void) strncpy(dirent->d_name, "node0", LXSNSIZ);
+
+			dirent->d_ino = lxsys_inode(LXSYS_DEV_SYS_NODE,
+			    1, 0);
+			reclen = DIRENT64_RECLEN(strlen(dirent->d_name));
+
+			uresid = uiop->uio_resid;
+			if (reclen > uresid) {
+				if (uresid == oresid) {
+					/* Not enough space for one record */
+					return (EINVAL);
+				}
+				return (0);
+			}
+			error = lxsys_dirent_out(dirent, reclen, uiop);
+		}
+		/* Indicate EOF */
+		if (error == 0) {
+			*eofp = 1;
+		}
+	} else if (lnp->lxsys_endpoint == 0) {
+		/* node-level sub-item listing */
+		error = lxsys_readdir_subdir(lnp, uiop, eofp,
+		    dirlist_devices_sysnode,
+		    SYSDIRLISTSZ(dirlist_devices_sysnode));
+	} else {
+		/* there shouldn't be subdirs below this */
+		error = ENOTDIR;
+	}
+
+	return (error);
+}
+
+/*
+ * lxsys_readlink(): Vnode operation for VOP_READLINK()
+ */
+/* ARGSUSED */
+static int
+lxsys_readlink(vnode_t *vp, uio_t *uiop, cred_t *cr, caller_context_t *ct)
+{
+	char buf[MAXPATHLEN + 1];
+	lxsys_node_t *lnp = VTOLXS(vp);
+	lxsys_nodetype_t type = lnp->lxsys_type;
+	int (*rlfunc)();
+	int error;
+
+	VERIFY(type > LXSYS_NONE && type < LXSYS_MAXTYPE);
+
+	if (vp->v_type != VLNK) {
+		return (EINVAL);
+	}
+
+	rlfunc = lxsys_readlink_function[lnp->lxsys_type];
+	if (rlfunc != NULL) {
+		if ((error = rlfunc(lnp, buf, sizeof (buf))) == 0) {
+			error = uiomove(buf, strlen(buf), UIO_READ, uiop);
+		}
+	} else {
+		error = EINVAL;
+	}
+
+	return (error);
+}
+
+
+static int
+lxsys_readlink_class_net(lxsys_node_t *lnp, char *buf, size_t len)
+{
+	netstack_t *ns;
+	ip_stack_t *ipst;
+	avl_tree_t *phytree;
+	phyint_t *phyi;
+	uint_t ifindex;
+	char ifname[LIFNAMSIZ];
+	int error = EINVAL;
+
+	if ((ifindex = lnp->lxsys_instance) == 0) {
+		return (error);
+	}
+
+	if ((ns = lxsys_netstack(lnp)) == NULL) {
+		return (error);
+	}
+	ipst = ns->netstack_ip;
+	rw_enter(&ipst->ips_ill_g_lock, RW_READER);
+
+	phytree = &ipst->ips_phyint_g_list->phyint_list_avl_by_index;
+	phyi = avl_find(phytree, &ifindex, NULL);
+	if (phyi != NULL) {
+		(void) strncpy(ifname, phyi->phyint_name, LIFNAMSIZ);
+		lx_ifname_convert(ifname, LX_IF_FROMNATIVE);
+		(void) snprintf(buf, len, "/sys/devices/virtual/net/%s",
+		    ifname);
+		error = 0;
+	}
+
+	rw_exit(&ipst->ips_ill_g_lock);
+	netstack_rele(ns);
+	return (error);
+}
+
+static int
+lxsys_readlink_block(lxsys_node_t *lnp, char *buf, size_t len)
+{
+	int inst, error = EINVAL;
+	lx_zone_data_t *lxzdata;
+	lx_virt_disk_t *vd;
+
+	if ((inst = lnp->lxsys_instance) == 0) {
+		return (error);
+	}
+
+	lxzdata = ztolxzd(curproc->p_zone);
+	if (lxzdata == NULL)
+		return (error);
+	ASSERT(lxzdata->lxzd_vdisks != NULL);
+
+	vd = list_head(lxzdata->lxzd_vdisks);
+	while (vd != NULL) {
+		int vinst = getminor(vd->lxvd_emul_dev) & 0xffff;
+
+		if (vinst == inst) {
+			(void) snprintf(buf, len,
+			    "../devices/zfs/%s", vd->lxvd_name);
+			error = 0;
+			break;
+		}
+		vd = list_next(lxzdata->lxzd_vdisks, vd);
+	}
+
+	return (error);
+}
+
+/*
+ * lxsys_inactive(): Vnode operation for VOP_INACTIVE()
+ * Vnode is no longer referenced, deallocate the file
+ * and all its resources.
+ */
+/* ARGSUSED */
+static void
+lxsys_inactive(vnode_t *vp, cred_t *cr, caller_context_t *ct)
+{
+	lxsys_freenode(VTOLXS(vp));
+}
+
+/*
+ * lxsys_sync(): Vnode operation for VOP_SYNC()
+ */
+static int
+lxsys_sync()
+{
+	/*
+	 * Nothing to sync but this function must never fail
+	 */
+	return (0);
+}
+
+/*
+ * lxsys_cmp(): Vnode operation for VOP_CMP()
+ */
+static int
+lxsys_cmp(vnode_t *vp1, vnode_t *vp2, caller_context_t *ct)
+{
+	if (vn_matchops(vp1, lxsys_vnodeops) ||
+	    vn_matchops(vp2, lxsys_vnodeops))
+		return (vp1 == vp2);
+	return (VOP_CMP(vp1, vp2, ct));
+}
--- old/usr/src/uts/common/brand/sn1/sn1_brand.c	Wed Oct  5 00:32:11 2016
+++ new/usr/src/uts/common/brand/sn1/sn1_brand.c	Wed Oct  5 00:32:11 2016
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 
 #include <sys/errno.h>
@@ -42,43 +43,66 @@
 
 char *sn1_emulation_table = NULL;
 
-void	sn1_init_brand_data(zone_t *);
+void	sn1_init_brand_data(zone_t *, kmutex_t *);
 void	sn1_free_brand_data(zone_t *);
 void	sn1_setbrand(proc_t *);
 int	sn1_getattr(zone_t *, int, void *, size_t *);
 int	sn1_setattr(zone_t *, int, void *, size_t);
 int	sn1_brandsys(int, int64_t *, uintptr_t, uintptr_t, uintptr_t,
-		uintptr_t, uintptr_t, uintptr_t);
+		uintptr_t);
 void	sn1_copy_procdata(proc_t *, proc_t *);
-void	sn1_proc_exit(struct proc *, klwp_t *);
+void	sn1_proc_exit(struct proc *);
 void	sn1_exec();
-int	sn1_initlwp(klwp_t *);
+void	sn1_initlwp(klwp_t *, void *);
 void	sn1_forklwp(klwp_t *, klwp_t *);
 void	sn1_freelwp(klwp_t *);
 void	sn1_lwpexit(klwp_t *);
 int	sn1_elfexec(vnode_t *, execa_t *, uarg_t *, intpdata_t *, int,
-	long *, int, caddr_t, cred_t *, int);
+	long *, int, caddr_t, cred_t *, int *);
 
 /* sn1 brand */
 struct brand_ops sn1_brops = {
-	sn1_init_brand_data,
-	sn1_free_brand_data,
-	sn1_brandsys,
-	sn1_setbrand,
-	sn1_getattr,
-	sn1_setattr,
-	sn1_copy_procdata,
-	sn1_proc_exit,
-	sn1_exec,
-	lwp_setrval,
-	sn1_initlwp,
-	sn1_forklwp,
-	sn1_freelwp,
-	sn1_lwpexit,
-	sn1_elfexec,
-	NULL,
-	NULL,
-	NSIG,
+	sn1_init_brand_data,		/* b_init_brand_data */
+	sn1_free_brand_data,		/* b_free_brand_data */
+	sn1_brandsys,			/* b_brandsys */
+	sn1_setbrand,			/* b_setbrand */
+	sn1_getattr,			/* b_getattr */
+	sn1_setattr,			/* b_setattr */
+	sn1_copy_procdata,		/* b_copy_procdata */
+	sn1_proc_exit,			/* b_proc_exit */
+	sn1_exec,			/* b_exec */
+	lwp_setrval,			/* b_lwp_setrval */
+	NULL,				/* b_lwpdata_alloc */
+	NULL,				/* b_lwpdata_free */
+	sn1_initlwp,			/* b_initlwp */
+	NULL,				/* b_initlwp_post */
+	sn1_forklwp,			/* b_forklwp */
+	sn1_freelwp,			/* b_freelwp */
+	sn1_lwpexit,			/* b_lwpexit */
+	sn1_elfexec,			/* b_elfexec */
+	NULL,				/* b_sigset_native_to_brand */
+	NULL,				/* b_sigset_brand_to_native */
+	NULL,				/* b_sigfd_translate */
+	NSIG,				/* b_nsig */
+	NULL,				/* b_exit_with_sig */
+	NULL,				/* b_wait_filter */
+	NULL,				/* b_native_exec */
+	NULL,				/* b_map32limit */
+	NULL,				/* b_stop_notify */
+	NULL,				/* b_waitid_helper */
+	NULL,				/* b_sigcld_repost */
+	NULL,				/* b_issig_stop */
+	NULL,				/* b_sig_ignorable */
+	NULL,				/* b_savecontext */
+#if defined(_SYSCALL32_IMPL)
+	NULL,				/* b_savecontext32 */
+#endif
+	NULL,				/* b_restorecontext */
+	NULL,				/* b_sendsig_stack */
+	NULL,				/* b_sendsig */
+	NULL,				/* b_setid_clear */
+	NULL,				/* b_pagefault */
+	B_TRUE				/* b_intp_parse_arg */
 };
 
 #ifdef	sparc
@@ -94,9 +118,12 @@
 
 struct brand_mach_ops sn1_mops = {
 	sn1_brand_sysenter_callback,
+	NULL,
 	sn1_brand_int91_callback,
 	sn1_brand_syscall_callback,
-	sn1_brand_syscall32_callback
+	sn1_brand_syscall32_callback,
+	NULL,
+	NULL
 };
 
 #else	/* ! __amd64 */
@@ -104,7 +131,10 @@
 struct brand_mach_ops sn1_mops = {
 	sn1_brand_sysenter_callback,
 	NULL,
+	NULL,
 	sn1_brand_syscall_callback,
+	NULL,
+	NULL,
 	NULL
 };
 #endif	/* __amd64 */
@@ -115,7 +145,8 @@
 	BRAND_VER_1,
 	"sn1",
 	&sn1_brops,
-	&sn1_mops
+	&sn1_mops,
+	sizeof (brand_proc_data_t),
 };
 
 static struct modlbrand modlbrand = {
@@ -148,10 +179,10 @@
 	return (EINVAL);
 }
 
-/*ARGSUSED*/
+/* ARGSUSED5 */
 int
 sn1_brandsys(int cmd, int64_t *rval, uintptr_t arg1, uintptr_t arg2,
-    uintptr_t arg3, uintptr_t arg4, uintptr_t arg5, uintptr_t arg6)
+    uintptr_t arg3, uintptr_t arg4)
 {
 	int	res;
 
@@ -171,9 +202,9 @@
 }
 
 void
-sn1_proc_exit(struct proc *p, klwp_t *l)
+sn1_proc_exit(struct proc *p)
 {
-	brand_solaris_proc_exit(p, l, &sn1_brand);
+	brand_solaris_proc_exit(p, &sn1_brand);
 }
 
 void
@@ -182,10 +213,11 @@
 	brand_solaris_exec(&sn1_brand);
 }
 
-int
-sn1_initlwp(klwp_t *l)
+/* ARGSUSED */
+void
+sn1_initlwp(klwp_t *l, void *bd)
 {
-	return (brand_solaris_initlwp(l, &sn1_brand));
+	brand_solaris_initlwp(l, &sn1_brand);
 }
 
 void
@@ -214,18 +246,18 @@
 
 /*ARGSUSED*/
 void
-sn1_init_brand_data(zone_t *zone)
+sn1_init_brand_data(zone_t *zone, kmutex_t *zsl)
 {
 }
 
 int
 sn1_elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
-	int level, long *execsz, int setid, caddr_t exec_file, cred_t *cred,
-	int brand_action)
+    int level, long *execsz, int setid, caddr_t exec_file, cred_t *cred,
+    int *brand_action)
 {
 	return (brand_solaris_elfexec(vp, uap, args, idatap, level, execsz,
 	    setid, exec_file, cred, brand_action, &sn1_brand, SN1_BRANDNAME,
-	    SN1_LIB, SN1_LIB32, SN1_LINKER, SN1_LINKER32));
+	    SN1_LIB, SN1_LIB32));
 }
 
 int
--- old/usr/src/uts/common/brand/sn1/sn1_brand.h	Wed Oct  5 00:32:11 2016
+++ new/usr/src/uts/common/brand/sn1/sn1_brand.h	Wed Oct  5 00:32:11 2016
@@ -20,6 +20,7 @@
  */
 /*
  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, Joyent, Inc. All rights reserved.
  */
 
 #ifndef _SN1_BRAND_H
@@ -37,20 +38,14 @@
 #define	SN1_VERSION		SN1_VERSION_1
 
 #define	SN1_LIB_NAME		"sn1_brand.so.1"
-#define	SN1_LINKER_NAME		"ld.so.1"
 
 #define	SN1_LIB32		BRAND_NATIVE_DIR "usr/lib/" SN1_LIB_NAME
-#define	SN1_LINKER32		"/lib/" SN1_LINKER_NAME
-
 #define	SN1_LIB64		BRAND_NATIVE_DIR "usr/lib/64/" SN1_LIB_NAME
-#define	SN1_LINKER64		"/lib/64/" SN1_LINKER_NAME
 
 #if defined(_LP64)
 #define	SN1_LIB		SN1_LIB64
-#define	SN1_LINKER	SN1_LINKER64
 #else /* !_LP64 */
 #define	SN1_LIB		SN1_LIB32
-#define	SN1_LINKER	SN1_LINKER32
 #endif /* !_LP64 */
 
 #if defined(_KERNEL)
--- old/usr/src/uts/common/brand/solaris10/s10_brand.c	Wed Oct  5 00:32:11 2016
+++ new/usr/src/uts/common/brand/solaris10/s10_brand.c	Wed Oct  5 00:32:11 2016
@@ -22,6 +22,7 @@
 /*
  * Copyright (c) 2013, OmniTI Computer Consulting, Inc. All rights reserved.
  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2016, Joyent, Inc.
  */
 
 #include <sys/errno.h>
@@ -45,45 +46,68 @@
 
 char *s10_emulation_table = NULL;
 
-void	s10_init_brand_data(zone_t *);
+void	s10_init_brand_data(zone_t *, kmutex_t *);
 void	s10_free_brand_data(zone_t *);
 void	s10_setbrand(proc_t *);
 int	s10_getattr(zone_t *, int, void *, size_t *);
 int	s10_setattr(zone_t *, int, void *, size_t);
 int	s10_brandsys(int, int64_t *, uintptr_t, uintptr_t, uintptr_t,
-		uintptr_t, uintptr_t, uintptr_t);
+		uintptr_t);
 void	s10_copy_procdata(proc_t *, proc_t *);
-void	s10_proc_exit(struct proc *, klwp_t *);
+void	s10_proc_exit(struct proc *);
 void	s10_exec();
-int	s10_initlwp(klwp_t *);
+void	s10_initlwp(klwp_t *, void *);
 void	s10_forklwp(klwp_t *, klwp_t *);
 void	s10_freelwp(klwp_t *);
 void	s10_lwpexit(klwp_t *);
 int	s10_elfexec(vnode_t *, execa_t *, uarg_t *, intpdata_t *, int,
-	long *, int, caddr_t, cred_t *, int);
+	long *, int, caddr_t, cred_t *, int *);
 void	s10_sigset_native_to_s10(sigset_t *);
 void	s10_sigset_s10_to_native(sigset_t *);
 
 /* s10 brand */
 struct brand_ops s10_brops = {
-	s10_init_brand_data,
-	s10_free_brand_data,
-	s10_brandsys,
-	s10_setbrand,
-	s10_getattr,
-	s10_setattr,
-	s10_copy_procdata,
-	s10_proc_exit,
-	s10_exec,
-	lwp_setrval,
-	s10_initlwp,
-	s10_forklwp,
-	s10_freelwp,
-	s10_lwpexit,
-	s10_elfexec,
-	s10_sigset_native_to_s10,
-	s10_sigset_s10_to_native,
-	S10_NSIG,
+	s10_init_brand_data,		/* b_init_brand_data */
+	s10_free_brand_data,		/* b_free_brand_data */
+	s10_brandsys,			/* b_brandsys */
+	s10_setbrand,			/* b_setbrand */
+	s10_getattr,			/* b_getattr */
+	s10_setattr,			/* b_setattr */
+	s10_copy_procdata,		/* b_copy_procdata */
+	s10_proc_exit,			/* b_proc_exit */
+	s10_exec,			/* b_exec */
+	lwp_setrval,			/* b_lwp_setrval */
+	NULL,				/* b_lwpdata_alloc */
+	NULL,				/* b_lwpdata_free */
+	s10_initlwp,			/* b_initlwp */
+	NULL,				/* b_initlwp_post */
+	s10_forklwp,			/* b_forklwp */
+	s10_freelwp,			/* b_freelwp */
+	s10_lwpexit,			/* b_lwpexit */
+	s10_elfexec,			/* b_elfexec */
+	s10_sigset_native_to_s10,	/* b_sigset_native_to_brand */
+	s10_sigset_s10_to_native,	/* b_sigset_brand_to_native */
+	NULL,				/* b_sigfd_translate */
+	S10_NSIG,			/* b_nsig */
+	NULL,				/* b_exit_with_sig */
+	NULL,				/* b_wait_filter */
+	NULL,				/* b_native_exec */
+	NULL,				/* b_map32limit */
+	NULL,				/* b_stop_notify */
+	NULL,				/* b_waitid_helper */
+	NULL,				/* b_sigcld_repost */
+	NULL,				/* b_issig_stop */
+	NULL,				/* b_sig_ignorable */
+	NULL,				/* b_savecontext */
+#if defined(_SYSCALL32_IMPL)
+	NULL,				/* b_savecontext32 */
+#endif
+	NULL,				/* b_restorecontext */
+	NULL,				/* b_sendsig_stack */
+	NULL,				/* b_sendsig */
+	NULL,				/* b_setid_clear */
+	NULL,				/* b_pagefault */
+	B_TRUE				/* b_intp_parse_arg */
 };
 
 #ifdef	sparc
@@ -99,9 +123,12 @@
 
 struct brand_mach_ops s10_mops = {
 	s10_brand_sysenter_callback,
+	NULL,
 	s10_brand_int91_callback,
 	s10_brand_syscall_callback,
-	s10_brand_syscall32_callback
+	s10_brand_syscall32_callback,
+	NULL,
+	NULL
 };
 
 #else	/* ! __amd64 */
@@ -109,7 +136,10 @@
 struct brand_mach_ops s10_mops = {
 	s10_brand_sysenter_callback,
 	NULL,
+	NULL,
 	s10_brand_syscall_callback,
+	NULL,
+	NULL,
 	NULL
 };
 #endif	/* __amd64 */
@@ -120,7 +150,8 @@
 	BRAND_VER_1,
 	"solaris10",
 	&s10_brops,
-	&s10_mops
+	&s10_mops,
+	sizeof (brand_proc_data_t),
 };
 
 static struct modlbrand modlbrand = {
@@ -249,10 +280,10 @@
 	return (0);
 }
 
-/*ARGSUSED*/
+/* ARGSUSED5 */
 int
 s10_brandsys(int cmd, int64_t *rval, uintptr_t arg1, uintptr_t arg2,
-    uintptr_t arg3, uintptr_t arg4, uintptr_t arg5, uintptr_t arg6)
+    uintptr_t arg3, uintptr_t arg4)
 {
 	proc_t	*p = curproc;
 	int	res;
@@ -326,9 +357,9 @@
 }
 
 void
-s10_proc_exit(struct proc *p, klwp_t *l)
+s10_proc_exit(struct proc *p)
 {
-	brand_solaris_proc_exit(p, l, &s10_brand);
+	brand_solaris_proc_exit(p, &s10_brand);
 }
 
 void
@@ -337,10 +368,11 @@
 	brand_solaris_exec(&s10_brand);
 }
 
-int
-s10_initlwp(klwp_t *l)
+/* ARGSUSED */
+void
+s10_initlwp(klwp_t *l, void *bd)
 {
-	return (brand_solaris_initlwp(l, &s10_brand));
+	brand_solaris_initlwp(l, &s10_brand);
 }
 
 void
@@ -379,8 +411,9 @@
 	kmem_free(zone->zone_brand_data, sizeof (s10_zone_data_t));
 }
 
+/* ARGSUSED */
 void
-s10_init_brand_data(zone_t *zone)
+s10_init_brand_data(zone_t *zone, kmutex_t *zsl)
 {
 	ASSERT(zone->zone_brand == &s10_brand);
 	ASSERT(zone->zone_brand_data == NULL);
@@ -389,12 +422,12 @@
 
 int
 s10_elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
-	int level, long *execsz, int setid, caddr_t exec_file, cred_t *cred,
-	int brand_action)
+    int level, long *execsz, int setid, caddr_t exec_file, cred_t *cred,
+    int *brand_action)
 {
 	return (brand_solaris_elfexec(vp, uap, args, idatap, level, execsz,
 	    setid, exec_file, cred, brand_action, &s10_brand, S10_BRANDNAME,
-	    S10_LIB, S10_LIB32, S10_LINKER, S10_LINKER32));
+	    S10_LIB, S10_LIB32));
 }
 
 void
--- old/usr/src/uts/common/brand/solaris10/s10_brand.h	Wed Oct  5 00:32:11 2016
+++ new/usr/src/uts/common/brand/solaris10/s10_brand.h	Wed Oct  5 00:32:11 2016
@@ -22,6 +22,7 @@
 /*
  * Copyright (c) 2013, OmniTI Computer Consulting, Inc. All rights reserved.
  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, Joyent, Inc. All rights reserved.
  */
 
 #ifndef _S10_BRAND_H
@@ -42,17 +43,12 @@
 #define	S10_LINKER_NAME		"ld.so.1"
 
 #define	S10_LIB32		BRAND_NATIVE_DIR "usr/lib/" S10_LIB_NAME
-#define	S10_LINKER32		"/lib/" S10_LINKER_NAME
-
 #define	S10_LIB64		BRAND_NATIVE_DIR "usr/lib/64/" S10_LIB_NAME
-#define	S10_LINKER64		"/lib/64/" S10_LINKER_NAME
 
 #if defined(_LP64)
 #define	S10_LIB		S10_LIB64
-#define	S10_LINKER	S10_LINKER64
 #else /* !_LP64 */
 #define	S10_LIB		S10_LIB32
-#define	S10_LINKER	S10_LINKER32
 #endif /* !_LP64 */
 
 /*
--- old/usr/src/uts/common/contract/process.c	Wed Oct  5 00:32:11 2016
+++ new/usr/src/uts/common/contract/process.c	Wed Oct  5 00:32:11 2016
@@ -21,6 +21,7 @@
 /*
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
  */
 
 #include <sys/mutex.h>
@@ -955,6 +956,18 @@
 		(void) cte_publish_all(ct, event, nvl, NULL);
 		mutex_enter(&ct->ct_lock);
 	}
+
+	/*
+	 * CT_PR_EV_EXIT is not part of the CT_PR_ALLFATAL definition since
+	 * we never allow including this in the fatal set via a user-land
+	 * application, but we do allow CT_PR_EV_EXIT in the contract's fatal
+	 * set for a process setup for zone init. See zone_start_init().
+	 */
+	if (EVFATALP(ctp, CT_PR_EV_EXIT)) {
+		ASSERT(MUTEX_HELD(&ct->ct_lock));
+		contract_process_kill(ct, p, B_TRUE);
+	}
+
 	if (empty) {
 		/*
 		 * Send EMPTY message.
@@ -1057,6 +1070,17 @@
 		event->cte_type = CT_PR_EV_FORK;
 		(void) cte_publish_all(ct, event, nvl, NULL);
 	}
+
+	/*
+	 * Because the CT_PR_KEEP_EXEC flag is meant to be used by applications
+	 * which are not contract aware, we can assume that these applications
+	 * will never explicitly abandon the child's new contract. Thus, we
+	 * abandon it now.
+	 */
+	if (ctp->conp_params & CT_PR_KEEP_EXEC) {
+		(void) contract_abandon(ct, pp, 1);
+	}
+
 	return (ctp);
 }
 
--- old/usr/src/uts/common/disp/fx.c	Wed Oct  5 00:32:11 2016
+++ new/usr/src/uts/common/disp/fx.c	Wed Oct  5 00:32:11 2016
@@ -21,7 +21,7 @@
 
 /*
  * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
  */
 
 #include <sys/types.h>
@@ -71,16 +71,6 @@
 };
 
 
-/*
- * control flags (kparms->fx_cflags).
- */
-#define	FX_DOUPRILIM	0x01    /* change user priority limit */
-#define	FX_DOUPRI	0x02    /* change user priority */
-#define	FX_DOTQ		0x04    /* change FX time quantum */
-
-
-#define	FXMAXUPRI 60		/* maximum user priority setting */
-
 #define	FX_MAX_UNPRIV_PRI	0	/* maximum unpriviledge priority */
 
 /*
--- old/usr/src/uts/common/disp/rt.c	Wed Oct  5 00:32:11 2016
+++ new/usr/src/uts/common/disp/rt.c	Wed Oct  5 00:32:11 2016
@@ -22,7 +22,7 @@
 /*
  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
- * Copyright 2013 Joyent, Inc.  All rights reserved.
+ * Copyright 2015 Joyent, Inc.
  */
 
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
@@ -103,13 +103,6 @@
 pri_t rt_maxpri = RTMAXPRI;	/* maximum real-time priority */
 rtdpent_t *rt_dptbl;	  /* real-time dispatcher parameter table */
 
-/*
- * control flags (kparms->rt_cflags).
- */
-#define	RT_DOPRI	0x01	/* change priority */
-#define	RT_DOTQ		0x02	/* change RT time quantum */
-#define	RT_DOSIG	0x04	/* change RT time quantum signal */
-
 static int	rt_admin(caddr_t, cred_t *);
 static int	rt_enterclass(kthread_t *, id_t, void *, cred_t *, void *);
 static int	rt_fork(kthread_t *, kthread_t *, void *);
--- old/usr/src/uts/common/disp/rt_dptbl.c	Wed Oct  5 00:32:11 2016
+++ new/usr/src/uts/common/disp/rt_dptbl.c	Wed Oct  5 00:32:12 2016
@@ -28,8 +28,6 @@
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T	*/
 /*	  All Rights Reserved  	*/
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <sys/proc.h>
 #include <sys/priocntl.h>
 #include <sys/class.h>
@@ -70,8 +68,6 @@
 	return (mod_info(&modlinkage, modinfop));
 }
 
-#define	RTGPPRIO0	100	/* Global priority for RT priority 0 */
-
 rtdpent_t	config_rt_dptbl[] = {
 
 /*   	prilevel    Time quantum */
--- old/usr/src/uts/common/disp/thread_intr.c	Wed Oct  5 00:32:12 2016
+++ new/usr/src/uts/common/disp/thread_intr.c	Wed Oct  5 00:32:12 2016
@@ -23,19 +23,10 @@
  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
-
 /*
- * FILE NOTICE BEGIN
- *
- * This file should not be modified.  If you wish to modify it or have it
- * modified, please contact Sun Microsystems at <LFI149367@-sun-.-com->
- * (without anti-spam dashes)
- *
- * FILE NOTICE END
+ * Copyright 2015, Joyent, Inc.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <sys/cpuvar.h>
 #include <sys/stack.h>
 #include <vm/seg_kp.h>
@@ -44,6 +35,17 @@
 #include <sys/sysmacros.h>
 
 /*
+ * Use a slightly larger thread stack size for interrupt threads rather than the
+ * default. This is useful for cases where the networking stack may do an rx and
+ * a tx in the context of a single interrupt and when combined with various
+ * promisc hooks that need memory, can cause us to get dangerously close to the
+ * edge of the traditional stack sizes. This is only a few pages more than a
+ * traditional stack and given that we don't have that many interrupt threads,
+ * the memory costs end up being more than worthwhile.
+ */
+#define	LL_INTR_STKSZ	(32 * 1024)
+
+/*
  * Create and initialize an interrupt thread.
  */
 static void
@@ -51,7 +53,7 @@
 {
 	kthread_t *tp;
 
-	tp = thread_create(NULL, 0,
+	tp = thread_create(NULL, LL_INTR_STKSZ,
 	    (void (*)())thread_create_intr, NULL, 0, &p0, TS_ONPROC, 0);
 
 	/*
@@ -97,9 +99,12 @@
 }
 
 /*
- * Allocate a given number of interrupt threads for a given CPU.
- * These threads will get freed by cpu_destroy_bound_threads()
- * when CPU gets unconfigured.
+ * Allocate a given number of interrupt threads for a given CPU.  These threads
+ * will get freed by cpu_destroy_bound_threads() when CPU gets unconfigured.
+ *
+ * Note, high level interrupts are always serviced using cpu_intr_stack and are
+ * not allowed to block. Low level interrupts or soft-interrupts use the
+ * kthread_t's that we create through the calls to thread_create_intr().
  */
 void
 cpu_intr_alloc(cpu_t *cp, int n)
@@ -110,6 +115,6 @@
 		thread_create_intr(cp);
 
 	cp->cpu_intr_stack = (caddr_t)segkp_get(segkp, INTR_STACK_SIZE,
-		KPD_HASREDZONE | KPD_NO_ANON | KPD_LOCKED) +
-		INTR_STACK_SIZE - SA(MINFRAME);
+	    KPD_HASREDZONE | KPD_NO_ANON | KPD_LOCKED) +
+	    INTR_STACK_SIZE - SA(MINFRAME);
 }
--- old/usr/src/uts/common/exec/aout/aout.c	Wed Oct  5 00:32:12 2016
+++ new/usr/src/uts/common/exec/aout/aout.c	Wed Oct  5 00:32:12 2016
@@ -22,6 +22,7 @@
  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  * Copyright (c) 2011 Bayard G. Bell. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
  */
 
 #include <sys/types.h>
@@ -54,7 +55,7 @@
 
 static int aoutexec(vnode_t *vp, execa_t *uap, uarg_t *args,
     intpdata_t *idatap, int level, long *execsz, int setid,
-    caddr_t exec_file, cred_t *cred, int brand_action);
+    caddr_t exec_file, cred_t *cred, int *brand_action);
 static int get_aout_head(struct vnode **vpp, struct exdata *edp, long *execsz,
     int *isdyn);
 static int aoutcore(vnode_t *vp, proc_t *pp, cred_t *credp,
@@ -130,7 +131,7 @@
 static int
 aoutexec(vnode_t *vp, struct execa *uap, struct uarg *args,
     struct intpdata *idatap, int level, long *execsz, int setid,
-    caddr_t exec_file, cred_t *cred, int brand_action)
+    caddr_t exec_file, cred_t *cred, int *brand_action)
 {
 	auxv32_t auxflags_auxv32;
 	int error;
--- old/usr/src/uts/common/exec/elf/elf.c	Wed Oct  5 00:32:12 2016
+++ new/usr/src/uts/common/exec/elf/elf.c	Wed Oct  5 00:32:12 2016
@@ -26,7 +26,7 @@
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
 /*	  All Rights Reserved  	*/
 /*
- * Copyright (c) 2013, Joyent, Inc.  All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 
 #include <sys/types.h>
@@ -66,6 +66,11 @@
 #include <sys/sdt.h>
 #include <sys/siginfo.h>
 
+#if defined(__x86)
+#include <sys/comm_page_util.h>
+#endif /* defined(__x86) */
+
+
 extern int at_flags;
 
 #define	ORIGIN_STR	"ORIGIN"
@@ -163,12 +168,16 @@
 }
 
 /*
- * Map in the executable pointed to by vp. Returns 0 on success.
+ * Map in the executable pointed to by vp. Returns 0 on success.  Note that
+ * this function currently has the maximum number of arguments allowed by
+ * modstubs on x86 (MAXNARG)!  Do _not_ add to this function signature without
+ * adding to MAXNARG.  (Better yet, do not add to this monster of a function
+ * signature!)
  */
 int
 mapexec_brand(vnode_t *vp, uarg_t *args, Ehdr *ehdr, Addr *uphdr_vaddr,
-    intptr_t *voffset, caddr_t exec_file, int *interp, caddr_t *bssbase,
-    caddr_t *brkbase, size_t *brksize, uintptr_t *lddatap)
+    intptr_t *voffset, caddr_t exec_file, char **interpp, caddr_t *bssbase,
+    caddr_t *brkbase, size_t *brksize, uintptr_t *lddatap, uintptr_t *minaddrp)
 {
 	size_t		len;
 	struct vattr	vat;
@@ -180,6 +189,7 @@
 	Phdr		*junk = NULL;
 	Phdr		*dynphdr = NULL;
 	Phdr		*dtrphdr = NULL;
+	char		*interp = NULL;
 	uintptr_t	lddata;
 	long		execsz;
 	intptr_t	minaddr;
@@ -187,6 +197,9 @@
 	if (lddatap != NULL)
 		*lddatap = NULL;
 
+	if (minaddrp != NULL)
+		*minaddrp = NULL;
+
 	if (error = execpermissions(vp, &vat, args)) {
 		uprintf("%s: Cannot execute %s\n", exec_file, args->pathname);
 		return (error);
@@ -212,25 +225,89 @@
 	    &junk, &dtrphdr, NULL, bssbase, brkbase, voffset, &minaddr,
 	    len, &execsz, brksize)) {
 		uprintf("%s: Cannot map %s\n", exec_file, args->pathname);
+		if (uphdr != NULL && uphdr->p_flags == 0)
+			kmem_free(uphdr, sizeof (Phdr));
 		kmem_free(phdrbase, phdrsize);
 		return (error);
 	}
 
+	if (minaddrp != NULL)
+		*minaddrp = minaddr;
+
 	/*
-	 * Inform our caller if the executable needs an interpreter.
+	 * If the executable requires an interpreter, determine its name.
 	 */
-	*interp = (dynphdr == NULL) ? 0 : 1;
+	if (dynphdr != NULL) {
+		ssize_t	resid;
 
+		if (dynphdr->p_filesz > MAXPATHLEN || dynphdr->p_filesz == 0) {
+			uprintf("%s: Invalid interpreter\n", exec_file);
+			kmem_free(phdrbase, phdrsize);
+			return (ENOEXEC);
+		}
+
+		interp = kmem_alloc(MAXPATHLEN, KM_SLEEP);
+
+		if ((error = vn_rdwr(UIO_READ, vp, interp, dynphdr->p_filesz,
+		    (offset_t)dynphdr->p_offset, UIO_SYSSPACE, 0,
+		    (rlim64_t)0, CRED(), &resid)) != 0 || resid != 0 ||
+		    interp[dynphdr->p_filesz - 1] != '\0') {
+			uprintf("%s: Cannot obtain interpreter pathname\n",
+			    exec_file);
+			kmem_free(interp, MAXPATHLEN);
+			kmem_free(phdrbase, phdrsize);
+			return (error != 0 ? error : ENOEXEC);
+		}
+	}
+
 	/*
 	 * If this is a statically linked executable, voffset should indicate
 	 * the address of the executable itself (it normally holds the address
 	 * of the interpreter).
 	 */
-	if (ehdr->e_type == ET_EXEC && *interp == 0)
+	if (ehdr->e_type == ET_EXEC && interp == NULL)
 		*voffset = minaddr;
 
+	/*
+	 * If the caller has asked for the interpreter name, return it (it's
+	 * up to the caller to free it); if the caller hasn't asked for it,
+	 * free it ourselves.
+	 */
+	if (interpp != NULL) {
+		*interpp = interp;
+	} else if (interp != NULL) {
+		kmem_free(interp, MAXPATHLEN);
+	}
+
 	if (uphdr != NULL) {
 		*uphdr_vaddr = uphdr->p_vaddr;
+
+		if (uphdr->p_flags == 0)
+			kmem_free(uphdr, sizeof (Phdr));
+	} else if (ehdr->e_type == ET_DYN) {
+		/*
+		 * If we don't have a uphdr, we'll apply the logic found
+		 * in mapelfexec() and use the p_vaddr of the first PT_LOAD
+		 * section as the base address of the object.
+		 */
+		Phdr *phdr = (Phdr *)phdrbase;
+		int i, hsize = ehdr->e_phentsize;
+
+		for (i = nphdrs; i > 0; i--) {
+			if (phdr->p_type == PT_LOAD) {
+				*uphdr_vaddr = (uintptr_t)phdr->p_vaddr +
+				    ehdr->e_phoff;
+				break;
+			}
+
+			phdr = (Phdr *)((caddr_t)phdr + hsize);
+		}
+
+		/*
+		 * If we don't have a PT_LOAD segment, we should have returned
+		 * ENOEXEC when elfsize() returned 0, above.
+		 */
+		VERIFY(i > 0);
 	} else {
 		*uphdr_vaddr = (Addr)-1;
 	}
@@ -243,13 +320,13 @@
 int
 elfexec(vnode_t *vp, execa_t *uap, uarg_t *args, intpdata_t *idatap,
     int level, long *execsz, int setid, caddr_t exec_file, cred_t *cred,
-    int brand_action)
+    int *brand_action)
 {
 	caddr_t		phdrbase = NULL;
 	caddr_t 	bssbase = 0;
 	caddr_t 	brkbase = 0;
 	size_t		brksize = 0;
-	ssize_t		dlnsize;
+	ssize_t		dlnsize, nsize = 0;
 	aux_entry_t	*aux;
 	int		error;
 	ssize_t		resid;
@@ -273,6 +350,7 @@
 	int		hasauxv = 0;
 	int		hasdy = 0;
 	int		branded = 0;
+	int		dynuphdr = 0;
 
 	struct proc *p = ttoproc(curthread);
 	struct user *up = PTOU(p);
@@ -327,7 +405,9 @@
 		*execsz = btopr(SINCR) + btopr(SSIZE) + btopr(NCARGS32-1);
 	} else {
 		args->to_model = DATAMODEL_LP64;
-		args->stk_prot &= ~PROT_EXEC;
+		if (!args->stk_prot_override) {
+			args->stk_prot &= ~PROT_EXEC;
+		}
 #if defined(__i386) || defined(__amd64)
 		args->dat_prot &= ~PROT_EXEC;
 #endif
@@ -339,12 +419,26 @@
 #endif	/* _LP64 */
 
 	/*
-	 * We delay invoking the brand callback until we've figured out
-	 * what kind of elf binary we're trying to run, 32-bit or 64-bit.
-	 * We do this because now the brand library can just check
-	 * args->to_model to see if the target is 32-bit or 64-bit without
-	 * having do duplicate all the code above.
+	 * We delay invoking the brand callback until we've figured out what
+	 * kind of elf binary we're trying to run, 32-bit or 64-bit.  We do this
+	 * because now the brand library can just check args->to_model to see if
+	 * the target is 32-bit or 64-bit without having do duplicate all the
+	 * code above.
 	 *
+	 * We also give the brand a chance to indicate that based on the ELF
+	 * OSABI of the target binary it should become unbranded and optionally
+	 * indicate that it should be treated as existing in a specific prefix.
+	 *
+	 * Note that if a brand opts to go down this route it does not actually
+	 * end up being debranded. In other words, future programs that exec
+	 * will still be considered for branding unless this escape hatch is
+	 * used. Consider the case of lx brand for example. If a user runs
+	 * /native/usr/sbin/dtrace -c /bin/ls, the isaexec and normal executable
+	 * of DTrace that's in /native will take this escape hatch and be run
+	 * and interpreted using the normal system call table; however, the
+	 * execution of a non-illumos binary in the form of /bin/ls will still
+	 * be branded and be subject to all of the normal actions of the brand.
+	 *
 	 * The level checks associated with brand handling below are used to
 	 * prevent a loop since the brand elfexec function typically comes back
 	 * through this function. We must check <= here since the nested
@@ -351,8 +445,20 @@
 	 * handling in the #! interpreter code will increment the level before
 	 * calling gexec to run the final elfexec interpreter.
 	 */
+	if ((level <= INTP_MAXDEPTH) && (*brand_action != EBA_NATIVE) &&
+	    (PROC_IS_BRANDED(p)) && (BROP(p)->b_native_exec != NULL)) {
+		if (BROP(p)->b_native_exec(ehdrp->e_ident[EI_OSABI],
+		    &args->brand_nroot) == B_TRUE) {
+			ASSERT(ehdrp->e_ident[EI_OSABI]);
+			*brand_action = EBA_NATIVE;
+			/* Add one for the trailing '/' in the path */
+			if (args->brand_nroot != NULL)
+				nsize = strlen(args->brand_nroot) + 1;
+		}
+	}
+
 	if ((level <= INTP_MAXDEPTH) &&
-	    (brand_action != EBA_NATIVE) && (PROC_IS_BRANDED(p))) {
+	    (*brand_action != EBA_NATIVE) && (PROC_IS_BRANDED(p))) {
 		error = BROP(p)->b_elfexec(vp, uap, args,
 		    idatap, level + 1, execsz, setid, exec_file, cred,
 		    brand_action);
@@ -423,6 +529,7 @@
 		 *	AT_BASE
 		 *	AT_FLAGS
 		 *	AT_PAGESZ
+		 *	AT_RANDOM
 		 *	AT_SUN_AUXFLAGS
 		 *	AT_SUN_HWCAP
 		 *	AT_SUN_HWCAP2
@@ -430,7 +537,7 @@
 		 *	AT_SUN_EXECNAME (added in stk_copyout)
 		 *	AT_NULL
 		 *
-		 * total == 9
+		 * total == 10
 		 */
 		if (hasdy && hasu) {
 			/*
@@ -445,7 +552,7 @@
 			 *
 			 * total = 5
 			 */
-			args->auxsize = (9 + 5) * sizeof (aux_entry_t);
+			args->auxsize = (10 + 5) * sizeof (aux_entry_t);
 		} else if (hasdy) {
 			/*
 			 * Has PT_INTERP but no PT_PHDR
@@ -455,9 +562,9 @@
 			 *
 			 * total = 2
 			 */
-			args->auxsize = (9 + 2) * sizeof (aux_entry_t);
+			args->auxsize = (10 + 2) * sizeof (aux_entry_t);
 		} else {
-			args->auxsize = 9 * sizeof (aux_entry_t);
+			args->auxsize = 10 * sizeof (aux_entry_t);
 		}
 	} else {
 		args->auxsize = 0;
@@ -470,13 +577,42 @@
 	if (args->emulator != NULL)
 		args->auxsize += sizeof (aux_entry_t);
 
-	if ((brand_action != EBA_NATIVE) && (PROC_IS_BRANDED(p))) {
+	/*
+	 * If this is a native binary that's been given a modified interpreter
+	 * root, inform it that the native system exists at that root.
+	 */
+	if (args->brand_nroot != NULL) {
+		args->auxsize += sizeof (aux_entry_t);
+	}
+
+
+	/*
+	 * On supported kernels (x86_64) make room in the auxv for the
+	 * AT_SUN_COMMPAGE entry.  This will go unpopulated on i86xpv systems
+	 * which do not provide such functionality.
+	 */
+#if defined(__amd64)
+	args->auxsize += sizeof (aux_entry_t);
+#endif /* defined(__amd64) */
+
+	/*
+	 * If we have user credentials, we'll supply the following entries:
+	 *	AT_SUN_UID
+	 *	AT_SUN_RUID
+	 *	AT_SUN_GID
+	 *	AT_SUN_RGID
+	 */
+	if (cred != NULL) {
+		args->auxsize += 4 * sizeof (aux_entry_t);
+	}
+
+	if ((*brand_action != EBA_NATIVE) && (PROC_IS_BRANDED(p))) {
 		branded = 1;
 		/*
-		 * We will be adding 4 entries to the aux vectors.  One for
-		 * the the brandname and 3 for the brand specific aux vectors.
+		 * We will be adding 5 entries to the aux vectors.  One for
+		 * the the brandname and 4 for the brand specific aux vectors.
 		 */
-		args->auxsize += 4 * sizeof (aux_entry_t);
+		args->auxsize += 5 * sizeof (aux_entry_t);
 	}
 
 	/* Hardware/Software capabilities */
@@ -534,6 +670,14 @@
 	    len, execsz, &brksize)) != 0)
 		goto bad;
 
+	if (uphdr != NULL) {
+		/*
+		 * Our uphdr has been dynamically allocated if (and only if)
+		 * its program header flags are clear.
+		 */
+		dynuphdr = (uphdr->p_flags == 0);
+	}
+
 	if (uphdr != NULL && dyphdr == NULL)
 		goto bad;
 
@@ -548,17 +692,22 @@
 		char		*p;
 		struct vnode	*nvp;
 
-		dlnsize = dyphdr->p_filesz;
+		dlnsize = dyphdr->p_filesz + nsize;
 
 		if (dlnsize > MAXPATHLEN || dlnsize <= 0)
 			goto bad;
 
+		if (nsize != 0) {
+			bcopy(args->brand_nroot, dlnp, nsize - 1);
+			dlnp[nsize - 1] = '/';
+		}
+
 		/*
 		 * Read in "interpreter" pathname.
 		 */
-		if ((error = vn_rdwr(UIO_READ, vp, dlnp, dyphdr->p_filesz,
-		    (offset_t)dyphdr->p_offset, UIO_SYSSPACE, 0, (rlim64_t)0,
-		    CRED(), &resid)) != 0) {
+		if ((error = vn_rdwr(UIO_READ, vp, dlnp + nsize,
+		    dyphdr->p_filesz, (offset_t)dyphdr->p_offset, UIO_SYSSPACE,
+		    0, (rlim64_t)0, CRED(), &resid)) != 0) {
 			uprintf("%s: Cannot obtain interpreter pathname\n",
 			    exec_file);
 			goto bad;
@@ -703,9 +852,10 @@
 
 		dtrphdr = NULL;
 
-		error = mapelfexec(nvp, ehdrp, nphdrs, phdrbase, &junk, &junk,
+		error = mapelfexec(nvp, ehdrp, nphdrs, phdrbase, NULL, &junk,
 		    &junk, &dtrphdr, NULL, NULL, NULL, &voffset, NULL, len,
 		    execsz, NULL);
+
 		if (error || junk != NULL) {
 			VN_RELE(nvp);
 			uprintf("%s: Cannot map %s\n", exec_file, dlnp);
@@ -732,8 +882,9 @@
 
 	if (hasauxv) {
 		int auxf = AF_SUN_HWCAPVERIFY;
+
 		/*
-		 * Note: AT_SUN_PLATFORM and AT_SUN_EXECNAME were filled in via
+		 * Note: AT_SUN_PLATFORM and AT_RANDOM were filled in via
 		 * exec_args()
 		 */
 		ADDAUX(aux, AT_BASE, voffset)
@@ -762,7 +913,7 @@
 		 * malicious user within the zone from crafting a wrapper to
 		 * run native suid commands with unsecure libraries interposed.
 		 */
-		if ((brand_action == EBA_NATIVE) && (PROC_IS_BRANDED(p) &&
+		if ((*brand_action == EBA_NATIVE) && (PROC_IS_BRANDED(p) &&
 		    (setid &= ~EXECSETID_SETID) != 0))
 			auxf &= ~AF_SUN_SETUGID;
 
@@ -775,7 +926,19 @@
 		    ((char *)&aux->a_type -
 		    (char *)bigwad->elfargs));
 		ADDAUX(aux, AT_SUN_AUXFLAGS, auxf);
+
 		/*
+		 * Record information about the real and effective user and
+		 * group IDs.
+		 */
+		if (cred != NULL) {
+			ADDAUX(aux, AT_SUN_UID, crgetuid(cred));
+			ADDAUX(aux, AT_SUN_RUID, crgetruid(cred));
+			ADDAUX(aux, AT_SUN_GID, crgetgid(cred));
+			ADDAUX(aux, AT_SUN_RGID, crgetrgid(cred));
+		}
+
+		/*
 		 * Hardware capability flag word (performance hints)
 		 * Used for choosing faster library routines.
 		 * (Potentially different between 32-bit and 64-bit ABIs)
@@ -804,8 +967,25 @@
 			ADDAUX(aux, AT_SUN_BRAND_AUX1, 0)
 			ADDAUX(aux, AT_SUN_BRAND_AUX2, 0)
 			ADDAUX(aux, AT_SUN_BRAND_AUX3, 0)
+			ADDAUX(aux, AT_SUN_BRAND_AUX4, 0)
 		}
 
+		/*
+		 * Add the comm page auxv entry, mapping it in if needed.
+		 */
+#if defined(__amd64)
+		if (args->commpage != NULL ||
+		    (args->commpage = (uintptr_t)comm_page_mapin()) != NULL) {
+			ADDAUX(aux, AT_SUN_COMMPAGE, args->commpage)
+		} else {
+			/*
+			 * If the comm page cannot be mapped, pad out the auxv
+			 * to satisfy later size checks.
+			 */
+			ADDAUX(aux, AT_NULL, 0)
+		}
+#endif /* defined(__amd64) */
+
 		ADDAUX(aux, AT_NULL, 0)
 		postfixsize = (char *)aux - (char *)bigwad->elfargs;
 
@@ -845,6 +1025,7 @@
 	}
 
 	bzero(up->u_auxv, sizeof (up->u_auxv));
+	up->u_commpagep = args->commpage;
 	if (postfixsize) {
 		int num_auxv;
 
@@ -911,6 +1092,8 @@
 	if (error == 0)
 		error = ENOEXEC;
 out:
+	if (dynuphdr)
+		kmem_free(uphdr, sizeof (Phdr));
 	if (phdrbase != NULL)
 		kmem_free(phdrbase, phdrsize);
 	if (cap != NULL)
@@ -1177,6 +1360,29 @@
 	return (0);
 }
 
+
+#ifdef _ELF32_COMPAT
+int
+elf32readhdr(vnode_t *vp, cred_t *credp, Ehdr *ehdrp, int *nphdrs,
+    caddr_t *phbasep, ssize_t *phsizep)
+#else
+int
+elfreadhdr(vnode_t *vp, cred_t *credp, Ehdr *ehdrp, int *nphdrs,
+    caddr_t *phbasep, ssize_t *phsizep)
+#endif
+{
+	int error, nshdrs, shstrndx;
+
+	if ((error = getelfhead(vp, credp, ehdrp, &nshdrs, &shstrndx,
+	    nphdrs)) != 0 ||
+	    (error = getelfphdr(vp, credp, ehdrp, *nphdrs, phbasep,
+	    phsizep)) != 0) {
+		return (error);
+	}
+	return (0);
+}
+
+
 static int
 mapelfexec(
 	vnode_t *vp,
@@ -1197,7 +1403,7 @@
 	size_t *brksize)
 {
 	Phdr *phdr;
-	int i, prot, error;
+	int i, prot, error, lastprot = 0;
 	caddr_t addr = NULL;
 	size_t zfodsz;
 	int ptload = 0;
@@ -1205,43 +1411,78 @@
 	off_t offset;
 	int hsize = ehdr->e_phentsize;
 	caddr_t mintmp = (caddr_t)-1;
+	uintptr_t lastaddr = NULL;
 	extern int use_brk_lpg;
 
 	if (ehdr->e_type == ET_DYN) {
-		/*
-		 * Obtain the virtual address of a hole in the
-		 * address space to map the "interpreter".
-		 */
-		map_addr(&addr, len, (offset_t)0, 1, 0);
-		if (addr == NULL)
-			return (ENOMEM);
-		*voffset = (intptr_t)addr;
+		caddr_t vaddr;
 
 		/*
-		 * Calculate the minimum vaddr so it can be subtracted out.
-		 * According to the ELF specification, since PT_LOAD sections
-		 * must be sorted by increasing p_vaddr values, this is
-		 * guaranteed to be the first PT_LOAD section.
+		 * Despite the fact that mmapobj(2) refuses to load them, we
+		 * need to support executing ET_DYN objects that have a
+		 * non-NULL p_vaddr.  When found in the wild, these objects
+		 * are likely to be due to an old (and largely obviated) Linux
+		 * facility, prelink(8), that rewrites shared objects to
+		 * prefer specific (disjoint) virtual address ranges.  (Yes,
+		 * this is putatively for performance -- and yes, it has
+		 * limited applicability, many edge conditions and grisly
+		 * failure modes; even for Linux, it's insane.)  As ELF
+		 * mandates that the PT_LOAD segments be in p_vaddr order, we
+		 * find the lowest p_vaddr by finding the first PT_LOAD
+		 * segment.
 		 */
 		phdr = (Phdr *)phdrbase;
 		for (i = nphdrs; i > 0; i--) {
 			if (phdr->p_type == PT_LOAD) {
-				*voffset -= (uintptr_t)phdr->p_vaddr;
+				addr = (caddr_t)(uintptr_t)phdr->p_vaddr;
 				break;
 			}
 			phdr = (Phdr *)((caddr_t)phdr + hsize);
 		}
 
+		/*
+		 * We have a non-zero p_vaddr in the first PT_LOAD segment --
+		 * presumably because we're directly executing a prelink(8)'d
+		 * ld-linux.so.  While we could correctly execute such an
+		 * object without locating it at its desired p_vaddr (it is,
+		 * after all, still relocatable), our inner antiquarian
+		 * derives a perverse pleasure in accommodating the steampunk
+		 * prelink(8) contraption -- goggles on!
+		 */
+		if ((vaddr = addr) != NULL) {
+			if (as_gap(curproc->p_as, len,
+			    &addr, &len, AH_LO, NULL) == -1 || addr != vaddr) {
+				addr = NULL;
+			}
+		}
+
+		if (addr == NULL) {
+			/*
+			 * We either have a NULL p_vaddr (the common case, by
+			 * many orders of magnitude) or we have a non-NULL
+			 * p_vaddr and we were unable to obtain the specified
+			 * VA range (presumably because it's an illegal
+			 * address).  Either way, obtain an address in which
+			 * to map the interpreter.
+			 */
+			map_addr(&addr, len, (offset_t)0, 1, 0);
+			if (addr == NULL)
+				return (ENOMEM);
+		}
+
+		/*
+		 * Our voffset is the difference between where we landed and
+		 * where we wanted to be.
+		 */
+		*voffset = (uintptr_t)addr - (uintptr_t)vaddr;
 	} else {
 		*voffset = 0;
 	}
+
 	phdr = (Phdr *)phdrbase;
 	for (i = nphdrs; i > 0; i--) {
 		switch (phdr->p_type) {
 		case PT_LOAD:
-			if ((*dyphdr != NULL) && (*uphdr == NULL))
-				return (0);
-
 			ptload = 1;
 			prot = PROT_USER;
 			if (phdr->p_flags & PF_R)
@@ -1253,6 +1494,34 @@
 
 			addr = (caddr_t)((uintptr_t)phdr->p_vaddr + *voffset);
 
+			if ((*dyphdr != NULL) && uphdr != NULL &&
+			    (*uphdr == NULL)) {
+				/*
+				 * The PT_PHDR program header is, strictly
+				 * speaking, optional.  If we find that this
+				 * is missing, we will determine the location
+				 * of the program headers based on the address
+				 * of the lowest PT_LOAD segment (namely, this
+				 * one):  we subtract the p_offset to get to
+				 * the ELF header and then add back the program
+				 * header offset to get to the program headers.
+				 * We then cons up a Phdr that corresponds to
+				 * the (missing) PT_PHDR, setting the flags
+				 * to 0 to denote that this is artificial and
+				 * should (must) be freed by the caller.
+				 */
+				Phdr *cons;
+
+				cons = kmem_zalloc(sizeof (Phdr), KM_SLEEP);
+
+				cons->p_flags = 0;
+				cons->p_type = PT_PHDR;
+				cons->p_vaddr = ((uintptr_t)addr -
+				    phdr->p_offset) + ehdr->e_phoff;
+
+				*uphdr = cons;
+			}
+
 			/*
 			 * Keep track of the segment with the lowest starting
 			 * address.
@@ -1260,6 +1529,41 @@
 			if (addr < mintmp)
 				mintmp = addr;
 
+			/*
+			 * Segments need not correspond to page boundaries:
+			 * they are permitted to share a page.  If two PT_LOAD
+			 * segments share the same page, and the permissions
+			 * of the segments differ, the behavior is historically
+			 * that the permissions of the latter segment are used
+			 * for the page that the two segments share.  This is
+			 * also historically a non-issue:  binaries generated
+			 * by most anything will make sure that two PT_LOAD
+			 * segments with differing permissions don't actually
+			 * share any pages.  However, there exist some crazy
+			 * things out there (including at least an obscure
+			 * Portuguese teaching language called G-Portugol) that
+			 * actually do the wrong thing and expect it to work:
+			 * they have a segment with execute permission share
+			 * a page with a subsequent segment that does not
+			 * have execute permissions and expect the resulting
+			 * shared page to in fact be executable.  To accommodate
+			 * such broken link editors, we take advantage of a
+			 * latitude explicitly granted to the loader:  it is
+			 * permitted to make _any_ PT_LOAD segment executable
+			 * (provided that it is readable or writable).  If we
+			 * see that we're sharing a page and that the previous
+			 * page was executable, we will add execute permissions
+			 * to our segment.
+			 */
+			if (btop(lastaddr) == btop((uintptr_t)addr) &&
+			    (phdr->p_flags & (PF_R | PF_W)) &&
+			    (lastprot & PROT_EXEC)) {
+				prot |= PROT_EXEC;
+			}
+
+			lastaddr = (uintptr_t)addr + phdr->p_filesz;
+			lastprot = prot;
+
 			zfodsz = (size_t)phdr->p_memsz - phdr->p_filesz;
 
 			offset = phdr->p_offset;
@@ -1324,8 +1628,22 @@
 			break;
 
 		case PT_INTERP:
-			if (ptload)
-				goto bad;
+			/*
+			 * The ELF specification is unequivocal about the
+			 * PT_INTERP program header with respect to any PT_LOAD
+			 * program header:  "If it is present, it must precede
+			 * any loadable segment entry." Linux, however, makes
+			 * no attempt to enforce this -- which has allowed some
+			 * binary editing tools to get away with generating
+			 * invalid ELF binaries in the respect that PT_INTERP
+			 * occurs after the first PT_LOAD program header.  This
+			 * is unfortunate (and of course, disappointing) but
+			 * it's no worse than that: there is no reason that we
+			 * can't process the PT_INTERP entry (if present) after
+			 * one or more PT_LOAD entries.  We therefore
+			 * deliberately do not check ptload here and always
+			 * store dyphdr to be the PT_INTERP program header.
+			 */
 			*dyphdr = phdr;
 			break;
 
@@ -1334,9 +1652,12 @@
 			break;
 
 		case PT_PHDR:
-			if (ptload)
+			if (ptload || phdr->p_flags == 0)
 				goto bad;
-			*uphdr = phdr;
+
+			if (uphdr != NULL)
+				*uphdr = phdr;
+
 			break;
 
 		case PT_NULL:
@@ -2185,7 +2506,7 @@
 extern int elf32exec(vnode_t *vp, execa_t *uap, uarg_t *args,
 			intpdata_t *idatap, int level, long *execsz,
 			int setid, caddr_t exec_file, cred_t *cred,
-			int brand_action);
+			int *brand_action);
 extern int elf32core(vnode_t *vp, proc_t *p, cred_t *credp,
 			rlim64_t rlimit, int sig, core_content_t content);
 
--- old/usr/src/uts/common/exec/intp/intp.c	Wed Oct  5 00:32:12 2016
+++ new/usr/src/uts/common/exec/intp/intp.c	Wed Oct  5 00:32:12 2016
@@ -22,6 +22,7 @@
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  * Copyright 2012 Milan Jurik. All rights reserved.
+ * Copyright 2016, Joyent, Inc.
  */
 
 /*	Copyright (c) 1988 AT&T	*/
@@ -47,6 +48,7 @@
 #include <sys/kmem.h>
 #include <sys/note.h>
 #include <sys/sdt.h>
+#include <sys/brand.h>
 
 /*
  * This is the loadable module wrapper.
@@ -54,7 +56,7 @@
 #include <sys/modctl.h>
 
 extern int intpexec(struct vnode *, struct execa *, struct uarg *,
-    struct intpdata *, int, long *, int, caddr_t, struct cred *, int);
+    struct intpdata *, int, long *, int, caddr_t, struct cred *, int *);
 
 static struct execsw esw = {
 	intpmagicstr,
@@ -126,13 +128,20 @@
 	*cp = '\0';
 
 	/*
-	 * Locate the beginning and end of the interpreter name.
-	 * In addition to the name, one additional argument may
-	 * optionally be included here, to be prepended to the
-	 * arguments provided on the command line.  Thus, for
-	 * example, you can say
+	 * Locate the beginning and end of the interpreter name. Historically,
+	 * for illumos and its predecessors, in addition to the name, one
+	 * additional argument may optionally be included here, to be prepended
+	 * to the arguments provided on the command line. Thus, for example,
+	 * you can say
 	 *
 	 * 	#! /usr/bin/awk -f
+	 *
+	 * However, handling of interpreter arguments varies across operating
+	 * systems and other systems allow more than one argument. In
+	 * particular, Linux allows more than one and delivers all arguments
+	 * as a single string (argv[1] is "-arg1 -arg2 ..."). We support this
+	 * style of argument handling as a brand-specific option (setting
+	 * b_intp_parse_arg to B_FALSE).
 	 */
 	for (cp = &linep[2]; *cp == ' '; cp++)
 		;
@@ -151,9 +160,12 @@
 			idatap->intp_arg[0] = NULL;
 		else {
 			idatap->intp_arg[0] = cp;
-			while (*cp && *cp != ' ')
-				cp++;
-			*cp = '\0';
+			if (!PROC_IS_BRANDED(curproc) ||
+			    BROP(curproc)->b_intp_parse_arg) {
+				while (*cp && *cp != ' ')
+					cp++;
+				*cp = '\0';
+			}
 		}
 	}
 	return (0);
@@ -188,9 +200,8 @@
 	int setid,
 	caddr_t exec_file,
 	struct cred *cred,
-	int brand_action)
+	int *brand_action)
 {
-	_NOTE(ARGUNUSED(brand_action))
 	vnode_t *nvp;
 	int error = 0;
 	struct intpdata idata;
@@ -281,7 +292,7 @@
 	}
 
 	error = gexec(&nvp, uap, args, &idata, ++level, execsz, exec_file, cred,
-	    EBA_NONE);
+	    brand_action);
 
 	if (!error) {
 		/*
--- old/usr/src/uts/common/exec/java/java.c	Wed Oct  5 00:32:12 2016
+++ new/usr/src/uts/common/exec/java/java.c	Wed Oct  5 00:32:12 2016
@@ -21,6 +21,7 @@
 /*
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2015, Joyent, Inc.
  */
 
 /*
@@ -85,7 +86,7 @@
 static int
 javaexec(vnode_t *vp, struct execa *uap, struct uarg *args,
     struct intpdata *idatap, int level, long *execsz, int setid,
-    caddr_t execfile, cred_t *cred, int brand_action)
+    caddr_t execfile, cred_t *cred, int *brand_action)
 {
 	struct intpdata idata;
 	int error;
--- old/usr/src/uts/common/exec/shbin/shbin.c	Wed Oct  5 00:32:12 2016
+++ new/usr/src/uts/common/exec/shbin/shbin.c	Wed Oct  5 00:32:12 2016
@@ -22,6 +22,7 @@
 /*
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2015, Joyent, Inc.
  */
 
 #include <sys/types.h>
@@ -58,7 +59,7 @@
 	int setid,
 	caddr_t exec_file,
 	struct cred *cred,
-	int brand_action);
+	int *brand_action);
 
 #define	SHBIN_CNTL(x)	((x)&037)
 #define	SHBINMAGIC_LEN	4
@@ -162,7 +163,7 @@
 	int setid,
 	caddr_t exec_file,
 	struct cred *cred,
-	int brand_action)
+	int *brand_action)
 {
 	_NOTE(ARGUNUSED(brand_action))
 	vnode_t *nvp;
--- old/usr/src/uts/common/fs/dev/sdev_subr.c	Wed Oct  5 00:32:13 2016
+++ new/usr/src/uts/common/fs/dev/sdev_subr.c	Wed Oct  5 00:32:13 2016
@@ -962,6 +962,7 @@
 	(void) memset((void *)&dv->sdev_instance_data, 0,
 	    sizeof (dv->sdev_instance_data));
 	vn_invalid(SDEVTOV(dv));
+	dv->sdev_private = NULL;
 	kmem_cache_free(sdev_node_cache, dv);
 }
 
--- old/usr/src/uts/common/fs/dev/sdev_vnops.c	Wed Oct  5 00:32:13 2016
+++ new/usr/src/uts/common/fs/dev/sdev_vnops.c	Wed Oct  5 00:32:13 2016
@@ -22,7 +22,7 @@
  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 /*
- * Copyright (c) 2013, Joyent, Inc.  All rights reserved.
+ * Copyright 2016, Joyent, Inc.
  */
 
 /*
@@ -864,6 +864,9 @@
 		}
 	}
 
+	if (error == 0)
+		i_ddi_di_cache_invalidate();
+
 	return (error);
 }
 
@@ -1188,6 +1191,7 @@
 	sdev_update_timestamps(dvp, kcred, AT_MTIME|AT_ATIME);
 	if (SDEV_IS_GLOBAL(parent))
 		atomic_inc_ulong(&parent->sdev_gdir_gen);
+	i_ddi_di_cache_invalidate();
 
 	/* wake up other threads blocked on looking up this node */
 	mutex_enter(&self->sdev_lookup_lock);
@@ -1260,6 +1264,7 @@
 	sdev_update_timestamps(dvp, kcred, AT_MTIME|AT_ATIME);
 	if (SDEV_IS_GLOBAL(parent))
 		atomic_inc_ulong(&parent->sdev_gdir_gen);
+	i_ddi_di_cache_invalidate();
 
 	/* wake up other threads blocked on looking up this node */
 	mutex_enter(&self->sdev_lookup_lock);
@@ -1375,6 +1380,9 @@
 
 	}
 
+	if (error == 0)
+		i_ddi_di_cache_invalidate();
+
 	return (error);
 }
 
--- old/usr/src/uts/common/fs/dev/sdev_zvolops.c	Wed Oct  5 00:32:13 2016
+++ new/usr/src/uts/common/fs/dev/sdev_zvolops.c	Wed Oct  5 00:32:13 2016
@@ -472,8 +472,10 @@
 		ASSERT(dvp->v_count > 0);
 		rc = VOP_LOOKUP(dvp, nvpair_name(elem), &vp, NULL, 0,
 		    NULL, kcred, NULL, 0, NULL);
-		/* should either work, or not be visible from a zone */
-		ASSERT(rc == 0 || rc == ENOENT);
+		/*
+		 * should either work or we should get an error if this should
+		 * not be visible from the zone, or disallowed in the zone
+		 */
 		if (rc == 0)
 			VN_RELE(vp);
 		pools++;
--- old/usr/src/uts/common/fs/fifofs/fifosubr.c	Wed Oct  5 00:32:13 2016
+++ new/usr/src/uts/common/fs/fifofs/fifosubr.c	Wed Oct  5 00:32:13 2016
@@ -614,9 +614,12 @@
 	/*
 	 * The other end of the pipe is almost closed so
 	 * reject any other open on this end of the pipe
-	 * This only happens with a pipe mounted under namefs
+	 * This normally only happens with a pipe mounted under namefs, but
+	 * we can also see an open via proc/fd, which should still succeed.
+	 * To indicate the proc/fd case the FKLYR flag is passed.
 	 */
-	if ((fnp->fn_flag & (FIFOCLOSE|ISPIPE)) == (FIFOCLOSE|ISPIPE)) {
+	if ((fnp->fn_flag & (FIFOCLOSE|ISPIPE)) == (FIFOCLOSE|ISPIPE) &&
+	    (flag & FKLYR) == 0) {
 		fifo_cleanup(oldvp, flag);
 		cv_broadcast(&fnp->fn_wait_cv);
 		if (!lockheld)
--- old/usr/src/uts/common/fs/lookup.c	Wed Oct  5 00:32:13 2016
+++ new/usr/src/uts/common/fs/lookup.c	Wed Oct  5 00:32:13 2016
@@ -21,6 +21,7 @@
 
 /*
  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright (c) 2015, Joyent, Inc. All rights reserved.
  * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
@@ -57,6 +58,7 @@
 #include <sys/zone.h>
 #include <sys/dnlc.h>
 #include <sys/fs/snode.h>
+#include <sys/brand.h>
 
 /* Controls whether paths are stored with vnodes. */
 int vfs_vnode_path = 1;
@@ -1408,8 +1410,21 @@
 
 	pn_free(&pn);
 
-	if (vp->v_type != VDIR) {
+	if (PROC_IS_BRANDED(curproc)) {
 		/*
+		 * If v_path doesn't work out and we're in a branded zone,
+		 * we're not going to bother doing more work here:  because
+		 * directories from the global can be lofs mounted into odd
+		 * locations (e.g., /native in an lx zone), it is likely that
+		 * the DNLC reverse lookup will yield nothing.  Indeed, the
+		 * only certainty is that the DNLC reverse lookup will be
+		 * exceedingly painful; we save ourselves the substantial
+		 * grief of scanning the entire DNLC and kick out with ENOENT
+		 * in this case.
+		 */
+		ret = ENOENT;
+	} else if (vp->v_type != VDIR) {
+		/*
 		 * If we don't have a directory, try to find it in the dnlc via
 		 * reverse lookup.  Once this is found, we can use the regular
 		 * directory search to find the full path.
--- old/usr/src/uts/common/fs/nfs/nfs3_vnops.c	Wed Oct  5 00:32:13 2016
+++ new/usr/src/uts/common/fs/nfs/nfs3_vnops.c	Wed Oct  5 00:32:13 2016
@@ -29,7 +29,7 @@
  */
 
 /*
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2014, Joyent, Inc. All rights reserved.
  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
  */
 
@@ -3353,10 +3353,9 @@
 		if (nvp)
 			vnevent_rename_dest(nvp, ndvp, nnm, ct);
 
-		if (odvp != ndvp)
-			vnevent_rename_dest_dir(ndvp, ct);
 		ASSERT(ovp != NULL);
 		vnevent_rename_src(ovp, odvp, onm, ct);
+		vnevent_rename_dest_dir(ndvp, ovp, nnm, ct);
 	}
 
 	if (nvp) {
@@ -5523,8 +5522,13 @@
 			va.va_size = bfp->l_start;
 			error = nfs3setattr(vp, &va, 0, cr);
 
-			if (error == 0 && bfp->l_start == 0)
-				vnevent_truncate(vp, ct);
+			if (error == 0) {
+				if (bfp->l_start == 0) {
+					vnevent_truncate(vp, ct);
+				} else {
+					vnevent_resize(vp, ct);
+				}
+			}
 		} else
 			error = EINVAL;
 	}
--- old/usr/src/uts/common/fs/nfs/nfs4_vnops.c	Wed Oct  5 00:32:14 2016
+++ new/usr/src/uts/common/fs/nfs/nfs4_vnops.c	Wed Oct  5 00:32:14 2016
@@ -34,7 +34,7 @@
  */
 
 /*
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2014, Joyent, Inc. All rights reserved.
  */
 
 #include <sys/param.h>
@@ -3739,8 +3739,13 @@
 	 */
 	error = nfs4setattr(vp, vap, flags, cr, NULL);
 
-	if (error == 0 && (vap->va_mask & AT_SIZE) && vap->va_size == 0)
-		vnevent_truncate(vp, ct);
+	if (error == 0 && (vap->va_mask & AT_SIZE)) {
+		if (vap->va_size == 0) {
+			vnevent_truncate(vp, ct);
+		} else {
+			vnevent_resize(vp, ct);
+		}
+	}
 
 	return (error);
 }
@@ -8063,8 +8068,9 @@
 	 * vnode if it already existed.
 	 */
 	if (error == 0) {
-		vnode_t *tvp;
+		vnode_t *tvp, *tovp;
 		rnode4_t *trp;
+
 		/*
 		 * Notify the vnode. Each links is represented by
 		 * a different vnode, in nfsv4.
@@ -8077,23 +8083,20 @@
 			vnevent_rename_dest(tvp, ndvp, nnm, ct);
 		}
 
-		/*
-		 * if the source and destination directory are not the
-		 * same notify the destination directory.
-		 */
-		if (VTOR4(odvp) != VTOR4(ndvp)) {
-			trp = VTOR4(ndvp);
-			tvp = ndvp;
-			if (IS_SHADOW(ndvp, trp))
-				tvp = RTOV4(trp);
-			vnevent_rename_dest_dir(tvp, ct);
-		}
-
 		trp = VTOR4(ovp);
-		tvp = ovp;
+		tovp = ovp;
 		if (IS_SHADOW(ovp, trp))
+			tovp = RTOV4(trp);
+
+		vnevent_rename_src(tovp, odvp, onm, ct);
+
+		trp = VTOR4(ndvp);
+		tvp = ndvp;
+
+		if (IS_SHADOW(ndvp, trp))
 			tvp = RTOV4(trp);
-		vnevent_rename_src(tvp, odvp, onm, ct);
+
+		vnevent_rename_dest_dir(tvp, tovp, nnm, ct);
 	}
 
 	if (nvp) {
@@ -11002,8 +11005,13 @@
 			va.va_size = bfp->l_start;
 			error = nfs4setattr(vp, &va, 0, cr, NULL);
 
-			if (error == 0 && bfp->l_start == 0)
-				vnevent_truncate(vp, ct);
+			if (error == 0) {
+				if (bfp->l_start == 0) {
+					vnevent_truncate(vp, ct);
+				} else {
+					vnevent_resize(vp, ct);
+				}
+			}
 		} else
 			error = EINVAL;
 	}
--- old/usr/src/uts/common/fs/nfs/nfs_vnops.c	Wed Oct  5 00:32:15 2016
+++ new/usr/src/uts/common/fs/nfs/nfs_vnops.c	Wed Oct  5 00:32:15 2016
@@ -26,7 +26,7 @@
  */
 
 /*
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2014, Joyent, Inc. All rights reserved.
  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
  */
 
@@ -1174,8 +1174,13 @@
 
 	error = nfssetattr(vp, vap, flags, cr);
 
-	if (error == 0 && (mask & AT_SIZE) && vap->va_size == 0)
-		vnevent_truncate(vp, ct);
+	if (error == 0 && (mask & AT_SIZE)) {
+		if (vap->va_size == 0) {
+			vnevent_truncate(vp, ct);
+		} else {
+			vnevent_resize(vp, ct);
+		}
+	}
 
 	return (error);
 }
@@ -2688,11 +2693,9 @@
 		if (nvp)
 			vnevent_rename_dest(nvp, ndvp, nnm, ct);
 
-		if (odvp != ndvp)
-			vnevent_rename_dest_dir(ndvp, ct);
-
 		ASSERT(ovp != NULL);
 		vnevent_rename_src(ovp, odvp, onm, ct);
+		vnevent_rename_dest_dir(ndvp, ovp, nnm, ct);
 	}
 
 	if (nvp) {
@@ -4620,8 +4623,13 @@
 			va.va_size = bfp->l_start;
 			error = nfssetattr(vp, &va, 0, cr);
 
-			if (error == 0 && bfp->l_start == 0)
-				vnevent_truncate(vp, ct);
+			if (error == 0) {
+				if (bfp->l_start == 0) {
+					vnevent_truncate(vp, ct);
+				} else {
+					vnevent_resize(vp, ct);
+				}
+			}
 		} else
 			error = EINVAL;
 	}
--- old/usr/src/uts/common/fs/pcfs/pc_dir.c	Wed Oct  5 00:32:15 2016
+++ new/usr/src/uts/common/fs/pcfs/pc_dir.c	Wed Oct  5 00:32:15 2016
@@ -22,7 +22,7 @@
 /*
  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
- * Copyright 2015 Joyent, Inc.
+ * Copyright 2016 Joyent, Inc.
  */
 
 #include <sys/param.h>
@@ -826,8 +826,7 @@
 
 	if (error == 0) {
 		vnevent_rename_src(PCTOV(pcp), PCTOV(dp), snm, ctp);
-		if (dp != tdp)
-			vnevent_rename_dest_dir(PCTOV(tdp), ctp);
+		vnevent_rename_dest_dir(PCTOV(tdp), PCTOV(pcp), tnm, ctp);
 	}
 
 done:
--- old/usr/src/uts/common/fs/pcfs/pc_vnops.c	Wed Oct  5 00:32:16 2016
+++ new/usr/src/uts/common/fs/pcfs/pc_vnops.c	Wed Oct  5 00:32:16 2016
@@ -781,8 +781,11 @@
 		if (error)
 			goto out;
 
-		if (vap->va_size == 0)
+		if (vap->va_size == 0) {
 			vnevent_truncate(vp, ct);
+		} else {
+			vnevent_resize(vp, ct);
+		}
 	}
 	/*
 	 * Change file modified times.
--- /dev/null	Wed Oct  5 00:32:16 2016
+++ new/usr/src/uts/common/fs/proc/prargv.c	Wed Oct  5 00:32:16 2016
@@ -0,0 +1,441 @@
+/*
+ * 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 2015, Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <sys/sunddi.h>
+#include <sys/proc.h>
+#include <sys/procfs.h>
+#include <sys/sysmacros.h>
+#include <vm/as.h>
+
+/*
+ * Safely read a contiguous region of memory from 'addr' in the address space
+ * of a particular process into the supplied kernel buffer (*buf, sz).
+ * Partially mapped regions will result in a partial read terminating at the
+ * first hole in the address space.  The number of bytes actually read is
+ * returned to the caller via 'rdsz'.
+ */
+int
+prreadbuf(proc_t *p, uintptr_t ustart, uint8_t *buf, size_t sz, size_t *rdsz)
+{
+	int error = 0;
+	size_t rem = sz;
+	off_t pos = 0;
+
+	if (rdsz != NULL)
+		*rdsz = 0;
+
+	while (rem != 0) {
+		uintptr_t addr = ustart + pos;
+		size_t len = MIN(rem, PAGESIZE - (addr & PAGEOFFSET));
+
+		if ((error = uread(p, buf + pos, len, addr)) != 0) {
+			if (error == ENXIO) {
+				/*
+				 * ENXIO from uread() indicates that the page
+				 * does not exist.  This will simply be a
+				 * partial read.
+				 */
+				error = 0;
+			}
+			break;
+		}
+
+		rem -= len;
+		pos += len;
+	}
+
+	if (rdsz != NULL)
+		*rdsz = pos;
+
+	return (error);
+}
+
+/*
+ * Attempt to read the argument vector (argv) from this process.  The caller
+ * must hold the p_lock mutex, and have marked the process P_PR_LOCK (e.g. via
+ * prlock or lx_prlock).
+ *
+ * The caller must provide a buffer (buf, buflen).  We will concatenate each
+ * argument string (including the NUL terminator) into this buffer.  The number
+ * of characters written to this buffer (including the final NUL terminator)
+ * will be stored in 'slen'.
+ */
+int
+prreadargv(proc_t *p, char *buf, size_t bufsz, size_t *slen)
+{
+	int error;
+	user_t *up;
+	struct as *as;
+	size_t pos = 0;
+	caddr_t *argv = NULL;
+	size_t argvsz = 0;
+	int i;
+
+	VERIFY(MUTEX_HELD(&p->p_lock));
+	VERIFY(p->p_proc_flag & P_PR_LOCK);
+
+	up = PTOU(p);
+	as = p->p_as;
+
+	if ((p->p_flag & SSYS) || as == &kas || up->u_argv == NULL) {
+		/*
+		 * Return the regular psargs string to the caller.
+		 */
+		bcopy(up->u_psargs, buf, MIN(bufsz, sizeof (up->u_psargs)));
+		buf[bufsz - 1] = '\0';
+		*slen = strlen(buf) + 1;
+
+		return (0);
+	}
+
+	/*
+	 * Allocate space to store argv array.
+	 */
+	argvsz = up->u_argc * (p->p_model == DATAMODEL_ILP32 ?
+	    sizeof (caddr32_t) : sizeof (caddr_t));
+	argv = kmem_alloc(argvsz, KM_SLEEP);
+
+	/*
+	 * Extract the argv array from the target process.  Drop p_lock
+	 * while we do I/O to avoid deadlock with the clock thread.
+	 */
+	mutex_exit(&p->p_lock);
+	if ((error = prreadbuf(p, up->u_argv, (uint8_t *)argv, argvsz,
+	    NULL)) != 0) {
+		kmem_free(argv, argvsz);
+		mutex_enter(&p->p_lock);
+		VERIFY(p->p_proc_flag & P_PR_LOCK);
+		return (-1);
+	}
+
+	/*
+	 * Read each argument string from the pointers in the argv array.
+	 */
+	pos = 0;
+	for (i = 0; i < up->u_argc; i++) {
+		size_t rdsz, trysz;
+		uintptr_t arg;
+		off_t j;
+		boolean_t found_nul;
+		boolean_t do_retry = B_TRUE;
+
+#ifdef	_SYSCALL32_IMPL
+		if (p->p_model == DATAMODEL_ILP32) {
+			arg = (uintptr_t)((caddr32_t *)argv)[i];
+		} else {
+			arg = (uintptr_t)argv[i];
+		}
+#else
+		arg = (uintptr_t)argv[i];
+#endif
+
+		/*
+		 * Stop trying to read arguments if we reach a NULL
+		 * pointer in the vector.
+		 */
+		if (arg == NULL)
+			break;
+
+		/*
+		 * Stop reading if we have read the maximum length
+		 * we can return to the user.
+		 */
+		if (pos >= bufsz)
+			break;
+
+		/*
+		 * Initially we try a short read, on the assumption that
+		 * most individual argument strings are less than 80
+		 * characters long.
+		 */
+		if ((trysz = MIN(80, bufsz - pos - 1)) < 80) {
+			/*
+			 * We don't have room in the target buffer for even
+			 * an entire short read, so there is no need to retry
+			 * with a longer read.
+			 */
+			do_retry = B_FALSE;
+		}
+
+retry:
+		/*
+		 * Read string data for this argument.  Leave room
+		 * in the buffer for a final NUL terminator.
+		 */
+		if ((error = prreadbuf(p, arg, (uint8_t *)&buf[pos], trysz,
+		    &rdsz)) != 0) {
+			/*
+			 * There was a problem reading this string
+			 * from the process.  Give up.
+			 */
+			break;
+		}
+
+		/*
+		 * Find the NUL terminator.
+		 */
+		found_nul = B_FALSE;
+		for (j = 0; j < rdsz; j++) {
+			if (buf[pos + j] == '\0') {
+				found_nul = B_TRUE;
+				break;
+			}
+		}
+
+		if (!found_nul && do_retry) {
+			/*
+			 * We did not find a NUL terminator, but this
+			 * was a first pass short read.  Try once more
+			 * with feeling.
+			 */
+			trysz = bufsz - pos - 1;
+			do_retry = B_FALSE;
+			goto retry;
+		}
+
+		/*
+		 * Commit the string we read to the buffer.
+		 */
+		pos += j + 1;
+		if (!found_nul && pos < bufsz) {
+			/*
+			 * A NUL terminator was not found; add one.
+			 */
+			buf[pos++] = '\0';
+		}
+	}
+
+	/*
+	 * Ensure the entire string is NUL-terminated.
+	 */
+	buf[bufsz - 1] = '\0';
+
+	mutex_enter(&p->p_lock);
+	VERIFY(p->p_proc_flag & P_PR_LOCK);
+	kmem_free(argv, argvsz);
+
+	/*
+	 * If the operation was a success, return the copied string length
+	 * to the caller.
+	 */
+	*slen = (error == 0) ? pos : 0;
+
+	return (error);
+}
+
+/*
+ * Similar to prreadargv except reads the env vector. This is slightly more
+ * complex because there is no count for the env vector that corresponds to
+ * u_argc.
+ */
+int
+prreadenvv(proc_t *p, char *buf, size_t bufsz, size_t *slen)
+{
+	int error;
+	user_t *up;
+	struct as *as;
+	size_t pos = 0;
+	caddr_t *envp = NULL;
+	uintptr_t tmpp = NULL;
+	size_t envpsz = 0, rdsz = 0;
+	int i;
+	int cnt, bound;
+
+	VERIFY(MUTEX_HELD(&p->p_lock));
+	VERIFY(p->p_proc_flag & P_PR_LOCK);
+
+	up = PTOU(p);
+	as = p->p_as;
+
+	if ((p->p_flag & SSYS) || as == &kas || up->u_envp == NULL) {
+		/*
+		 * Return empty string.
+		 */
+		buf[0] = '\0';
+		*slen = 1;
+
+		return (0);
+	}
+
+	/*
+	 * Drop p_lock while we do I/O to avoid deadlock with the clock thread.
+	 */
+	mutex_exit(&p->p_lock);
+
+	/*
+	 * We first have to count how many env entries we have. This is
+	 * somewhat painful. We extract the env entries from the target process
+	 * one entry at a time. Stop trying to read env entries if we reach a
+	 * NULL pointer in the vector or hit our upper bound (which we take
+	 * as the bufsz/4) to ensure we don't run off.
+	 */
+	rdsz = (p->p_model == DATAMODEL_ILP32 ?
+	    sizeof (caddr32_t) : sizeof (caddr_t));
+	bound = (int)(bufsz / 4);
+	for (cnt = 0, tmpp = up->u_envp; cnt < bound; cnt++, tmpp += rdsz) {
+		caddr_t tmp = NULL;
+
+		if ((error = prreadbuf(p, tmpp, (uint8_t *)&tmp, rdsz,
+		    NULL)) != 0) {
+			mutex_enter(&p->p_lock);
+			VERIFY(p->p_proc_flag & P_PR_LOCK);
+			return (-1);
+		}
+
+		if (tmp == NULL)
+			break;
+	}
+	if (cnt == 0) {
+		/* Return empty string. */
+		buf[0] = '\0';
+		*slen = 1;
+		mutex_enter(&p->p_lock);
+		VERIFY(p->p_proc_flag & P_PR_LOCK);
+		return (0);
+	}
+
+	/*
+	 * Allocate space to store env array.
+	 */
+	envpsz = cnt * (p->p_model == DATAMODEL_ILP32 ?
+	    sizeof (caddr32_t) : sizeof (caddr_t));
+	envp = kmem_alloc(envpsz, KM_SLEEP);
+
+	/*
+	 * Extract the env array from the target process.
+	 */
+	if ((error = prreadbuf(p, up->u_envp, (uint8_t *)envp, envpsz,
+	    NULL)) != 0) {
+		kmem_free(envp, envpsz);
+		mutex_enter(&p->p_lock);
+		VERIFY(p->p_proc_flag & P_PR_LOCK);
+		return (-1);
+	}
+
+	/*
+	 * Read each env string from the pointers in the env array.
+	 */
+	pos = 0;
+	for (i = 0; i < cnt; i++) {
+		size_t rdsz, trysz;
+		uintptr_t ev;
+		off_t j;
+		boolean_t found_nul;
+		boolean_t do_retry = B_TRUE;
+
+#ifdef	_SYSCALL32_IMPL
+		if (p->p_model == DATAMODEL_ILP32) {
+			ev = (uintptr_t)((caddr32_t *)envp)[i];
+		} else {
+			ev = (uintptr_t)envp[i];
+		}
+#else
+		ev = (uintptr_t)envp[i];
+#endif
+
+		/*
+		 * Stop trying to read env entries if we reach a NULL
+		 * pointer in the vector.
+		 */
+		if (ev == NULL)
+			break;
+
+		/*
+		 * Stop reading if we have read the maximum length
+		 * we can return to the user.
+		 */
+		if (pos >= bufsz)
+			break;
+
+		/*
+		 * Initially we try a short read, on the assumption that
+		 * most individual env strings are less than 80
+		 * characters long.
+		 */
+		if ((trysz = MIN(80, bufsz - pos - 1)) < 80) {
+			/*
+			 * We don't have room in the target buffer for even
+			 * an entire short read, so there is no need to retry
+			 * with a longer read.
+			 */
+			do_retry = B_FALSE;
+		}
+
+retry:
+		/*
+		 * Read string data for this env var.  Leave room
+		 * in the buffer for a final NUL terminator.
+		 */
+		if ((error = prreadbuf(p, ev, (uint8_t *)&buf[pos], trysz,
+		    &rdsz)) != 0) {
+			/*
+			 * There was a problem reading this string
+			 * from the process.  Give up.
+			 */
+			break;
+		}
+
+		/*
+		 * Find the NUL terminator.
+		 */
+		found_nul = B_FALSE;
+		for (j = 0; j < rdsz; j++) {
+			if (buf[pos + j] == '\0') {
+				found_nul = B_TRUE;
+				break;
+			}
+		}
+
+		if (!found_nul && do_retry) {
+			/*
+			 * We did not find a NUL terminator, but this
+			 * was a first pass short read.  Try once more
+			 * with feeling.
+			 */
+			trysz = bufsz - pos - 1;
+			do_retry = B_FALSE;
+			goto retry;
+		}
+
+		/*
+		 * Commit the string we read to the buffer.
+		 */
+		pos += j + 1;
+		if (!found_nul && pos < bufsz) {
+			/*
+			 * A NUL terminator was not found; add one.
+			 */
+			buf[pos++] = '\0';
+		}
+	}
+
+	/*
+	 * Ensure the entire string is NUL-terminated.
+	 */
+	buf[bufsz - 1] = '\0';
+
+	mutex_enter(&p->p_lock);
+	VERIFY(p->p_proc_flag & P_PR_LOCK);
+	kmem_free(envp, envpsz);
+
+	/*
+	 * If the operation was a success, return the copied string length
+	 * to the caller.
+	 */
+	*slen = (error == 0) ? pos : 0;
+
+	return (error);
+}
--- old/usr/src/uts/common/fs/proc/prcontrol.c	Wed Oct  5 00:32:16 2016
+++ new/usr/src/uts/common/fs/proc/prcontrol.c	Wed Oct  5 00:32:16 2016
@@ -25,7 +25,7 @@
  */
 
 /*
- * Copyright (c) 2013, Joyent, Inc.  All rights reserved.
+ * Copyright 2015, Joyent, Inc.
  */
 
 #include <sys/types.h>
@@ -1481,7 +1481,7 @@
 		} else if (t->t_state == TS_STOPPED && sig == SIGKILL) {
 			/* If SIGKILL, set stopped lwp running */
 			p->p_stopsig = 0;
-			t->t_schedflag |= TS_XSTART | TS_PSTART;
+			t->t_schedflag |= TS_XSTART | TS_PSTART | TS_BSTART;
 			t->t_dtrace_stop = 0;
 			setrun_locked(t);
 		}
--- old/usr/src/uts/common/fs/proc/prdata.h	Wed Oct  5 00:32:16 2016
+++ new/usr/src/uts/common/fs/proc/prdata.h	Wed Oct  5 00:32:16 2016
@@ -27,7 +27,7 @@
 /*	  All Rights Reserved  	*/
 
 /*
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
  */
 
 #ifndef _SYS_PROC_PRDATA_H
@@ -123,6 +123,7 @@
 #if defined(__i386) || defined(__amd64)
 	PR_LDT,			/* /proc/<pid>/ldt			*/
 #endif
+	PR_ARGV,		/* /proc/<pid>/argv			*/
 	PR_USAGE,		/* /proc/<pid>/usage			*/
 	PR_LUSAGE,		/* /proc/<pid>/lusage			*/
 	PR_PAGEDATA,		/* /proc/<pid>/pagedata			*/
@@ -347,6 +348,8 @@
 extern	void	pr_sethold(prnode_t *, sigset_t *);
 extern	void	pr_setfault(proc_t *, fltset_t *);
 extern	int	prusrio(proc_t *, enum uio_rw, struct uio *, int);
+extern	int	prreadargv(proc_t *, char *, size_t, size_t *);
+extern	int	prreadenvv(proc_t *, char *, size_t, size_t *);
 extern	int	prwritectl(vnode_t *, struct uio *, cred_t *);
 extern	int	prlock(prnode_t *, int);
 extern	void	prunmark(proc_t *);
--- old/usr/src/uts/common/fs/proc/prsubr.c	Wed Oct  5 00:32:16 2016
+++ new/usr/src/uts/common/fs/proc/prsubr.c	Wed Oct  5 00:32:16 2016
@@ -21,7 +21,7 @@
 
 /*
  * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2016, Joyent, Inc.
  */
 
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
@@ -201,6 +201,7 @@
 			case PR_SYSEXIT:
 			case PR_SIGNALLED:
 			case PR_FAULTED:
+			case PR_BRAND:
 				/*
 				 * Make an lwp calling exit() be the
 				 * last lwp seen in the process.
@@ -534,6 +535,12 @@
 			pcp->prc_tslot = tslot;
 		}
 	}
+
+	/*
+	 * There may be threads waiting for the flag change blocked behind the
+	 * pr_pid_cv as well.
+	 */
+	cv_signal(&pr_pid_cv[p->p_slot]);
 }
 
 /*
--- old/usr/src/uts/common/fs/proc/prvnops.c	Wed Oct  5 00:32:16 2016
+++ new/usr/src/uts/common/fs/proc/prvnops.c	Wed Oct  5 00:32:16 2016
@@ -21,7 +21,7 @@
 
 /*
  * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
  */
 
 /*	Copyright (c) 1984,	 1986, 1987, 1988, 1989 AT&T	*/
@@ -96,6 +96,11 @@
 #define	PRSDSIZE	(sizeof (struct prdirect))
 
 /*
+ * Maximum length of the /proc/$$/argv file:
+ */
+int prmaxargvlen = 4096;
+
+/*
  * Directory characteristics.
  */
 typedef struct prdirent {
@@ -166,6 +171,8 @@
 	{ PR_LDT,	27 * sizeof (prdirent_t), sizeof (prdirent_t),
 		"ldt" },
 #endif
+	{ PR_ARGV,	28 * sizeof (prdirent_t), sizeof (prdirent_t),
+		"argv" },
 };
 
 #define	NPIDDIRFILES	(sizeof (piddir) / sizeof (piddir[0]) - 2)
@@ -582,6 +589,7 @@
 #if defined(__x86)
 	pr_read_ldt(),
 #endif
+	pr_read_argv(),
 	pr_read_usage(), pr_read_lusage(), pr_read_pagedata(),
 	pr_read_watch(), pr_read_lwpstatus(), pr_read_lwpsinfo(),
 	pr_read_lwpusage(), pr_read_xregs(), pr_read_priv(),
@@ -610,6 +618,7 @@
 #if defined(__x86)
 	pr_read_ldt,		/* /proc/<pid>/ldt			*/
 #endif
+	pr_read_argv,		/* /proc/<pid>/argv			*/
 	pr_read_usage,		/* /proc/<pid>/usage			*/
 	pr_read_lusage,		/* /proc/<pid>/lusage			*/
 	pr_read_pagedata,	/* /proc/<pid>/pagedata			*/
@@ -672,6 +681,41 @@
 }
 
 static int
+pr_read_argv(prnode_t *pnp, uio_t *uiop)
+{
+	char *args;
+	int error;
+	size_t asz = prmaxargvlen, sz;
+
+	/*
+	 * Allocate a scratch buffer for collection of the process arguments.
+	 */
+	args = kmem_alloc(asz, KM_SLEEP);
+
+	ASSERT(pnp->pr_type == PR_ARGV);
+
+	if ((error = prlock(pnp, ZNO)) != 0) {
+		kmem_free(args, asz);
+		return (error);
+	}
+
+	if ((error = prreadargv(pnp->pr_common->prc_proc, args, asz,
+	    &sz)) != 0) {
+		prunlock(pnp);
+		kmem_free(args, asz);
+		return (error);
+	}
+
+	prunlock(pnp);
+
+	error = pr_uioread(args, sz, uiop);
+
+	kmem_free(args, asz);
+
+	return (error);
+}
+
+static int
 pr_read_as(prnode_t *pnp, uio_t *uiop)
 {
 	int error;
@@ -1767,6 +1811,7 @@
 #if defined(__x86)
 	pr_read_ldt,		/* /proc/<pid>/ldt			*/
 #endif
+	pr_read_argv,		/* /proc/<pid>/argv			*/
 	pr_read_usage_32,	/* /proc/<pid>/usage			*/
 	pr_read_lusage_32,	/* /proc/<pid>/lusage			*/
 	pr_read_pagedata_32,	/* /proc/<pid>/pagedata			*/
@@ -2686,6 +2731,103 @@
 #endif
 }
 
+/*
+ * We make pr_write_psinfo_fname() somewhat simpler by asserting at compile
+ * time that PRFNSZ has the same definition as MAXCOMLEN.
+ */
+#if PRFNSZ != MAXCOMLEN
+#error PRFNSZ/MAXCOMLEN mismatch
+#endif
+
+static int
+pr_write_psinfo_fname(prnode_t *pnp, uio_t *uiop)
+{
+	char fname[PRFNSZ];
+	int offset = offsetof(psinfo_t, pr_fname), error;
+
+#ifdef _SYSCALL32_IMPL
+	if (curproc->p_model != DATAMODEL_LP64)
+		offset = offsetof(psinfo32_t, pr_fname);
+#endif
+
+	/*
+	 * If this isn't a write to pr_fname (or if the size doesn't match
+	 * PRFNSZ) return.
+	 */
+	if (uiop->uio_offset != offset || uiop->uio_resid != PRFNSZ)
+		return (0);
+
+	if ((error = uiomove(fname, PRFNSZ, UIO_WRITE, uiop)) != 0)
+		return (error);
+
+	fname[PRFNSZ - 1] = '\0';
+
+	if ((error = prlock(pnp, ZNO)) != 0)
+		return (error);
+
+	bcopy(fname, pnp->pr_common->prc_proc->p_user.u_comm, PRFNSZ);
+
+	prunlock(pnp);
+
+	return (0);
+}
+
+/*
+ * We make pr_write_psinfo_psargs() somewhat simpler by asserting at compile
+ * time that PRARGSZ has the same definition as PSARGSZ.
+ */
+#if PRARGSZ != PSARGSZ
+#error PRARGSZ/PSARGSZ mismatch
+#endif
+
+static int
+pr_write_psinfo_psargs(prnode_t *pnp, uio_t *uiop)
+{
+	char psargs[PRARGSZ];
+	int offset = offsetof(psinfo_t, pr_psargs), error;
+
+#ifdef _SYSCALL32_IMPL
+	if (curproc->p_model != DATAMODEL_LP64)
+		offset = offsetof(psinfo32_t, pr_psargs);
+#endif
+
+	/*
+	 * If this isn't a write to pr_psargs (or if the size doesn't match
+	 * PRARGSZ) return.
+	 */
+	if (uiop->uio_offset != offset || uiop->uio_resid != PRARGSZ)
+		return (0);
+
+	if ((error = uiomove(psargs, PRARGSZ, UIO_WRITE, uiop)) != 0)
+		return (error);
+
+	psargs[PRARGSZ - 1] = '\0';
+
+	if ((error = prlock(pnp, ZNO)) != 0)
+		return (error);
+
+	bcopy(psargs, pnp->pr_common->prc_proc->p_user.u_psargs, PRARGSZ);
+
+	prunlock(pnp);
+
+	return (0);
+}
+
+int
+pr_write_psinfo(prnode_t *pnp, uio_t *uiop)
+{
+	int error;
+
+	if ((error = pr_write_psinfo_fname(pnp, uiop)) != 0)
+		return (error);
+
+	if ((error = pr_write_psinfo_psargs(pnp, uiop)) != 0)
+		return (error);
+
+	return (0);
+}
+
+
 /* ARGSUSED */
 static int
 prwrite(vnode_t *vp, uio_t *uiop, int ioflag, cred_t *cr, caller_context_t *ct)
@@ -2764,6 +2906,9 @@
 			uiop->uio_resid = resid;
 		return (error);
 
+	case PR_PSINFO:
+		return (pr_write_psinfo(pnp, uiop));
+
 	default:
 		return ((vp->v_type == VDIR)? EISDIR : EBADF);
 	}
@@ -3047,6 +3192,13 @@
 	case PR_AUXV:
 		vap->va_size = __KERN_NAUXV_IMPL * PR_OBJSIZE(auxv32_t, auxv_t);
 		break;
+	case PR_ARGV:
+		if ((p->p_flag & SSYS) || p->p_as == &kas) {
+			vap->va_size = PSARGSZ;
+		} else {
+			vap->va_size = prmaxargvlen;
+		}
+		break;
 #if defined(__x86)
 	case PR_LDT:
 		mutex_exit(&p->p_lock);
@@ -3222,6 +3374,7 @@
 	case PR_USAGE:
 	case PR_LUSAGE:
 	case PR_LWPUSAGE:
+	case PR_ARGV:
 		p = pr_p_lock(pnp);
 		mutex_exit(&pr_pidlock);
 		if (p == NULL)
@@ -3307,6 +3460,7 @@
 #if defined(__x86)
 	pr_lookup_notdir,	/* /proc/<pid>/ldt			*/
 #endif
+	pr_lookup_notdir,	/* /proc/<pid>/argv			*/
 	pr_lookup_notdir,	/* /proc/<pid>/usage			*/
 	pr_lookup_notdir,	/* /proc/<pid>/lusage			*/
 	pr_lookup_notdir,	/* /proc/<pid>/pagedata			*/
@@ -4546,11 +4700,15 @@
 		break;
 
 	case PR_PSINFO:
+		pnp->pr_mode = 0644;	/* readable by all + owner can write */
+		break;
+
 	case PR_LPSINFO:
 	case PR_LWPSINFO:
 	case PR_USAGE:
 	case PR_LUSAGE:
 	case PR_LWPUSAGE:
+	case PR_ARGV:
 		pnp->pr_mode = 0444;	/* read-only by all */
 		break;
 
@@ -4656,6 +4814,7 @@
 #if defined(__x86)
 	pr_readdir_notdir,	/* /proc/<pid>/ldt			*/
 #endif
+	pr_readdir_notdir,	/* /proc/<pid>/argv			*/
 	pr_readdir_notdir,	/* /proc/<pid>/usage			*/
 	pr_readdir_notdir,	/* /proc/<pid>/lusage			*/
 	pr_readdir_notdir,	/* /proc/<pid>/pagedata			*/
@@ -4805,6 +4964,7 @@
 			case PR_PROCDIR:
 			case PR_PSINFO:
 			case PR_USAGE:
+			case PR_ARGV:
 				break;
 			default:
 				continue;
--- old/usr/src/uts/common/fs/sockfs/sockcommon_sops.c	Wed Oct  5 00:32:17 2016
+++ new/usr/src/uts/common/fs/sockfs/sockcommon_sops.c	Wed Oct  5 00:32:17 2016
@@ -128,7 +128,7 @@
 {
 	int error;
 
-	SO_BLOCK_FALLBACK(so, SOP_BIND(so, name, namelen, flags, cr));
+	SO_BLOCK_FALLBACK_SAFE(so, SOP_BIND(so, name, namelen, flags, cr));
 
 	ASSERT(flags == _SOBIND_XPG4_2 || flags == _SOBIND_SOCKBSD);
 
@@ -305,7 +305,7 @@
 	 * This can happen if a non blocking operation caused an error.
 	 */
 
-	if (so->so_error != 0) {
+	if (so->so_error != 0 && (so->so_mode & SM_DEFERERR) == 0) {
 		mutex_enter(&so->so_lock);
 		error = sogeterr(so, B_TRUE);
 		mutex_exit(&so->so_lock);
@@ -404,7 +404,7 @@
 			break;
 		}
 
-		if (so->so_error != 0) {
+		if (so->so_error != 0 && (so->so_mode & SM_DEFERERR) == 0) {
 			mutex_enter(&so->so_lock);
 			error = sogeterr(so, B_TRUE);
 			mutex_exit(&so->so_lock);
@@ -513,7 +513,7 @@
 			error = EPIPE;
 			break;
 		}
-		if (so->so_error != 0) {
+		if (so->so_error != 0 && (so->so_mode & SM_DEFERERR) == 0) {
 			mutex_enter(&so->so_lock);
 			error = sogeterr(so, B_TRUE);
 			mutex_exit(&so->so_lock);
@@ -586,11 +586,6 @@
 
 	SO_BLOCK_FALLBACK(so, SOP_SENDMBLK(so, msg, fflag, cr, mpp));
 
-	if ((so->so_mode & SM_SENDFILESUPP) == 0) {
-		SO_UNBLOCK_FALLBACK(so);
-		return (EOPNOTSUPP);
-	}
-
 	error = so_sendmblk_impl(so, msg, fflag, cr, mpp, so->so_filter_top,
 	    B_FALSE);
 
@@ -653,7 +648,7 @@
 {
 	int error;
 
-	SO_BLOCK_FALLBACK(so, SOP_GETSOCKNAME(so, addr, addrlen, cr));
+	SO_BLOCK_FALLBACK_SAFE(so, SOP_GETSOCKNAME(so, addr, addrlen, cr));
 
 	if (so->so_filter_active == 0 ||
 	    (error = sof_filter_getsockname(so, addr, addrlen, cr)) < 0)
@@ -702,7 +697,7 @@
 	if (level == SOL_FILTER)
 		return (sof_getsockopt(so, option_name, optval, optlenp, cr));
 
-	SO_BLOCK_FALLBACK(so,
+	SO_BLOCK_FALLBACK_SAFE(so,
 	    SOP_GETSOCKOPT(so, level, option_name, optval, optlenp, flags, cr));
 
 	if ((so->so_filter_active == 0 ||
@@ -791,7 +786,7 @@
 	if (level == SOL_FILTER)
 		return (sof_setsockopt(so, option_name, optval, optlen, cr));
 
-	SO_BLOCK_FALLBACK(so,
+	SO_BLOCK_FALLBACK_SAFE(so,
 	    SOP_SETSOCKOPT(so, level, option_name, optval, optlen, cr));
 
 	/* X/Open requires this check */
@@ -876,7 +871,7 @@
 	 * If there is a pending error, return error
 	 * This can happen if a non blocking operation caused an error.
 	 */
-	if (so->so_error != 0) {
+	if (so->so_error != 0 && (so->so_mode & SM_DEFERERR) == 0) {
 		mutex_enter(&so->so_lock);
 		error = sogeterr(so, B_TRUE);
 		mutex_exit(&so->so_lock);
--- old/usr/src/uts/common/fs/sockfs/sockcommon_subr.c	Wed Oct  5 00:32:17 2016
+++ new/usr/src/uts/common/fs/sockfs/sockcommon_subr.c	Wed Oct  5 00:32:17 2016
@@ -670,10 +670,15 @@
 	int more = 0;
 	int error;
 	ssize_t oobmark;
+	ssize_t copied = 0;
 	sodirect_t *sodp = so->so_direct;
+	xuio_t *xuio = NULL;
 
 	partial_read = B_FALSE;
 	*mctlp = NULL;
+	if ((uiop->uio_extflg & UIO_XUIO) != 0) {
+		xuio = (xuio_t *)uiop;
+	}
 again:
 	mutex_enter(&so->so_lock);
 again1:
@@ -784,8 +789,6 @@
 		 * enabled socket, uio_resid can be 0.
 		 */
 		if (uiop->uio_resid >= 0) {
-			ssize_t copied = 0;
-
 			if (sodp != NULL && (DB_FLAGS(mp) & DBLK_UIOA)) {
 				mutex_enter(&so->so_lock);
 				ASSERT(uiop == (uio_t *)&sodp->sod_uioa);
@@ -843,6 +846,18 @@
 		}
 		if (mp != NULL) { /* more data blocks in msg */
 			more |= MOREDATA;
+
+			/*
+			 * If requested, tally up remaining data along with the
+			 * amount already copied.
+			 */
+			if (xuio != NULL &&
+			    xuio->xu_type == UIOTYPE_PEEKSIZE) {
+				xuio->xu_ext.xu_ps.xu_ps_set = B_TRUE;
+				xuio->xu_ext.xu_ps.xu_ps_size =
+				    copied + msgdsize(mp);
+			}
+
 			if ((flags & (MSG_PEEK|MSG_TRUNC))) {
 				if (flags & MSG_PEEK) {
 					freemsg(mp);
--- old/usr/src/uts/common/fs/sockfs/sockfilter.c	Wed Oct  5 00:32:17 2016
+++ new/usr/src/uts/common/fs/sockfs/sockfilter.c	Wed Oct  5 00:32:17 2016
@@ -20,6 +20,7 @@
  */
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
  */
 
 #include <sys/systm.h>
@@ -246,6 +247,18 @@
 
 		/* Module loaded OK, so there must be an ops vector */
 		ASSERT(ent->sofe_mod != NULL);
+
+		/*
+		 * Check again to confirm ATTACH is ok. See if the the module
+		 * is not SOF_ATT_SAFE after an unsafe operation has taken
+		 * place.
+		 */
+		if ((ent->sofe_mod->sofm_flags & SOF_ATT_SAFE) == 0 &&
+		    so->so_state & SS_FILOP_UNSF) {
+			sof_instance_destroy(inst);
+			return (EINVAL);
+		}
+
 		inst->sofi_ops = &ent->sofe_mod->sofm_ops;
 
 		SOF_STAT_ADD(inst, tot_active_attach, 1);
@@ -1444,7 +1457,13 @@
  * sof_register(version, name, ops, flags)
  *
  * Register a socket filter identified by name `name' and which should use
- * the ops vector `ops' for event notification. `flags' should be set to 0.
+ * the ops vector `ops' for event notification. `flags' should be set to 0
+ * by default for "unsafe" modules or SOF_ATT_SAFE for "safe" modules. An
+ * unsafe filter is one that cannot be attached after any socket operation has
+ * occured. This is the legacy default. A "safe" filter can be attached even
+ * after some basic initial socket operations have taken place. This set is
+ * currently bind, getsockname, getsockopt and setsockopt. The order in which
+ * a "safe" filter can be attached is more relaxed, and thus more flexible.
  * On success 0 is returned, otherwise an errno is returned.
  */
 int
@@ -1452,8 +1471,6 @@
 {
 	sof_module_t *mod;
 
-	_NOTE(ARGUNUSED(flags));
-
 	if (version != SOF_VERSION)
 		return (EINVAL);
 
@@ -1460,6 +1477,7 @@
 	mod = kmem_zalloc(sizeof (sof_module_t), KM_SLEEP);
 	mod->sofm_name = kmem_alloc(strlen(name) + 1, KM_SLEEP);
 	(void) strcpy(mod->sofm_name, name);
+	mod->sofm_flags = flags;
 	mod->sofm_ops = *ops;
 
 	mutex_enter(&sof_module_lock);
--- old/usr/src/uts/common/fs/sockfs/sockfilter_impl.h	Wed Oct  5 00:32:17 2016
+++ new/usr/src/uts/common/fs/sockfs/sockfilter_impl.h	Wed Oct  5 00:32:17 2016
@@ -20,6 +20,7 @@
  */
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
  */
 
 #ifndef	_SOCKFS_SOCKFILTER_H
@@ -51,6 +52,7 @@
 
 struct sof_module {
 	char		*sofm_name;
+	int		sofm_flags;
 	sof_ops_t	sofm_ops;
 	uint_t		sofm_refcnt;
 	list_node_t	sofm_node;
--- old/usr/src/uts/common/fs/sockfs/socksubr.c	Wed Oct  5 00:32:17 2016
+++ new/usr/src/uts/common/fs/sockfs/socksubr.c	Wed Oct  5 00:32:17 2016
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015, Joyent, Inc. All rights reserved.
  */
 
 #include <sys/types.h>
@@ -436,10 +437,12 @@
  *
  * The underlying filesystem VSOCK vnode has a v_stream pointer that
  * references the actual stream head (hence indirectly the actual sonode).
+ *
+ * This function is non-static so it can be used by brand emulation.
  */
-static int
+int
 so_ux_lookup(struct sonode *so, struct sockaddr_un *soun, int checkaccess,
-		vnode_t **vpp)
+    vnode_t **vpp)
 {
 	vnode_t		*vp;	/* Underlying filesystem vnode */
 	vnode_t		*rvp;	/* real vnode */
@@ -1879,7 +1882,7 @@
 soreadfile(file_t *fp, uchar_t *buf, u_offset_t fileoff, int *err, size_t size)
 {
 	struct uio auio;
-	struct iovec aiov[MSG_MAXIOVLEN];
+	struct iovec aiov[1];
 	register vnode_t *vp;
 	int ioflag, rwflag;
 	ssize_t cnt;
--- old/usr/src/uts/common/fs/sockfs/socksyscalls.c	Wed Oct  5 00:32:17 2016
+++ new/usr/src/uts/common/fs/sockfs/socksyscalls.c	Wed Oct  5 00:32:17 2016
@@ -21,6 +21,8 @@
 
 /*
  * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, OmniTI Computer Consulting, Inc. All rights reserved.
+ * Copyright 2015, Joyent, Inc.  All rights reserved.
  */
 
 /* Copyright (c) 2013, OmniTI Computer Consulting, Inc. All rights reserved. */
@@ -54,6 +56,7 @@
 #include <sys/cmn_err.h>
 #include <sys/vmsystm.h>
 #include <sys/policy.h>
+#include <sys/limits.h>
 
 #include <sys/socket.h>
 #include <sys/socketvar.h>
@@ -86,12 +89,6 @@
 extern int	sockfs_defer_nl7c_init;
 
 /*
- * Note: DEF_IOV_MAX is defined and used as it is in "fs/vncalls.c"
- *	 as there isn't a formal definition of IOV_MAX ???
- */
-#define	MSG_MAXIOVLEN	16
-
-/*
  * Kernel component of socket creation.
  *
  * The socket library determines which version number to use.
@@ -1026,9 +1023,10 @@
 	STRUCT_HANDLE(nmsghdr, umsgptr);
 	struct nmsghdr lmsg;
 	struct uio auio;
-	struct iovec aiov[MSG_MAXIOVLEN];
+	struct iovec buf[IOV_MAX_STACK], *aiov = buf;
+	ssize_t iovsize = 0;
 	int iovcnt;
-	ssize_t len;
+	ssize_t len, rval;
 	int i;
 	int *flagsp;
 	model_t	model;
@@ -1071,10 +1069,15 @@
 
 	iovcnt = lmsg.msg_iovlen;
 
-	if (iovcnt <= 0 || iovcnt > MSG_MAXIOVLEN) {
+	if (iovcnt <= 0 || iovcnt > IOV_MAX) {
 		return (set_errno(EMSGSIZE));
 	}
 
+	if (iovcnt > IOV_MAX_STACK) {
+		iovsize = iovcnt * sizeof (struct iovec);
+		aiov = kmem_alloc(iovsize, KM_SLEEP);
+	}
+
 #ifdef _SYSCALL32_IMPL
 	/*
 	 * 32-bit callers need to have their iovec expanded, while ensuring
@@ -1081,12 +1084,22 @@
 	 * that they can't move more than 2Gbytes of data in a single call.
 	 */
 	if (model == DATAMODEL_ILP32) {
-		struct iovec32 aiov32[MSG_MAXIOVLEN];
+		struct iovec32 buf32[IOV_MAX_STACK], *aiov32 = buf32;
+		ssize_t iov32size;
 		ssize32_t count32;
 
-		if (copyin((struct iovec32 *)lmsg.msg_iov, aiov32,
-		    iovcnt * sizeof (struct iovec32)))
+		iov32size = iovcnt * sizeof (struct iovec32);
+		if (iovsize != 0)
+			aiov32 = kmem_alloc(iov32size, KM_SLEEP);
+
+		if (copyin((struct iovec32 *)lmsg.msg_iov, aiov32, iov32size)) {
+			if (iovsize != 0) {
+				kmem_free(aiov32, iov32size);
+				kmem_free(aiov, iovsize);
+			}
+
 			return (set_errno(EFAULT));
+		}
 
 		count32 = 0;
 		for (i = 0; i < iovcnt; i++) {
@@ -1094,15 +1107,28 @@
 
 			iovlen32 = aiov32[i].iov_len;
 			count32 += iovlen32;
-			if (iovlen32 < 0 || count32 < 0)
+			if (iovlen32 < 0 || count32 < 0) {
+				if (iovsize != 0) {
+					kmem_free(aiov32, iov32size);
+					kmem_free(aiov, iovsize);
+				}
+
 				return (set_errno(EINVAL));
+			}
+
 			aiov[i].iov_len = iovlen32;
 			aiov[i].iov_base =
 			    (caddr_t)(uintptr_t)aiov32[i].iov_base;
 		}
+
+		if (iovsize != 0)
+			kmem_free(aiov32, iov32size);
 	} else
 #endif /* _SYSCALL32_IMPL */
 	if (copyin(lmsg.msg_iov, aiov, iovcnt * sizeof (struct iovec))) {
+		if (iovsize != 0)
+			kmem_free(aiov, iovsize);
+
 		return (set_errno(EFAULT));
 	}
 	len = 0;
@@ -1110,6 +1136,9 @@
 		ssize_t iovlen = aiov[i].iov_len;
 		len += iovlen;
 		if (iovlen < 0 || len < 0) {
+			if (iovsize != 0)
+				kmem_free(aiov, iovsize);
+
 			return (set_errno(EINVAL));
 		}
 	}
@@ -1124,12 +1153,20 @@
 	    (do_useracc == 0 ||
 	    useracc(lmsg.msg_control, lmsg.msg_controllen,
 	    B_WRITE) != 0)) {
+		if (iovsize != 0)
+			kmem_free(aiov, iovsize);
+
 		return (set_errno(EFAULT));
 	}
 
-	return (recvit(sock, &lmsg, &auio, flags,
+	rval = recvit(sock, &lmsg, &auio, flags,
 	    STRUCT_FADDR(umsgptr, msg_namelen),
-	    STRUCT_FADDR(umsgptr, msg_controllen), flagsp));
+	    STRUCT_FADDR(umsgptr, msg_controllen), flagsp);
+
+	if (iovsize != 0)
+		kmem_free(aiov, iovsize);
+
+	return (rval);
 }
 
 /*
@@ -1267,9 +1304,10 @@
 	struct nmsghdr lmsg;
 	STRUCT_DECL(nmsghdr, u_lmsg);
 	struct uio auio;
-	struct iovec aiov[MSG_MAXIOVLEN];
+	struct iovec buf[IOV_MAX_STACK], *aiov = buf;
+	ssize_t iovsize = 0;
 	int iovcnt;
-	ssize_t len;
+	ssize_t len, rval;
 	int i;
 	model_t	model;
 
@@ -1312,7 +1350,7 @@
 
 	iovcnt = lmsg.msg_iovlen;
 
-	if (iovcnt <= 0 || iovcnt > MSG_MAXIOVLEN) {
+	if (iovcnt <= 0 || iovcnt > IOV_MAX) {
 		/*
 		 * Unless this is XPG 4.2 we allow iovcnt == 0 to
 		 * be compatible with SunOS 4.X and 4.4BSD.
@@ -1321,6 +1359,11 @@
 			return (set_errno(EMSGSIZE));
 	}
 
+	if (iovcnt > IOV_MAX_STACK) {
+		iovsize = iovcnt * sizeof (struct iovec);
+		aiov = kmem_alloc(iovsize, KM_SLEEP);
+	}
+
 #ifdef _SYSCALL32_IMPL
 	/*
 	 * 32-bit callers need to have their iovec expanded, while ensuring
@@ -1327,13 +1370,23 @@
 	 * that they can't move more than 2Gbytes of data in a single call.
 	 */
 	if (model == DATAMODEL_ILP32) {
-		struct iovec32 aiov32[MSG_MAXIOVLEN];
+		struct iovec32 buf32[IOV_MAX_STACK], *aiov32 = buf32;
+		ssize_t iov32size;
 		ssize32_t count32;
 
+		iov32size = iovcnt * sizeof (struct iovec32);
+		if (iovsize != 0)
+			aiov32 = kmem_alloc(iov32size, KM_SLEEP);
+
 		if (iovcnt != 0 &&
-		    copyin((struct iovec32 *)lmsg.msg_iov, aiov32,
-		    iovcnt * sizeof (struct iovec32)))
+		    copyin((struct iovec32 *)lmsg.msg_iov, aiov32, iov32size)) {
+			if (iovsize != 0) {
+				kmem_free(aiov32, iov32size);
+				kmem_free(aiov, iovsize);
+			}
+
 			return (set_errno(EFAULT));
+		}
 
 		count32 = 0;
 		for (i = 0; i < iovcnt; i++) {
@@ -1341,17 +1394,30 @@
 
 			iovlen32 = aiov32[i].iov_len;
 			count32 += iovlen32;
-			if (iovlen32 < 0 || count32 < 0)
+			if (iovlen32 < 0 || count32 < 0) {
+				if (iovsize != 0) {
+					kmem_free(aiov32, iov32size);
+					kmem_free(aiov, iovsize);
+				}
+
 				return (set_errno(EINVAL));
+			}
+
 			aiov[i].iov_len = iovlen32;
 			aiov[i].iov_base =
 			    (caddr_t)(uintptr_t)aiov32[i].iov_base;
 		}
+
+		if (iovsize != 0)
+			kmem_free(aiov32, iov32size);
 	} else
 #endif /* _SYSCALL32_IMPL */
 	if (iovcnt != 0 &&
 	    copyin(lmsg.msg_iov, aiov,
 	    (unsigned)iovcnt * sizeof (struct iovec))) {
+		if (iovsize != 0)
+			kmem_free(aiov, iovsize);
+
 		return (set_errno(EFAULT));
 	}
 	len = 0;
@@ -1359,6 +1425,9 @@
 		ssize_t iovlen = aiov[i].iov_len;
 		len += iovlen;
 		if (iovlen < 0 || len < 0) {
+			if (iovsize != 0)
+				kmem_free(aiov, iovsize);
+
 			return (set_errno(EINVAL));
 		}
 	}
@@ -1369,7 +1438,12 @@
 	auio.uio_segflg = UIO_USERSPACE;
 	auio.uio_limit = 0;
 
-	return (sendit(sock, &lmsg, &auio, flags));
+	rval = sendit(sock, &lmsg, &auio, flags);
+
+	if (iovsize != 0)
+		kmem_free(aiov, iovsize);
+
+	return (rval);
 }
 
 ssize_t
--- old/usr/src/uts/common/fs/sockfs/socktpi_impl.h	Wed Oct  5 00:32:18 2016
+++ new/usr/src/uts/common/fs/sockfs/socktpi_impl.h	Wed Oct  5 00:32:18 2016
@@ -22,6 +22,7 @@
 /*
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
  */
 
 #ifndef _SOCKFS_SOCKTPI_IMPL_H
@@ -56,6 +57,8 @@
 extern int	sogetwrerr(vnode_t *, int, int *);
 extern int	so_addr_verify(struct sonode *, const struct sockaddr *,
 			socklen_t);
+extern int	so_ux_lookup(struct sonode *, struct sockaddr_un *, int,
+		vnode_t **);
 extern int	so_ux_addr_xlate(struct sonode *, struct sockaddr *,
 			socklen_t, int, void **, socklen_t *);
 extern void	so_unix_close(struct sonode *);
--- old/usr/src/uts/common/fs/tmpfs/tmp_subr.c	Wed Oct  5 00:32:18 2016
+++ new/usr/src/uts/common/fs/tmpfs/tmp_subr.c	Wed Oct  5 00:32:18 2016
@@ -20,6 +20,7 @@
  */
 /*
  * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
  */
 
 #include <sys/types.h>
@@ -40,9 +41,19 @@
 #include <sys/policy.h>
 #include <sys/fs/tmp.h>
 #include <sys/fs/tmpnode.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
 
+#define	KILOBYTE	1024
+#define	MEGABYTE	(1024 * KILOBYTE)
+#define	GIGABYTE	(1024 * MEGABYTE)
+
 #define	MODESHIFT	3
 
+#define	VALIDMODEBITS	07777
+
+extern pgcnt_t swapfs_minfree;
+
 int
 tmp_taccess(void *vtp, int mode, struct cred *cred)
 {
@@ -71,7 +82,6 @@
  * a plain file and you have write access to that file.
  * Function returns 0 if remove access is granted.
  */
-
 int
 tmp_sticky_remove_access(struct tmpnode *dir, struct tmpnode *entry,
 	struct cred *cr)
@@ -128,72 +138,117 @@
  * maximum value encoded in 'str' is PAGESIZE * ULONG_MAX, while the value
  * returned in 'maxpg' is at most ULONG_MAX.
  *
- * If the number is followed by a "k" or "K", the value is converted from
- * kilobytes to bytes.  If it is followed by an "m" or "M" it is converted
- * from megabytes to bytes.  If it is not followed by a character it is
- * assumed to be in bytes. Multiple letter options are allowed, so for instance
- * '2mk' is interpreted as 2gb.
+ * The number may be followed by a magnitude suffix: "k" or "K" for kilobytes;
+ * "m" or "M" for megabytes; "g" or "G" for gigabytes.  This interface allows
+ * for an arguably esoteric interpretation of multiple suffix characters:
+ * namely, they cascade.  For example, the caller may specify "2mk", which is
+ * interpreted as 2 gigabytes.  It would seem, at this late stage, that the
+ * horse has left not only the barn but indeed the country, and possibly the
+ * entire planetary system. Alternatively, the number may be followed by a
+ * single '%' sign, indicating the size is a percentage of either the zone's
+ * swap limit or the system's overall swap size.
  *
  * Parse and overflow errors are detected and a non-zero number returned on
  * error.
  */
-
 int
 tmp_convnum(char *str, pgcnt_t *maxpg)
 {
-	uint64_t num = 0, oldnum;
+	u_longlong_t num = 0;
 #ifdef _LP64
-	uint64_t max_bytes = ULONG_MAX;
+	u_longlong_t max_bytes = ULONG_MAX;
 #else
-	uint64_t max_bytes = PAGESIZE * (uint64_t)ULONG_MAX;
+	u_longlong_t max_bytes = PAGESIZE * (uint64_t)ULONG_MAX;
 #endif
 	char *c;
+	const struct convchar {
+		char *cc_char;
+		uint64_t cc_factor;
+	} convchars[] = {
+		{ "kK", KILOBYTE },
+		{ "mM", MEGABYTE },
+		{ "gG", GIGABYTE },
+		{ NULL, 0 }
+	};
 
-	if (str == NULL)
+	if (str == NULL) {
 		return (EINVAL);
+	}
 	c = str;
 
 	/*
-	 * Convert str to number
+	 * Convert the initial numeric portion of the input string.
 	 */
-	while ((*c >= '0') && (*c <= '9')) {
-		oldnum = num;
-		num = num * 10 + (*c++ - '0');
-		if (oldnum > num) /* overflow */
+	if (ddi_strtoull(str, &c, 10, &num) != 0) {
+		return (EINVAL);
+	}
+
+	/*
+	 * Handle a size in percent. Anything other than a single percent
+	 * modifier is invalid. We use either the zone's swap limit or the
+	 * system's total available swap size as the initial value. Perform the
+	 * intermediate calculation in pages to avoid overflow.
+	 */
+	if (*c == '%') {
+		u_longlong_t cap;
+
+		if (*(c + 1) != '\0')
 			return (EINVAL);
+
+		if (num > 100)
+			return (EINVAL);
+
+		cap = (u_longlong_t)curproc->p_zone->zone_max_swap_ctl;
+		if (cap == UINT64_MAX) {
+			/*
+			 * Use the amount of available physical and memory swap
+			 */
+			mutex_enter(&anoninfo_lock);
+			cap = TOTAL_AVAILABLE_SWAP;
+			mutex_exit(&anoninfo_lock);
+		} else {
+			cap = btop(cap);
+		}
+
+		num = ptob(cap * num / 100);
+		goto done;
 	}
 
 	/*
-	 * Terminate on null
+	 * Apply the (potentially cascading) magnitude suffixes until an
+	 * invalid character is found, or the string comes to an end.
 	 */
-	while (*c != '\0') {
-		switch (*c++) {
+	for (; *c != '\0'; c++) {
+		int i;
 
-		/*
-		 * convert from kilobytes
-		 */
-		case 'k':
-		case 'K':
-			if (num > max_bytes / 1024) /* will overflow */
-				return (EINVAL);
-			num *= 1024;
-			break;
+		for (i = 0; convchars[i].cc_char != NULL; i++) {
+			/*
+			 * Check if this character matches this multiplier
+			 * class:
+			 */
+			if (strchr(convchars[i].cc_char, *c) != NULL) {
+				/*
+				 * Check for overflow:
+				 */
+				if (num > max_bytes / convchars[i].cc_factor) {
+					return (EINVAL);
+				}
 
+				num *= convchars[i].cc_factor;
+				goto valid_char;
+			}
+		}
+
 		/*
-		 * convert from megabytes
+		 * This was not a valid multiplier suffix character.
 		 */
-		case 'm':
-		case 'M':
-			if (num > max_bytes / (1024 * 1024)) /* will overflow */
-				return (EINVAL);
-			num *= 1024 * 1024;
-			break;
+		return (EINVAL);
 
-		default:
-			return (EINVAL);
-		}
+valid_char:
+		continue;
 	}
 
+done:
 	/*
 	 * Since btopr() rounds up to page granularity, this round-up can
 	 * cause an overflow only if 'num' is between (max_bytes - PAGESIZE)
@@ -204,3 +259,29 @@
 		return (EINVAL);
 	return (0);
 }
+
+/*
+ * Parse an octal mode string for use as the permissions set for the root
+ * of the tmpfs mount.
+ */
+int
+tmp_convmode(char *str, mode_t *mode)
+{
+	ulong_t num;
+	char *c;
+
+	if (str == NULL) {
+		return (EINVAL);
+	}
+
+	if (ddi_strtoul(str, &c, 8, &num) != 0) {
+		return (EINVAL);
+	}
+
+	if ((num & ~VALIDMODEBITS) != 0) {
+		return (EINVAL);
+	}
+
+	*mode = VALIDMODEBITS & num;
+	return (0);
+}
--- old/usr/src/uts/common/fs/tmpfs/tmp_vfsops.c	Wed Oct  5 00:32:18 2016
+++ new/usr/src/uts/common/fs/tmpfs/tmp_vfsops.c	Wed Oct  5 00:32:18 2016
@@ -20,7 +20,7 @@
  */
 /*
  * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2011, Joyent, Inc. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
  */
 
 #include <sys/types.h>
@@ -91,7 +91,8 @@
 	/* Option name		Cancel Opt	Arg	Flags		Data */
 	{ MNTOPT_XATTR,		xattr_cancel,	NULL,	MO_DEFAULT,	NULL},
 	{ MNTOPT_NOXATTR,	noxattr_cancel,	NULL,	NULL,		NULL},
-	{ "size",		NULL,		"0",	MO_HASVALUE,	NULL}
+	{ "size",		NULL,		"0",	MO_HASVALUE,	NULL},
+	{ "mode",		NULL,		NULL,	MO_HASVALUE,	NULL}
 };
 
 
@@ -227,11 +228,7 @@
 }
 
 static int
-tmp_mount(
-	struct vfs *vfsp,
-	struct vnode *mvp,
-	struct mounta *uap,
-	struct cred *cr)
+tmp_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr)
 {
 	struct tmount *tm = NULL;
 	struct tmpnode *tp;
@@ -240,9 +237,10 @@
 	pgcnt_t anonmax;
 	struct vattr rattr;
 	int got_attrs;
+	boolean_t mode_arg = B_FALSE;
+	mode_t root_mode = 0777;
+	char *argstr;
 
-	char *sizestr;
-
 	if ((error = secpolicy_fs_mount(cr, mvp, vfsp)) != 0)
 		return (error);
 
@@ -276,13 +274,24 @@
 	 * tm_anonmax is set according to the mount arguments
 	 * if any.  Otherwise, it is set to a maximum value.
 	 */
-	if (vfs_optionisset(vfsp, "size", &sizestr)) {
-		if ((error = tmp_convnum(sizestr, &anonmax)) != 0)
+	if (vfs_optionisset(vfsp, "size", &argstr)) {
+		if ((error = tmp_convnum(argstr, &anonmax)) != 0)
 			goto out;
 	} else {
 		anonmax = ULONG_MAX;
 	}
 
+	/*
+	 * The "mode" mount argument allows the operator to override the
+	 * permissions of the root of the tmpfs mount.
+	 */
+	if (vfs_optionisset(vfsp, "mode", &argstr)) {
+		if ((error = tmp_convmode(argstr, &root_mode)) != 0) {
+			goto out;
+		}
+		mode_arg = B_TRUE;
+	}
+
 	if (error = pn_get(uap->dir,
 	    (uap->flags & MS_SYSSPACE) ? UIO_SYSSPACE : UIO_USERSPACE, &dpn))
 		goto out;
@@ -341,7 +350,7 @@
 	 * allocate and initialize root tmpnode structure
 	 */
 	bzero(&rattr, sizeof (struct vattr));
-	rattr.va_mode = (mode_t)(S_IFDIR | 0777);	/* XXX modes */
+	rattr.va_mode = (mode_t)(S_IFDIR | root_mode);
 	rattr.va_type = VDIR;
 	rattr.va_rdev = 0;
 	tp = tmp_memalloc(sizeof (struct tmpnode), TMP_MUSTHAVE);
@@ -361,7 +370,14 @@
 	 * the previously set hardwired defaults to prevail.
 	 */
 	if (got_attrs == 0) {
-		tp->tn_mode = rattr.va_mode;
+		if (!mode_arg) {
+			/*
+			 * Only use the underlying mount point for the
+			 * mode if the "mode" mount argument was not
+			 * provided.
+			 */
+			tp->tn_mode = rattr.va_mode;
+		}
 		tp->tn_uid = rattr.va_uid;
 		tp->tn_gid = rattr.va_gid;
 	}
--- old/usr/src/uts/common/fs/tmpfs/tmp_vnops.c	Wed Oct  5 00:32:18 2016
+++ new/usr/src/uts/common/fs/tmpfs/tmp_vnops.c	Wed Oct  5 00:32:18 2016
@@ -25,7 +25,7 @@
  */
 
 /*
- * Copyright (c) 2015, Joyent, Inc. All rights reserved.
+ * Copyright 2016, Joyent, Inc.
  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
  * Copyright 2016 RackTop Systems.
  */
@@ -787,8 +787,13 @@
 		rw_exit(&tp->tn_contents);
 		rw_exit(&tp->tn_rwlock);
 
-		if (error == 0 && vap->va_size == 0)
-			vnevent_truncate(vp, ct);
+		if (error == 0) {
+			if (vap->va_size == 0) {
+				vnevent_truncate(vp, ct);
+			} else {
+				vnevent_resize(vp, ct);
+			}
+		}
 
 		goto out1;
 	}
@@ -1304,10 +1309,8 @@
 		vnevent_rename_src(TNTOV(fromtp), odvp, onm, ct);
 		/*
 		 * vnevent_rename_dest is called in tdirenter().
-		 * Notify the target dir if not same as source dir.
 		 */
-		if (ndvp != odvp)
-			vnevent_rename_dest_dir(ndvp, ct);
+		vnevent_rename_dest_dir(ndvp, TNTOV(fromtp), nnm, ct);
 	}
 
 done:
@@ -2344,8 +2347,13 @@
 			return (EFBIG);
 		error = tmp_freesp(vp, bfp, flag);
 
-		if (error == 0 && bfp->l_start == 0)
-			vnevent_truncate(vp, ct);
+		if (error == 0) {
+			if (bfp->l_start == 0) {
+				vnevent_truncate(vp, ct);
+			} else {
+				vnevent_resize(vp, ct);
+			}
+		}
 	}
 	return (error);
 }
--- old/usr/src/uts/common/fs/udfs/udf_dir.c	Wed Oct  5 00:32:18 2016
+++ new/usr/src/uts/common/fs/udfs/udf_dir.c	Wed Oct  5 00:32:18 2016
@@ -20,6 +20,7 @@
  */
 /*
  * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, Joyent, Inc. All rights reserved.
  */
 
 #include <sys/types.h>
@@ -562,9 +563,8 @@
 				    namep, ctp);
 			}
 
-			if (sdp != tdp) {
-				vnevent_rename_dest_dir(ITOV(tdp), ctp);
-			}
+			vnevent_rename_dest_dir(ITOV(tdp), ITOV(tip),
+			    namep, ctp);
 		}
 
 		/*
--- old/usr/src/uts/common/fs/udfs/udf_vnops.c	Wed Oct  5 00:32:18 2016
+++ new/usr/src/uts/common/fs/udfs/udf_vnops.c	Wed Oct  5 00:32:18 2016
@@ -569,8 +569,11 @@
 			goto update_inode;
 		}
 
-		if (vap->va_size == 0)
+		if (vap->va_size == 0) {
 			vnevent_truncate(vp, ct);
+		} else {
+			vnevent_resize(vp, ct);
+		}
 	}
 	/*
 	 * Change file access or modified times.
@@ -1649,8 +1652,13 @@
 	} else if ((error = convoff(vp, bfp, 0, offset)) == 0) {
 		error = ud_freesp(vp, bfp, flag, cr);
 
-		if (error == 0 && bfp->l_start == 0)
-			vnevent_truncate(vp, ct);
+		if (error == 0) {
+			if (bfp->l_start == 0) {
+				vnevent_truncate(vp, ct);
+			} else {
+				vnevent_resize(vp, ct);
+			}
+		}
 	}
 
 	return (error);
--- old/usr/src/uts/common/fs/ufs/ufs_vnops.c	Wed Oct  5 00:32:19 2016
+++ new/usr/src/uts/common/fs/ufs/ufs_vnops.c	Wed Oct  5 00:32:19 2016
@@ -21,7 +21,7 @@
 
 /*
  * Copyright (c) 1984, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2015, Joyent, Inc.
+ * Copyright 2016, Joyent, Inc.
  */
 
 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
@@ -2193,8 +2193,13 @@
 			goto update_inode;
 		}
 
-		if (error == 0 && vap->va_size)
-			vnevent_truncate(vp, ct);
+		if (error == 0) {
+			if (vap->va_size) {
+				vnevent_truncate(vp, ct);
+			} else {
+				vnevent_resize(vp, ct);
+			}
+		}
 	}
 
 	if (ulp) {
@@ -3726,12 +3731,7 @@
 
 	if (error == 0) {
 		vnevent_rename_src(ITOV(sip), sdvp, snm, ct);
-		/*
-		 * Notify the target directory of the rename event
-		 * if source and target directories are not the same.
-		 */
-		if (sdvp != tdvp)
-			vnevent_rename_dest_dir(tdvp, ct);
+		vnevent_rename_dest_dir(tdvp, ITOV(sip), tnm, ct);
 	}
 
 errout:
@@ -4478,8 +4478,13 @@
 				return (error);
 			error = ufs_freesp(vp, bfp, flag, cr);
 
-			if (error == 0 && bfp->l_start == 0)
-				vnevent_truncate(vp, ct);
+			if (error == 0) {
+				if (bfp->l_start == 0) {
+					vnevent_truncate(vp, ct);
+				} else {
+					vnevent_resize(vp, ct);
+				}
+			}
 		} else if (cmd == F_ALLOCSP) {
 			error = ufs_lockfs_begin(ufsvfsp, &ulp,
 			    ULOCKFS_FALLOCATE_MASK);
--- old/usr/src/uts/common/fs/vnode.c	Wed Oct  5 00:32:19 2016
+++ new/usr/src/uts/common/fs/vnode.c	Wed Oct  5 00:32:19 2016
@@ -21,7 +21,7 @@
 
 /*
  * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2014, Joyent, Inc. All rights reserved.
  */
 
 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
@@ -200,6 +200,10 @@
 		cr = crgetmapped(cr);					\
 	}
 
+#define	VOP_LATENCY_10MS	10000000
+#define	VOP_LATENCY_100MS	100000000
+#define	VOP_LATENCY_1S		1000000000
+
 /*
  * Convert stat(2) formats to vnode types and vice versa.  (Knows about
  * numerical order of S_IFMT and vnode types.)
@@ -2516,6 +2520,7 @@
 	if (vp == NULL || vp->v_femhead == NULL) {
 		return;
 	}
+	(void) VOP_VNEVENT(dvp, VE_RENAME_SRC_DIR, vp, name, ct);
 	(void) VOP_VNEVENT(vp, VE_RENAME_SRC, dvp, name, ct);
 }
 
@@ -2530,12 +2535,13 @@
 }
 
 void
-vnevent_rename_dest_dir(vnode_t *vp, caller_context_t *ct)
+vnevent_rename_dest_dir(vnode_t *vp, vnode_t *nvp, char *name,
+    caller_context_t *ct)
 {
 	if (vp == NULL || vp->v_femhead == NULL) {
 		return;
 	}
-	(void) VOP_VNEVENT(vp, VE_RENAME_DEST_DIR, NULL, NULL, ct);
+	(void) VOP_VNEVENT(vp, VE_RENAME_DEST_DIR, nvp, name, ct);
 }
 
 void
@@ -2622,6 +2628,15 @@
 	(void) VOP_VNEVENT(vp, VE_TRUNCATE, NULL, NULL, ct);
 }
 
+void
+vnevent_resize(vnode_t *vp, caller_context_t *ct)
+{
+	if (vp == NULL || vp->v_femhead == NULL) {
+		return;
+	}
+	(void) VOP_VNEVENT(vp, VE_RESIZE, NULL, NULL, ct);
+}
+
 /*
  * Vnode accessors.
  */
@@ -3261,14 +3276,53 @@
 	cred_t *cr,
 	caller_context_t *ct)
 {
-	int	err;
 	ssize_t	resid_start = uiop->uio_resid;
+	zone_t	*zonep = curzone;
+	zone_vfs_kstat_t *zvp = zonep->zone_vfs_stats;
 
+	hrtime_t start = 0, lat;
+	ssize_t len;
+	int err;
+
+	if ((vp->v_type == VREG || vp->v_type == VDIR || vp->v_type == VBLK) &&
+	    vp->v_vfsp != NULL && (vp->v_vfsp->vfs_flag & VFS_STATS)) {
+		start = gethrtime();
+
+		mutex_enter(&zonep->zone_vfs_lock);
+		kstat_runq_enter(&zonep->zone_vfs_rwstats);
+		mutex_exit(&zonep->zone_vfs_lock);
+	}
+
 	VOPXID_MAP_CR(vp, cr);
 
 	err = (*(vp)->v_op->vop_read)(vp, uiop, ioflag, cr, ct);
-	VOPSTATS_UPDATE_IO(vp, read,
-	    read_bytes, (resid_start - uiop->uio_resid));
+	len = resid_start - uiop->uio_resid;
+
+	VOPSTATS_UPDATE_IO(vp, read, read_bytes, len);
+
+	if (start != 0) {
+		mutex_enter(&zonep->zone_vfs_lock);
+		zonep->zone_vfs_rwstats.reads++;
+		zonep->zone_vfs_rwstats.nread += len;
+		kstat_runq_exit(&zonep->zone_vfs_rwstats);
+		mutex_exit(&zonep->zone_vfs_lock);
+
+		lat = gethrtime() - start;
+
+		if (lat >= VOP_LATENCY_10MS) {
+			if (lat < VOP_LATENCY_100MS)
+				atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+			else if (lat < VOP_LATENCY_1S) {
+				atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+				atomic_inc_64(&zvp->zv_100ms_ops.value.ui64);
+			} else {
+				atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+				atomic_inc_64(&zvp->zv_100ms_ops.value.ui64);
+				atomic_inc_64(&zvp->zv_1s_ops.value.ui64);
+			}
+		}
+	}
+
 	return (err);
 }
 
@@ -3280,14 +3334,58 @@
 	cred_t *cr,
 	caller_context_t *ct)
 {
-	int	err;
 	ssize_t	resid_start = uiop->uio_resid;
+	zone_t	*zonep = curzone;
+	zone_vfs_kstat_t *zvp = zonep->zone_vfs_stats;
 
+	hrtime_t start = 0, lat;
+	ssize_t len;
+	int	err;
+
+	/*
+	 * For the purposes of VFS kstat consumers, the "waitq" calculation is
+	 * repurposed as the active queue for VFS write operations.  There's no
+	 * actual wait queue for VFS operations.
+	 */
+	if ((vp->v_type == VREG || vp->v_type == VDIR || vp->v_type == VBLK) &&
+	    vp->v_vfsp != NULL && (vp->v_vfsp->vfs_flag & VFS_STATS)) {
+		start = gethrtime();
+
+		mutex_enter(&zonep->zone_vfs_lock);
+		kstat_waitq_enter(&zonep->zone_vfs_rwstats);
+		mutex_exit(&zonep->zone_vfs_lock);
+	}
+
 	VOPXID_MAP_CR(vp, cr);
 
 	err = (*(vp)->v_op->vop_write)(vp, uiop, ioflag, cr, ct);
-	VOPSTATS_UPDATE_IO(vp, write,
-	    write_bytes, (resid_start - uiop->uio_resid));
+	len = resid_start - uiop->uio_resid;
+
+	VOPSTATS_UPDATE_IO(vp, write, write_bytes, len);
+
+	if (start != 0) {
+		mutex_enter(&zonep->zone_vfs_lock);
+		zonep->zone_vfs_rwstats.writes++;
+		zonep->zone_vfs_rwstats.nwritten += len;
+		kstat_waitq_exit(&zonep->zone_vfs_rwstats);
+		mutex_exit(&zonep->zone_vfs_lock);
+
+		lat = gethrtime() - start;
+
+		if (lat >= VOP_LATENCY_10MS) {
+			if (lat < VOP_LATENCY_100MS)
+				atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+			else if (lat < VOP_LATENCY_1S) {
+				atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+				atomic_inc_64(&zvp->zv_100ms_ops.value.ui64);
+			} else {
+				atomic_inc_64(&zvp->zv_10ms_ops.value.ui64);
+				atomic_inc_64(&zvp->zv_100ms_ops.value.ui64);
+				atomic_inc_64(&zvp->zv_1s_ops.value.ui64);
+			}
+		}
+	}
+
 	return (err);
 }
 
--- old/usr/src/uts/common/fs/zfs/zfs_vnops.c	Wed Oct  5 00:32:19 2016
+++ new/usr/src/uts/common/fs/zfs/zfs_vnops.c	Wed Oct  5 00:32:19 2016
@@ -23,7 +23,7 @@
  * Copyright (c) 2012, 2015 by Delphix. All rights reserved.
  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
  * Copyright (c) 2014 Integros [integros.com]
- * Copyright 2015 Joyent, Inc.
+ * Copyright 2016 Joyent, Inc.
  */
 
 /* Portions Copyright 2007 Jeremy Teo */
@@ -664,6 +664,7 @@
 	rl_t		*rl;
 	int		max_blksz = zfsvfs->z_max_blksz;
 	int		error = 0;
+	int		prev_error;
 	arc_buf_t	*abuf;
 	iovec_t		*aiov = NULL;
 	xuio_t		*xuio = NULL;
@@ -968,7 +969,6 @@
 		while ((end_size = zp->z_size) < uio->uio_loffset) {
 			(void) atomic_cas_64(&zp->z_size, end_size,
 			    uio->uio_loffset);
-			ASSERT(error == 0);
 		}
 		/*
 		 * If we are replaying and eof is non zero then force
@@ -978,12 +978,17 @@
 		if (zfsvfs->z_replay && zfsvfs->z_replay_eof != 0)
 			zp->z_size = zfsvfs->z_replay_eof;
 
+		/*
+		 * Keep track of a possible pre-existing error from a partial
+		 * write via dmu_write_uio_dbuf above.
+		 */
+		prev_error = error;
 		error = sa_bulk_update(zp->z_sa_hdl, bulk, count, tx);
 
 		zfs_log_write(zilog, tx, TX_WRITE, zp, woff, tx_bytes, ioflag);
 		dmu_tx_commit(tx);
 
-		if (error != 0)
+		if (prev_error != 0 || error != 0)
 			break;
 		ASSERT(tx_bytes == nbytes);
 		n -= nbytes;
@@ -2832,8 +2837,11 @@
 			return (err);
 		}
 
-		if (vap->va_size == 0)
+		if (vap->va_size == 0) {
 			vnevent_truncate(ZTOV(zp), ct);
+		} else {
+			vnevent_resize(ZTOV(zp), ct);
+		}
 	}
 
 	if (mask & (AT_ATIME|AT_MTIME) ||
@@ -3761,9 +3769,7 @@
 
 	if (error == 0) {
 		vnevent_rename_src(ZTOV(szp), sdvp, snm, ct);
-		/* notify the target dir if it is not the same as source dir */
-		if (tdvp != sdvp)
-			vnevent_rename_dest_dir(tdvp, ct);
+		vnevent_rename_dest_dir(tdvp, ZTOV(szp), tnm, ct);
 	}
 out:
 	if (zl != NULL)
@@ -4859,8 +4865,13 @@
 
 	error = zfs_freesp(zp, off, len, flag, TRUE);
 
-	if (error == 0 && off == 0 && len == 0)
-		vnevent_truncate(ZTOV(zp), ct);
+	if (error == 0 && len == 0) {
+		if (off == 0) {
+			vnevent_truncate(ZTOV(zp), ct);
+		} else {
+			vnevent_resize(ZTOV(zp), ct);
+		}
+	}
 
 	ZFS_EXIT(zfsvfs);
 	return (error);
--- /dev/null	Wed Oct  5 00:32:20 2016
+++ new/usr/src/uts/common/inet/bpf.h	Wed Oct  5 00:32:20 2016
@@ -0,0 +1,49 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#ifndef	_INET_BPF_H
+#define	_INET_BPF_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+
+#ifdef	_KERNEL
+
+#include <sys/types.h>
+
+/*
+ * Clone bpf_insn definition so that consumers don't need net/bpf.h to reason
+ * about struct sizing.
+ */
+typedef struct ip_bpf_insn {
+	uint16_t	code;
+	uint8_t		jt;
+	uint8_t		jf;
+	uint32_t	k;
+} ip_bpf_insn_t;
+
+extern uint32_t ip_bpf_filter(ip_bpf_insn_t *, uchar_t *, uint_t, uint_t);
+extern boolean_t ip_bpf_validate(ip_bpf_insn_t *, uint_t);
+
+
+#endif	/* _KERNEL */
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _INET_BPF_H */
--- old/usr/src/uts/common/inet/ip/conn_opt.c	Wed Oct  5 00:32:20 2016
+++ new/usr/src/uts/common/inet/ip/conn_opt.c	Wed Oct  5 00:32:20 2016
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 /* Copyright (c) 1990 Mentat Inc. */
 
@@ -619,6 +620,9 @@
 		case SO_REUSEADDR:
 			*i1 = connp->conn_reuseaddr ? SO_REUSEADDR : 0;
 			break;	/* goto sizeof (int) option return */
+		case SO_REUSEPORT:
+			*i1 = connp->conn_reuseport;
+			break;	/* goto sizeof (int) option return */
 		case SO_TYPE:
 			*i1 = connp->conn_so_type;
 			break;	/* goto sizeof (int) option return */
@@ -1186,8 +1190,24 @@
 	ip_stack_t	*ipst = connp->conn_netstack->netstack_ip;
 	int		error;
 
-	if (connp->conn_family != AF_INET)
+	if (connp->conn_family == AF_INET6 &&
+	    connp->conn_ipversion == IPV4_VERSION) {
+		/*
+		 * Allow certain IPv4 options to be set on an AF_INET6 socket
+		 * if the connection is still IPv4.
+		 */
+		switch (name) {
+		case IP_TOS:
+		case T_IP_TOS:
+		case IP_TTL:
+		case IP_DONTFRAG:
+			break;
+		default:
+			return (EINVAL);
+		}
+	} else if (connp->conn_family != AF_INET) {
 		return (EINVAL);
+	}
 
 	switch (name) {
 	case IP_TTL:
--- old/usr/src/uts/common/inet/ip/icmp.c	Wed Oct  5 00:32:20 2016
+++ new/usr/src/uts/common/inet/ip/icmp.c	Wed Oct  5 00:32:20 2016
@@ -22,6 +22,7 @@
  * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2013 by Delphix. All rights reserved.
  * Copyright 2014, OmniTI Computer Consulting, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 /* Copyright (c) 1990 Mentat Inc. */
 
@@ -80,6 +81,7 @@
 #include <sys/tsol/tnet.h>
 
 #include <inet/rawip_impl.h>
+#include <net/bpf.h>
 
 #include <sys/disp.h>
 
@@ -1011,6 +1013,12 @@
 		icmp->icmp_filter = NULL;
 	}
 
+	if (icmp->icmp_bpf_len != 0) {
+		kmem_free(icmp->icmp_bpf_prog, icmp->icmp_bpf_len);
+		icmp->icmp_bpf_len = 0;
+		icmp->icmp_bpf_prog = NULL;
+	}
+
 	/*
 	 * Clear any fields which the kmem_cache constructor clears.
 	 * Only icmp_connp needs to be preserved.
@@ -1964,6 +1972,104 @@
 	return (err);
 }
 
+static int
+icmp_attach_filter(icmp_t *icmp, uint_t inlen, const uchar_t *invalp)
+{
+	struct bpf_program prog;
+	ip_bpf_insn_t *insns = NULL;
+	unsigned int size;
+
+#ifdef _LP64
+	if (get_udatamodel() != DATAMODEL_NATIVE) {
+		struct bpf_program32 *prog32;
+
+		if (inlen != sizeof (struct bpf_program32)) {
+			return (EINVAL);
+		}
+		prog32 = (struct bpf_program32 *)invalp;
+		prog.bf_len = prog32->bf_len;
+		prog.bf_insns = (void *)(uint64_t)prog32->bf_insns;
+	} else
+#endif
+	if (inlen == sizeof (struct bpf_program)) {
+		bcopy(invalp, &prog, sizeof (prog));
+	} else {
+		return (EINVAL);
+	}
+
+	if (prog.bf_len > BPF_MAXINSNS || prog.bf_len == 0) {
+		return (EINVAL);
+	}
+	size = prog.bf_len * sizeof (struct bpf_insn);
+	insns = kmem_alloc(size, KM_SLEEP);
+	if (copyin(prog.bf_insns, insns, size) != 0) {
+		kmem_free(insns, size);
+		return (EFAULT);
+	}
+	if (!ip_bpf_validate(insns, prog.bf_len)) {
+		kmem_free(insns, size);
+		return (EINVAL);
+	}
+
+	rw_enter(&icmp->icmp_bpf_lock, RW_WRITER);
+	if (icmp->icmp_bpf_len != 0) {
+		ASSERT(icmp->icmp_bpf_prog != NULL);
+
+		kmem_free(icmp->icmp_bpf_prog, icmp->icmp_bpf_len);
+	}
+	icmp->icmp_bpf_len = size;
+	icmp->icmp_bpf_prog = insns;
+	rw_exit(&icmp->icmp_bpf_lock);
+	return (0);
+}
+
+static int
+icmp_detach_filter(icmp_t *icmp)
+{
+	int error;
+
+	rw_enter(&icmp->icmp_bpf_lock, RW_WRITER);
+	if (icmp->icmp_bpf_len == 0) {
+		ASSERT(icmp->icmp_bpf_prog == NULL);
+		error = ENOENT;
+	} else {
+		kmem_free(icmp->icmp_bpf_prog,
+		    icmp->icmp_bpf_len);
+		icmp->icmp_bpf_len = 0;
+		icmp->icmp_bpf_prog = NULL;
+		error = 0;
+	}
+	rw_exit(&icmp->icmp_bpf_lock);
+	return (error);
+}
+
+static boolean_t
+icmp_eval_filter(icmp_t *icmp, mblk_t *mp, ip_recv_attr_t *ira)
+{
+	boolean_t res;
+	uchar_t *buf = mp->b_rptr;
+	uint_t wirelen, len = MBLKL(mp);
+
+	rw_enter(&icmp->icmp_bpf_lock, RW_READER);
+	if (icmp->icmp_bpf_len == 0) {
+		rw_exit(&icmp->icmp_bpf_lock);
+		return (B_FALSE);
+	}
+	if (ira->ira_flags & IRAF_IS_IPV4) {
+		ipha_t *ipha = (ipha_t *)buf;
+
+		wirelen = ntohs(ipha->ipha_length);
+	} else {
+		ip6_t *ip6h = (ip6_t *)buf;
+
+		wirelen = ntohs(ip6h->ip6_plen) + IPV6_HDR_LEN;
+	}
+	res = !ip_bpf_filter(icmp->icmp_bpf_prog, buf, wirelen, len);
+	rw_exit(&icmp->icmp_bpf_lock);
+
+	return (res);
+}
+
 /*
  * This routine sets socket options.
  */
@@ -2053,6 +2159,10 @@
 				return (ENOBUFS);
 			}
 			break;
+		case SO_ATTACH_FILTER:
+			return (icmp_attach_filter(icmp, inlen, invalp));
+		case SO_DETACH_FILTER:
+			return (icmp_detach_filter(icmp));
 		}
 		break;
 
@@ -2598,6 +2708,14 @@
 	/* Initialize regardless of IP version */
 	ipps.ipp_fields = 0;
 
+	/* Apply socket filter, if needed */
+	if (icmp->icmp_bpf_len != 0) {
+		if (icmp_eval_filter(icmp, mp, ira)) {
+			freemsg(mp);
+			return;
+		}
+	}
+
 	if (ira->ira_flags & IRAF_IS_IPV4) {
 		ASSERT(IPH_HDR_VERSION(rptr) == IPV4_VERSION);
 		ASSERT(MBLKL(mp) >= sizeof (ipha_t));
@@ -5027,7 +5145,8 @@
 }
 
 static void *
-rawip_kstat_init(netstackid_t stackid) {
+rawip_kstat_init(netstackid_t stackid)
+{
 	kstat_t	*ksp;
 
 	rawip_named_kstat_t template = {
@@ -5039,9 +5158,7 @@
 	};
 
 	ksp = kstat_create_netstack("icmp", 0, "rawip", "mib2",
-					KSTAT_TYPE_NAMED,
-					NUM_OF_FIELDS(rawip_named_kstat_t),
-					0, stackid);
+	    KSTAT_TYPE_NAMED, NUM_OF_FIELDS(rawip_named_kstat_t), 0, stackid);
 	if (ksp == NULL || ksp->ks_data == NULL)
 		return (NULL);
 
--- old/usr/src/uts/common/inet/ip/icmp_opt_data.c	Wed Oct  5 00:32:21 2016
+++ new/usr/src/uts/common/inet/ip/icmp_opt_data.c	Wed Oct  5 00:32:21 2016
@@ -21,6 +21,7 @@
 /*
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
  */
 
 #include <sys/types.h>
@@ -41,6 +42,7 @@
 #include <netinet/ip_mroute.h>
 #include <inet/optcom.h>
 #include <inet/rawip_impl.h>
+#include <net/bpf.h>
 
 /*
  * Table of all known options handled on a ICMP protocol stack.
@@ -86,6 +88,10 @@
 	0 },
 { SO_DOMAIN,	SOL_SOCKET, OA_R, OA_R, OP_NP, 0, sizeof (int), 0 },
 
+{ SO_ATTACH_FILTER,	SOL_SOCKET, OA_W, OA_W, OP_NP, 0,
+	sizeof (struct bpf_program), 0 },
+{ SO_DETACH_FILTER,	SOL_SOCKET, OA_W, OA_W, OP_NP, 0, 0, 0 },
+
 { IP_OPTIONS,	IPPROTO_IP, OA_RW, OA_RW, OP_NP,
 	(OP_VARLEN|OP_NODEFAULT),
 	IP_MAX_OPT_LENGTH + IP_ADDR_LEN, -1 /* not initialized */ },
--- old/usr/src/uts/common/inet/ip/ipclassifier.c	Wed Oct  5 00:32:21 2016
+++ new/usr/src/uts/common/inet/ip/ipclassifier.c	Wed Oct  5 00:32:21 2016
@@ -20,6 +20,7 @@
  */
 /*
  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 
 /*
@@ -868,67 +869,90 @@
 	mutex_exit(&(connfp)->connf_lock);				\
 }
 
-#define	IPCL_HASH_INSERT_BOUND(connfp, connp) {				\
-	conn_t *pconnp = NULL, *nconnp;					\
-	IPCL_HASH_REMOVE((connp));					\
-	mutex_enter(&(connfp)->connf_lock);				\
-	nconnp = (connfp)->connf_head;					\
-	while (nconnp != NULL &&					\
-	    !_IPCL_V4_MATCH_ANY(nconnp->conn_laddr_v6)) {		\
-		pconnp = nconnp;					\
-		nconnp = nconnp->conn_next;				\
-	}								\
-	if (pconnp != NULL) {						\
-		pconnp->conn_next = (connp);				\
-		(connp)->conn_prev = pconnp;				\
-	} else {							\
-		(connfp)->connf_head = (connp);				\
-	}								\
-	if (nconnp != NULL) {						\
-		(connp)->conn_next = nconnp;				\
-		nconnp->conn_prev = (connp);				\
-	}								\
-	(connp)->conn_fanout = (connfp);				\
-	(connp)->conn_flags = ((connp)->conn_flags & ~IPCL_REMOVED) |	\
-	    IPCL_BOUND;							\
-	CONN_INC_REF(connp);						\
-	mutex_exit(&(connfp)->connf_lock);				\
-}
+/*
+ * When inserting bound or wildcard entries into the hash, ordering rules are
+ * used to facilitate timely and correct lookups.  The order is as follows:
+ * 1. Entries bound to a specific address
+ * 2. Entries bound to INADDR_ANY
+ * 3. Entries bound to ADDR_UNSPECIFIED
+ * Entries in a category which share conn_lport (such as those using
+ * SO_REUSEPORT) will be ordered such that the newest inserted is first.
+ */
 
-#define	IPCL_HASH_INSERT_WILDCARD(connfp, connp) {			\
-	conn_t **list, *prev, *next;					\
-	boolean_t isv4mapped =						\
-	    IN6_IS_ADDR_V4MAPPED(&(connp)->conn_laddr_v6);		\
-	IPCL_HASH_REMOVE((connp));					\
-	mutex_enter(&(connfp)->connf_lock);				\
-	list = &(connfp)->connf_head;					\
-	prev = NULL;							\
-	while ((next = *list) != NULL) {				\
-		if (isv4mapped &&					\
-		    IN6_IS_ADDR_UNSPECIFIED(&next->conn_laddr_v6) &&	\
-		    connp->conn_zoneid == next->conn_zoneid) {		\
-			(connp)->conn_next = next;			\
-			if (prev != NULL)				\
-				prev = next->conn_prev;			\
-			next->conn_prev = (connp);			\
-			break;						\
-		}							\
-		list = &next->conn_next;				\
-		prev = next;						\
-	}								\
-	(connp)->conn_prev = prev;					\
-	*list = (connp);						\
-	(connp)->conn_fanout = (connfp);				\
-	(connp)->conn_flags = ((connp)->conn_flags & ~IPCL_REMOVED) |	\
-	    IPCL_BOUND;							\
-	CONN_INC_REF((connp));						\
-	mutex_exit(&(connfp)->connf_lock);				\
+void
+ipcl_hash_insert_bound(connf_t *connfp, conn_t *connp)
+{
+	conn_t *pconnp, *nconnp;
+
+	IPCL_HASH_REMOVE(connp);
+	mutex_enter(&connfp->connf_lock);
+	nconnp = connfp->connf_head;
+	pconnp = NULL;
+	while (nconnp != NULL) {
+		/*
+		 * Walk though entries associated with the fanout until one is
+		 * found which fulfills any of these conditions:
+		 * 1. Listen address of ADDR_ANY/ADDR_UNSPECIFIED
+		 * 2. Listen port the same as connp
+		 */
+		if (_IPCL_V4_MATCH_ANY(nconnp->conn_laddr_v6) ||
+		    connp->conn_lport == nconnp->conn_lport)
+			break;
+		pconnp = nconnp;
+		nconnp = nconnp->conn_next;
+	}
+	if (pconnp != NULL) {
+		pconnp->conn_next = connp;
+		connp->conn_prev = pconnp;
+	} else {
+		connfp->connf_head = connp;
+	}
+	if (nconnp != NULL) {
+		connp->conn_next = nconnp;
+		nconnp->conn_prev = connp;
+	}
+	connp->conn_fanout = connfp;
+	connp->conn_flags = (connp->conn_flags & ~IPCL_REMOVED) | IPCL_BOUND;
+	CONN_INC_REF(connp);
+	mutex_exit(&connfp->connf_lock);
 }
 
 void
 ipcl_hash_insert_wildcard(connf_t *connfp, conn_t *connp)
 {
-	IPCL_HASH_INSERT_WILDCARD(connfp, connp);
+	conn_t *pconnp = NULL, *nconnp;
+	boolean_t isv4mapped = IN6_IS_ADDR_V4MAPPED(&connp->conn_laddr_v6);
+
+	IPCL_HASH_REMOVE(connp);
+	mutex_enter(&connfp->connf_lock);
+	nconnp = connfp->connf_head;
+	pconnp = NULL;
+	while (nconnp != NULL) {
+		if (IN6_IS_ADDR_V4MAPPED_ANY(&nconnp->conn_laddr_v6) &&
+		    isv4mapped && connp->conn_lport == nconnp->conn_lport)
+			break;
+		if (IN6_IS_ADDR_UNSPECIFIED(&nconnp->conn_laddr_v6) &&
+		    (isv4mapped ||
+		    connp->conn_lport == nconnp->conn_lport))
+			break;
+
+		pconnp = nconnp;
+		nconnp = nconnp->conn_next;
+	}
+	if (pconnp != NULL) {
+		pconnp->conn_next = connp;
+		connp->conn_prev = pconnp;
+	} else {
+		connfp->connf_head = connp;
+	}
+	if (nconnp != NULL) {
+		connp->conn_next = nconnp;
+		nconnp->conn_prev = connp;
+	}
+	connp->conn_fanout = connfp;
+	connp->conn_flags = (connp->conn_flags & ~IPCL_REMOVED) | IPCL_BOUND;
+	CONN_INC_REF(connp);
+	mutex_exit(&connfp->connf_lock);
 }
 
 /*
@@ -1034,9 +1058,9 @@
 	    IN6_IS_ADDR_V4MAPPED_ANY(&connp->conn_faddr_v6)) {
 		if (IN6_IS_ADDR_UNSPECIFIED(&connp->conn_laddr_v6) ||
 		    IN6_IS_ADDR_V4MAPPED_ANY(&connp->conn_laddr_v6)) {
-			IPCL_HASH_INSERT_WILDCARD(connfp, connp);
+			ipcl_hash_insert_wildcard(connfp, connp);
 		} else {
-			IPCL_HASH_INSERT_BOUND(connfp, connp);
+			ipcl_hash_insert_bound(connfp, connp);
 		}
 	} else {
 		IPCL_HASH_INSERT_CONNECTED(connfp, connp);
@@ -1205,9 +1229,9 @@
 		if (connp->conn_faddr_v4 != INADDR_ANY) {
 			IPCL_HASH_INSERT_CONNECTED(connfp, connp);
 		} else if (connp->conn_laddr_v4 != INADDR_ANY) {
-			IPCL_HASH_INSERT_BOUND(connfp, connp);
+			ipcl_hash_insert_bound(connfp, connp);
 		} else {
-			IPCL_HASH_INSERT_WILDCARD(connfp, connp);
+			ipcl_hash_insert_wildcard(connfp, connp);
 		}
 		if (protocol == IPPROTO_RSVP)
 			ill_set_inputfn_all(ipst);
@@ -1219,9 +1243,9 @@
 		connfp = &ipst->ips_ipcl_bind_fanout[
 		    IPCL_BIND_HASH(lport, ipst)];
 		if (connp->conn_laddr_v4 != INADDR_ANY) {
-			IPCL_HASH_INSERT_BOUND(connfp, connp);
+			ipcl_hash_insert_bound(connfp, connp);
 		} else {
-			IPCL_HASH_INSERT_WILDCARD(connfp, connp);
+			ipcl_hash_insert_wildcard(connfp, connp);
 		}
 		if (cl_inet_listen != NULL) {
 			ASSERT(connp->conn_ipversion == IPV4_VERSION);
@@ -1271,9 +1295,9 @@
 		if (!IN6_IS_ADDR_UNSPECIFIED(&connp->conn_faddr_v6)) {
 			IPCL_HASH_INSERT_CONNECTED(connfp, connp);
 		} else if (!IN6_IS_ADDR_UNSPECIFIED(&connp->conn_laddr_v6)) {
-			IPCL_HASH_INSERT_BOUND(connfp, connp);
+			ipcl_hash_insert_bound(connfp, connp);
 		} else {
-			IPCL_HASH_INSERT_WILDCARD(connfp, connp);
+			ipcl_hash_insert_wildcard(connfp, connp);
 		}
 		break;
 
@@ -1283,9 +1307,9 @@
 		connfp = &ipst->ips_ipcl_bind_fanout[
 		    IPCL_BIND_HASH(lport, ipst)];
 		if (!IN6_IS_ADDR_UNSPECIFIED(&connp->conn_laddr_v6)) {
-			IPCL_HASH_INSERT_BOUND(connfp, connp);
+			ipcl_hash_insert_bound(connfp, connp);
 		} else {
-			IPCL_HASH_INSERT_WILDCARD(connfp, connp);
+			ipcl_hash_insert_wildcard(connfp, connp);
 		}
 		if (cl_inet_listen != NULL) {
 			sa_family_t	addr_family;
@@ -1416,9 +1440,9 @@
 		if (connp->conn_faddr_v4 != INADDR_ANY) {
 			IPCL_HASH_INSERT_CONNECTED(connfp, connp);
 		} else if (connp->conn_laddr_v4 != INADDR_ANY) {
-			IPCL_HASH_INSERT_BOUND(connfp, connp);
+			ipcl_hash_insert_bound(connfp, connp);
 		} else {
-			IPCL_HASH_INSERT_WILDCARD(connfp, connp);
+			ipcl_hash_insert_wildcard(connfp, connp);
 		}
 		break;
 	}
@@ -1504,9 +1528,9 @@
 		if (!IN6_IS_ADDR_UNSPECIFIED(&connp->conn_faddr_v6)) {
 			IPCL_HASH_INSERT_CONNECTED(connfp, connp);
 		} else if (!IN6_IS_ADDR_UNSPECIFIED(&connp->conn_laddr_v6)) {
-			IPCL_HASH_INSERT_BOUND(connfp, connp);
+			ipcl_hash_insert_bound(connfp, connp);
 		} else {
-			IPCL_HASH_INSERT_WILDCARD(connfp, connp);
+			ipcl_hash_insert_wildcard(connfp, connp);
 		}
 		break;
 	}
@@ -2092,6 +2116,7 @@
 	connp->conn_flags = IPCL_RAWIPCONN;
 	connp->conn_proto = IPPROTO_ICMP;
 	icmp->icmp_connp = connp;
+	rw_init(&icmp->icmp_bpf_lock, NULL, RW_DEFAULT, NULL);
 	rw_init(&connp->conn_ilg_lock, NULL, RW_DEFAULT, NULL);
 	connp->conn_ixa = kmem_zalloc(sizeof (ip_xmit_attr_t), kmflags);
 	if (connp->conn_ixa == NULL)
@@ -2116,6 +2141,7 @@
 	mutex_destroy(&connp->conn_lock);
 	cv_destroy(&connp->conn_cv);
 	rw_destroy(&connp->conn_ilg_lock);
+	rw_destroy(&icmp->icmp_bpf_lock);
 
 	/* Can be NULL if constructor failed */
 	if (connp->conn_ixa != NULL) {
--- old/usr/src/uts/common/inet/ipclassifier.h	Wed Oct  5 00:32:21 2016
+++ new/usr/src/uts/common/inet/ipclassifier.h	Wed Oct  5 00:32:21 2016
@@ -21,6 +21,7 @@
 /*
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
  */
 
 #ifndef	_INET_IPCLASSIFIER_H
@@ -293,7 +294,8 @@
 		conn_ipv6_recvpathmtu : 1,	/* IPV6_RECVPATHMTU */
 		conn_mcbc_bind : 1,		/* Bound to multi/broadcast */
 
-		conn_pad_to_bit_31 : 12;
+		conn_reuseport : 1,		/* SO_REUSEPORT state */
+		conn_pad_to_bit_31 : 11;
 
 	boolean_t	conn_blocked;		/* conn is flow-controlled */
 
--- old/usr/src/uts/common/inet/rawip_impl.h	Wed Oct  5 00:32:21 2016
+++ new/usr/src/uts/common/inet/rawip_impl.h	Wed Oct  5 00:32:21 2016
@@ -20,6 +20,7 @@
  */
 /*
  * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 /* Copyright (c) 1990 Mentat Inc. */
 
@@ -43,6 +44,7 @@
 #include <inet/ip.h>
 #include <inet/optcom.h>
 #include <inet/tunables.h>
+#include <inet/bpf.h>
 
 /*
  * ICMP stack instances
@@ -84,6 +86,10 @@
 	mblk_t		*icmp_fallback_queue_head;
 	mblk_t		*icmp_fallback_queue_tail;
 	struct sockaddr_storage	icmp_delayed_addr;
+
+	krwlock_t	icmp_bpf_lock;	/* protects icmp_bpf */
+	ip_bpf_insn_t	*icmp_bpf_prog; /* SO_ATTACH_FILTER bpf */
+	uint_t		icmp_bpf_len;
 } icmp_t;
 
 /*
--- old/usr/src/uts/common/inet/sockmods/datafilt.c	Wed Oct  5 00:32:21 2016
+++ new/usr/src/uts/common/inet/sockmods/datafilt.c	Wed Oct  5 00:32:21 2016
@@ -85,8 +85,13 @@
 {
 	int error;
 
-	if ((error = sof_register(SOF_VERSION, DATAFILT_MODULE, &dataf_ops, 0))
-	    != 0)
+	/*
+	 * This module is safe to attach even after some preliminary socket
+	 * setup calls have taken place. See the comment for SOF_ATT_SAFE.
+	 */
+	error = sof_register(SOF_VERSION, DATAFILT_MODULE, &dataf_ops,
+	    SOF_ATT_SAFE);
+	if (error != 0)
 		return (error);
 	if ((error = mod_install(&dataf_modlinkage)) != 0)
 		(void) sof_unregister(DATAFILT_MODULE);
--- old/usr/src/uts/common/inet/sockmods/sockmod_pfp.c	Wed Oct  5 00:32:21 2016
+++ new/usr/src/uts/common/inet/sockmods/sockmod_pfp.c	Wed Oct  5 00:32:21 2016
@@ -21,7 +21,7 @@
 
 /*
  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2015 Joyent, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 
 #include <sys/types.h>
@@ -51,6 +51,7 @@
 #include <sys/mac_client.h>
 #include <sys/mac_provider.h>
 #include <sys/mac_client_priv.h>
+#include <inet/bpf.h>
 
 #include <netpacket/packet.h>
 
@@ -448,7 +449,7 @@
 			buffer = (uchar_t *)mp;
 		}
 		rw_enter(&ps->ps_bpflock, RW_READER);
-		if (bpf_filter(ps->ps_bpf.bf_insns, buffer,
+		if (ip_bpf_filter((ip_bpf_insn_t *)ps->ps_bpf.bf_insns, buffer,
 		    hdr.mhi_pktsize, buflen) == 0) {
 			rw_exit(&ps->ps_bpflock);
 			ps->ps_stats.tp_drops++;
@@ -1336,7 +1337,7 @@
     const void *optval, socklen_t optlen)
 {
 	struct bpf_program prog;
-	struct bpf_insn *fcode;
+	ip_bpf_insn_t *fcode;
 	struct pfpsock *ps;
 	struct sock_proto_props sopp;
 	int error = 0;
@@ -1370,10 +1371,10 @@
 			return (EFAULT);
 		}
 
-		if (bpf_validate(fcode, (int)prog.bf_len)) {
+		if (ip_bpf_validate(fcode, prog.bf_len)) {
 			rw_enter(&ps->ps_bpflock, RW_WRITER);
 			pfp_release_bpf(ps);
-			ps->ps_bpf.bf_insns = fcode;
+			ps->ps_bpf.bf_insns = (struct bpf_insn *)fcode;
 			ps->ps_bpf.bf_len = size;
 			rw_exit(&ps->ps_bpflock);
 
--- old/usr/src/uts/common/inet/tcp.h	Wed Oct  5 00:32:22 2016
+++ new/usr/src/uts/common/inet/tcp.h	Wed Oct  5 00:32:22 2016
@@ -20,7 +20,7 @@
  */
 /*
  * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2011, Joyent, Inc. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
  * Copyright (c) 2011 Nexenta Systems, Inc. All rights reserved.
  * Copyright (c) 2014 by Delphix. All rights reserved.
  */
@@ -134,6 +134,7 @@
 
 struct conn_s;
 struct tcp_listen_cnt_s;
+struct tcp_rg_s;
 
 /*
  * Control structure for each open TCP stream,
@@ -404,6 +405,13 @@
 	struct tcp_s *tcp_bind_hash_port; /* tcp_t's bound to the same lport */
 	struct tcp_s **tcp_ptpbhn;
 
+	/*
+	 * Group of tcp_t entries bound to the same adress and port via
+	 * SO_REUSEPORT.  The pointer itself is protected by tf_lock in the
+	 * containing tcps_bind_fanout slot.
+	 */
+	struct tcp_rg_s	*tcp_rg_bind;
+
 	uint_t		tcp_maxpsz_multiplier;
 
 	uint32_t	tcp_lso_max; /* maximum LSO payload */
--- old/usr/src/uts/common/inet/tcp/tcp.c	Wed Oct  5 00:32:22 2016
+++ new/usr/src/uts/common/inet/tcp/tcp.c	Wed Oct  5 00:32:22 2016
@@ -21,7 +21,7 @@
 
 /*
  * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2011, Joyent Inc. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
  * Copyright (c) 2011 Nexenta Systems, Inc. All rights reserved.
  * Copyright (c) 2013,2014 by Delphix. All rights reserved.
  * Copyright 2014, OmniTI Computer Consulting, Inc. All rights reserved.
@@ -1423,6 +1423,21 @@
 	tcp_close_mpp(&tcp->tcp_conn.tcp_eager_conn_ind);
 
 	/*
+	 * Destroy any association with SO_REUSEPORT group.
+	 */
+	if (tcp->tcp_rg_bind != NULL) {
+		/*
+		 * This is only necessary for connections which enabled
+		 * SO_REUSEPORT but were never bound.  Such connections should
+		 * be the one and only member of the tcp_rg_tp to which they
+		 * have been associated.
+		 */
+		VERIFY(tcp_rg_remove(tcp->tcp_rg_bind, tcp));
+		tcp_rg_destroy(tcp->tcp_rg_bind);
+		tcp->tcp_rg_bind = NULL;
+	}
+
+	/*
 	 * If this is a non-STREAM socket still holding on to an upper
 	 * handle, release it. As a result of fallback we might also see
 	 * STREAMS based conns with upper handles, in which case there is
@@ -2054,8 +2069,7 @@
  * structure!
  */
 static void
-tcp_reinit_values(tcp)
-	tcp_t *tcp;
+tcp_reinit_values(tcp_t *tcp)
 {
 	tcp_stack_t	*tcps = tcp->tcp_tcps;
 	conn_t		*connp = tcp->tcp_connp;
--- old/usr/src/uts/common/inet/tcp/tcp_bind.c	Wed Oct  5 00:32:22 2016
+++ new/usr/src/uts/common/inet/tcp/tcp_bind.c	Wed Oct  5 00:32:22 2016
@@ -22,6 +22,7 @@
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 
 #include <sys/types.h>
@@ -55,6 +56,7 @@
 static int	tcp_bind_select_lport(tcp_t *, in_port_t *, boolean_t,
 		    cred_t *cr);
 static in_port_t	tcp_get_next_priv_port(const tcp_t *);
+static int tcp_rg_insert(tcp_rg_t *, struct tcp_s *);
 
 /*
  * Hash list insertion routine for tcp_t structures. Each hash bucket
@@ -172,6 +174,16 @@
 
 	ASSERT(lockp != NULL);
 	mutex_enter(lockp);
+
+	/* destroy any association with SO_REUSEPORT group */
+	if (tcp->tcp_rg_bind != NULL) {
+		if (tcp_rg_remove(tcp->tcp_rg_bind, tcp)) {
+			/* Last one out turns off the lights */
+			tcp_rg_destroy(tcp->tcp_rg_bind);
+		}
+		tcp->tcp_rg_bind = NULL;
+	}
+
 	if (tcp->tcp_ptpbhn) {
 		tcpnext = tcp->tcp_bind_hash_port;
 		if (tcpnext != NULL) {
@@ -636,13 +648,12 @@
 }
 
 /*
- * If the "bind_to_req_port_only" parameter is set, if the requested port
- * number is available, return it, If not return 0
+ * If the "bind_to_req_port_only" parameter is set and the requested port
+ * number is available, return it (else return 0).
  *
- * If "bind_to_req_port_only" parameter is not set and
- * If the requested port number is available, return it.  If not, return
- * the first anonymous port we happen across.  If no anonymous ports are
- * available, return 0. addr is the requested local address, if any.
+ * If "bind_to_req_port_only" parameter is not set and the requested port
+ * number is available, return it.  If not, return the first anonymous port we
+ * happen across.  If no anonymous ports are available, return 0.
  *
  * In either case, when succeeding update the tcp_t to record the port number
  * and insert it in the bind hash table.
@@ -662,6 +673,7 @@
 	int loopmax;
 	conn_t *connp = tcp->tcp_connp;
 	tcp_stack_t	*tcps = tcp->tcp_tcps;
+	boolean_t reuseport = connp->conn_reuseport;
 
 	/*
 	 * Lookup for free addresses is done in a loop and "loopmax"
@@ -698,6 +710,7 @@
 		tf_t		*tbf;
 		tcp_t		*ltcp;
 		conn_t		*lconnp;
+		boolean_t	attempt_reuse = B_FALSE;
 
 		lport = htons(port);
 
@@ -724,6 +737,7 @@
 		for (; ltcp != NULL; ltcp = ltcp->tcp_bind_hash_port) {
 			boolean_t not_socket;
 			boolean_t exclbind;
+			boolean_t addrmatch;
 
 			lconnp = ltcp->tcp_connp;
 
@@ -829,22 +843,35 @@
 			    &lconnp->conn_faddr_v6)))
 				continue;
 
+			addrmatch = IN6_ARE_ADDR_EQUAL(laddr,
+			    &lconnp->conn_bound_addr_v6);
+
+			if (addrmatch && reuseport && bind_to_req_port_only &&
+			    (ltcp->tcp_state == TCPS_BOUND ||
+			    ltcp->tcp_state == TCPS_LISTEN)) {
+				/*
+				 * This entry is bound to the exact same
+				 * address and port.  If SO_REUSEPORT is set on
+				 * the calling socket, attempt to reuse this
+				 * binding if it too had SO_REUSEPORT enabled
+				 * when it was bound.
+				 */
+				attempt_reuse = (ltcp->tcp_rg_bind != NULL);
+				break;
+			}
+
 			if (!reuseaddr) {
 				/*
-				 * No socket option SO_REUSEADDR.
-				 * If existing port is bound to
-				 * a non-wildcard IP address
-				 * and the requesting stream is
-				 * bound to a distinct
-				 * different IP addresses
-				 * (non-wildcard, also), keep
-				 * going.
+				 * No socket option SO_REUSEADDR.  If an
+				 * existing port is bound to a non-wildcard IP
+				 * address and the requesting stream is bound
+				 * to a distinct different IP address
+				 * (non-wildcard, also), keep going.
 				 */
 				if (!V6_OR_V4_INADDR_ANY(*laddr) &&
 				    !V6_OR_V4_INADDR_ANY(
 				    lconnp->conn_bound_addr_v6) &&
-				    !IN6_ARE_ADDR_EQUAL(laddr,
-				    &lconnp->conn_bound_addr_v6))
+				    !addrmatch)
 					continue;
 				if (ltcp->tcp_state >= TCPS_BOUND) {
 					/*
@@ -859,27 +886,49 @@
 				 * socket option SO_REUSEADDR is set on the
 				 * binding tcp_t.
 				 *
-				 * If two streams are bound to
-				 * same IP address or both addr
-				 * and bound source are wildcards
-				 * (INADDR_ANY), we want to stop
-				 * searching.
-				 * We have found a match of IP source
-				 * address and source port, which is
-				 * refused regardless of the
-				 * SO_REUSEADDR setting, so we break.
+				 * If two streams are bound to the same IP
+				 * address or both addr and bound source are
+				 * wildcards (INADDR_ANY), we want to stop
+				 * searching.  We have found a match of IP
+				 * source address and source port, which is
+				 * refused regardless of the SO_REUSEADDR
+				 * setting, so we break.
 				 */
-				if (IN6_ARE_ADDR_EQUAL(laddr,
-				    &lconnp->conn_bound_addr_v6) &&
+				if (addrmatch &&
 				    (ltcp->tcp_state == TCPS_LISTEN ||
 				    ltcp->tcp_state == TCPS_BOUND))
 					break;
 			}
 		}
-		if (ltcp != NULL) {
+		if (ltcp != NULL && !attempt_reuse) {
 			/* The port number is busy */
 			mutex_exit(&tbf->tf_lock);
 		} else {
+			if (attempt_reuse) {
+				int err;
+				struct tcp_rg_s *rg;
+
+				ASSERT(ltcp != NULL);
+				ASSERT(ltcp->tcp_rg_bind != NULL);
+				ASSERT(tcp->tcp_rg_bind != NULL);
+				ASSERT(ltcp->tcp_rg_bind != tcp->tcp_rg_bind);
+
+				err = tcp_rg_insert(ltcp->tcp_rg_bind, tcp);
+				if (err != 0) {
+					mutex_exit(&tbf->tf_lock);
+					return (0);
+				}
+				/*
+				 * Now that the newly-binding socket has joined
+				 * the existing reuseport group on ltcp, it
+				 * should clean up its own (empty) group.
+				 */
+				rg = tcp->tcp_rg_bind;
+				tcp->tcp_rg_bind = ltcp->tcp_rg_bind;
+				VERIFY(tcp_rg_remove(rg, tcp));
+				tcp_rg_destroy(rg);
+			}
+
 			/*
 			 * This port is ours. Insert in fanout and mark as
 			 * bound to prevent others from getting the port
@@ -943,4 +992,126 @@
 		 */
 	} while (++count < loopmax);
 	return (0);
+}
+
+/* Max number of members in TCP SO_REUSEPORT group */
+#define	TCP_RG_SIZE_MAX		64
+/* Step size when expanding members array */
+#define	TCP_RG_SIZE_STEP	2
+
+
+tcp_rg_t *
+tcp_rg_init(tcp_t *tcp)
+{
+	tcp_rg_t *rg;
+	rg = kmem_alloc(sizeof (tcp_rg_t), KM_NOSLEEP|KM_NORMALPRI);
+	if (rg == NULL)
+		return (NULL);
+	rg->tcprg_members = kmem_zalloc(2 * sizeof (tcp_t *),
+	    KM_NOSLEEP|KM_NORMALPRI);
+	if (rg->tcprg_members == NULL) {
+		kmem_free(rg, sizeof (tcp_rg_t));
+		return (NULL);
+	}
+
+	mutex_init(&rg->tcprg_lock, NULL, MUTEX_DEFAULT, NULL);
+	rg->tcprg_size = 2;
+	rg->tcprg_count = 1;
+	rg->tcprg_active = 1;
+	rg->tcprg_members[0] = tcp;
+	return (rg);
+}
+
+void
+tcp_rg_destroy(tcp_rg_t *rg)
+{
+	mutex_enter(&rg->tcprg_lock);
+	ASSERT(rg->tcprg_count == 0);
+	ASSERT(rg->tcprg_active == 0);
+	kmem_free(rg->tcprg_members, rg->tcprg_size * sizeof (tcp_t *));
+	mutex_destroy(&rg->tcprg_lock);
+	kmem_free(rg, sizeof (struct tcp_rg_s));
+}
+
+static int
+tcp_rg_insert(tcp_rg_t *rg, tcp_t *tcp)
+{
+	mutex_enter(&rg->tcprg_lock);
+
+	VERIFY(rg->tcprg_size > 0);
+	VERIFY(rg->tcprg_count <= rg->tcprg_size);
+	if (rg->tcprg_count != 0) {
+		cred_t *oldcred = rg->tcprg_members[0]->tcp_connp->conn_cred;
+		cred_t *newcred = tcp->tcp_connp->conn_cred;
+
+		if (crgetuid(oldcred) != crgetuid(newcred) ||
+		    crgetzoneid(oldcred) != crgetzoneid(newcred)) {
+			mutex_exit(&rg->tcprg_lock);
+			return (EPERM);
+		}
+	}
+
+	if (rg->tcprg_count == rg->tcprg_size) {
+		unsigned int oldalloc = rg->tcprg_size * sizeof (tcp_t *);
+		unsigned int newsize = rg->tcprg_size + TCP_RG_SIZE_STEP;
+		tcp_t **newmembers;
+
+		if (newsize > TCP_RG_SIZE_MAX) {
+			mutex_exit(&rg->tcprg_lock);
+			return (EINVAL);
+		}
+		newmembers = kmem_zalloc(newsize * sizeof (tcp_t *),
+		    KM_NOSLEEP|KM_NORMALPRI);
+		if (newmembers == NULL) {
+			mutex_exit(&rg->tcprg_lock);
+			return (ENOMEM);
+		}
+		bcopy(rg->tcprg_members, newmembers, oldalloc);
+		kmem_free(rg->tcprg_members, oldalloc);
+		rg->tcprg_members = newmembers;
+		rg->tcprg_size = newsize;
+	}
+
+	rg->tcprg_members[rg->tcprg_count] = tcp;
+	rg->tcprg_count++;
+	rg->tcprg_active++;
+
+	mutex_exit(&rg->tcprg_lock);
+	return (0);
+}
+
+boolean_t
+tcp_rg_remove(tcp_rg_t *rg, tcp_t *tcp)
+{
+	int i;
+	boolean_t is_empty;
+
+	mutex_enter(&rg->tcprg_lock);
+	for (i = 0; i < rg->tcprg_count; i++) {
+		if (rg->tcprg_members[i] == tcp)
+			break;
+	}
+	/* The item should be present */
+	ASSERT(i < rg->tcprg_count);
+	/* Move the last member into this position */
+	rg->tcprg_count--;
+	rg->tcprg_members[i] = rg->tcprg_members[rg->tcprg_count];
+	rg->tcprg_members[rg->tcprg_count] = NULL;
+	if (tcp->tcp_connp->conn_reuseport != 0)
+		rg->tcprg_active--;
+	is_empty = (rg->tcprg_count == 0);
+	mutex_exit(&rg->tcprg_lock);
+	return (is_empty);
+}
+
+void
+tcp_rg_setactive(tcp_rg_t *rg, boolean_t is_active)
+{
+	mutex_enter(&rg->tcprg_lock);
+	if (is_active) {
+		rg->tcprg_active++;
+	} else {
+		rg->tcprg_active--;
+	}
+	mutex_exit(&rg->tcprg_lock);
 }
--- old/usr/src/uts/common/inet/tcp/tcp_opt_data.c	Wed Oct  5 00:32:22 2016
+++ new/usr/src/uts/common/inet/tcp/tcp_opt_data.c	Wed Oct  5 00:32:22 2016
@@ -21,6 +21,7 @@
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 
 #include <sys/types.h>
@@ -62,7 +63,8 @@
 { SO_USELOOPBACK, SOL_SOCKET, OA_RW, OA_RW, OP_NP, 0, sizeof (int), 0
 	},
 { SO_BROADCAST,	SOL_SOCKET, OA_RW, OA_RW, OP_NP, 0, sizeof (int), 0 },
-{ SO_REUSEADDR, SOL_SOCKET, OA_RW, OA_RW, OP_NP, 0, sizeof (int), 0 },
+{ SO_REUSEADDR,	SOL_SOCKET, OA_RW, OA_RW, OP_NP, 0, sizeof (int), 0 },
+{ SO_REUSEPORT,	SOL_SOCKET, OA_RW, OA_RW, OP_NP, 0, sizeof (int), 0 },
 { SO_OOBINLINE, SOL_SOCKET, OA_RW, OA_RW, OP_NP, 0, sizeof (int), 0 },
 { SO_TYPE,	SOL_SOCKET, OA_R, OA_R, OP_NP, 0, sizeof (int), 0 },
 { SO_SNDBUF,	SOL_SOCKET, OA_RW, OA_RW, OP_NP, 0, sizeof (int), 0 },
@@ -484,6 +486,104 @@
 }
 
 /*
+ * Set a TCP connection's participation in SO_REUSEPORT.  This operation is
+ * performed under the protection of the squeue via tcp_setsockopt.
+ * The manipulation of tcp_rg_bind, as part of this operation, is subject to
+ * these constraints:
+ * 1. Prior to bind(), tcp_rg_bind can be set/cleared in tcp_set_reuseport
+ *    under the protection of the squeue.
+ * 2. Once the connection has been bound, the tcp_rg_bind pointer must not be
+ *    altered until such time as tcp_free() cleans up the connection.
+ * 3. A connection undergoing bind, which matches to a connection participating
+ *    in port-reuse, will switch its tcp_rg_bind pointer when it joins the
+ *    group of an existing connection in tcp_bindi().
+ */
+static int
+tcp_set_reuseport(conn_t *connp, boolean_t do_enable)
+{
+	tcp_t *tcp = connp->conn_tcp;
+	struct tcp_rg_s *rg;
+
+	if (!IPCL_IS_NONSTR(connp)) {
+		if (do_enable) {
+			/*
+			 * SO_REUSEPORT cannot be enabled on sockets which have
+			 * fallen back to the STREAMS API.
+			 */
+			return (EINVAL);
+		} else {
+			/*
+			 * A connection with SO_REUSEPORT enabled should be
+			 * prevented from falling back to STREAMS mode via
+			 * logic in tcp_fallback.  It is legal, however, for
+			 * fallen-back connections to affirm the disabled state
+			 * of SO_REUSEPORT.
+			 */
+			ASSERT(connp->conn_reuseport == 0);
+			return (0);
+		}
+	}
+	if (tcp->tcp_state <= TCPS_CLOSED) {
+		return (EINVAL);
+	}
+	if (connp->conn_reuseport == 0 && do_enable) {
+		/* disabled -> enabled */
+		if (tcp->tcp_rg_bind != NULL) {
+			tcp_rg_setactive(tcp->tcp_rg_bind, do_enable);
+		} else {
+			/*
+			 * Connection state is not a concern when initially
+			 * populating tcp_rg_bind.  Setting it to non-NULL on a
+			 * bound or listening connection would only mean that
+			 * new reused-port binds become a possibility.
+			 */
+			if ((rg = tcp_rg_init(tcp)) == NULL) {
+				return (ENOMEM);
+			}
+			tcp->tcp_rg_bind = rg;
+		}
+		connp->conn_reuseport = 1;
+	} else if (connp->conn_reuseport != 0 && !do_enable) {
+		/* enabled -> disabled */
+		ASSERT(tcp->tcp_rg_bind != NULL);
+		if (tcp->tcp_state == TCPS_IDLE) {
+			/*
+			 * If the connection has not been bound yet, discard
+			 * the reuse group state.  Since disabling SO_REUSEPORT
+			 * on a bound socket will _not_ prevent others from
+			 * reusing the port, the presence of tcp_rg_bind is
+			 * used to determine reuse availability, not
+			 * conn_reuseport.
+			 *
+			 * This allows proper behavior for examples such as:
+			 *
+			 * setsockopt(fd1, ... SO_REUSEPORT, &on_val...);
+			 * bind(fd1, &myaddr, ...);
+			 * setsockopt(fd1, ... SO_REUSEPORT, &off_val...);
+			 *
+			 * setsockopt(fd2, ... SO_REUSEPORT, &on_val...);
+			 * bind(fd2, &myaddr, ...); // <- SHOULD SUCCEED
+			 *
+			 */
+			rg = tcp->tcp_rg_bind;
+			tcp->tcp_rg_bind = NULL;
+			VERIFY(tcp_rg_remove(rg, tcp));
+			tcp_rg_destroy(rg);
+		} else {
+			/*
+			 * If a connection has been bound, it's no longer safe
+			 * to manipulate tcp_rg_bind until connection clean-up
+			 * during tcp_free.  Just mark the member status of the
+			 * connection as inactive.
+			 */
+			tcp_rg_setactive(tcp->tcp_rg_bind, do_enable);
+		}
+		connp->conn_reuseport = 0;
+	}
+	return (0);
+}
+
+/*
  * We declare as 'int' rather than 'void' to satisfy pfi_t arg requirements.
  * Parameters are assumed to be verified by the caller.
  */
@@ -653,6 +753,11 @@
 			}
 			*outlenp = inlen;
 			return (0);
+		case SO_REUSEPORT:
+			if (!checkonly) {
+				return (tcp_set_reuseport(connp, *i1 != 0));
+			}
+			return (0);
 		}
 		break;
 	case IPPROTO_TCP:
@@ -769,14 +874,37 @@
 			if (*i1 == 0) {
 				return (EINVAL);
 			} else if (tcp->tcp_ka_rinterval == 0) {
-				if ((tcp->tcp_ka_abort_thres / *i1) <
-				    tcp->tcp_rto_min ||
-				    (tcp->tcp_ka_abort_thres / *i1) >
-				    tcp->tcp_rto_max)
-					return (EINVAL);
+				/*
+				 * When TCP_KEEPCNT is specified without first
+				 * specifying a TCP_KEEPINTVL, we infer an
+				 * interval based on a tunable specific to our
+				 * stack: the tcp_keepalive_abort_interval.
+				 * (Or the TCP_KEEPALIVE_ABORT_THRESHOLD, in
+				 * the unlikely event that that has been set.)
+				 * Given the abort interval's default value of
+				 * 480 seconds, low TCP_KEEPCNT values can
+				 * result in intervals that exceed the default
+				 * maximum RTO of 60 seconds.  Rather than
+				 * fail in these cases, we (implicitly) clamp
+				 * the interval at the maximum RTO; if the
+				 * TCP_KEEPCNT is shortly followed by a
+				 * TCP_KEEPINTVL (as we expect), the abort
+				 * threshold will be recalculated correctly --
+				 * and if a TCP_KEEPINTVL is not forthcoming,
+				 * keep-alive will at least operate reasonably
+				 * given the underconfigured state.
+				 */
+				uint32_t interval;
 
-				tcp->tcp_ka_rinterval =
-				    tcp->tcp_ka_abort_thres / *i1;
+				interval = tcp->tcp_ka_abort_thres / *i1;
+
+				if (interval < tcp->tcp_rto_min)
+					interval = tcp->tcp_rto_min;
+
+				if (interval > tcp->tcp_rto_max)
+					interval = tcp->tcp_rto_max;
+
+				tcp->tcp_ka_rinterval = interval;
 			} else {
 				if ((*i1 * tcp->tcp_ka_rinterval) <
 				    tcps->tcps_keepalive_abort_interval_low ||
@@ -953,10 +1081,6 @@
 		}
 		break;
 	case IPPROTO_IP:
-		if (connp->conn_family != AF_INET) {
-			*outlenp = 0;
-			return (EINVAL);
-		}
 		switch (name) {
 		case IP_SEC_OPT:
 			/*
--- old/usr/src/uts/common/inet/tcp/tcp_socket.c	Wed Oct  5 00:32:22 2016
+++ new/usr/src/uts/common/inet/tcp/tcp_socket.c	Wed Oct  5 00:32:22 2016
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
  */
 
 /* This file contains all TCP kernel socket related functions. */
@@ -1021,6 +1022,16 @@
 		return (ENOMEM);
 	}
 
+	/*
+	 * Do not allow fallback on connections making use of SO_REUSEPORT.
+	 */
+	if (tcp->tcp_rg_bind != NULL) {
+		freeb(stropt_mp);
+		freeb(ordrel_mp);
+		squeue_synch_exit(connp);
+		return (EINVAL);
+	}
+
 	/*
 	 * Both endpoints must be of the same type (either STREAMS or
 	 * non-STREAMS) for fusion to be enabled. So if we are fused,
--- old/usr/src/uts/common/inet/tcp_impl.h	Wed Oct  5 00:32:22 2016
+++ new/usr/src/uts/common/inet/tcp_impl.h	Wed Oct  5 00:32:22 2016
@@ -406,6 +406,22 @@
 	uint32_t	tlc_drop;
 } tcp_listen_cnt_t;
 
+/*
+ * Track tcp_t entities bound to the same port/address tuple via SO_REUSEPORT.
+ * - tcprg_lock:	Protects the other fields
+ * - tcprg_size:	Allocated size (in entries) of tcprg_members array
+ * - tcprg_count:	Count of occupied tcprg_members slots
+ * - tcprg_active:	Count of members which still have SO_REUSEPORT set
+ * - tcprg_members:	Connections associated with address/port group
+ */
+typedef struct tcp_rg_s {
+	kmutex_t	tcprg_lock;
+	unsigned int	tcprg_size;
+	unsigned int	tcprg_count;
+	unsigned int	tcprg_active;
+	tcp_t		**tcprg_members;
+} tcp_rg_t;
+
 #define	TCP_TLC_REPORT_INTERVAL	(30 * MINUTES)
 
 #define	TCP_DECR_LISTEN_CNT(tcp)					\
@@ -649,6 +665,10 @@
 			    int, boolean_t, boolean_t, boolean_t);
 extern in_port_t	tcp_update_next_port(in_port_t, const tcp_t *,
 			    boolean_t);
+extern tcp_rg_t *tcp_rg_init(tcp_t *);
+extern boolean_t tcp_rg_remove(tcp_rg_t *, tcp_t *);
+extern void tcp_rg_destroy(tcp_rg_t *);
+extern void tcp_rg_setactive(tcp_rg_t *, boolean_t);
 
 /*
  * Fusion related functions in tcp_fusion.c.
--- old/usr/src/uts/common/inet/udp/udp.c	Wed Oct  5 00:32:23 2016
+++ new/usr/src/uts/common/inet/udp/udp.c	Wed Oct  5 00:32:23 2016
@@ -1583,6 +1583,11 @@
 			*i1 = udp->udp_rcvhdr ? 1 : 0;
 			mutex_exit(&connp->conn_lock);
 			return (sizeof (int));
+		case UDP_SND_TO_CONNECTED:
+			mutex_enter(&connp->conn_lock);
+			*i1 = udp->udp_snd_to_conn ? 1 : 0;
+			mutex_exit(&connp->conn_lock);
+			return (sizeof (int));
 		}
 	}
 	mutex_enter(&connp->conn_lock);
@@ -1718,6 +1723,11 @@
 			udp->udp_rcvhdr = onoff;
 			mutex_exit(&connp->conn_lock);
 			return (0);
+		case UDP_SND_TO_CONNECTED:
+			mutex_enter(&connp->conn_lock);
+			udp->udp_snd_to_conn = onoff;
+			mutex_exit(&connp->conn_lock);
+			return (0);
 		}
 		break;
 	}
@@ -5910,6 +5920,7 @@
 	ushort_t	ipversion;
 	pid_t		pid = curproc->p_pid;
 	ip_xmit_attr_t	*ixa;
+	boolean_t	snd_to_conn;
 
 	ASSERT(DB_TYPE(mp) == M_DATA);
 
@@ -5947,10 +5958,21 @@
 		else
 			return (error);
 	}
-	if (udp->udp_state == TS_DATA_XFER) {
+
+	/*
+	 * Check if we're allowed to send to a connection on which we've
+	 * already called 'connect'. The posix spec. allows both behaviors but
+	 * historically we've returned an error if already connected. The
+	 * client can allow this via a sockopt.
+	 */
+	mutex_enter(&connp->conn_lock);
+	snd_to_conn = (udp->udp_snd_to_conn != 0);
+	mutex_exit(&connp->conn_lock);
+	if (udp->udp_state == TS_DATA_XFER && !snd_to_conn) {
 		UDPS_BUMP_MIB(us, udpOutErrors);
 		return (EISCONN);
 	}
+
 	error = proto_verify_ip_addr(connp->conn_family,
 	    (struct sockaddr *)msg->msg_name, msg->msg_namelen);
 	if (error != 0) {
--- old/usr/src/uts/common/inet/udp/udp_opt_data.c	Wed Oct  5 00:32:23 2016
+++ new/usr/src/uts/common/inet/udp/udp_opt_data.c	Wed Oct  5 00:32:23 2016
@@ -292,6 +292,8 @@
 	},
 { UDP_NAT_T_ENDPOINT, IPPROTO_UDP, OA_RW, OA_RW, OP_PRIVPORT, 0, sizeof (int),
 	0 },
+{ UDP_SND_TO_CONNECTED, IPPROTO_UDP, OA_R, OA_RW, OP_CONFIG, 0, sizeof (int),
+	0 }
 };
 
 /*
--- old/usr/src/uts/common/inet/udp_impl.h	Wed Oct  5 00:32:23 2016
+++ new/usr/src/uts/common/inet/udp_impl.h	Wed Oct  5 00:32:23 2016
@@ -178,8 +178,9 @@
 		udp_issocket : 1,	/* socket mode; sockfs is on top */
 		udp_nat_t_endpoint : 1,	/* UDP_NAT_T_ENDPOINT option */
 		udp_rcvhdr : 1,		/* UDP_RCVHDR option */
+		udp_snd_to_conn: 1,	/* UDP_SND_TO_CONNECTED option */
 
-		udp_pad_to_bit_31 : 29;
+		udp_pad_to_bit_31 : 28;
 
 	/* Following 2 fields protected by the uf_lock */
 	struct udp_s	*udp_bind_hash; /* Bind hash chain */
--- /dev/null	Wed Oct  5 00:32:23 2016
+++ new/usr/src/uts/common/io/bpf/bpf_wrap.c	Wed Oct  5 00:32:23 2016
@@ -0,0 +1,35 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#include <sys/types.h>
+#include <net/bpf.h>
+#include <inet/bpf.h>
+
+/*
+ * With BPF filter validation and evaluation moved into the 'ip' module, these
+ * wrapper functions are provided to expose the original interface.
+ */
+
+uint_t
+bpf_filter(struct bpf_insn *pc, uchar_t *p, uint_t wirelen, uint_t buflen)
+{
+	return ((uint_t)ip_bpf_filter((ip_bpf_insn_t *)pc, p, wirelen, buflen));
+}
+
+int
+bpf_validate(struct bpf_insn *f, int len)
+{
+	return ((int)ip_bpf_validate((ip_bpf_insn_t *)f, (uint_t)len));
+}
--- old/usr/src/uts/common/io/cons.c	Wed Oct  5 00:32:23 2016
+++ new/usr/src/uts/common/io/cons.c	Wed Oct  5 00:32:23 2016
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 1982, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015, Joyent, Inc. All rights reserved.
  */
 
 /*
@@ -53,6 +54,7 @@
 #include <sys/vnode.h>
 #include <sys/uio.h>
 #include <sys/stat.h>
+#include <sys/limits.h>
 
 #include <sys/console.h>
 #include <sys/consdev.h>
@@ -414,14 +416,24 @@
 	 */
 	if (vsconsvp != NULL && vsconsvp->v_stream != NULL) {
 		struiod_t uiod;
+		struct iovec buf[IOV_MAX_STACK];
+		int iovlen = 0;
 
+		if (uio->uio_iovcnt > IOV_MAX_STACK) {
+			iovlen = uio->uio_iovcnt * sizeof (iovec_t);
+			uiod.d_iov = kmem_alloc(iovlen, KM_SLEEP);
+		} else {
+			uiod.d_iov = buf;
+		}
+
 		/*
 		 * strwrite modifies uio so need to make copy.
 		 */
-		(void) uiodup(uio, &uiod.d_uio, uiod.d_iov,
-		    sizeof (uiod.d_iov) / sizeof (*uiod.d_iov));
+		(void) uiodup(uio, &uiod.d_uio, uiod.d_iov, uio->uio_iovcnt);
 
 		(void) strwrite(vsconsvp, &uiod.d_uio, cred);
+		if (iovlen != 0)
+			kmem_free(uiod.d_iov, iovlen);
 	}
 
 	if (rconsvp->v_stream != NULL)
--- old/usr/src/uts/common/io/devpoll.c	Wed Oct  5 00:32:23 2016
+++ new/usr/src/uts/common/io/devpoll.c	Wed Oct  5 00:32:23 2016
@@ -353,8 +353,9 @@
 					pdp->pd_fp = NULL;
 					pdp->pd_events = 0;
 
-					if (php != NULL) {
-						pollhead_delete(php, pdp);
+					if (pdp->pd_php != NULL) {
+						pollhead_delete(pdp->pd_php,
+						    pdp);
 						pdp->pd_php = NULL;
 					}
 
@@ -503,8 +504,9 @@
 					pdp->pd_fp = NULL;
 					pdp->pd_events = 0;
 
-					if (php != NULL) {
-						pollhead_delete(php, pdp);
+					if (pdp->pd_php != NULL) {
+						pollhead_delete(pdp->pd_php,
+						    pdp);
 						pdp->pd_php = NULL;
 					}
 
@@ -639,6 +641,7 @@
 	uintptr_t	limit;
 	int		error, size;
 	ssize_t		uiosize;
+	size_t		copysize;
 	nfds_t		pollfdnum;
 	struct pollhead	*php = NULL;
 	polldat_t	*pdp;
@@ -704,11 +707,19 @@
 	 * here for every call.
 	 */
 	uiop->uio_loffset = 0;
-	if ((error = uiomove((caddr_t)pollfdp, uiosize, UIO_WRITE, uiop))
-	    != 0) {
+
+	/*
+	 * Use uiocopy instead of uiomove when populating pollfdp, keeping
+	 * uio_resid untouched for now.  Write syscalls will translate EINTR
+	 * into a success if they detect "successfully transfered" data via an
+	 * updated uio_resid.  Falsely suppressing such errors is disastrous.
+	 */
+	if ((error = uiocopy((caddr_t)pollfdp, uiosize, UIO_WRITE, uiop,
+	    &copysize)) != 0) {
 		kmem_free(pollfdp, uiosize);
 		return (error);
 	}
+
 	/*
 	 * We are about to enter the core portion of dpwrite(). Make sure this
 	 * write has exclusive access in this portion of the code, i.e., no
@@ -981,6 +992,13 @@
 	cv_broadcast(&dpep->dpe_cv);
 	mutex_exit(&dpep->dpe_lock);
 	kmem_free(pollfdp, uiosize);
+	if (error == 0) {
+		/*
+		 * The state of uio_resid is updated only after the pollcache
+		 * is successfully modified.
+		 */
+		uioskip(uiop, copysize);
+	}
 	return (error);
 }
 
@@ -1123,14 +1141,18 @@
 			void *setp = STRUCT_FGETP(dvpoll, dp_setp);
 
 			if (setp != NULL) {
-				if (copyin(setp, &set, sizeof (set))) {
-					DP_REFRELE(dpep);
-					return (EFAULT);
+				if ((mode & FKIOCTL) != 0) {
+					/* Use the signal set directly */
+					ksetp = (k_sigset_t *)setp;
+				} else {
+					if (copyin(setp, &set, sizeof (set))) {
+						DP_REFRELE(dpep);
+						return (EFAULT);
+					}
+					sigutok(&set, &kset);
+					ksetp = &kset;
 				}
 
-				sigutok(&set, &kset);
-				ksetp = &kset;
-
 				mutex_enter(&p->p_lock);
 				schedctl_finish_sigblock(t);
 				lwp->lwp_sigoldmask = t->t_hold;
@@ -1279,6 +1301,10 @@
 		DP_SIGMASK_RESTORE(ksetp);
 
 		if (error == 0 && fdcnt > 0) {
+			/*
+			 * It should be noted that FKIOCTL does not influence
+			 * the copyout (vs bcopy) of dp_fds at this time.
+			 */
 			if (copyout(ps->ps_dpbuf,
 			    STRUCT_FGETP(dvpoll, dp_fds), fdcnt * fdsize)) {
 				DP_REFRELE(dpep);
--- old/usr/src/uts/common/io/eventfd.c	Wed Oct  5 00:32:23 2016
+++ new/usr/src/uts/common/io/eventfd.c	Wed Oct  5 00:32:23 2016
@@ -10,7 +10,7 @@
  */
 
 /*
- * Copyright (c) 2015 Joyent, Inc.  All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 
 /*
@@ -37,6 +37,7 @@
 	kcondvar_t efd_cv;			/* condvar */
 	pollhead_t efd_pollhd;			/* poll head */
 	uint64_t efd_value;			/* value */
+	size_t efd_bwriters;			/* count of blocked writers */
 	eventfd_state_t *efd_next;		/* next state on global list */
 };
 
@@ -126,10 +127,21 @@
 
 	err = uiomove(&val, sizeof (val), UIO_READ, uio);
 
+	/*
+	 * Wake any writers blocked on this eventfd as this read operation may
+	 * have created adequate capacity for their values.
+	 */
+	if (state->efd_bwriters != 0) {
+		cv_broadcast(&state->efd_cv);
+	}
 	mutex_exit(&state->efd_lock);
 
+	/*
+	 * It is necessary to emit POLLOUT events only when the eventfd
+	 * transitions from EVENTFD_VALMAX to a lower value.  At all other
+	 * times, it is already considered writable by poll.
+	 */
 	if (oval == EVENTFD_VALMAX) {
-		cv_broadcast(&state->efd_cv);
 		pollwakeup(&state->efd_pollhd, POLLWRNORM | POLLOUT);
 	}
 
@@ -164,10 +176,13 @@
 			return (EAGAIN);
 		}
 
+		state->efd_bwriters++;
 		if (!cv_wait_sig_swap(&state->efd_cv, &state->efd_lock)) {
+			state->efd_bwriters--;
 			mutex_exit(&state->efd_lock);
 			return (EINTR);
 		}
+		state->efd_bwriters--;
 	}
 
 	/*
@@ -175,10 +190,19 @@
 	 */
 	state->efd_value = (oval = state->efd_value) + val;
 
+	/*
+	 * If the value was previously "empty", notify blocked readers that
+	 * data is available.
+	 */
+	if (oval == 0) {
+		cv_broadcast(&state->efd_cv);
+	}
 	mutex_exit(&state->efd_lock);
 
+	/*
+	 * Notify pollers as well if the eventfd is now readable.
+	 */
 	if (oval == 0) {
-		cv_broadcast(&state->efd_cv);
 		pollwakeup(&state->efd_pollhd, POLLRDNORM | POLLIN);
 	}
 
--- /dev/null	Wed Oct  5 00:32:24 2016
+++ new/usr/src/uts/common/io/inotify.c	Wed Oct  5 00:32:24 2016
@@ -0,0 +1,1505 @@
+/*
+ * 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 (c) 2015 Joyent, Inc.  All rights reserved.
+ * Copyright (c) 2015 The MathWorks, Inc.  All rights reserved.
+ */
+
+/*
+ * Support for the inotify facility, a Linux-borne facility for asynchronous
+ * notification of certain events on specified files or directories.  Our
+ * implementation broadly leverages the file event monitoring facility, and
+ * would actually be quite straightforward were it not for a very serious
+ * blunder in the inotify interface:  in addition to allowing for one to be
+ * notified on events on a particular file or directory, inotify also allows
+ * for one to be notified on certain events on files _within_ a watched
+ * directory -- even though those events have absolutely nothing to do with
+ * the directory itself.  This leads to all sorts of madness because file
+ * operations are (of course) not undertaken on paths but rather on open
+ * files -- and the relationships between open files and the paths that resolve
+ * to those files are neither static nor isomorphic.  We implement this
+ * concept by having _child watches_ when directories are watched with events
+ * in IN_CHILD_EVENTS.  We add child watches when a watch on a directory is
+ * first added, and we modify those child watches dynamically as files are
+ * created, deleted, moved into or moved out of the specified directory.  This
+ * mechanism works well, absent hard links.  Hard links, unfortunately, break
+ * this rather badly, and the user is warned that watches on directories that
+ * have multiple directory entries referring to the same file may behave
+ * unexpectedly.
+ */
+
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/inotify.h>
+#include <sys/fem.h>
+#include <sys/conf.h>
+#include <sys/stat.h>
+#include <sys/vfs_opreg.h>
+#include <sys/vmem.h>
+#include <sys/avl.h>
+#include <sys/sysmacros.h>
+#include <sys/cyclic.h>
+#include <sys/filio.h>
+
+struct inotify_state;
+struct inotify_kevent;
+
+typedef struct inotify_watch inotify_watch_t;
+typedef struct inotify_state inotify_state_t;
+typedef struct inotify_kevent inotify_kevent_t;
+
+struct inotify_watch {
+	kmutex_t inw_lock;			/* lock protecting ref count */
+	int inw_refcnt;				/* reference count */
+	uint8_t inw_zombie:1;			/* boolean: is zombie */
+	uint8_t inw_fired:1;			/* boolean: fired one-shot */
+	uint8_t inw_active:1;			/* boolean: watch is active */
+	uint8_t inw_orphaned:1;			/* boolean: orphaned */
+	kcondvar_t inw_cv;			/* condvar for zombifier */
+	uint32_t inw_mask;			/* mask of watch */
+	int32_t inw_wd;				/* watch descriptor */
+	vnode_t *inw_vp;			/* underlying vnode */
+	inotify_watch_t *inw_parent;		/* parent, if a child */
+	avl_node_t inw_byvp;			/* watches by vnode */
+	avl_node_t inw_bywd;			/* watches by descriptor */
+	avl_tree_t inw_children;		/* children, if a parent */
+	char *inw_name;				/* name, if a child */
+	list_node_t inw_orphan;			/* orphan list */
+	cred_t *inw_cred;			/* cred, if orphaned */
+	inotify_state_t *inw_state;		/* corresponding state */
+};
+
+struct inotify_kevent {
+	inotify_kevent_t *ine_next;		/* next event in queue */
+	struct inotify_event ine_event;		/* event (variable size) */
+};
+
+#define	INOTIFY_EVENT_LENGTH(ev) \
+	(sizeof (inotify_kevent_t) + (ev)->ine_event.len)
+
+struct inotify_state {
+	kmutex_t ins_lock;			/* lock protecting state */
+	avl_tree_t ins_byvp;			/* watches by vnode */
+	avl_tree_t ins_bywd;			/* watches by descriptor */
+	vmem_t *ins_wds;			/* watch identifier arena */
+	int ins_maxwatches;			/* maximum number of watches */
+	int ins_maxevents;			/* maximum number of events */
+	int ins_nevents;			/* current # of events */
+	int32_t ins_size;			/* total size of events */
+	inotify_kevent_t *ins_head;		/* head of event queue */
+	inotify_kevent_t *ins_tail;		/* tail of event queue */
+	pollhead_t ins_pollhd;			/* poll head */
+	kcondvar_t ins_cv;			/* condvar for reading */
+	list_t ins_orphans;			/* orphan list */
+	ddi_periodic_t ins_cleaner;		/* cyclic for cleaning */
+	inotify_watch_t *ins_zombies;		/* zombie watch list */
+	cred_t *ins_cred;			/* creator's credentials */
+	inotify_state_t *ins_next;		/* next state on global list */
+};
+
+/*
+ * Tunables (exported read-only in lx-branded zones via /proc).
+ */
+int	inotify_maxwatches = 8192;		/* max watches per instance */
+int	inotify_maxevents = 16384;		/* max events */
+int	inotify_maxinstances = 128;		/* max instances per user */
+
+/*
+ * Internal global variables.
+ */
+static kmutex_t		inotify_lock;		/* lock protecting state */
+static dev_info_t	*inotify_devi;		/* device info */
+static fem_t		*inotify_femp;		/* FEM pointer */
+static vmem_t		*inotify_minor;		/* minor number arena */
+static void		*inotify_softstate;	/* softstate pointer */
+static inotify_state_t	*inotify_state;		/* global list if state */
+
+static void inotify_watch_event(inotify_watch_t *, uint64_t, char *);
+static void inotify_watch_insert(inotify_watch_t *, vnode_t *, char *);
+static void inotify_watch_delete(inotify_watch_t *, uint32_t);
+static void inotify_watch_remove(inotify_state_t *state,
+	inotify_watch_t *watch);
+
+static int
+inotify_fop_close(femarg_t *vf, int flag, int count, offset_t offset,
+    cred_t *cr, caller_context_t *ct)
+{
+	inotify_watch_t *watch = vf->fa_fnode->fn_available;
+	int rval;
+
+	if ((rval = vnext_close(vf, flag, count, offset, cr, ct)) == 0) {
+		inotify_watch_event(watch, flag & FWRITE ?
+		    IN_CLOSE_WRITE : IN_CLOSE_NOWRITE, NULL);
+	}
+
+	return (rval);
+}
+
+static int
+inotify_fop_create(femarg_t *vf, char *name, vattr_t *vap, vcexcl_t excl,
+    int mode, vnode_t **vpp, cred_t *cr, int flag, caller_context_t *ct,
+    vsecattr_t *vsecp)
+{
+	inotify_watch_t *watch = vf->fa_fnode->fn_available;
+	int rval;
+
+	if ((rval = vnext_create(vf, name, vap, excl, mode,
+	    vpp, cr, flag, ct, vsecp)) == 0) {
+		inotify_watch_insert(watch, *vpp, name);
+		inotify_watch_event(watch, IN_CREATE, name);
+	}
+
+	return (rval);
+}
+
+static int
+inotify_fop_link(femarg_t *vf, vnode_t *svp, char *tnm, cred_t *cr,
+    caller_context_t *ct, int flags)
+{
+	inotify_watch_t *watch = vf->fa_fnode->fn_available;
+	int rval;
+
+	if ((rval = vnext_link(vf, svp, tnm, cr, ct, flags)) == 0) {
+		inotify_watch_insert(watch, svp, tnm);
+		inotify_watch_event(watch, IN_CREATE, tnm);
+	}
+
+	return (rval);
+}
+
+static int
+inotify_fop_mkdir(femarg_t *vf, char *name, vattr_t *vap, vnode_t **vpp,
+    cred_t *cr, caller_context_t *ct, int flags, vsecattr_t *vsecp)
+{
+	inotify_watch_t *watch = vf->fa_fnode->fn_available;
+	int rval;
+
+	if ((rval = vnext_mkdir(vf, name, vap, vpp, cr,
+	    ct, flags, vsecp)) == 0) {
+		inotify_watch_insert(watch, *vpp, name);
+		inotify_watch_event(watch, IN_CREATE | IN_ISDIR, name);
+	}
+
+	return (rval);
+}
+
+static int
+inotify_fop_open(femarg_t *vf, int mode, cred_t *cr, caller_context_t *ct)
+{
+	inotify_watch_t *watch = vf->fa_fnode->fn_available;
+	int rval;
+
+	if ((rval = vnext_open(vf, mode, cr, ct)) == 0)
+		inotify_watch_event(watch, IN_OPEN, NULL);
+
+	return (rval);
+}
+
+static int
+inotify_fop_read(femarg_t *vf, struct uio *uiop, int ioflag, struct cred *cr,
+    caller_context_t *ct)
+{
+	inotify_watch_t *watch = vf->fa_fnode->fn_available;
+	int rval = vnext_read(vf, uiop, ioflag, cr, ct);
+	inotify_watch_event(watch, IN_ACCESS, NULL);
+
+	return (rval);
+}
+
+static int
+inotify_fop_readdir(femarg_t *vf, uio_t *uiop, cred_t *cr, int *eofp,
+    caller_context_t *ct, int flags)
+{
+	inotify_watch_t *watch = vf->fa_fnode->fn_available;
+	int rval = vnext_readdir(vf, uiop, cr, eofp, ct, flags);
+	inotify_watch_event(watch, IN_ACCESS | IN_ISDIR, NULL);
+
+	return (rval);
+}
+
+int
+inotify_fop_remove(femarg_t *vf, char *nm, cred_t *cr, caller_context_t *ct,
+    int flags)
+{
+	inotify_watch_t *watch = vf->fa_fnode->fn_available;
+	int rval;
+
+	if ((rval = vnext_remove(vf, nm, cr, ct, flags)) == 0)
+		inotify_watch_event(watch, IN_DELETE, nm);
+
+	return (rval);
+}
+
+int
+inotify_fop_rmdir(femarg_t *vf, char *nm, vnode_t *cdir, cred_t *cr,
+    caller_context_t *ct, int flags)
+{
+	inotify_watch_t *watch = vf->fa_fnode->fn_available;
+	int rval;
+
+	if ((rval = vnext_rmdir(vf, nm, cdir, cr, ct, flags)) == 0)
+		inotify_watch_event(watch, IN_DELETE | IN_ISDIR, nm);
+
+	return (rval);
+}
+
+static int
+inotify_fop_setattr(femarg_t *vf, vattr_t *vap, int flags, cred_t *cr,
+    caller_context_t *ct)
+{
+	inotify_watch_t *watch = vf->fa_fnode->fn_available;
+	int rval;
+
+	if ((rval = vnext_setattr(vf, vap, flags, cr, ct)) == 0)
+		inotify_watch_event(watch, IN_ATTRIB, NULL);
+
+	return (rval);
+}
+
+static int
+inotify_fop_write(femarg_t *vf, struct uio *uiop, int ioflag, struct cred *cr,
+    caller_context_t *ct)
+{
+	inotify_watch_t *watch = vf->fa_fnode->fn_available;
+	int rval = vnext_write(vf, uiop, ioflag, cr, ct);
+	inotify_watch_event(watch, IN_MODIFY, NULL);
+
+	return (rval);
+}
+
+static int
+inotify_fop_vnevent(femarg_t *vf, vnevent_t vnevent, vnode_t *dvp, char *name,
+    caller_context_t *ct)
+{
+	inotify_watch_t *watch = vf->fa_fnode->fn_available;
+
+	switch (vnevent) {
+	case VE_RENAME_SRC:
+		inotify_watch_event(watch, IN_MOVE_SELF, NULL);
+		inotify_watch_delete(watch, IN_MOVE_SELF);
+		break;
+	case VE_REMOVE:
+		/*
+		 * Linux will apparently fire an IN_ATTRIB event when the link
+		 * count changes (including when it drops to 0 on a remove).
+		 * This is merely somewhat odd; what is amazing is that this
+		 * IN_ATTRIB event is not visible on an inotify watch on the
+		 * parent directory.  (IN_ATTRIB events are normally sent to
+		 * watches on the parent directory).  While it's hard to
+		 * believe that this constitutes desired semantics, ltp
+		 * unfortunately tests this case (if implicitly); in the name
+		 * of bug-for-bug compatibility, we fire IN_ATTRIB iff we are
+		 * explicitly watching the file that has been removed.
+		 */
+		if (watch->inw_parent == NULL)
+			inotify_watch_event(watch, IN_ATTRIB, NULL);
+
+		/*FALLTHROUGH*/
+	case VE_RENAME_DEST:
+		inotify_watch_event(watch, IN_DELETE_SELF, NULL);
+		inotify_watch_delete(watch, IN_DELETE_SELF);
+		break;
+	case VE_RMDIR:
+		/*
+		 * It seems that IN_ISDIR should really be OR'd in here, but
+		 * Linux doesn't seem to do that in this case; for the sake of
+		 * bug-for-bug compatibility, we don't do it either.
+		 */
+		inotify_watch_event(watch, IN_DELETE_SELF, NULL);
+		inotify_watch_delete(watch, IN_DELETE_SELF);
+		break;
+	case VE_CREATE:
+	case VE_TRUNCATE:
+	case VE_RESIZE:
+		inotify_watch_event(watch, IN_MODIFY | IN_ATTRIB, NULL);
+		break;
+	case VE_LINK:
+		inotify_watch_event(watch, IN_ATTRIB, NULL);
+		break;
+	case VE_RENAME_SRC_DIR:
+		inotify_watch_event(watch, IN_MOVED_FROM, name);
+		break;
+	case VE_RENAME_DEST_DIR:
+		if (name == NULL)
+			name = dvp->v_path;
+
+		inotify_watch_insert(watch, dvp, name);
+		inotify_watch_event(watch, IN_MOVED_TO, name);
+		break;
+	case VE_SUPPORT:
+	case VE_MOUNTEDOVER:
+	case VE_PRE_RENAME_SRC:
+	case VE_PRE_RENAME_DEST:
+	case VE_PRE_RENAME_DEST_DIR:
+		break;
+	}
+
+	return (vnext_vnevent(vf, vnevent, dvp, name, ct));
+}
+
+const fs_operation_def_t inotify_vnodesrc_template[] = {
+	VOPNAME_CLOSE,		{ .femop_close = inotify_fop_close },
+	VOPNAME_CREATE,		{ .femop_create = inotify_fop_create },
+	VOPNAME_LINK,		{ .femop_link = inotify_fop_link },
+	VOPNAME_MKDIR,		{ .femop_mkdir = inotify_fop_mkdir },
+	VOPNAME_OPEN,		{ .femop_open = inotify_fop_open },
+	VOPNAME_READ,		{ .femop_read = inotify_fop_read },
+	VOPNAME_READDIR,	{ .femop_readdir = inotify_fop_readdir },
+	VOPNAME_REMOVE,		{ .femop_remove = inotify_fop_remove },
+	VOPNAME_RMDIR,		{ .femop_rmdir = inotify_fop_rmdir },
+	VOPNAME_SETATTR,	{ .femop_setattr = inotify_fop_setattr },
+	VOPNAME_WRITE,		{ .femop_write = inotify_fop_write },
+	VOPNAME_VNEVENT,	{ .femop_vnevent = inotify_fop_vnevent },
+	NULL, NULL
+};
+
+static int
+inotify_watch_cmpwd(inotify_watch_t *lhs, inotify_watch_t *rhs)
+{
+	if (lhs->inw_wd < rhs->inw_wd)
+		return (-1);
+
+	if (lhs->inw_wd > rhs->inw_wd)
+		return (1);
+
+	return (0);
+}
+
+static int
+inotify_watch_cmpvp(inotify_watch_t *lhs, inotify_watch_t *rhs)
+{
+	uintptr_t lvp = (uintptr_t)lhs->inw_vp, rvp = (uintptr_t)rhs->inw_vp;
+
+	if (lvp < rvp)
+		return (-1);
+
+	if (lvp > rvp)
+		return (1);
+
+	return (0);
+}
+
+static void
+inotify_watch_hold(inotify_watch_t *watch)
+{
+	mutex_enter(&watch->inw_lock);
+	VERIFY(watch->inw_refcnt > 0);
+	watch->inw_refcnt++;
+	mutex_exit(&watch->inw_lock);
+}
+
+static void
+inotify_watch_release(inotify_watch_t *watch)
+{
+	mutex_enter(&watch->inw_lock);
+	VERIFY(watch->inw_refcnt > 1);
+
+	if (--watch->inw_refcnt == 1 && watch->inw_zombie) {
+		/*
+		 * We're down to our last reference; kick anyone that might be
+		 * waiting.
+		 */
+		cv_signal(&watch->inw_cv);
+	}
+
+	mutex_exit(&watch->inw_lock);
+}
+
+static void
+inotify_watch_event(inotify_watch_t *watch, uint64_t mask, char *name)
+{
+	inotify_kevent_t *event, *tail;
+	inotify_state_t *state = watch->inw_state;
+	uint32_t wd = watch->inw_wd, cookie = 0, len;
+	boolean_t removal = mask & IN_REMOVAL ? B_TRUE : B_FALSE;
+	inotify_watch_t *source = watch;
+
+	if (!(mask &= watch->inw_mask) || mask == IN_ISDIR)
+		return;
+
+	if (watch->inw_parent != NULL) {
+		/*
+		 * This is an event on the child; if this isn't a valid child
+		 * event, return.  Otherwise, we move our watch to be our
+		 * parent (which we know is around because we have a hold on
+		 * it) and continue.
+		 */
+		if (!(mask & IN_CHILD_EVENTS))
+			return;
+
+		name = watch->inw_name;
+		watch = watch->inw_parent;
+		wd = watch->inw_wd;
+	}
+
+	if (!removal) {
+		mutex_enter(&state->ins_lock);
+
+		if (watch->inw_zombie ||
+		    watch->inw_fired || !watch->inw_active) {
+			mutex_exit(&state->ins_lock);
+			return;
+		}
+	} else {
+		if (!watch->inw_active)
+			return;
+
+		VERIFY(MUTEX_HELD(&state->ins_lock));
+	}
+
+	/*
+	 * If this is an operation on a directory and it's a child event
+	 * (event if it's not on a child), we specify IN_ISDIR.
+	 */
+	if (source->inw_vp->v_type == VDIR && (mask & IN_CHILD_EVENTS))
+		mask |= IN_ISDIR;
+
+	if (mask & (IN_MOVED_FROM | IN_MOVED_TO))
+		cookie = (uint32_t)curthread->t_did;
+
+	if (state->ins_nevents >= state->ins_maxevents) {
+		/*
+		 * We're at our maximum number of events -- turn our event
+		 * into an IN_Q_OVERFLOW event, which will be coalesced if
+		 * it's already the tail event.
+		 */
+		mask = IN_Q_OVERFLOW;
+		wd = (uint32_t)-1;
+		cookie = 0;
+		len = 0;
+	}
+
+	if ((tail = state->ins_tail) != NULL && tail->ine_event.wd == wd &&
+	    tail->ine_event.mask == mask && tail->ine_event.cookie == cookie &&
+	    ((tail->ine_event.len == 0 && len == 0) ||
+	    (name != NULL && tail->ine_event.len != 0 &&
+	    strcmp(tail->ine_event.name, name) == 0))) {
+		/*
+		 * This is an implicitly coalesced event; we're done.
+		 */
+		if (!removal)
+			mutex_exit(&state->ins_lock);
+		return;
+	}
+
+	if (name != NULL) {
+		len = strlen(name) + 1;
+		len = roundup(len, sizeof (struct inotify_event));
+	} else {
+		len = 0;
+	}
+
+	event = kmem_zalloc(sizeof (inotify_kevent_t) + len, KM_SLEEP);
+	event->ine_event.wd = wd;
+	event->ine_event.mask = (uint32_t)mask;
+	event->ine_event.cookie = cookie;
+	event->ine_event.len = len;
+
+	if (name != NULL)
+		(void) strcpy(event->ine_event.name, name);
+
+	if (tail != NULL) {
+		tail->ine_next = event;
+	} else {
+		VERIFY(state->ins_head == NULL);
+		state->ins_head = event;
+		cv_broadcast(&state->ins_cv);
+	}
+
+	state->ins_tail = event;
+	state->ins_nevents++;
+	state->ins_size += sizeof (event->ine_event) + len;
+
+	if (removal)
+		return;
+
+	if ((watch->inw_mask & IN_ONESHOT) && !watch->inw_fired) {
+		/*
+		 * If this is a one-shot, we need to remove the watch.  (Note
+		 * that this will recurse back into inotify_watch_event() to
+		 * fire the IN_IGNORED event -- but with "removal" set.)
+		 */
+		watch->inw_fired = 1;
+		inotify_watch_remove(state, watch);
+	}
+
+	mutex_exit(&state->ins_lock);
+	pollwakeup(&state->ins_pollhd, POLLRDNORM | POLLIN);
+}
+
+/*
+ * Destroy a watch.  By the time we're in here, the watch must have exactly
+ * one reference.
+ */
+static void
+inotify_watch_destroy(inotify_watch_t *watch)
+{
+	VERIFY(MUTEX_HELD(&watch->inw_lock));
+
+	if (watch->inw_name != NULL)
+		kmem_free(watch->inw_name, strlen(watch->inw_name) + 1);
+
+	kmem_free(watch, sizeof (inotify_watch_t));
+}
+
+/*
+ * Zombify a watch.  By the time we come in here, it must be true that the
+ * watch has already been fem_uninstall()'d -- the only reference should be
+ * in the state's data structure.  If we can get away with freeing it, we'll
+ * do that -- but if the reference count is greater than one due to an active
+ * vnode operation, we'll put this watch on the zombie list on the state
+ * structure.
+ */
+static void
+inotify_watch_zombify(inotify_watch_t *watch)
+{
+	inotify_state_t *state = watch->inw_state;
+
+	VERIFY(MUTEX_HELD(&state->ins_lock));
+	VERIFY(!watch->inw_zombie);
+
+	watch->inw_zombie = 1;
+
+	if (watch->inw_parent != NULL) {
+		inotify_watch_release(watch->inw_parent);
+	} else {
+		avl_remove(&state->ins_byvp, watch);
+		avl_remove(&state->ins_bywd, watch);
+		vmem_free(state->ins_wds, (void *)(uintptr_t)watch->inw_wd, 1);
+		watch->inw_wd = -1;
+	}
+
+	mutex_enter(&watch->inw_lock);
+
+	if (watch->inw_refcnt == 1) {
+		/*
+		 * There are no operations in flight and there is no way
+		 * for anyone to discover this watch -- we can destroy it.
+		 */
+		inotify_watch_destroy(watch);
+	} else {
+		/*
+		 * There are operations in flight; we will need to enqueue
+		 * this for later destruction.
+		 */
+		watch->inw_parent = state->ins_zombies;
+		state->ins_zombies = watch;
+		mutex_exit(&watch->inw_lock);
+	}
+}
+
+static inotify_watch_t *
+inotify_watch_add(inotify_state_t *state, inotify_watch_t *parent,
+    const char *name, vnode_t *vp, uint32_t mask)
+{
+	inotify_watch_t *watch;
+	int err;
+
+	VERIFY(MUTEX_HELD(&state->ins_lock));
+
+	watch = kmem_zalloc(sizeof (inotify_watch_t), KM_SLEEP);
+
+	watch->inw_vp = vp;
+	watch->inw_mask = mask;
+	watch->inw_state = state;
+	watch->inw_refcnt = 1;
+
+	if (parent == NULL) {
+		watch->inw_wd = (int)(uintptr_t)vmem_alloc(state->ins_wds,
+		    1, VM_BESTFIT | VM_SLEEP);
+		avl_add(&state->ins_byvp, watch);
+		avl_add(&state->ins_bywd, watch);
+
+		avl_create(&watch->inw_children,
+		    (int(*)(const void *, const void *))inotify_watch_cmpvp,
+		    sizeof (inotify_watch_t),
+		    offsetof(inotify_watch_t, inw_byvp));
+	} else {
+		VERIFY(name != NULL);
+		inotify_watch_hold(parent);
+		watch->inw_mask &= IN_CHILD_EVENTS;
+		watch->inw_parent = parent;
+		watch->inw_name = kmem_alloc(strlen(name) + 1, KM_SLEEP);
+		/* strcpy() is safe, because strlen(name) bounds us. */
+		(void) strcpy(watch->inw_name, name);
+
+		avl_add(&parent->inw_children, watch);
+	}
+
+	/*
+	 * Add our monitor to the vnode.  We must not have the watch lock held
+	 * when we do this, as it will immediately hold our watch.
+	 */
+	err = fem_install(vp, inotify_femp, watch, OPARGUNIQ,
+	    (void (*)(void *))inotify_watch_hold,
+	    (void (*)(void *))inotify_watch_release);
+
+	VERIFY(err == 0);
+
+	return (watch);
+}
+
+/*
+ * Remove a (non-child) watch.  This is called from either synchronous context
+ * via inotify_rm_watch() or monitor context via either a vnevent or a
+ * one-shot.
+ */
+static void
+inotify_watch_remove(inotify_state_t *state, inotify_watch_t *watch)
+{
+	inotify_watch_t *child;
+	int err;
+
+	VERIFY(MUTEX_HELD(&state->ins_lock));
+	VERIFY(watch->inw_parent == NULL);
+
+	err = fem_uninstall(watch->inw_vp, inotify_femp, watch);
+	VERIFY(err == 0);
+
+	/*
+	 * If we have children, we're going to remove them all and set them
+	 * all to be zombies.
+	 */
+	while ((child = avl_first(&watch->inw_children)) != NULL) {
+		VERIFY(child->inw_parent == watch);
+		avl_remove(&watch->inw_children, child);
+
+		err = fem_uninstall(child->inw_vp, inotify_femp, child);
+		VERIFY(err == 0);
+
+		/*
+		 * If this child watch has been orphaned, remove it from the
+		 * state's list of orphans.
+		 */
+		if (child->inw_orphaned) {
+			list_remove(&state->ins_orphans, child);
+			crfree(child->inw_cred);
+		}
+
+		VN_RELE(child->inw_vp);
+
+		/*
+		 * We're down (or should be down) to a single reference to
+		 * this child watch; it's safe to zombify it.
+		 */
+		inotify_watch_zombify(child);
+	}
+
+	inotify_watch_event(watch, IN_IGNORED | IN_REMOVAL, NULL);
+	VN_RELE(watch->inw_vp);
+
+	/*
+	 * It's now safe to zombify the watch -- we know that the only reference
+	 * can come from operations in flight.
+	 */
+	inotify_watch_zombify(watch);
+}
+
+/*
+ * Delete a watch.  Should only be called from VOP context.
+ */
+static void
+inotify_watch_delete(inotify_watch_t *watch, uint32_t event)
+{
+	inotify_state_t *state = watch->inw_state;
+	inotify_watch_t cmp = { .inw_vp = watch->inw_vp }, *parent;
+	int err;
+
+	if (event != IN_DELETE_SELF && !(watch->inw_mask & IN_CHILD_EVENTS))
+		return;
+
+	mutex_enter(&state->ins_lock);
+
+	if (watch->inw_zombie) {
+		mutex_exit(&state->ins_lock);
+		return;
+	}
+
+	if ((parent = watch->inw_parent) == NULL) {
+		if (event == IN_DELETE_SELF) {
+			/*
+			 * If we're here because we're being deleted and we
+			 * are not a child watch, we need to delete the entire
+			 * watch, children and all.
+			 */
+			inotify_watch_remove(state, watch);
+		}
+
+		mutex_exit(&state->ins_lock);
+		return;
+	} else {
+		if (event == IN_DELETE_SELF &&
+		    !(parent->inw_mask & IN_EXCL_UNLINK)) {
+			/*
+			 * This is a child watch for a file that is being
+			 * removed and IN_EXCL_UNLINK has not been specified;
+			 * indicate that it is orphaned and add it to the list
+			 * of orphans.  (This list will be checked by the
+			 * cleaning cyclic to determine when the watch has
+			 * become the only hold on the vnode, at which point
+			 * the watch can be zombified.)  Note that we check
+			 * if the watch is orphaned before we orphan it:  hard
+			 * links make it possible for VE_REMOVE to be called
+			 * multiple times on the same vnode. (!)
+			 */
+			if (!watch->inw_orphaned) {
+				watch->inw_orphaned = 1;
+				watch->inw_cred = CRED();
+				crhold(watch->inw_cred);
+				list_insert_head(&state->ins_orphans, watch);
+			}
+
+			mutex_exit(&state->ins_lock);
+			return;
+		}
+
+		if (watch->inw_orphaned) {
+			/*
+			 * If we're here, a file was orphaned and then later
+			 * moved -- which almost certainly means that hard
+			 * links are on the scene.  We choose the orphan over
+			 * the move because we don't want to spuriously
+			 * drop events if we can avoid it.
+			 */
+			crfree(watch->inw_cred);
+			list_remove(&state->ins_orphans, watch);
+		}
+	}
+
+	if (avl_find(&parent->inw_children, &cmp, NULL) == NULL) {
+		/*
+		 * This watch has already been deleted from the parent.
+		 */
+		mutex_exit(&state->ins_lock);
+		return;
+	}
+
+	avl_remove(&parent->inw_children, watch);
+	err = fem_uninstall(watch->inw_vp, inotify_femp, watch);
+	VERIFY(err == 0);
+
+	VN_RELE(watch->inw_vp);
+
+	/*
+	 * It's now safe to zombify the watch -- which won't actually delete
+	 * it as we know that the reference count is greater than 1.
+	 */
+	inotify_watch_zombify(watch);
+	mutex_exit(&state->ins_lock);
+}
+
+/*
+ * Insert a new child watch.  Should only be called from VOP context when
+ * a child is created in a watched directory.
+ */
+static void
+inotify_watch_insert(inotify_watch_t *watch, vnode_t *vp, char *name)
+{
+	inotify_state_t *state = watch->inw_state;
+	inotify_watch_t cmp = { .inw_vp = vp };
+
+	if (!(watch->inw_mask & IN_CHILD_EVENTS))
+		return;
+
+	mutex_enter(&state->ins_lock);
+
+	if (watch->inw_zombie || watch->inw_parent != NULL || vp == NULL) {
+		mutex_exit(&state->ins_lock);
+		return;
+	}
+
+	if (avl_find(&watch->inw_children, &cmp, NULL) != NULL) {
+		mutex_exit(&state->ins_lock);
+		return;
+	}
+
+	VN_HOLD(vp);
+	watch = inotify_watch_add(state, watch, name, vp, watch->inw_mask);
+	VERIFY(watch != NULL);
+
+	mutex_exit(&state->ins_lock);
+}
+
+
+static int
+inotify_add_watch(inotify_state_t *state, vnode_t *vp, uint32_t mask,
+    int32_t *wdp)
+{
+	inotify_watch_t *watch, cmp = { .inw_vp = vp };
+	uint32_t set;
+
+	set = (mask & (IN_ALL_EVENTS | IN_MODIFIERS)) | IN_UNMASKABLE;
+
+	/*
+	 * Lookup our vnode to determine if we already have a watch on it.
+	 */
+	mutex_enter(&state->ins_lock);
+
+	if ((watch = avl_find(&state->ins_byvp, &cmp, NULL)) == NULL) {
+		/*
+		 * We don't have this watch; allocate a new one, provided that
+		 * we have fewer than our limit.
+		 */
+		if (avl_numnodes(&state->ins_bywd) >= state->ins_maxwatches) {
+			mutex_exit(&state->ins_lock);
+			return (ENOSPC);
+		}
+
+		VN_HOLD(vp);
+		watch = inotify_watch_add(state, NULL, NULL, vp, set);
+		*wdp = watch->inw_wd;
+		mutex_exit(&state->ins_lock);
+
+		return (0);
+	}
+
+	VERIFY(!watch->inw_zombie);
+
+	if (!(mask & IN_MASK_ADD)) {
+		/*
+		 * Note that if we're resetting our event mask and we're
+		 * transitioning from an event mask that includes child events
+		 * to one that doesn't, there will be potentially some stale
+		 * child watches.  This is basically fine:  they won't fire,
+		 * and they will correctly be removed when the watch is
+		 * removed.
+		 */
+		watch->inw_mask = 0;
+	}
+
+	watch->inw_mask |= set;
+
+	*wdp = watch->inw_wd;
+
+	mutex_exit(&state->ins_lock);
+
+	return (0);
+}
+
+static int
+inotify_add_child(inotify_state_t *state, vnode_t *vp, char *name)
+{
+	inotify_watch_t *watch, cmp = { .inw_vp = vp };
+	vnode_t *cvp;
+	int err;
+
+	/*
+	 * Verify that the specified child doesn't have a directory component
+	 * within it.
+	 */
+	if (strchr(name, '/') != NULL)
+		return (EINVAL);
+
+	/*
+	 * Lookup the underlying file.  Note that this will succeed even if
+	 * we don't have permissions to actually read the file.
+	 */
+	if ((err = lookupnameat(name,
+	    UIO_SYSSPACE, NO_FOLLOW, NULL, &cvp, vp)) != 0) {
+		return (err);
+	}
+
+	/*
+	 * Use our vnode to find our watch, and then add our child watch to it.
+	 */
+	mutex_enter(&state->ins_lock);
+
+	if ((watch = avl_find(&state->ins_byvp, &cmp, NULL)) == NULL) {
+		/*
+		 * This is unexpected -- it means that we don't have the
+		 * watch that we thought we had.
+		 */
+		mutex_exit(&state->ins_lock);
+		VN_RELE(cvp);
+		return (ENXIO);
+	}
+
+	/*
+	 * Now lookup the child vnode in the watch; we'll only add it if it
+	 * isn't already there.
+	 */
+	cmp.inw_vp = cvp;
+
+	if (avl_find(&watch->inw_children, &cmp, NULL) != NULL) {
+		mutex_exit(&state->ins_lock);
+		VN_RELE(cvp);
+		return (0);
+	}
+
+	watch = inotify_watch_add(state, watch, name, cvp, watch->inw_mask);
+	VERIFY(watch != NULL);
+	mutex_exit(&state->ins_lock);
+
+	return (0);
+}
+
+static int
+inotify_rm_watch(inotify_state_t *state, int32_t wd)
+{
+	inotify_watch_t *watch, cmp = { .inw_wd = wd };
+
+	mutex_enter(&state->ins_lock);
+
+	if ((watch = avl_find(&state->ins_bywd, &cmp, NULL)) == NULL) {
+		mutex_exit(&state->ins_lock);
+		return (EINVAL);
+	}
+
+	inotify_watch_remove(state, watch);
+	mutex_exit(&state->ins_lock);
+
+	return (0);
+}
+
+static int
+inotify_activate(inotify_state_t *state, int32_t wd)
+{
+	inotify_watch_t *watch, cmp = { .inw_wd = wd };
+
+	mutex_enter(&state->ins_lock);
+
+	if ((watch = avl_find(&state->ins_bywd, &cmp, NULL)) == NULL) {
+		mutex_exit(&state->ins_lock);
+		return (EINVAL);
+	}
+
+	watch->inw_active = 1;
+
+	mutex_exit(&state->ins_lock);
+
+	return (0);
+}
+
+/*
+ * Called periodically as a cyclic to process the orphans and zombies.
+ */
+static void
+inotify_clean(void *arg)
+{
+	inotify_state_t *state = arg;
+	inotify_watch_t *watch, *parent, *next, **prev;
+	cred_t *savecred;
+	int err;
+
+	mutex_enter(&state->ins_lock);
+
+	for (watch = list_head(&state->ins_orphans);
+	    watch != NULL; watch = next) {
+		next = list_next(&state->ins_orphans, watch);
+
+		VERIFY(!watch->inw_zombie);
+		VERIFY((parent = watch->inw_parent) != NULL);
+
+		if (watch->inw_vp->v_count > 1)
+			continue;
+
+		avl_remove(&parent->inw_children, watch);
+		err = fem_uninstall(watch->inw_vp, inotify_femp, watch);
+		VERIFY(err == 0);
+
+		list_remove(&state->ins_orphans, watch);
+
+		/*
+		 * For purposes of releasing the vnode, we need to switch our
+		 * cred to be the cred of the orphaning thread (which we held
+		 * at the time this watch was orphaned).
+		 */
+		savecred = curthread->t_cred;
+		curthread->t_cred = watch->inw_cred;
+		VN_RELE(watch->inw_vp);
+		crfree(watch->inw_cred);
+		curthread->t_cred = savecred;
+
+		inotify_watch_zombify(watch);
+	}
+
+	prev = &state->ins_zombies;
+
+	while ((watch = *prev) != NULL) {
+		mutex_enter(&watch->inw_lock);
+
+		if (watch->inw_refcnt == 1) {
+			*prev = watch->inw_parent;
+			inotify_watch_destroy(watch);
+			continue;
+		}
+
+		prev = &watch->inw_parent;
+		mutex_exit(&watch->inw_lock);
+	}
+
+	mutex_exit(&state->ins_lock);
+}
+
+/*ARGSUSED*/
+static int
+inotify_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
+{
+	inotify_state_t *state;
+	major_t major = getemajor(*devp);
+	minor_t minor = getminor(*devp);
+	int instances = 0;
+	char c[64];
+
+	if (minor != INOTIFYMNRN_INOTIFY)
+		return (ENXIO);
+
+	mutex_enter(&inotify_lock);
+
+	for (state = inotify_state; state != NULL; state = state->ins_next) {
+		if (state->ins_cred == cred_p)
+			instances++;
+	}
+
+	if (instances >= inotify_maxinstances) {
+		mutex_exit(&inotify_lock);
+		return (EMFILE);
+	}
+
+	minor = (minor_t)(uintptr_t)vmem_alloc(inotify_minor, 1,
+	    VM_BESTFIT | VM_SLEEP);
+
+	if (ddi_soft_state_zalloc(inotify_softstate, minor) != DDI_SUCCESS) {
+		vmem_free(inotify_minor, (void *)(uintptr_t)minor, 1);
+		mutex_exit(&inotify_lock);
+		return (NULL);
+	}
+
+	state = ddi_get_soft_state(inotify_softstate, minor);
+	*devp = makedevice(major, minor);
+
+	crhold(cred_p);
+	state->ins_cred = cred_p;
+	state->ins_next = inotify_state;
+	inotify_state = state;
+
+	(void) snprintf(c, sizeof (c), "inotify_watchid_%d", minor);
+	state->ins_wds = vmem_create(c, (void *)1, UINT32_MAX, 1,
+	    NULL, NULL, NULL, 0, VM_SLEEP | VMC_IDENTIFIER);
+
+	avl_create(&state->ins_bywd,
+	    (int(*)(const void *, const void *))inotify_watch_cmpwd,
+	    sizeof (inotify_watch_t),
+	    offsetof(inotify_watch_t, inw_bywd));
+
+	avl_create(&state->ins_byvp,
+	    (int(*)(const void *, const void *))inotify_watch_cmpvp,
+	    sizeof (inotify_watch_t),
+	    offsetof(inotify_watch_t, inw_byvp));
+
+	list_create(&state->ins_orphans, sizeof (inotify_watch_t),
+	    offsetof(inotify_watch_t, inw_orphan));
+
+	state->ins_maxwatches = inotify_maxwatches;
+	state->ins_maxevents = inotify_maxevents;
+
+	mutex_exit(&inotify_lock);
+
+	state->ins_cleaner = ddi_periodic_add(inotify_clean,
+	    state, NANOSEC, DDI_IPL_0);
+
+	return (0);
+}
+
+/*ARGSUSED*/
+static int
+inotify_read(dev_t dev, uio_t *uio, cred_t *cr)
+{
+	inotify_state_t *state;
+	inotify_kevent_t *event;
+	minor_t minor = getminor(dev);
+	int err = 0, nevents = 0;
+	size_t len;
+
+	state = ddi_get_soft_state(inotify_softstate, minor);
+
+	mutex_enter(&state->ins_lock);
+
+	while (state->ins_head == NULL) {
+		if (uio->uio_fmode & (FNDELAY|FNONBLOCK)) {
+			mutex_exit(&state->ins_lock);
+			return (EAGAIN);
+		}
+
+		if (!cv_wait_sig_swap(&state->ins_cv, &state->ins_lock)) {
+			mutex_exit(&state->ins_lock);
+			return (EINTR);
+		}
+	}
+
+	/*
+	 * We have events and we have our lock; return as many as we can.
+	 */
+	while ((event = state->ins_head) != NULL) {
+		len = sizeof (event->ine_event) + event->ine_event.len;
+
+		if (uio->uio_resid < len) {
+			if (nevents == 0)
+				err = EINVAL;
+			break;
+		}
+
+		nevents++;
+
+		if ((err = uiomove(&event->ine_event, len, UIO_READ, uio)) != 0)
+			break;
+
+		VERIFY(state->ins_nevents > 0);
+		state->ins_nevents--;
+
+		VERIFY(state->ins_size > 0);
+		state->ins_size -= len;
+
+		if ((state->ins_head = event->ine_next) == NULL) {
+			VERIFY(event == state->ins_tail);
+			VERIFY(state->ins_nevents == 0);
+			state->ins_tail = NULL;
+		}
+
+		kmem_free(event, INOTIFY_EVENT_LENGTH(event));
+	}
+
+	mutex_exit(&state->ins_lock);
+
+	return (err);
+}
+
+/*ARGSUSED*/
+static int
+inotify_poll(dev_t dev, short events, int anyyet, short *reventsp,
+    struct pollhead **phpp)
+{
+	inotify_state_t *state;
+	minor_t minor = getminor(dev);
+
+	state = ddi_get_soft_state(inotify_softstate, minor);
+
+	mutex_enter(&state->ins_lock);
+
+	if (state->ins_head != NULL) {
+		*reventsp = events & (POLLRDNORM | POLLIN);
+	} else {
+		*reventsp = 0;
+
+		if (!anyyet)
+			*phpp = &state->ins_pollhd;
+	}
+
+	mutex_exit(&state->ins_lock);
+
+	return (0);
+}
+
+/*ARGSUSED*/
+static int
+inotify_ioctl(dev_t dev, int cmd, intptr_t arg, int md, cred_t *cr, int *rv)
+{
+	inotify_state_t *state;
+	minor_t minor = getminor(dev);
+	file_t *fp;
+	int rval;
+
+	state = ddi_get_soft_state(inotify_softstate, minor);
+
+	switch (cmd) {
+	case INOTIFYIOC_ADD_WATCH: {
+		inotify_addwatch_t addwatch;
+		file_t *fp;
+
+		if (copyin((void *)arg, &addwatch, sizeof (addwatch)) != 0)
+			return (EFAULT);
+
+		if ((fp = getf(addwatch.inaw_fd)) == NULL)
+			return (EBADF);
+
+		rval = inotify_add_watch(state, fp->f_vnode,
+		    addwatch.inaw_mask, rv);
+
+		releasef(addwatch.inaw_fd);
+		return (rval);
+	}
+
+	case INOTIFYIOC_ADD_CHILD: {
+		inotify_addchild_t addchild;
+		char name[MAXPATHLEN];
+
+		if (copyin((void *)arg, &addchild, sizeof (addchild)) != 0)
+			return (EFAULT);
+
+		if (copyinstr(addchild.inac_name, name, MAXPATHLEN, NULL) != 0)
+			return (EFAULT);
+
+		if ((fp = getf(addchild.inac_fd)) == NULL)
+			return (EBADF);
+
+		rval = inotify_add_child(state, fp->f_vnode, name);
+
+		releasef(addchild.inac_fd);
+		return (rval);
+	}
+
+	case INOTIFYIOC_RM_WATCH:
+		return (inotify_rm_watch(state, arg));
+
+	case INOTIFYIOC_ACTIVATE:
+		return (inotify_activate(state, arg));
+
+	case FIONREAD: {
+		int32_t size;
+
+		mutex_enter(&state->ins_lock);
+		size = state->ins_size;
+		mutex_exit(&state->ins_lock);
+
+		if (copyout(&size, (void *)arg, sizeof (size)) != 0)
+			return (EFAULT);
+
+		return (0);
+	}
+
+	default:
+		break;
+	}
+
+	return (ENOTTY);
+}
+
+/*ARGSUSED*/
+static int
+inotify_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
+{
+	inotify_state_t *state, **sp;
+	inotify_watch_t *watch, *zombies;
+	inotify_kevent_t *event;
+	minor_t minor = getminor(dev);
+
+	state = ddi_get_soft_state(inotify_softstate, minor);
+
+	if (state->ins_pollhd.ph_list != NULL) {
+		pollwakeup(&state->ins_pollhd, POLLERR);
+		pollhead_clean(&state->ins_pollhd);
+	}
+
+	mutex_enter(&state->ins_lock);
+
+	/*
+	 * First, destroy all of our watches.
+	 */
+	while ((watch = avl_first(&state->ins_bywd)) != NULL)
+		inotify_watch_remove(state, watch);
+
+	/*
+	 * And now destroy our event queue.
+	 */
+	while ((event = state->ins_head) != NULL) {
+		state->ins_head = event->ine_next;
+		kmem_free(event, INOTIFY_EVENT_LENGTH(event));
+	}
+
+	zombies = state->ins_zombies;
+	state->ins_zombies = NULL;
+	mutex_exit(&state->ins_lock);
+
+	/*
+	 * Now that our state lock is dropped, we can synchronously wait on
+	 * any zombies.
+	 */
+	while ((watch = zombies) != NULL) {
+		zombies = zombies->inw_parent;
+
+		mutex_enter(&watch->inw_lock);
+
+		while (watch->inw_refcnt > 1)
+			cv_wait(&watch->inw_cv, &watch->inw_lock);
+
+		inotify_watch_destroy(watch);
+	}
+
+	if (state->ins_cleaner != NULL) {
+		ddi_periodic_delete(state->ins_cleaner);
+		state->ins_cleaner = NULL;
+	}
+
+	mutex_enter(&inotify_lock);
+
+	/*
+	 * Remove our state from our global list, and release our hold on
+	 * the cred.
+	 */
+	for (sp = &inotify_state; *sp != state; sp = &((*sp)->ins_next))
+		VERIFY(*sp != NULL);
+
+	*sp = (*sp)->ins_next;
+	crfree(state->ins_cred);
+	vmem_destroy(state->ins_wds);
+
+	ddi_soft_state_free(inotify_softstate, minor);
+	vmem_free(inotify_minor, (void *)(uintptr_t)minor, 1);
+
+	mutex_exit(&inotify_lock);
+
+	return (0);
+}
+
+/*ARGSUSED*/
+static int
+inotify_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
+{
+	mutex_enter(&inotify_lock);
+
+	if (ddi_soft_state_init(&inotify_softstate,
+	    sizeof (inotify_state_t), 0) != 0) {
+		cmn_err(CE_NOTE, "/dev/inotify failed to create soft state");
+		mutex_exit(&inotify_lock);
+		return (DDI_FAILURE);
+	}
+
+	if (ddi_create_minor_node(devi, "inotify", S_IFCHR,
+	    INOTIFYMNRN_INOTIFY, DDI_PSEUDO, NULL) == DDI_FAILURE) {
+		cmn_err(CE_NOTE, "/dev/inotify couldn't create minor node");
+		ddi_soft_state_fini(&inotify_softstate);
+		mutex_exit(&inotify_lock);
+		return (DDI_FAILURE);
+	}
+
+	if (fem_create("inotify_fem",
+	    inotify_vnodesrc_template, &inotify_femp) != 0) {
+		cmn_err(CE_NOTE, "/dev/inotify couldn't create FEM state");
+		ddi_remove_minor_node(devi, NULL);
+		ddi_soft_state_fini(&inotify_softstate);
+		mutex_exit(&inotify_lock);
+		return (DDI_FAILURE);
+	}
+
+	ddi_report_dev(devi);
+	inotify_devi = devi;
+
+	inotify_minor = vmem_create("inotify_minor", (void *)INOTIFYMNRN_CLONE,
+	    UINT32_MAX - INOTIFYMNRN_CLONE, 1, NULL, NULL, NULL, 0,
+	    VM_SLEEP | VMC_IDENTIFIER);
+
+	mutex_exit(&inotify_lock);
+
+	return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+inotify_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+	switch (cmd) {
+	case DDI_DETACH:
+		break;
+
+	case DDI_SUSPEND:
+		return (DDI_SUCCESS);
+
+	default:
+		return (DDI_FAILURE);
+	}
+
+	mutex_enter(&inotify_lock);
+	fem_free(inotify_femp);
+	vmem_destroy(inotify_minor);
+
+	ddi_remove_minor_node(inotify_devi, NULL);
+	inotify_devi = NULL;
+
+	ddi_soft_state_fini(&inotify_softstate);
+	mutex_exit(&inotify_lock);
+
+	return (DDI_SUCCESS);
+}
+
+/*ARGSUSED*/
+static int
+inotify_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
+{
+	int error;
+
+	switch (infocmd) {
+	case DDI_INFO_DEVT2DEVINFO:
+		*result = (void *)inotify_devi;
+		error = DDI_SUCCESS;
+		break;
+	case DDI_INFO_DEVT2INSTANCE:
+		*result = (void *)0;
+		error = DDI_SUCCESS;
+		break;
+	default:
+		error = DDI_FAILURE;
+	}
+	return (error);
+}
+
+static struct cb_ops inotify_cb_ops = {
+	inotify_open,		/* open */
+	inotify_close,		/* close */
+	nulldev,		/* strategy */
+	nulldev,		/* print */
+	nodev,			/* dump */
+	inotify_read,		/* read */
+	nodev,			/* write */
+	inotify_ioctl,		/* ioctl */
+	nodev,			/* devmap */
+	nodev,			/* mmap */
+	nodev,			/* segmap */
+	inotify_poll,		/* poll */
+	ddi_prop_op,		/* cb_prop_op */
+	0,			/* streamtab  */
+	D_NEW | D_MP		/* Driver compatibility flag */
+};
+
+static struct dev_ops inotify_ops = {
+	DEVO_REV,		/* devo_rev */
+	0,			/* refcnt */
+	inotify_info,		/* get_dev_info */
+	nulldev,		/* identify */
+	nulldev,		/* probe */
+	inotify_attach,		/* attach */
+	inotify_detach,		/* detach */
+	nodev,			/* reset */
+	&inotify_cb_ops,	/* driver operations */
+	NULL,			/* bus operations */
+	nodev,			/* dev power */
+	ddi_quiesce_not_needed,	/* quiesce */
+};
+
+static struct modldrv modldrv = {
+	&mod_driverops,		/* module type (this is a pseudo driver) */
+	"inotify support",	/* name of module */
+	&inotify_ops,		/* driver ops */
+};
+
+static struct modlinkage modlinkage = {
+	MODREV_1,
+	(void *)&modldrv,
+	NULL
+};
+
+int
+_init(void)
+{
+	return (mod_install(&modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+	return (mod_remove(&modlinkage));
+}
--- /dev/null	Wed Oct  5 00:32:24 2016
+++ new/usr/src/uts/common/io/inotify.conf	Wed Oct  5 00:32:24 2016
@@ -0,0 +1,16 @@
+#
+# 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 (c) 2014 Joyent, Inc.  All rights reserved.
+#
+
+name="inotify" parent="pseudo" instance=0;
--- old/usr/src/uts/common/io/pseudo.conf	Wed Oct  5 00:32:24 2016
+++ new/usr/src/uts/common/io/pseudo.conf	Wed Oct  5 00:32:24 2016
@@ -22,9 +22,8 @@
 #
 # Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
+# Copyright 2014 Joyent, Inc.  All rights reserved.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
-#
 # This file is private to the pseudonex driver.  It should not be edited.
 #
 
@@ -38,3 +37,9 @@
 # /pseudo; it has as its children the zone console pseudo nodes.
 #
 name="zconsnex" parent="/pseudo" instance=1 valid-children="zcons";
+
+#
+# zfdnex is an alias for pseudo; this node is instantiated as a child of
+# /pseudo; it has as its children the zone fd pseudo nodes.
+#
+name="zfdnex" parent="/pseudo" instance=2 valid-children="zfd";
--- old/usr/src/uts/common/io/pseudonex.c	Wed Oct  5 00:32:24 2016
+++ new/usr/src/uts/common/io/pseudonex.c	Wed Oct  5 00:32:24 2016
@@ -83,6 +83,8 @@
 static int pseudonex_open(dev_t *, int, int, cred_t *);
 static int pseudonex_close(dev_t, int, int, cred_t *);
 static int pseudonex_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
+static int pseudonex_fm_init(dev_info_t *, dev_info_t *, int,
+    ddi_iblock_cookie_t *);
 static int pseudonex_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *,
     void *);
 
@@ -90,6 +92,8 @@
 
 typedef struct pseudonex_state {
 	dev_info_t *pnx_devi;
+	int pnx_fmcap;
+	ddi_iblock_cookie_t pnx_fm_ibc;
 } pseudonex_state_t;
 
 static struct bus_ops pseudonex_bus_ops = {
@@ -116,7 +120,7 @@
 	NULL,			/* bus_intr_ctl */
 	NULL,			/* bus_config */
 	NULL,			/* bus_unconfig */
-	NULL,			/* bus_fm_init */
+	pseudonex_fm_init,	/* bus_fm_init */
 	NULL,			/* bus_fm_fini */
 	NULL,			/* bus_fm_access_enter */
 	NULL,			/* bus_fm_access_exit */
@@ -228,6 +232,9 @@
 	pnx_state = ddi_get_soft_state(pseudonex_state, instance);
 	pnx_state->pnx_devi = devi;
 
+	pnx_state->pnx_fmcap = DDI_FM_EREPORT_CAPABLE;
+	ddi_fm_init(devi, &pnx_state->pnx_fmcap, &pnx_state->pnx_fm_ibc);
+
 	if (ddi_create_minor_node(devi, "devctl", S_IFCHR, instance,
 	    DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
 		ddi_remove_minor_node(devi, NULL);
@@ -247,6 +254,10 @@
 	if (cmd == DDI_SUSPEND)
 		return (DDI_SUCCESS);
 
+	if (cmd != DDI_DETACH)
+		return (DDI_FAILURE);
+
+	ddi_fm_fini(devi);
 	ddi_remove_minor_node(devi, NULL);
 	ddi_soft_state_free(pseudonex_state, instance);
 	return (DDI_SUCCESS);
@@ -374,6 +385,20 @@
 	return (-1);
 }
 
+/* ARGSUSED */
+static int
+pseudonex_fm_init(dev_info_t *dip, dev_info_t *tdip, int cap,
+    ddi_iblock_cookie_t *ibc)
+{
+	pseudonex_state_t *pnx_state;
+
+	pnx_state = ddi_get_soft_state(pseudonex_state, ddi_get_instance(dip));
+	ASSERT(pnx_state != NULL);
+	ASSERT(ibc != NULL);
+	*ibc = pnx_state->pnx_fm_ibc;
+	return (pnx_state->pnx_fmcap & cap);
+}
+
 static int
 pseudonex_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop,
     void *arg, void *result)
--- old/usr/src/uts/common/io/ptm.c	Wed Oct  5 00:32:24 2016
+++ new/usr/src/uts/common/io/ptm.c	Wed Oct  5 00:32:24 2016
@@ -447,6 +447,18 @@
 	return (0);
 }
 
+static boolean_t
+ptmptsopencb(ptmptsopencb_arg_t arg)
+{
+	struct pt_ttys	*ptmp = (struct pt_ttys *)arg;
+	boolean_t rval;
+
+	PT_ENTER_READ(ptmp);
+	rval = (ptmp->pt_nullmsg != NULL);
+	PT_EXIT_READ(ptmp);
+	return (rval);
+}
+
 /*
  * The wput procedure will only handle ioctl and flush messages.
  */
@@ -574,6 +586,41 @@
 			miocack(qp, mp, 0, 0);
 			break;
 		}
+		case PTMPTSOPENCB:
+		{
+			mblk_t		*dp;	/* ioctl reply data */
+			ptmptsopencb_t	*ppocb;
+
+			/* only allow the kernel to invoke this ioctl */
+			if (iocp->ioc_cr != kcred) {
+				miocnak(qp, mp, 0, EINVAL);
+				break;
+			}
+
+			/* we don't support transparent ioctls */
+			ASSERT(iocp->ioc_count != TRANSPARENT);
+			if (iocp->ioc_count == TRANSPARENT) {
+				miocnak(qp, mp, 0, EINVAL);
+				break;
+			}
+
+			/* allocate a response message */
+			dp = allocb(sizeof (ptmptsopencb_t), BPRI_MED);
+			if (dp == NULL) {
+				miocnak(qp, mp, 0, EAGAIN);
+				break;
+			}
+
+			/* initialize the ioctl results */
+			ppocb = (ptmptsopencb_t *)dp->b_rptr;
+			ppocb->ppocb_func = ptmptsopencb;
+			ppocb->ppocb_arg = (ptmptsopencb_arg_t)ptmp;
+
+			/* send the reply data */
+			mioc2ack(mp, dp, sizeof (ptmptsopencb_t), 0);
+			qreply(qp, mp);
+			break;
+		}
 		}
 		break;
 
--- old/usr/src/uts/common/io/random.c	Wed Oct  5 00:32:24 2016
+++ new/usr/src/uts/common/io/random.c	Wed Oct  5 00:32:24 2016
@@ -20,6 +20,8 @@
  *
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ *
+ * Copyright 2016 Joyent, Inc.
  */
 
 
@@ -289,6 +291,9 @@
 		if ((error = uiomove(buf, bytes, UIO_WRITE, uiop)) != 0)
 			return (error);
 
+		if (crgetzone(credp) != global_zone)
+			continue;
+
 		switch (devno) {
 		case DEVRANDOM:
 			if ((error = random_add_entropy(buf, bytes, 0)) != 0)
--- old/usr/src/uts/common/io/signalfd.c	Wed Oct  5 00:32:24 2016
+++ new/usr/src/uts/common/io/signalfd.c	Wed Oct  5 00:32:24 2016
@@ -10,7 +10,7 @@
  */
 
 /*
- * Copyright 2015 Joyent, Inc.
+ * Copyright 2016 Joyent, Inc.
  */
 
 /*
@@ -19,97 +19,81 @@
  *
  * As described on the signalfd(3C) man page, the general idea behind these
  * file descriptors is that they can be used to synchronously consume signals
- * via the read(2) syscall. That capability already exists with the
- * sigwaitinfo(3C) function but the key advantage of signalfd is that, because
- * it is file descriptor based, poll(2) can be used to determine when signals
- * are available to be consumed.
+ * via the read(2) syscall.  While that capability already exists with the
+ * sigwaitinfo(3C) function, signalfd holds an advantage since it is file
+ * descriptor based: It is able use the event facilities (poll(2), /dev/poll,
+ * event ports) to notify interested parties when consumable signals arrive.
  *
- * The general implementation uses signalfd_state to hold both the signal set
- * and poll head for an open file descriptor. Because a process can be using
- * different sigfds with different signal sets, each signalfd_state poll head
- * can be thought of as an independent signal stream and the thread(s) waiting
- * on that stream will get poll notification when any signal in the
- * corresponding set is received.
+ * The signalfd lifecycle begins When a process opens /dev/signalfd.  A minor
+ * will be allocated for them along with an associated signalfd_state_t struct.
+ * It is there where the mask of desired signals resides.
  *
- * The sigfd_proc_state_t struct lives on the proc_t and maintains per-proc
- * state for function callbacks and data when the proc needs to do work during
- * signal delivery for pollwakeup.
+ * Reading from the signalfd is straightforward and mimics the kernel behavior
+ * for sigtimedwait().  Signals continue to live on either the proc's p_sig, or
+ * thread's t_sig, member.  During a read operation, those which match the mask
+ * are consumed so they are no longer pending.
  *
- * The read side of the implementation is straightforward and mimics the
- * kernel behavior for sigtimedwait(). Signals continue to live on either
- * the proc's p_sig, or thread's t_sig, member. Read consumes the signal so
- * that it is no longer pending.
+ * The poll side is more complex.  Every time a signal is delivered, all of the
+ * signalfds on the process need to be examined in order to pollwake threads
+ * waiting for signal arrival.
  *
- * The poll side is more complex since all of the sigfds on the process need
- * to be examined every time a signal is delivered to the process in order to
- * pollwake any thread waiting in poll for that signal.
+ * When a thread polling on a signalfd requires a pollhead, several steps must
+ * be taken to safely ensure the proper result.  A sigfd_proc_state_t is
+ * created for the calling process if it does not yet exist.  It is there where
+ * a list of sigfd_poll_waiter_t structures reside which associate pollheads to
+ * signalfd_state_t entries.  The sigfd_proc_state_t list is walked to find a
+ * sigfd_poll_waiter_t matching the signalfd_state_t which corresponds to the
+ * polled resource.  If one is found, it is reused.  Otherwise a new one is
+ * created, incrementing the refcount on the signalfd_state_t, and it is added
+ * to the sigfd_poll_waiter_t list.
  *
- * Because it is likely that a process will only be using one, or a few, sigfds,
- * but many total file descriptors, we maintain a list of sigfds which need
- * pollwakeup. The list lives on the proc's p_sigfd struct. In this way only
- * zero, or a few, of the state structs will need to be examined every time a
- * signal is delivered to the process, instead of having to examine all of the
- * file descriptors to find the state structs. When a state struct with a
- * matching signal set is found then pollwakeup is called.
+ * The complications imposed by fork(2) are why the pollhead is stored in the
+ * associated sigfd_poll_waiter_t instead of directly in the signalfd_state_t.
+ * More than one process can hold a reference to the signalfd at a time but
+ * arriving signals should wake only process-local pollers.  Additionally,
+ * signalfd_close is called only when the last referencing fd is closed, hiding
+ * occurrences of preceeding threads which released their references.  This
+ * necessitates reference counting on the signalfd_state_t so it is able to
+ * persist after close until all poll references have been cleansed.  Doing so
+ * ensures that blocked pollers which hold references to the signalfd_state_t
+ * will be able to do clean-up after the descriptor itself has been closed.
  *
- * The sigfd_list is self-cleaning; as signalfd_pollwake_cb is called, the list
- * will clear out on its own. There is an exit helper (signalfd_exit_helper)
- * which cleans up any remaining per-proc state when the process exits.
+ * When a signal arrives in a process polling on signalfd, signalfd_pollwake_cb
+ * is called via the pointer in sigfd_proc_state_t.  It will walk over the
+ * sigfd_poll_waiter_t entries present in the list, searching for any
+ * associated with a signalfd_state_t with a matching signal mask.  The
+ * approach of keeping the poller list in p_sigfd was chosen because a process
+ * is likely to use few signalfds relative to its total file descriptors.  It
+ * reduces the work required for each received signal.
  *
- * The main complexity with signalfd is the interaction of forking and polling.
- * This interaction is complex because now two processes have a fd that
- * references the same dev_t (and its associated signalfd_state), but signals
- * go to only one of those processes. Also, we don't know when one of the
- * processes closes its fd because our 'close' entry point is only called when
- * the last fd is closed (which could be by either process).
+ * When matching sigfd_poll_waiter_t entries are encountered in the poller list
+ * during signalfd_pollwake_cb, they are dispatched into signalfd_wakeq to
+ * perform the pollwake.  This is due to a lock ordering conflict between
+ * signalfd_poll and signalfd_pollwake_cb.  The former acquires
+ * pollcache_t`pc_lock before proc_t`p_lock.  The latter (via sigtoproc)
+ * reverses the order.  Defering the pollwake into a taskq means it can be
+ * performed without proc_t`p_lock held, avoiding the deadlock.
  *
- * Because the state struct is referenced by both file descriptors, and the
- * state struct represents a signal stream needing a pollwakeup, if both
- * processes were polling then both processes would get a pollwakeup when a
- * signal arrives for either process (that is, the pollhead is associated with
- * our dev_t so when a signal arrives the pollwakeup wakes up all waiters).
+ * The sigfd_list is self-cleaning; as signalfd_pollwake_cb is called, the list
+ * will clear out on its own.  Any remaining per-process state which remains
+ * will be cleaned up by the exit helper (signalfd_exit_helper).
  *
- * Fortunately this is not a common problem in practice, but the implementation
- * attempts to mitigate unexpected behavior. The typical behavior is that the
- * parent has been polling the signalfd (which is why it was open in the first
- * place) and the parent might have a pending signalfd_state (with the
- * pollhead) on its per-process sigfd_list. After the fork the child will
- * simply close that fd (among others) as part of the typical fork/close/exec
- * pattern. Because the child will never poll that fd, it will never get any
- * state onto its own sigfd_list (the child starts with a null list). The
- * intention is that the child sees no pollwakeup activity for signals unless
- * it explicitly reinvokes poll on the sigfd.
+ * The structures associated with signalfd state are designed to operate
+ * correctly across fork, but there is one caveat that applies.  Using
+ * fork-shared signalfd descriptors in conjuction with fork-shared caching poll
+ * descriptors (such as /dev/poll or event ports) will result in missed poll
+ * wake-ups.  This is caused by the pollhead identity of signalfd descriptors
+ * being dependent on the process they are polled from.  Because it has a
+ * thread-local cache, poll(2) is unaffected by this limitation.
  *
- * As background, there are two primary polling cases to consider when the
- * parent process forks:
- * 1) If any thread is blocked in poll(2) then both the parent and child will
- *    return from the poll syscall with EINTR. This means that if either
- *    process wants to re-poll on a sigfd then it needs to re-run poll and
- *    would come back in to the signalfd_poll entry point. The parent would
- *    already have the dev_t's state on its sigfd_list and the child would not
- *    have anything there unless it called poll again on its fd.
- * 2) If the process is using /dev/poll(7D) then the polling info is being
- *    cached by the poll device and the process might not currently be blocked
- *    on anything polling related. A subsequent DP_POLL ioctl will not invoke
- *    our signalfd_poll entry point again. Because the parent still has its
- *    sigfd_list setup, an incoming signal will hit our signalfd_pollwake_cb
- *    entry point, which in turn calls pollwake, and /dev/poll will do the
- *    right thing on DP_POLL. The child will not have a sigfd_list yet so the
- *    signal will not cause a pollwakeup. The dp code does its own handling for
- *    cleaning up its cache.
+ * Lock ordering:
  *
- * This leaves only one odd corner case. If the parent and child both use
- * the dup-ed sigfd to poll then when a signal is delivered to either process
- * there is no way to determine which one should get the pollwakeup (since
- * both processes will be queued on the same signal stream poll head). What
- * happens in this case is that both processes will return from poll, but only
- * one of them will actually have a signal to read. The other will return
- * from read with EAGAIN, or block. This case is actually similar to the
- * situation within a single process which got two different sigfd's with the
- * same mask (or poll on two fd's that are dup-ed). Both would return from poll
- * when a signal arrives but only one read would consume the signal and the
- * other read would fail or block. Applications which poll on shared fd's
- * cannot assume that a subsequent read will actually obtain data.
+ * 1. signalfd_lock
+ * 2. signalfd_state_t`sfd_lock
+ *
+ * 1. proc_t`p_lock (to walk p_sigfd)
+ * 2. signalfd_state_t`sfd_lock
+ * 2a. signalfd_lock (after sfd_lock is dropped, when sfd_count falls to 0)
  */
 
 #include <sys/ddi.h>
@@ -123,100 +107,152 @@
 #include <sys/schedctl.h>
 #include <sys/id_space.h>
 #include <sys/sdt.h>
+#include <sys/brand.h>
+#include <sys/disp.h>
+#include <sys/taskq_impl.h>
 
 typedef struct signalfd_state signalfd_state_t;
 
 struct signalfd_state {
-	kmutex_t sfd_lock;			/* lock protecting state */
-	pollhead_t sfd_pollhd;			/* poll head */
-	k_sigset_t sfd_set;			/* signals for this fd */
-	signalfd_state_t *sfd_next;		/* next state on global list */
+	list_node_t	sfd_list;		/* node in global list */
+	kmutex_t	sfd_lock;		/* protects fields below */
+	uint_t		sfd_count;		/* ref count */
+	boolean_t	sfd_valid;		/* valid while open */
+	k_sigset_t	sfd_set;		/* signals for this fd */
 };
 
+typedef struct sigfd_poll_waiter {
+	list_node_t		spw_list;
+	signalfd_state_t	*spw_state;
+	pollhead_t		spw_pollhd;
+	taskq_ent_t		spw_taskent;
+	short			spw_pollev;
+} sigfd_poll_waiter_t;
+
 /*
- * Internal global variables.
+ * Protects global state in signalfd_devi, signalfd_minor, signalfd_softstate,
+ * and signalfd_state (including sfd_list field of members)
  */
-static kmutex_t		signalfd_lock;		/* lock protecting state */
+static kmutex_t		signalfd_lock;
 static dev_info_t	*signalfd_devi;		/* device info */
 static id_space_t	*signalfd_minor;	/* minor number arena */
 static void		*signalfd_softstate;	/* softstate pointer */
-static signalfd_state_t	*signalfd_state;	/* global list of state */
+static list_t		signalfd_state;		/* global list of state */
+static taskq_t		*signalfd_wakeq;	/* pollwake event taskq */
 
-/*
- * If we don't already have an entry in the proc's list for this state, add one.
- */
+
 static void
-signalfd_wake_list_add(signalfd_state_t *state)
+signalfd_state_enter_locked(signalfd_state_t *state)
 {
-	proc_t *p = curproc;
-	list_t *lst;
-	sigfd_wake_list_t *wlp;
+	ASSERT(MUTEX_HELD(&state->sfd_lock));
+	ASSERT(state->sfd_count > 0);
+	VERIFY(state->sfd_valid == B_TRUE);
 
-	ASSERT(MUTEX_HELD(&p->p_lock));
-	ASSERT(p->p_sigfd != NULL);
+	state->sfd_count++;
+}
 
-	lst = &((sigfd_proc_state_t *)p->p_sigfd)->sigfd_list;
-	for (wlp = list_head(lst); wlp != NULL; wlp = list_next(lst, wlp)) {
-		if (wlp->sigfd_wl_state == state)
-			break;
+static void
+signalfd_state_release(signalfd_state_t *state, boolean_t force_invalidate)
+{
+	mutex_enter(&state->sfd_lock);
+
+	if (force_invalidate) {
+		state->sfd_valid = B_FALSE;
 	}
 
-	if (wlp == NULL) {
-		wlp = kmem_zalloc(sizeof (sigfd_wake_list_t), KM_SLEEP);
-		wlp->sigfd_wl_state = state;
-		list_insert_head(lst, wlp);
+	ASSERT(state->sfd_count > 0);
+	if (state->sfd_count == 1) {
+		VERIFY(state->sfd_valid == B_FALSE);
+		mutex_exit(&state->sfd_lock);
+		if (force_invalidate) {
+			/*
+			 * The invalidation performed in signalfd_close is done
+			 * while signalfd_lock is held.
+			 */
+			ASSERT(MUTEX_HELD(&signalfd_lock));
+			list_remove(&signalfd_state, state);
+		} else {
+			ASSERT(MUTEX_NOT_HELD(&signalfd_lock));
+			mutex_enter(&signalfd_lock);
+			list_remove(&signalfd_state, state);
+			mutex_exit(&signalfd_lock);
+		}
+		kmem_free(state, sizeof (*state));
+		return;
 	}
+	state->sfd_count--;
+	mutex_exit(&state->sfd_lock);
 }
 
-static void
-signalfd_wake_rm(list_t *lst, sigfd_wake_list_t *wlp)
+static sigfd_poll_waiter_t *
+signalfd_wake_list_add(sigfd_proc_state_t *pstate, signalfd_state_t *state)
 {
-	list_remove(lst, wlp);
-	kmem_free(wlp, sizeof (sigfd_wake_list_t));
-}
+	list_t *lst = &pstate->sigfd_list;
+	sigfd_poll_waiter_t *pw;
 
-static void
-signalfd_wake_list_rm(proc_t *p, signalfd_state_t *state)
-{
-	sigfd_wake_list_t *wlp;
-	list_t *lst;
+	for (pw = list_head(lst); pw != NULL; pw = list_next(lst, pw)) {
+		if (pw->spw_state == state)
+			break;
+	}
 
-	ASSERT(MUTEX_HELD(&p->p_lock));
+	if (pw == NULL) {
+		pw = kmem_zalloc(sizeof (*pw), KM_SLEEP);
 
-	if (p->p_sigfd == NULL)
-		return;
+		mutex_enter(&state->sfd_lock);
+		signalfd_state_enter_locked(state);
+		pw->spw_state = state;
+		mutex_exit(&state->sfd_lock);
+		list_insert_head(lst, pw);
+	}
+	return (pw);
+}
 
-	lst = &((sigfd_proc_state_t *)p->p_sigfd)->sigfd_list;
-	for (wlp = list_head(lst); wlp != NULL; wlp = list_next(lst, wlp)) {
-		if (wlp->sigfd_wl_state == state) {
-			signalfd_wake_rm(lst, wlp);
+static sigfd_poll_waiter_t *
+signalfd_wake_list_rm(sigfd_proc_state_t *pstate, signalfd_state_t *state)
+{
+	list_t *lst = &pstate->sigfd_list;
+	sigfd_poll_waiter_t *pw;
+
+	for (pw = list_head(lst); pw != NULL; pw = list_next(lst, pw)) {
+		if (pw->spw_state == state) {
 			break;
 		}
 	}
 
-	if (list_is_empty(lst)) {
-		((sigfd_proc_state_t *)p->p_sigfd)->sigfd_pollwake_cb = NULL;
-		list_destroy(lst);
-		kmem_free(p->p_sigfd, sizeof (sigfd_proc_state_t));
-		p->p_sigfd = NULL;
+	if (pw != NULL) {
+		list_remove(lst, pw);
+		pw->spw_state = NULL;
+		signalfd_state_release(state, B_FALSE);
 	}
+
+	return (pw);
 }
 
 static void
 signalfd_wake_list_cleanup(proc_t *p)
 {
-	sigfd_wake_list_t *wlp;
+	sigfd_proc_state_t *pstate = p->p_sigfd;
+	sigfd_poll_waiter_t *pw;
 	list_t *lst;
 
 	ASSERT(MUTEX_HELD(&p->p_lock));
+	ASSERT(pstate != NULL);
 
-	((sigfd_proc_state_t *)p->p_sigfd)->sigfd_pollwake_cb = NULL;
+	lst = &pstate->sigfd_list;
+	while ((pw = list_remove_head(lst)) != NULL) {
+		signalfd_state_t *state = pw->spw_state;
 
-	lst = &((sigfd_proc_state_t *)p->p_sigfd)->sigfd_list;
-	while (!list_is_empty(lst)) {
-		wlp = (sigfd_wake_list_t *)list_remove_head(lst);
-		kmem_free(wlp, sizeof (sigfd_wake_list_t));
+		pw->spw_state = NULL;
+		signalfd_state_release(state, B_FALSE);
+
+		pollwakeup(&pw->spw_pollhd, POLLERR);
+		pollhead_clean(&pw->spw_pollhd);
+		kmem_free(pw, sizeof (*pw));
 	}
+	list_destroy(lst);
+
+	p->p_sigfd = NULL;
+	kmem_free(pstate, sizeof (*pstate));
 }
 
 static void
@@ -223,22 +259,32 @@
 signalfd_exit_helper(void)
 {
 	proc_t *p = curproc;
-	list_t *lst;
 
-	/* This being non-null is the only way we can get here */
-	ASSERT(p->p_sigfd != NULL);
-
 	mutex_enter(&p->p_lock);
-	lst = &((sigfd_proc_state_t *)p->p_sigfd)->sigfd_list;
-
 	signalfd_wake_list_cleanup(p);
-	list_destroy(lst);
-	kmem_free(p->p_sigfd, sizeof (sigfd_proc_state_t));
-	p->p_sigfd = NULL;
 	mutex_exit(&p->p_lock);
 }
 
 /*
+ * Perform pollwake for a sigfd_poll_waiter_t entry.
+ * Thanks to the strict and conflicting lock orders required for signalfd_poll
+ * (pc_lock before p_lock) and signalfd_pollwake_cb (p_lock before pc_lock),
+ * this is relegated to a taskq to avoid deadlock.
+ */
+static void
+signalfd_wake_task(void *arg)
+{
+	sigfd_poll_waiter_t *pw = arg;
+	signalfd_state_t *state = pw->spw_state;
+
+	pw->spw_state = NULL;
+	signalfd_state_release(state, B_FALSE);
+	pollwakeup(&pw->spw_pollhd, pw->spw_pollev);
+	pollhead_clean(&pw->spw_pollhd);
+	kmem_free(pw, sizeof (*pw));
+}
+
+/*
  * Called every time a signal is delivered to the process so that we can
  * see if any signal stream needs a pollwakeup. We maintain a list of
  * signal state elements so that we don't have to look at every file descriptor
@@ -254,35 +300,42 @@
 signalfd_pollwake_cb(void *arg0, int sig)
 {
 	proc_t *p = (proc_t *)arg0;
+	sigfd_proc_state_t *pstate = (sigfd_proc_state_t *)p->p_sigfd;
 	list_t *lst;
-	sigfd_wake_list_t *wlp;
+	sigfd_poll_waiter_t *pw;
 
 	ASSERT(MUTEX_HELD(&p->p_lock));
+	ASSERT(pstate != NULL);
 
-	if (p->p_sigfd == NULL)
-		return;
+	lst = &pstate->sigfd_list;
+	pw = list_head(lst);
+	while (pw != NULL) {
+		signalfd_state_t *state = pw->spw_state;
+		sigfd_poll_waiter_t *next;
 
-	lst = &((sigfd_proc_state_t *)p->p_sigfd)->sigfd_list;
-	wlp = list_head(lst);
-	while (wlp != NULL) {
-		signalfd_state_t *state = wlp->sigfd_wl_state;
-
 		mutex_enter(&state->sfd_lock);
-
-		if (sigismember(&state->sfd_set, sig) &&
-		    state->sfd_pollhd.ph_list != NULL) {
-			sigfd_wake_list_t *tmp = wlp;
-
-			/* remove it from the list */
-			wlp = list_next(lst, wlp);
-			signalfd_wake_rm(lst, tmp);
-
-			mutex_exit(&state->sfd_lock);
-			pollwakeup(&state->sfd_pollhd, POLLRDNORM | POLLIN);
+		if (!state->sfd_valid) {
+			pw->spw_pollev = POLLERR;
+		} else if (sigismember(&state->sfd_set, sig)) {
+			pw->spw_pollev = POLLRDNORM | POLLIN;
 		} else {
 			mutex_exit(&state->sfd_lock);
-			wlp = list_next(lst, wlp);
+			pw = list_next(lst, pw);
+			continue;
 		}
+		mutex_exit(&state->sfd_lock);
+
+		/*
+		 * Pull the sigfd_poll_waiter_t out of the list and dispatch it
+		 * to perform a pollwake.  This cannot be done synchronously
+		 * since signalfd_poll and signalfd_pollwake_cb have
+		 * conflicting lock orders which can deadlock.
+		 */
+		next = list_next(lst, pw);
+		list_remove(lst, pw);
+		taskq_dispatch_ent(signalfd_wakeq, signalfd_wake_task, pw, 0,
+		    &pw->spw_taskent);
+		pw = next;
 	}
 }
 
@@ -290,7 +343,7 @@
 static int
 signalfd_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
 {
-	signalfd_state_t *state;
+	signalfd_state_t *state, **sstate;
 	major_t major = getemajor(*devp);
 	minor_t minor = getminor(*devp);
 
@@ -300,7 +353,6 @@
 	mutex_enter(&signalfd_lock);
 
 	minor = (minor_t)id_allocff(signalfd_minor);
-
 	if (ddi_soft_state_zalloc(signalfd_softstate, minor) != DDI_SUCCESS) {
 		id_free(signalfd_minor, minor);
 		mutex_exit(&signalfd_lock);
@@ -307,12 +359,15 @@
 		return (ENODEV);
 	}
 
-	state = ddi_get_soft_state(signalfd_softstate, minor);
+	state = kmem_zalloc(sizeof (*state), KM_SLEEP);
+	state->sfd_valid = B_TRUE;
+	state->sfd_count = 1;
+	list_insert_head(&signalfd_state, (void *)state);
+
+	sstate = ddi_get_soft_state(signalfd_softstate, minor);
+	*sstate = state;
 	*devp = makedevice(major, minor);
 
-	state->sfd_next = signalfd_state;
-	signalfd_state = state;
-
 	mutex_exit(&signalfd_lock);
 
 	return (0);
@@ -405,6 +460,9 @@
 	lwp->lwp_extsig = 0;
 	mutex_exit(&p->p_lock);
 
+	if (PROC_IS_BRANDED(p) && BROP(p)->b_sigfd_translate)
+		BROP(p)->b_sigfd_translate(infop);
+
 	/* Convert k_siginfo into external, datamodel independent, struct. */
 	bzero(ssp, sizeof (*ssp));
 	ssp->ssi_signo = infop->si_signo;
@@ -439,7 +497,7 @@
 static int
 signalfd_read(dev_t dev, uio_t *uio, cred_t *cr)
 {
-	signalfd_state_t *state;
+	signalfd_state_t *state, **sstate;
 	minor_t minor = getminor(dev);
 	boolean_t block = B_TRUE;
 	k_sigset_t set;
@@ -449,7 +507,8 @@
 	if (uio->uio_resid < sizeof (signalfd_siginfo_t))
 		return (EINVAL);
 
-	state = ddi_get_soft_state(signalfd_softstate, minor);
+	sstate = ddi_get_soft_state(signalfd_softstate, minor);
+	state = *sstate;
 
 	if (uio->uio_fmode & (FNDELAY|FNONBLOCK))
 		block = B_FALSE;
@@ -462,15 +521,26 @@
 		return (set_errno(EINVAL));
 
 	do  {
-		res = consume_signal(state->sfd_set, uio, block);
-		if (res == 0)
+		res = consume_signal(set, uio, block);
+
+		if (res == 0) {
+			/*
+			 * After consuming one signal, do not block while
+			 * trying to consume more.
+			 */
 			got_one = B_TRUE;
+			block = B_FALSE;
 
-		/*
-		 * After consuming one signal we won't block trying to consume
-		 * further signals.
-		 */
-		block = B_FALSE;
+			/*
+			 * Refresh the matching signal set in case it was
+			 * updated during the wait.
+			 */
+			mutex_enter(&state->sfd_lock);
+			set = state->sfd_set;
+			mutex_exit(&state->sfd_lock);
+			if (sigisempty(&set))
+				break;
+		}
 	} while (res == 0 && uio->uio_resid >= sizeof (signalfd_siginfo_t));
 
 	if (got_one)
@@ -499,13 +569,14 @@
 signalfd_poll(dev_t dev, short events, int anyyet, short *reventsp,
     struct pollhead **phpp)
 {
-	signalfd_state_t *state;
+	signalfd_state_t *state, **sstate;
 	minor_t minor = getminor(dev);
 	kthread_t *t = curthread;
 	proc_t *p = ttoproc(t);
 	short revents = 0;
 
-	state = ddi_get_soft_state(signalfd_softstate, minor);
+	sstate = ddi_get_soft_state(signalfd_softstate, minor);
+	state = *sstate;
 
 	mutex_enter(&state->sfd_lock);
 
@@ -515,39 +586,36 @@
 	mutex_exit(&state->sfd_lock);
 
 	if (!(*reventsp = revents & events) && !anyyet) {
-		*phpp = &state->sfd_pollhd;
+		sigfd_proc_state_t *pstate;
+		sigfd_poll_waiter_t *pw;
 
 		/*
 		 * Enable pollwakeup handling.
 		 */
-		if (p->p_sigfd == NULL) {
-			sigfd_proc_state_t *pstate;
+		mutex_enter(&p->p_lock);
+		if ((pstate = (sigfd_proc_state_t *)p->p_sigfd) == NULL) {
 
-			pstate = kmem_zalloc(sizeof (sigfd_proc_state_t),
-			    KM_SLEEP);
+			mutex_exit(&p->p_lock);
+			pstate = kmem_zalloc(sizeof (*pstate), KM_SLEEP);
 			list_create(&pstate->sigfd_list,
-			    sizeof (sigfd_wake_list_t),
-			    offsetof(sigfd_wake_list_t, sigfd_wl_lst));
+			    sizeof (sigfd_poll_waiter_t),
+			    offsetof(sigfd_poll_waiter_t, spw_list));
+			pstate->sigfd_pollwake_cb = signalfd_pollwake_cb;
 
+			/* Check again, after blocking for the alloc. */
 			mutex_enter(&p->p_lock);
-			/* check again now that we're locked */
 			if (p->p_sigfd == NULL) {
 				p->p_sigfd = pstate;
 			} else {
 				/* someone beat us to it */
 				list_destroy(&pstate->sigfd_list);
-				kmem_free(pstate, sizeof (sigfd_proc_state_t));
+				kmem_free(pstate, sizeof (*pstate));
+				pstate = p->p_sigfd;
 			}
-			mutex_exit(&p->p_lock);
 		}
 
-		mutex_enter(&p->p_lock);
-		if (((sigfd_proc_state_t *)p->p_sigfd)->sigfd_pollwake_cb ==
-		    NULL) {
-			((sigfd_proc_state_t *)p->p_sigfd)->sigfd_pollwake_cb =
-			    signalfd_pollwake_cb;
-		}
-		signalfd_wake_list_add(state);
+		pw = signalfd_wake_list_add(pstate, state);
+		*phpp = &pw->spw_pollhd;
 		mutex_exit(&p->p_lock);
 	}
 
@@ -558,11 +626,12 @@
 static int
 signalfd_ioctl(dev_t dev, int cmd, intptr_t arg, int md, cred_t *cr, int *rv)
 {
-	signalfd_state_t *state;
+	signalfd_state_t *state, **sstate;
 	minor_t minor = getminor(dev);
 	sigset_t mask;
 
-	state = ddi_get_soft_state(signalfd_softstate, minor);
+	sstate = ddi_get_soft_state(signalfd_softstate, minor);
+	state = *sstate;
 
 	switch (cmd) {
 	case SIGNALFDIOC_MASK:
@@ -587,33 +656,40 @@
 static int
 signalfd_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
 {
-	signalfd_state_t *state, **sp;
+	signalfd_state_t *state, **sstate;
+	sigfd_poll_waiter_t *pw = NULL;
 	minor_t minor = getminor(dev);
 	proc_t *p = curproc;
 
-	state = ddi_get_soft_state(signalfd_softstate, minor);
+	sstate = ddi_get_soft_state(signalfd_softstate, minor);
+	state = *sstate;
 
-	if (state->sfd_pollhd.ph_list != NULL) {
-		pollwakeup(&state->sfd_pollhd, POLLERR);
-		pollhead_clean(&state->sfd_pollhd);
-	}
-
-	/* Make sure our state is removed from our proc's pollwake list. */
+	/* Make sure state is removed from this proc's pollwake list. */
 	mutex_enter(&p->p_lock);
-	signalfd_wake_list_rm(p, state);
+	if (p->p_sigfd != NULL) {
+		sigfd_proc_state_t *pstate = p->p_sigfd;
+
+		pw = signalfd_wake_list_rm(pstate, state);
+		if (list_is_empty(&pstate->sigfd_list)) {
+			signalfd_wake_list_cleanup(p);
+		}
+	}
 	mutex_exit(&p->p_lock);
 
+	if (pw != NULL) {
+		pollwakeup(&pw->spw_pollhd, POLLERR);
+		pollhead_clean(&pw->spw_pollhd);
+		kmem_free(pw, sizeof (*pw));
+	}
+
 	mutex_enter(&signalfd_lock);
 
-	/* Remove our state from our global list. */
-	for (sp = &signalfd_state; *sp != state; sp = &((*sp)->sfd_next))
-		VERIFY(*sp != NULL);
-
-	*sp = (*sp)->sfd_next;
-
+	*sstate = NULL;
 	ddi_soft_state_free(signalfd_softstate, minor);
 	id_free(signalfd_minor, minor);
 
+	signalfd_state_release(state, B_TRUE);
+
 	mutex_exit(&signalfd_lock);
 
 	return (0);
@@ -635,7 +711,7 @@
 	}
 
 	if (ddi_soft_state_init(&signalfd_softstate,
-	    sizeof (signalfd_state_t), 0) != 0) {
+	    sizeof (signalfd_state_t *), 0) != 0) {
 		cmn_err(CE_WARN, "signalfd failed to create soft state");
 		id_space_destroy(signalfd_minor);
 		mutex_exit(&signalfd_lock);
@@ -656,6 +732,12 @@
 
 	sigfd_exit_helper = signalfd_exit_helper;
 
+	list_create(&signalfd_state, sizeof (signalfd_state_t),
+	    offsetof(signalfd_state_t, sfd_list));
+
+	signalfd_wakeq = taskq_create("signalfd_wake", 1, minclsyspri,
+	    0, INT_MAX, TASKQ_PREPOPULATE);
+
 	mutex_exit(&signalfd_lock);
 
 	return (DDI_SUCCESS);
@@ -673,10 +755,25 @@
 		return (DDI_FAILURE);
 	}
 
-	/* list should be empty */
-	VERIFY(signalfd_state == NULL);
-
 	mutex_enter(&signalfd_lock);
+
+	if (!list_is_empty(&signalfd_state)) {
+		/*
+		 * There are dangling poll waiters holding signalfd_state_t
+		 * entries on the global list.  Detach is not possible until
+		 * they purge themselves.
+		 */
+		mutex_exit(&signalfd_lock);
+		return (DDI_FAILURE);
+	}
+	list_destroy(&signalfd_state);
+
+	/*
+	 * With no remaining entries in the signalfd_state list, the wake taskq
+	 * should be empty with no possibility for new entries.
+	 */
+	taskq_destroy(signalfd_wakeq);
+
 	id_space_destroy(signalfd_minor);
 
 	ddi_remove_minor_node(signalfd_devi, NULL);
--- /dev/null	Wed Oct  5 00:32:24 2016
+++ new/usr/src/uts/common/io/zfd.c	Wed Oct  5 00:32:24 2016
@@ -0,0 +1,1158 @@
+/*
+ * 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 2009 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2016 Joyent, Inc.  All rights reserved.
+ */
+
+/*
+ * Zone File Descriptor Driver.
+ *
+ * This driver is derived from the zcons driver which is in turn derived from
+ * the pts/ptm drivers. The purpose is to expose file descriptors within the
+ * zone which are connected to zoneadmd and used for logging or an interactive
+ * connection to a process within the zone.
+ *
+ * Its implementation is straightforward. Each instance of the driver
+ * represents a global-zone/local-zone pair. Unlike the zcons device, zoneadmd
+ * uses these devices unidirectionally to provide stdin, stdout and stderr to
+ * the process within the zone.
+ *
+ * Instances of zfd are onlined as children of /pseudo/zfdnex@2/ by zoneadmd,
+ * using the devctl framework; thus the driver does not need to maintain any
+ * sort of "admin" node.
+ *
+ * The driver shuttles I/O from master side to slave side and back.  In a break
+ * from the pts/ptm semantics, if one side is not open, I/O directed towards
+ * it will simply be discarded. This is so that if zoneadmd is not holding the
+ * master side fd open (i.e. it has died somehow), processes in the zone do not
+ * experience any errors and I/O to the fd does not cause the process to hang.
+ *
+ * The driver can also act as a multiplexer so that data written to the
+ * slave side within the zone is also redirected back to another zfd device
+ * inside the zone for consumption (i.e. it can be read). The intention is
+ * that a logging process within the zone can consume data that is being
+ * written by an application onto the primary stream. This is essentially
+ * a tee off of the primary stream into a log stream. This tee can also be
+ * configured to be flow controlled via an ioctl. Flow control happens on the
+ * primary stream and is used to ensure that the log stream receives all of
+ * the messages off the primary stream when consumption of the data off of
+ * the log stream gets behind. Configuring for flow control implies that the
+ * application writing to the primary stream will be blocked when the log
+ * consumer gets behind. Note that closing the log stream (e.g. when the zone
+ * halts) will cause the loss of all messages queued in the stream.
+ *
+ * The zone's zfd device configuration is driven by zoneadmd and a zone mode.
+ * The mode, which is controlled by the zone attribute "zlog-mode" is somewhat
+ * of a misnomer since its purpose has evolved. The attribute can have a
+ * variety of values, but the lowest two positions are used to control how many
+ * zfd devices are created inside the zone and if the primary stream is a tty.
+ *
+ * Here is a summary of how the 4 modes control what zfd devices are created
+ * and how they're used:
+ *
+ *    t-:  1 stdio zdev  (0) configured as a tty
+ *    --:  3 stdio zdevs (0, 1, 2), not configured as a tty
+ *    tn:  1 stdio zdev  (0) configured as a tty, 1 additional zdev (1)
+ *    -n:  3 stdio zdevs (0, 1, 2), not tty, 2 additional zdevs (3, 4)
+ *
+ * With the 't' flag set, stdin/out/err is multiplexed onto a single full-duplex
+ * stream which is configured as a tty. That is, ptem, ldterm and ttycompat are
+ * autopushed onto the stream when the slave side is opened. There is only a
+ * single zfd dev (0) needed for the primary stream.
+ *
+ * When the 'n' flag is set, it is assumed that output logging will be done
+ * within the zone itself. In this configuration 1 or 2 additional zfd devices,
+ * depending on tty mode ('t' flag) are created within the zone. An application
+ * can then configure the zfd streams driver into a multiplexer. Output from
+ * the stdout/stderr zfd(s) will be teed into the correspond logging zfd(s)
+ * within the zone.
+ *
+ * The following is a diagram of how this works for a '-n' configuration:
+ *
+ *
+ *              zoneadmd (for zlogin -I stdout)
+ * GZ:             ^
+ *                 |
+ *     --------------------------
+ *                 ^
+ * NGZ:            |
+ *      app >1 -> zfd1 -> zfd3 -> logger (for logger to consume app's stdout)
+ *
+ * There would be a similar path for the app's stderr into zfd4 for the logger
+ * to consume stderr.
+ */
+
+#include <sys/types.h>
+#include <sys/cmn_err.h>
+#include <sys/conf.h>
+#include <sys/cred.h>
+#include <sys/ddi.h>
+#include <sys/debug.h>
+#include <sys/devops.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/kstr.h>
+#include <sys/modctl.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/stream.h>
+#include <sys/stropts.h>
+#include <sys/strsun.h>
+#include <sys/sunddi.h>
+#include <sys/sysmacros.h>
+#include <sys/systm.h>
+#include <sys/types.h>
+#include <sys/zfd.h>
+#include <sys/vnode.h>
+#include <sys/fs/snode.h>
+#include <sys/zone.h>
+#include <sys/sdt.h>
+
+static kmutex_t zfd_mux_lock;
+
+static int zfd_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
+static int zfd_attach(dev_info_t *, ddi_attach_cmd_t);
+static int zfd_detach(dev_info_t *, ddi_detach_cmd_t);
+
+static int zfd_open(queue_t *, dev_t *, int, int, cred_t *);
+static int zfd_close(queue_t *, int, cred_t *);
+static void zfd_wput(queue_t *, mblk_t *);
+static void zfd_rsrv(queue_t *);
+static void zfd_wsrv(queue_t *);
+
+/*
+ * The instance number is encoded in the dev_t in the minor number; the lowest
+ * bit of the minor number is used to track the master vs. slave side of the
+ * fd. The rest of the bits in the minor number are the instance.
+ */
+#define	ZFD_MASTER_MINOR		0
+#define	ZFD_SLAVE_MINOR		1
+
+#define	ZFD_INSTANCE(x)		(getminor((x)) >> 1)
+#define	ZFD_NODE(x)		(getminor((x)) & 0x01)
+
+/*
+ * This macro converts a zfd_state_t pointer to the associated slave minor
+ * node's dev_t.
+ */
+#define	ZFD_STATE_TO_SLAVEDEV(x)	\
+	(makedevice(ddi_driver_major((x)->zfd_devinfo), \
+	(minor_t)(ddi_get_instance((x)->zfd_devinfo) << 1 | ZFD_SLAVE_MINOR)))
+
+int zfd_debug = 0;
+#define	DBG(a)		if (zfd_debug) cmn_err(CE_NOTE, a)
+#define	DBG1(a, b)	if (zfd_debug) cmn_err(CE_NOTE, a, b)
+
+/*
+ * ZFD Pseudo Terminal Module: stream data structure definitions,
+ * based on zcons.
+ */
+static struct module_info zfd_info = {
+	0x20FD,	/* ZOFD - 8445 */
+	"zfd",
+	0,		/* min packet size */
+	INFPSZ,		/* max packet size - infinity */
+	2048,		/* high water */
+	128		/* low water */
+};
+
+static struct qinit zfd_rinit = {
+	NULL,
+	(int (*)()) zfd_rsrv,
+	zfd_open,
+	zfd_close,
+	NULL,
+	&zfd_info,
+	NULL
+};
+
+static struct qinit zfd_winit = {
+	(int (*)()) zfd_wput,
+	(int (*)()) zfd_wsrv,
+	NULL,
+	NULL,
+	NULL,
+	&zfd_info,
+	NULL
+};
+
+static struct streamtab zfd_tab_info = {
+	&zfd_rinit,
+	&zfd_winit,
+	NULL,
+	NULL
+};
+
+#define	ZFD_CONF_FLAG	(D_MP | D_MTQPAIR | D_MTOUTPERIM | D_MTOCEXCL)
+
+/*
+ * this will define (struct cb_ops cb_zfd_ops) and (struct dev_ops zfd_ops)
+ */
+DDI_DEFINE_STREAM_OPS(zfd_ops, nulldev, nulldev, zfd_attach, zfd_detach, \
+	nodev, zfd_getinfo, ZFD_CONF_FLAG, &zfd_tab_info, \
+	ddi_quiesce_not_needed);
+
+/*
+ * Module linkage information for the kernel.
+ */
+
+static struct modldrv modldrv = {
+	&mod_driverops, 	/* Type of module (this is a pseudo driver) */
+	"Zone FD driver",	/* description of module */
+	&zfd_ops		/* driver ops */
+};
+
+static struct modlinkage modlinkage = {
+	MODREV_1,
+	&modldrv,
+	NULL
+};
+
+typedef enum {
+	ZFD_NO_MUX,
+	ZFD_PRIMARY_STREAM,
+	ZFD_LOG_STREAM
+} zfd_mux_type_t;
+
+typedef struct zfd_state {
+	dev_info_t *zfd_devinfo;	/* instance info */
+	queue_t *zfd_master_rdq;	/* GZ read queue */
+	queue_t *zfd_slave_rdq;		/* in-zone read queue */
+	int zfd_state;			/* ZFD_STATE_MOPEN, ZFD_STATE_SOPEN */
+	int zfd_tty;			/* ZFD_MAKETTY - strm mods will push */
+	boolean_t zfd_is_flowcon;	/* primary stream flow stopped */
+	boolean_t zfd_allow_flowcon;	/* use flow control */
+	zfd_mux_type_t zfd_muxt;	/* state type: none, primary, log */
+	struct zfd_state *zfd_inst_pri; /* log state's primary ptr */
+	struct zfd_state *zfd_inst_log;	/* primary state's log ptr */
+} zfd_state_t;
+
+#define	ZFD_STATE_MOPEN	0x01
+#define	ZFD_STATE_SOPEN	0x02
+
+static void *zfd_soft_state;
+
+/*
+ * List of STREAMS modules that are autopushed onto a slave instance when its
+ * opened, but only if the ZFD_MAKETTY ioctl has first been received by the
+ * master.
+ */
+static char *zfd_mods[] = {
+	"ptem",
+	"ldterm",
+	"ttcompat",
+	NULL
+};
+
+int
+_init(void)
+{
+	int err;
+
+	if ((err = ddi_soft_state_init(&zfd_soft_state, sizeof (zfd_state_t),
+	    0)) != 0) {
+		return (err);
+	}
+
+	if ((err = mod_install(&modlinkage)) != 0)
+		ddi_soft_state_fini(zfd_soft_state);
+
+	mutex_init(&zfd_mux_lock, NULL, MUTEX_DEFAULT, NULL);
+	return (err);
+}
+
+
+int
+_fini(void)
+{
+	int err;
+
+	if ((err = mod_remove(&modlinkage)) != 0) {
+		return (err);
+	}
+
+	ddi_soft_state_fini(&zfd_soft_state);
+	mutex_destroy(&zfd_mux_lock);
+	return (0);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&modlinkage, modinfop));
+}
+
+static int
+zfd_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+	zfd_state_t *zfds;
+	int instance;
+	char masternm[ZFD_NAME_LEN], slavenm[ZFD_NAME_LEN];
+
+	if (cmd != DDI_ATTACH)
+		return (DDI_FAILURE);
+
+	instance = ddi_get_instance(dip);
+	if (ddi_soft_state_zalloc(zfd_soft_state, instance) != DDI_SUCCESS)
+		return (DDI_FAILURE);
+
+	(void) snprintf(masternm, sizeof (masternm), "%s%d", ZFD_MASTER_NAME,
+	    instance);
+	(void) snprintf(slavenm, sizeof (slavenm), "%s%d", ZFD_SLAVE_NAME,
+	    instance);
+
+	/*
+	 * Create the master and slave minor nodes.
+	 */
+	if ((ddi_create_minor_node(dip, slavenm, S_IFCHR,
+	    instance << 1 | ZFD_SLAVE_MINOR, DDI_PSEUDO, 0) == DDI_FAILURE) ||
+	    (ddi_create_minor_node(dip, masternm, S_IFCHR,
+	    instance << 1 | ZFD_MASTER_MINOR, DDI_PSEUDO, 0) == DDI_FAILURE)) {
+		ddi_remove_minor_node(dip, NULL);
+		ddi_soft_state_free(zfd_soft_state, instance);
+		return (DDI_FAILURE);
+	}
+
+	VERIFY((zfds = ddi_get_soft_state(zfd_soft_state, instance)) != NULL);
+	zfds->zfd_devinfo = dip;
+	zfds->zfd_tty = 0;
+	zfds->zfd_muxt = ZFD_NO_MUX;
+	zfds->zfd_inst_log = NULL;
+	return (DDI_SUCCESS);
+}
+
+static int
+zfd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+	zfd_state_t *zfds;
+	int instance;
+
+	if (cmd != DDI_DETACH)
+		return (DDI_FAILURE);
+
+	instance = ddi_get_instance(dip);
+	if ((zfds = ddi_get_soft_state(zfd_soft_state, instance)) == NULL)
+		return (DDI_FAILURE);
+
+	if ((zfds->zfd_state & ZFD_STATE_MOPEN) ||
+	    (zfds->zfd_state & ZFD_STATE_SOPEN)) {
+		DBG1("zfd_detach: device (dip=%p) still open\n", (void *)dip);
+		return (DDI_FAILURE);
+	}
+
+	ddi_remove_minor_node(dip, NULL);
+	ddi_soft_state_free(zfd_soft_state, instance);
+
+	return (DDI_SUCCESS);
+}
+
+/*
+ * zfd_getinfo()
+ *	getinfo(9e) entrypoint.
+ */
+/*ARGSUSED*/
+static int
+zfd_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
+{
+	zfd_state_t *zfds;
+	int instance = ZFD_INSTANCE((dev_t)arg);
+
+	switch (infocmd) {
+	case DDI_INFO_DEVT2DEVINFO:
+		if ((zfds = ddi_get_soft_state(zfd_soft_state,
+		    instance)) == NULL)
+			return (DDI_FAILURE);
+		*result = zfds->zfd_devinfo;
+		return (DDI_SUCCESS);
+	case DDI_INFO_DEVT2INSTANCE:
+		*result = (void *)(uintptr_t)instance;
+		return (DDI_SUCCESS);
+	}
+	return (DDI_FAILURE);
+}
+
+/*
+ * Return the equivalent queue from the other side of the relationship.
+ * e.g.: given the slave's write queue, return the master's write queue.
+ */
+static queue_t *
+zfd_switch(queue_t *qp)
+{
+	zfd_state_t *zfds = qp->q_ptr;
+	ASSERT(zfds != NULL);
+
+	if (qp == zfds->zfd_master_rdq)
+		return (zfds->zfd_slave_rdq);
+	else if (OTHERQ(qp) == zfds->zfd_master_rdq && zfds->zfd_slave_rdq
+	    != NULL)
+		return (OTHERQ(zfds->zfd_slave_rdq));
+	else if (qp == zfds->zfd_slave_rdq)
+		return (zfds->zfd_master_rdq);
+	else if (OTHERQ(qp) == zfds->zfd_slave_rdq && zfds->zfd_master_rdq
+	    != NULL)
+		return (OTHERQ(zfds->zfd_master_rdq));
+	else
+		return (NULL);
+}
+
+/*
+ * For debugging and outputting messages.  Returns the name of the side of
+ * the relationship associated with this queue.
+ */
+static const char *
+zfd_side(queue_t *qp)
+{
+	zfd_state_t *zfds = qp->q_ptr;
+	ASSERT(zfds != NULL);
+
+	if (qp == zfds->zfd_master_rdq ||
+	    OTHERQ(qp) == zfds->zfd_master_rdq) {
+		return ("master");
+	}
+	ASSERT(qp == zfds->zfd_slave_rdq || OTHERQ(qp) == zfds->zfd_slave_rdq);
+	return ("slave");
+}
+
+/*ARGSUSED*/
+static int
+zfd_master_open(zfd_state_t *zfds,
+    queue_t	*rqp,	/* pointer to the read side queue */
+    dev_t	*devp,	/* pointer to stream tail's dev */
+    int		oflag,	/* the user open(2) supplied flags */
+    int		sflag,	/* open state flag */
+    cred_t	*credp)	/* credentials */
+{
+	mblk_t *mop;
+	struct stroptions *sop;
+
+	/*
+	 * Enforce exclusivity on the master side; the only consumer should
+	 * be the zoneadmd for the zone.
+	 */
+	if ((zfds->zfd_state & ZFD_STATE_MOPEN) != 0)
+		return (EBUSY);
+
+	if ((mop = allocb(sizeof (struct stroptions), BPRI_MED)) == NULL) {
+		DBG("zfd_master_open(): mop allocation failed\n");
+		return (ENOMEM);
+	}
+
+	zfds->zfd_state |= ZFD_STATE_MOPEN;
+
+	/*
+	 * q_ptr stores driver private data; stash the soft state data on both
+	 * read and write sides of the queue.
+	 */
+	WR(rqp)->q_ptr = rqp->q_ptr = zfds;
+	qprocson(rqp);
+
+	/*
+	 * Following qprocson(), the master side is fully plumbed into the
+	 * STREAM and may send/receive messages.  Setting zfds->zfd_master_rdq
+	 * will allow the slave to send messages to us (the master).
+	 * This cannot occur before qprocson() because the master is not
+	 * ready to process them until that point.
+	 */
+	zfds->zfd_master_rdq = rqp;
+
+	/*
+	 * set up hi/lo water marks on stream head read queue and add
+	 * controlling tty as needed.
+	 */
+	mop->b_datap->db_type = M_SETOPTS;
+	mop->b_wptr += sizeof (struct stroptions);
+	sop = (struct stroptions *)(void *)mop->b_rptr;
+	if (oflag & FNOCTTY)
+		sop->so_flags = SO_HIWAT | SO_LOWAT;
+	else
+		sop->so_flags = SO_HIWAT | SO_LOWAT | SO_ISTTY;
+	sop->so_hiwat = 512;
+	sop->so_lowat = 256;
+	putnext(rqp, mop);
+
+	return (0);
+}
+
+/*ARGSUSED*/
+static int
+zfd_slave_open(zfd_state_t *zfds,
+    queue_t	*rqp,	/* pointer to the read side queue */
+    dev_t	*devp,	/* pointer to stream tail's dev */
+    int		oflag,	/* the user open(2) supplied flags */
+    int		sflag,	/* open state flag */
+    cred_t	*credp)	/* credentials */
+{
+	mblk_t *mop;
+	struct stroptions *sop;
+	/*
+	 * The slave side can be opened as many times as needed.
+	 */
+	if ((zfds->zfd_state & ZFD_STATE_SOPEN) != 0) {
+		ASSERT((rqp != NULL) && (WR(rqp)->q_ptr == zfds));
+		return (0);
+	}
+
+	/* A log stream is read-only */
+	if (zfds->zfd_muxt == ZFD_LOG_STREAM &&
+	    (oflag & (FREAD | FWRITE)) != FREAD)
+		return (EINVAL);
+
+	if (zfds->zfd_tty == 1) {
+		major_t major;
+		minor_t minor;
+		minor_t lastminor;
+		uint_t anchorindex;
+
+		/*
+		 * Set up sad(7D) so that the necessary STREAMS modules will
+		 * be in place.  A wrinkle is that 'ptem' must be anchored
+		 * in place (see streamio(7i)) because we always want the
+		 * fd to have terminal semantics.
+		 */
+		minor =
+		    ddi_get_instance(zfds->zfd_devinfo) << 1 | ZFD_SLAVE_MINOR;
+		major = ddi_driver_major(zfds->zfd_devinfo);
+		lastminor = 0;
+		anchorindex = 1;
+		if (kstr_autopush(SET_AUTOPUSH, &major, &minor, &lastminor,
+		    &anchorindex, zfd_mods) != 0) {
+			DBG("zfd_slave_open(): kstr_autopush() failed\n");
+			return (EIO);
+		}
+	}
+
+	if ((mop = allocb(sizeof (struct stroptions), BPRI_MED)) == NULL) {
+		DBG("zfd_slave_open(): mop allocation failed\n");
+		return (ENOMEM);
+	}
+
+	zfds->zfd_state |= ZFD_STATE_SOPEN;
+
+	/*
+	 * q_ptr stores driver private data; stash the soft state data on both
+	 * read and write sides of the queue.
+	 */
+	WR(rqp)->q_ptr = rqp->q_ptr = zfds;
+
+	qprocson(rqp);
+
+	/*
+	 * Must follow qprocson(), since we aren't ready to process until then.
+	 */
+	zfds->zfd_slave_rdq = rqp;
+
+	/*
+	 * set up hi/lo water marks on stream head read queue and add
+	 * controlling tty as needed.
+	 */
+	mop->b_datap->db_type = M_SETOPTS;
+	mop->b_wptr += sizeof (struct stroptions);
+	sop = (struct stroptions *)(void *)mop->b_rptr;
+	sop->so_flags = SO_HIWAT | SO_LOWAT | SO_ISTTY;
+	sop->so_hiwat = 512;
+	sop->so_lowat = 256;
+	putnext(rqp, mop);
+
+	return (0);
+}
+
+/*
+ * open(9e) entrypoint; checks sflag, and rejects anything unordinary.
+ */
+static int
+zfd_open(queue_t *rqp,		/* pointer to the read side queue */
+	dev_t   *devp,		/* pointer to stream tail's dev */
+	int	oflag,		/* the user open(2) supplied flags */
+	int	sflag,		/* open state flag */
+	cred_t  *credp)		/* credentials */
+{
+	int instance = ZFD_INSTANCE(*devp);
+	int ret;
+	zfd_state_t *zfds;
+
+	if (sflag != 0)
+		return (EINVAL);
+
+	if ((zfds = ddi_get_soft_state(zfd_soft_state, instance)) == NULL)
+		return (ENXIO);
+
+	switch (ZFD_NODE(*devp)) {
+	case ZFD_MASTER_MINOR:
+		ret = zfd_master_open(zfds, rqp, devp, oflag, sflag, credp);
+		break;
+	case ZFD_SLAVE_MINOR:
+		ret = zfd_slave_open(zfds, rqp, devp, oflag, sflag, credp);
+		/*
+		 * If we just opened the log stream and flow control has
+		 * been enabled, we want to make sure the primary stream can
+		 * start flowing.
+		 */
+		if (ret == 0 && zfds->zfd_muxt == ZFD_LOG_STREAM &&
+		    zfds->zfd_inst_pri->zfd_allow_flowcon) {
+			zfds->zfd_inst_pri->zfd_is_flowcon = B_FALSE;
+			if (zfds->zfd_inst_pri->zfd_master_rdq != NULL)
+				qenable(RD(zfds->zfd_inst_pri->zfd_master_rdq));
+		}
+		break;
+	default:
+		ret = ENXIO;
+		break;
+	}
+
+	return (ret);
+}
+
+/*
+ * close(9e) entrypoint.
+ */
+/*ARGSUSED1*/
+static int
+zfd_close(queue_t *rqp, int flag, cred_t *credp)
+{
+	queue_t *wqp;
+	mblk_t	*bp;
+	zfd_state_t *zfds;
+	major_t major;
+	minor_t minor;
+
+	zfds = (zfd_state_t *)rqp->q_ptr;
+
+	if (rqp == zfds->zfd_master_rdq) {
+		DBG("Closing master side");
+
+		zfds->zfd_master_rdq = NULL;
+		zfds->zfd_state &= ~ZFD_STATE_MOPEN;
+
+		/*
+		 * qenable slave side write queue so that it can flush
+		 * its messages as master's read queue is going away
+		 */
+		if (zfds->zfd_slave_rdq != NULL) {
+			qenable(WR(zfds->zfd_slave_rdq));
+		}
+
+		qprocsoff(rqp);
+		WR(rqp)->q_ptr = rqp->q_ptr = NULL;
+
+	} else if (rqp == zfds->zfd_slave_rdq) {
+
+		DBG("Closing slave side");
+		zfds->zfd_state &= ~ZFD_STATE_SOPEN;
+		zfds->zfd_slave_rdq = NULL;
+
+		wqp = WR(rqp);
+		while ((bp = getq(wqp)) != NULL) {
+			if (zfds->zfd_master_rdq != NULL)
+				putnext(zfds->zfd_master_rdq, bp);
+			else if (bp->b_datap->db_type == M_IOCTL)
+				miocnak(wqp, bp, 0, 0);
+			else
+				freemsg(bp);
+		}
+
+		/*
+		 * Qenable master side write queue so that it can flush its
+		 * messages as slaves's read queue is going away.
+		 */
+		if (zfds->zfd_master_rdq != NULL)
+			qenable(WR(zfds->zfd_master_rdq));
+
+		/*
+		 * Qenable primary stream if necessary.
+		 */
+		if (zfds->zfd_muxt == ZFD_LOG_STREAM &&
+		    zfds->zfd_inst_pri->zfd_allow_flowcon) {
+			zfds->zfd_inst_pri->zfd_is_flowcon = B_FALSE;
+			if (zfds->zfd_inst_pri->zfd_master_rdq != NULL)
+				qenable(RD(zfds->zfd_inst_pri->zfd_master_rdq));
+		}
+
+		qprocsoff(rqp);
+		WR(rqp)->q_ptr = rqp->q_ptr = NULL;
+
+		if (zfds->zfd_tty == 1) {
+			/*
+			 * Clear the sad configuration so that reopening
+			 * doesn't fail to set up sad configuration.
+			 */
+			major = ddi_driver_major(zfds->zfd_devinfo);
+			minor = ddi_get_instance(zfds->zfd_devinfo) << 1 |
+			    ZFD_SLAVE_MINOR;
+			(void) kstr_autopush(CLR_AUTOPUSH, &major, &minor,
+			    NULL, NULL, NULL);
+		}
+	}
+
+	return (0);
+}
+
+static void
+handle_mflush(queue_t *qp, mblk_t *mp)
+{
+	mblk_t *nmp;
+	DBG1("M_FLUSH on %s side", zfd_side(qp));
+
+	if (*mp->b_rptr & FLUSHW) {
+		DBG1("M_FLUSH, FLUSHW, %s side", zfd_side(qp));
+		flushq(qp, FLUSHDATA);
+		*mp->b_rptr &= ~FLUSHW;
+		if ((*mp->b_rptr & FLUSHR) == 0) {
+			/*
+			 * FLUSHW only. Change to FLUSHR and putnext other side,
+			 * then we are done.
+			 */
+			*mp->b_rptr |= FLUSHR;
+			if (zfd_switch(RD(qp)) != NULL) {
+				putnext(zfd_switch(RD(qp)), mp);
+				return;
+			}
+		} else if ((zfd_switch(RD(qp)) != NULL) &&
+		    (nmp = copyb(mp)) != NULL) {
+			/*
+			 * It is a FLUSHRW; we copy the mblk and send
+			 * it to the other side, since we still need to use
+			 * the mblk in FLUSHR processing, below.
+			 */
+			putnext(zfd_switch(RD(qp)), nmp);
+		}
+	}
+
+	if (*mp->b_rptr & FLUSHR) {
+		DBG("qreply(qp) turning FLUSHR around\n");
+		qreply(qp, mp);
+		return;
+	}
+	freemsg(mp);
+}
+
+/*
+ * Evaluate the various conditionals to determine if we're teeing into a log
+ * stream and if the primary stream should be flow controlled. This function
+ * can set the zfd_is_flowcon flag as a side effect.
+ *
+ * When teeing with flow control, we always queue the teed msg here and if
+ * the queue is getting full, we set zfd_is_flowcon. The primary stream will
+ * always queue when zfd_is_flowcon and will also not be served when
+ * zfd_is_flowcon is set. This causes backpressure on the primary stream
+ * until the teed queue can drain.
+ */
+static void
+zfd_tee_handler(zfd_state_t *zfds, unsigned char type, mblk_t *mp)
+{
+	queue_t *log_qp;
+	zfd_state_t *log_zfds;
+	mblk_t *lmp;
+
+	if (zfds->zfd_muxt != ZFD_PRIMARY_STREAM)
+		return;
+
+	if (type != M_DATA)
+		return;
+
+	log_zfds = zfds->zfd_inst_log;
+	if (log_zfds == NULL)
+		return;
+
+	ASSERT(log_zfds->zfd_muxt == ZFD_LOG_STREAM);
+
+	if ((log_zfds->zfd_state & ZFD_STATE_SOPEN) == 0) {
+		if (zfds->zfd_allow_flowcon)
+			zfds->zfd_is_flowcon = B_TRUE;
+		return;
+	}
+
+	/* The zfd_slave_rdq is null until the log dev is opened in the zone */
+	log_qp = RD(log_zfds->zfd_slave_rdq);
+	DTRACE_PROBE2(zfd__tee__check, void *, log_qp, void *, zfds);
+
+	if (!zfds->zfd_allow_flowcon) {
+		/*
+		 * We're not supposed to tee with flow control and the tee is
+		 * full so we skip teeing into the log stream.
+		 */
+		if ((log_qp->q_flag & QFULL) != 0)
+			return;
+	}
+
+	/*
+	 * Tee the message into the log stream.
+	 */
+	lmp = dupmsg(mp);
+	if (lmp == NULL) {
+		if (zfds->zfd_allow_flowcon)
+			zfds->zfd_is_flowcon = B_TRUE;
+		return;
+	}
+
+	if (log_qp->q_first == NULL && bcanputnext(log_qp, lmp->b_band)) {
+		putnext(log_qp, lmp);
+	} else {
+		if (putq(log_qp, lmp) == 0) {
+			/* The logger queue is full, free the msg. */
+			freemsg(lmp);
+		}
+		/*
+		 * If we're supposed to tee with flow control and the tee is
+		 * over the high water mark then we want the primary stream to
+		 * stop flowing. We'll stop queueing the primary stream after
+		 * the log stream has drained.
+		 */
+		if (zfds->zfd_allow_flowcon &&
+		    log_qp->q_count > log_qp->q_hiwat) {
+			zfds->zfd_is_flowcon = B_TRUE;
+		}
+	}
+}
+
+/*
+ * wput(9E) is symmetric for master and slave sides, so this handles both
+ * without splitting the codepath.  (The only exception to this is the
+ * processing of zfd ioctls, which is restricted to the master side.)
+ *
+ * zfd_wput() looks at the other side; if there is no process holding that
+ * side open, it frees the message.  This prevents processes from hanging
+ * if no one is holding open the fd.  Otherwise, it putnext's high
+ * priority messages, putnext's normal messages if possible, and otherwise
+ * enqueues the messages; in the case that something is enqueued, wsrv(9E)
+ * will take care of eventually shuttling I/O to the other side.
+ *
+ * When configured as a multiplexer, then anything written to the stream
+ * from inside the zone is also teed off to the corresponding log stream
+ * for consumption within the zone (i.e. the log stream can be read, but never
+ * written to, by an application inside the zone).
+ */
+static void
+zfd_wput(queue_t *qp, mblk_t *mp)
+{
+	unsigned char type = mp->b_datap->db_type;
+	zfd_state_t *zfds;
+	struct iocblk *iocbp;
+	boolean_t must_queue = B_FALSE;
+
+	ASSERT(qp->q_ptr);
+
+	DBG1("entering zfd_wput, %s side", zfd_side(qp));
+
+	/*
+	 * Process zfd ioctl messages if qp is the master side's write queue.
+	 */
+	zfds = (zfd_state_t *)qp->q_ptr;
+
+	if (type == M_IOCTL) {
+		iocbp = (struct iocblk *)(void *)mp->b_rptr;
+
+		switch (iocbp->ioc_cmd) {
+		case ZFD_MAKETTY:
+			zfds->zfd_tty = 1;
+			miocack(qp, mp, 0, 0);
+			return;
+		case ZFD_EOF:
+			if (zfds->zfd_slave_rdq != NULL)
+				(void) putnextctl(zfds->zfd_slave_rdq,
+				    M_HANGUP);
+			miocack(qp, mp, 0, 0);
+			return;
+		case ZFD_HAS_SLAVE:
+			if ((zfds->zfd_state & ZFD_STATE_SOPEN) != 0) {
+				miocack(qp, mp, 0, 0);
+			} else {
+				miocack(qp, mp, 0, ENOTTY);
+			}
+			return;
+		case ZFD_MUX: {
+			/*
+			 * Setup the multiplexer configuration for the two
+			 * streams.
+			 *
+			 * We expect to be called on the stream that will
+			 * become the log stream and be passed one data block
+			 * with the minor number of the slave side of the
+			 * primary stream.
+			 */
+			int to;
+			int instance;
+			zfd_state_t *prim_zfds;
+
+			if (iocbp->ioc_count != TRANSPARENT ||
+			    mp->b_cont == NULL) {
+				miocack(qp, mp, 0, EINVAL);
+				return;
+			}
+
+			/* Get the primary slave minor device number */
+			ASSERT(IS_P2ALIGNED(mp->b_cont->b_rptr, 4));
+			/* LINTED - b_rptr will always be aligned. */
+			to = *(int *)mp->b_cont->b_rptr;
+			instance = ZFD_INSTANCE(to);
+
+			if ((prim_zfds = ddi_get_soft_state(zfd_soft_state,
+			    instance)) == NULL) {
+				miocack(qp, mp, 0, EINVAL);
+				return;
+			}
+
+			/* Disallow changing primary/log once set. */
+			mutex_enter(&zfd_mux_lock);
+			if (zfds->zfd_muxt != ZFD_NO_MUX ||
+			    prim_zfds->zfd_muxt != ZFD_NO_MUX) {
+				mutex_exit(&zfd_mux_lock);
+				miocack(qp, mp, 0, EINVAL);
+				return;
+			}
+
+			zfds->zfd_muxt = ZFD_LOG_STREAM;
+			zfds->zfd_inst_pri = prim_zfds;
+			prim_zfds->zfd_muxt = ZFD_PRIMARY_STREAM;
+			prim_zfds->zfd_inst_log = zfds;
+			mutex_exit(&zfd_mux_lock);
+			DTRACE_PROBE2(zfd__mux__link, void *, prim_zfds,
+			    void *, zfds);
+
+			miocack(qp, mp, 0, 0);
+			return;
+			}
+		case ZFD_MUX_FLOWCON: {
+			/*
+			 * We expect this ioctl to be issued against the
+			 * log stream. We don't use the primary stream since
+			 * there can be other streams modules pushed onto that
+			 * stream which would interfere with the ioctl.
+			 */
+			int val;
+			zfd_state_t *prim_zfds;
+
+			if (iocbp->ioc_count != TRANSPARENT ||
+			    mp->b_cont == NULL) {
+				miocack(qp, mp, 0, EINVAL);
+				return;
+			}
+
+			if (zfds->zfd_muxt != ZFD_LOG_STREAM) {
+				miocack(qp, mp, 0, EINVAL);
+				return;
+			}
+			prim_zfds = zfds->zfd_inst_pri;
+
+			/* Get the flow control setting */
+			ASSERT(IS_P2ALIGNED(mp->b_cont->b_rptr, 4));
+			/* LINTED - b_rptr will always be aligned. */
+			val = *(int *)mp->b_cont->b_rptr;
+			if (val != 0 && val != 1) {
+				miocack(qp, mp, 0, EINVAL);
+				return;
+			}
+
+			prim_zfds->zfd_allow_flowcon = (boolean_t)val;
+			if (!prim_zfds->zfd_allow_flowcon)
+				prim_zfds->zfd_is_flowcon = B_FALSE;
+
+			DTRACE_PROBE1(zfd__mux__flowcon, void *, prim_zfds);
+			miocack(qp, mp, 0, 0);
+			return;
+			}
+		default:
+			break;
+		}
+	}
+
+	/* if on the write side, may need to tee */
+	if (zfds->zfd_slave_rdq != NULL && qp == WR(zfds->zfd_slave_rdq)) {
+		/* tee output to any attached log stream */
+		zfd_tee_handler(zfds, type, mp);
+
+		/* high-priority msgs are not subject to flow control */
+		if (zfds->zfd_is_flowcon && type == M_DATA)
+			must_queue = B_TRUE;
+	}
+
+	if (zfd_switch(RD(qp)) == NULL) {
+		DBG1("wput to %s side (no one listening)", zfd_side(qp));
+		switch (type) {
+		case M_FLUSH:
+			handle_mflush(qp, mp);
+			break;
+		case M_IOCTL:
+			miocnak(qp, mp, 0, 0);
+			break;
+		default:
+			freemsg(mp);
+			break;
+		}
+		return;
+	}
+
+	if (type >= QPCTL) {
+		DBG1("(hipri) wput, %s side", zfd_side(qp));
+		switch (type) {
+		case M_READ:		/* supposedly from ldterm? */
+			DBG("zfd_wput: tossing M_READ\n");
+			freemsg(mp);
+			break;
+		case M_FLUSH:
+			handle_mflush(qp, mp);
+			break;
+		default:
+			/*
+			 * Put this to the other side.
+			 */
+			ASSERT(zfd_switch(RD(qp)) != NULL);
+			putnext(zfd_switch(RD(qp)), mp);
+			break;
+		}
+		DBG1("done (hipri) wput, %s side", zfd_side(qp));
+		return;
+	}
+
+	/*
+	 * If the primary stream has been stopped for flow control then
+	 * enqueue the msg, otherwise only putnext if there isn't already
+	 * something in the queue. If we don't do this then things would wind
+	 * up out of order.
+	 */
+	if (!must_queue && qp->q_first == NULL &&
+	    bcanputnext(RD(zfd_switch(qp)), mp->b_band)) {
+		putnext(RD(zfd_switch(qp)), mp);
+	} else {
+		/*
+		 * zfd_wsrv expects msgs queued on the primary queue. Those
+		 * will be handled by zfd_wsrv after zfd_rsrv performs the
+		 * qenable on the proper queue.
+		 */
+		(void) putq(qp, mp);
+	}
+
+	DBG1("done wput, %s side", zfd_side(qp));
+}
+
+/*
+ * Read server
+ *
+ * For primary stream:
+ * Under normal execution rsrv(9E) is symmetric for master and slave, so
+ * zfd_rsrv() can handle both without splitting up the codepath. We do this by
+ * enabling the write side of the partner.  This triggers the partner to send
+ * messages queued on its write side to this queue's read side.
+ *
+ * For log stream:
+ * Internally we've queued up the msgs that we've teed off to the log stream
+ * so when we're invoked we need to pass these along.
+ */
+static void
+zfd_rsrv(queue_t *qp)
+{
+	zfd_state_t *zfds;
+	zfds = (zfd_state_t *)qp->q_ptr;
+
+	/*
+	 * log stream server
+	 */
+	if (zfds->zfd_muxt == ZFD_LOG_STREAM && zfds->zfd_slave_rdq != NULL) {
+		queue_t *log_qp;
+		mblk_t *mp;
+
+		log_qp = RD(zfds->zfd_slave_rdq);
+
+		if ((zfds->zfd_state & ZFD_STATE_SOPEN) != 0) {
+			zfd_state_t *pzfds = zfds->zfd_inst_pri;
+
+			while ((mp = getq(qp)) != NULL) {
+				if (bcanputnext(log_qp, mp->b_band)) {
+					putnext(log_qp, mp);
+				} else {
+					(void) putbq(log_qp, mp);
+					break;
+				}
+			}
+
+			if (log_qp->q_count < log_qp->q_lowat) {
+				DTRACE_PROBE(zfd__flow__on);
+				pzfds->zfd_is_flowcon = B_FALSE;
+				if (pzfds->zfd_master_rdq != NULL)
+					qenable(RD(pzfds->zfd_master_rdq));
+			}
+		} else {
+			/* No longer open, drain the queue */
+			while ((mp = getq(qp)) != NULL) {
+				freemsg(mp);
+			}
+			flushq(qp, FLUSHALL);
+		}
+		return;
+	}
+
+	/*
+	 * Care must be taken here, as either of the master or slave side
+	 * qptr could be NULL.
+	 */
+	ASSERT(qp == zfds->zfd_master_rdq || qp == zfds->zfd_slave_rdq);
+	if (zfd_switch(qp) == NULL) {
+		DBG("zfd_rsrv: other side isn't listening\n");
+		return;
+	}
+	qenable(WR(zfd_switch(qp)));
+}
+
+/*
+ * Write server
+ *
+ * This routine is symmetric for master and slave, so it handles both without
+ * splitting up the codepath.
+ *
+ * If there are messages on this queue that can be sent to the other, send
+ * them via putnext(). Else, if queued messages cannot be sent, leave them
+ * on this queue.
+ */
+static void
+zfd_wsrv(queue_t *qp)
+{
+	queue_t *swq;
+	mblk_t *mp;
+	zfd_state_t *zfds = (zfd_state_t *)qp->q_ptr;
+
+	ASSERT(zfds != NULL);
+
+	/*
+	 * Partner has no read queue, so take the data, and throw it away.
+	 */
+	if (zfd_switch(RD(qp)) == NULL) {
+		DBG("zfd_wsrv: other side isn't listening");
+		while ((mp = getq(qp)) != NULL) {
+			if (mp->b_datap->db_type == M_IOCTL)
+				miocnak(qp, mp, 0, 0);
+			else
+				freemsg(mp);
+		}
+		flushq(qp, FLUSHALL);
+		return;
+	}
+
+	swq = RD(zfd_switch(qp));
+
+	/*
+	 * while there are messages on this write queue...
+	 */
+	while (!zfds->zfd_is_flowcon && (mp = getq(qp)) != NULL) {
+		/*
+		 * Due to the way zfd_wput is implemented, we should never
+		 * see a high priority control message here.
+		 */
+		ASSERT(mp->b_datap->db_type < QPCTL);
+
+		if (bcanputnext(swq, mp->b_band)) {
+			putnext(swq, mp);
+		} else {
+			(void) putbq(qp, mp);
+			break;
+		}
+	}
+}
--- old/usr/src/uts/common/netinet/udp.h	Wed Oct  5 00:32:25 2016
+++ new/usr/src/uts/common/netinet/udp.h	Wed Oct  5 00:32:25 2016
@@ -1,6 +1,7 @@
 /*
  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
  */
 
 /*
@@ -36,6 +37,8 @@
 #define	UDP_EXCLBIND		0x0101		/* for internal use only */
 #define	UDP_RCVHDR		0x0102		/* for internal use only */
 #define	UDP_NAT_T_ENDPOINT	0x0103		/* for internal use only */
+#define	UDP_SND_TO_CONNECTED	0x0105		/* for internal use only */
+
 /*
  * Following option in UDP_ namespace required to be exposed through
  * <xti.h> (It also requires exposing options not implemented). The options
--- old/usr/src/uts/common/os/brand.c	Wed Oct  5 00:32:25 2016
+++ new/usr/src/uts/common/os/brand.c	Wed Oct  5 00:32:25 2016
@@ -20,6 +20,7 @@
  */
 /*
  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, Joyent, Inc. All rights reserved.
  */
 
 #include <sys/kmem.h>
@@ -45,7 +46,7 @@
 };
 #else /* !__sparcv9 */
 struct brand_mach_ops native_mach_ops  = {
-		NULL, NULL, NULL, NULL
+		NULL, NULL, NULL, NULL, NULL, NULL, NULL
 };
 #endif /* !__sparcv9 */
 
@@ -53,7 +54,8 @@
 		BRAND_VER_1,
 		"native",
 		NULL,
-		&native_mach_ops
+		&native_mach_ops,
+		0
 };
 
 /*
@@ -310,46 +312,112 @@
 	mutex_exit(&brand_list_lock);
 }
 
-void
-brand_setbrand(proc_t *p)
+int
+brand_setbrand(proc_t *p, boolean_t lwps_ok)
 {
 	brand_t *bp = p->p_zone->zone_brand;
+	void *brand_data = NULL;
 
-	ASSERT(bp != NULL);
-	ASSERT(p->p_brand == &native_brand);
+	VERIFY(MUTEX_NOT_HELD(&p->p_lock));
+	VERIFY(bp != NULL);
 
 	/*
-	 * We should only be called from exec(), when we know the process
-	 * is single-threaded.
+	 * Process branding occurs during fork() and exec().  When it happens
+	 * during fork(), the LWP count will always be 0 since branding is
+	 * performed as part of getproc(), before LWPs have been associated.
+	 * The same is not true during exec(), where a multi-LWP process may
+	 * undergo branding just prior to gexec(). This is to ensure
+	 * exec-related brand hooks are available.  While it may seem
+	 * complicated to brand a multi-LWP process, the two possible outcomes
+	 * simplify things:
+	 *
+	 * 1. The exec() succeeds:  LWPs besides the caller will be killed and
+	 *    any further branding will occur in a single-LWP context.
+	 * 2. The exec() fails: The process will be promptly unbranded since
+	 *    the hooks are no longer needed.
+	 *
+	 * To prevent inconsistent brand state from being encountered during
+	 * the exec(), LWPs beyond the caller which are associated with this
+	 * process must be held temporarily.  They will be released either when
+	 * they are killed in the exec() success, or when the brand is cleared
+	 * after exec() failure.
 	 */
-	ASSERT(p->p_tlist == p->p_tlist->t_forw);
+	if (lwps_ok) {
+		/*
+		 * We've been called from a exec() context tolerating the
+		 * existence of multiple LWPs during branding is necessary.
+		 */
+		VERIFY(p == curproc);
+		VERIFY(p->p_tlist != NULL);
 
+		if (p->p_tlist != p->p_tlist->t_forw) {
+			/*
+			 * Multiple LWPs are present.  Hold all but the caller.
+			 */
+			if (!holdlwps(SHOLDFORK1)) {
+				return (-1);
+			}
+		}
+	} else {
+		/*
+		 * Processes branded during fork() should not have LWPs at all.
+		 */
+		VERIFY(p->p_tlist == NULL);
+	}
+
+	if (bp->b_data_size > 0) {
+		brand_data = kmem_zalloc(bp->b_data_size, KM_SLEEP);
+	}
+
+	mutex_enter(&p->p_lock);
+	ASSERT(!PROC_IS_BRANDED(p));
 	p->p_brand = bp;
+	p->p_brand_data = brand_data;
 	ASSERT(PROC_IS_BRANDED(p));
 	BROP(p)->b_setbrand(p);
+	mutex_exit(&p->p_lock);
+	return (0);
 }
 
 void
-brand_clearbrand(proc_t *p, boolean_t no_lwps)
+brand_clearbrand(proc_t *p, boolean_t lwps_ok)
 {
 	brand_t *bp = p->p_zone->zone_brand;
-	klwp_t *lwp = NULL;
-	ASSERT(bp != NULL);
-	ASSERT(!no_lwps || (p->p_tlist == NULL));
+	void *brand_data;
 
-	/*
-	 * If called from exec_common() or proc_exit(),
-	 * we know the process is single-threaded.
-	 * If called from fork_fail, p_tlist is NULL.
-	 */
-	if (!no_lwps) {
-		ASSERT(p->p_tlist == p->p_tlist->t_forw);
-		lwp = p->p_tlist->t_lwp;
-	}
+	VERIFY(MUTEX_NOT_HELD(&p->p_lock));
+	VERIFY(bp != NULL);
+	VERIFY(PROC_IS_BRANDED(p));
 
-	ASSERT(PROC_IS_BRANDED(p));
-	BROP(p)->b_proc_exit(p, lwp);
+	mutex_enter(&p->p_lock);
 	p->p_brand = &native_brand;
+	brand_data = p->p_brand_data;
+	p->p_brand_data = NULL;
+
+	if (lwps_ok) {
+		VERIFY(p == curproc);
+		/*
+		 * A process with multiple LWPs is being de-branded after
+		 * failing an exec.  The other LWPs were held as part of the
+		 * procedure, so they must be resumed now.
+		 */
+		if (p->p_tlist != NULL && p->p_tlist != p->p_tlist->t_forw) {
+			continuelwps(p);
+		}
+	} else {
+		/*
+		 * While clearing the brand, it's ok for one LWP to be present.
+		 * This happens when a native binary is executed inside a
+		 * branded zone, since the brand will be removed during the
+		 * course of a successful exec.
+		 */
+		VERIFY(p->p_tlist == NULL || p->p_tlist == p->p_tlist->t_forw);
+	}
+	mutex_exit(&p->p_lock);
+
+	if (brand_data != NULL) {
+		kmem_free(brand_data, bp->b_data_size);
+	}
 }
 
 #if defined(__sparcv9)
@@ -483,7 +551,7 @@
 		return (ENOSYS);
 
 	/* For all other operations this must be a branded process. */
-	if (p->p_brand == &native_brand)
+	if (!PROC_IS_BRANDED(p))
 		return (ENOSYS);
 
 	ASSERT(p->p_brand == pbrand);
@@ -601,8 +669,8 @@
 int
 brand_solaris_elfexec(vnode_t *vp, execa_t *uap, uarg_t *args,
     intpdata_t *idatap, int level, long *execsz, int setid, caddr_t exec_file,
-    cred_t *cred, int brand_action, struct brand *pbrand, char *bname,
-    char *brandlib, char *brandlib32, char *brandlinker, char *brandlinker32)
+    cred_t *cred, int *brand_action, struct brand *pbrand, char *bname,
+    char *brandlib, char *brandlib32)
 {
 
 	vnode_t		*nvp;
@@ -609,7 +677,7 @@
 	Ehdr		ehdr;
 	Addr		uphdr_vaddr;
 	intptr_t	voffset;
-	int		interp;
+	char		*interp;
 	int		i, err;
 	struct execenv	env;
 	struct execenv	origenv;
@@ -619,7 +687,6 @@
 	klwp_t		*lwp = ttolwp(curthread);
 	brand_proc_data_t	*spd;
 	brand_elf_data_t sed, *sedp;
-	char		*linker;
 	uintptr_t	lddata; /* lddata of executable's linker */
 
 	ASSERT(curproc->p_brand == pbrand);
@@ -636,12 +703,10 @@
 	 */
 	if (args->to_model == DATAMODEL_NATIVE) {
 		args->emulator = brandlib;
-		linker = brandlinker;
 	}
 #if defined(_LP64)
 	else {
 		args->emulator = brandlib32;
-		linker = brandlinker32;
 	}
 #endif  /* _LP64 */
 
@@ -725,7 +790,7 @@
 	if (args->to_model == DATAMODEL_NATIVE) {
 		err = mapexec_brand(vp, args, &ehdr, &uphdr_vaddr,
 		    &voffset, exec_file, &interp, &env.ex_bssbase,
-		    &env.ex_brkbase, &env.ex_brksize, NULL);
+		    &env.ex_brkbase, &env.ex_brksize, NULL, NULL);
 	}
 #if defined(_LP64)
 	else {
@@ -733,7 +798,7 @@
 		Elf32_Addr uphdr_vaddr32;
 		err = mapexec32_brand(vp, args, &ehdr32, &uphdr_vaddr32,
 		    &voffset, exec_file, &interp, &env.ex_bssbase,
-		    &env.ex_brkbase, &env.ex_brksize, NULL);
+		    &env.ex_brkbase, &env.ex_brksize, NULL, NULL);
 		Ehdr32to64(&ehdr32, &ehdr);
 
 		if (uphdr_vaddr32 == (Elf32_Addr)-1)
@@ -744,6 +809,10 @@
 #endif  /* _LP64 */
 	if (err != 0) {
 		restoreexecenv(&origenv, &orig_sigaltstack);
+
+		if (interp != NULL)
+			kmem_free(interp, MAXPATHLEN);
+
 		return (err);
 	}
 
@@ -761,7 +830,7 @@
 	sedp->sed_phent = ehdr.e_phentsize;
 	sedp->sed_phnum = ehdr.e_phnum;
 
-	if (interp) {
+	if (interp != NULL) {
 		if (ehdr.e_type == ET_DYN) {
 			/*
 			 * This is a shared object executable, so we
@@ -777,16 +846,20 @@
 		 * it in and store relevant information about it in the
 		 * aux vector, where the brand library can find it.
 		 */
-		if ((err = lookupname(linker, UIO_SYSSPACE,
+		if ((err = lookupname(interp, UIO_SYSSPACE,
 		    FOLLOW, NULLVPP, &nvp)) != 0) {
-			uprintf("%s: not found.", brandlinker);
+			uprintf("%s: not found.", interp);
 			restoreexecenv(&origenv, &orig_sigaltstack);
+			kmem_free(interp, MAXPATHLEN);
 			return (err);
 		}
+
+		kmem_free(interp, MAXPATHLEN);
+
 		if (args->to_model == DATAMODEL_NATIVE) {
 			err = mapexec_brand(nvp, args, &ehdr,
 			    &uphdr_vaddr, &voffset, exec_file, &interp,
-			    NULL, NULL, NULL, &lddata);
+			    NULL, NULL, NULL, &lddata, NULL);
 		}
 #if defined(_LP64)
 		else {
@@ -794,7 +867,7 @@
 			Elf32_Addr uphdr_vaddr32;
 			err = mapexec32_brand(nvp, args, &ehdr32,
 			    &uphdr_vaddr32, &voffset, exec_file, &interp,
-			    NULL, NULL, NULL, &lddata);
+			    NULL, NULL, NULL, &lddata, NULL);
 			Ehdr32to64(&ehdr32, &ehdr);
 
 			if (uphdr_vaddr32 == (Elf32_Addr)-1)
@@ -934,9 +1007,9 @@
 
 	/*
 	 * Third, the /proc aux vectors set up by elfexec() point to
-	 * brand emulation library and it's linker.  Copy these to the
+	 * brand emulation library and its linker.  Copy these to the
 	 * /proc brand specific aux vector, and update the regular
-	 * /proc aux vectors to point to the executable (and it's
+	 * /proc aux vectors to point to the executable (and its
 	 * linker).  This will enable debuggers to access the
 	 * executable via the usual /proc or elf notes aux vectors.
 	 *
@@ -1078,7 +1151,7 @@
 }
 
 /*ARGSUSED*/
-int
+void
 brand_solaris_initlwp(klwp_t *l, struct brand *pbrand)
 {
 	ASSERT(l->lwp_procp->p_brand == pbrand);
@@ -1085,7 +1158,6 @@
 	ASSERT(l->lwp_procp->p_brand_data != NULL);
 	ASSERT(l->lwp_brand == NULL);
 	l->lwp_brand = (void *)-1;
-	return (0);
 }
 
 /*ARGSUSED*/
@@ -1092,41 +1164,18 @@
 void
 brand_solaris_lwpexit(klwp_t *l, struct brand *pbrand)
 {
-	proc_t  *p = l->lwp_procp;
-
 	ASSERT(l->lwp_procp->p_brand == pbrand);
 	ASSERT(l->lwp_procp->p_brand_data != NULL);
 	ASSERT(l->lwp_brand != NULL);
-
-	/*
-	 * We should never be called for the last thread in a process.
-	 * (That case is handled by brand_solaris_proc_exit().)
-	 * Therefore this lwp must be exiting from a multi-threaded
-	 * process.
-	 */
-	ASSERT(p->p_tlist != p->p_tlist->t_forw);
-
-	l->lwp_brand = NULL;
 }
 
 /*ARGSUSED*/
 void
-brand_solaris_proc_exit(struct proc *p, klwp_t *l, struct brand *pbrand)
+brand_solaris_proc_exit(struct proc *p, struct brand *pbrand)
 {
 	ASSERT(p->p_brand == pbrand);
 	ASSERT(p->p_brand_data != NULL);
 
-	/*
-	 * When called from proc_exit(), we know that process is
-	 * single-threaded and free our lwp brand data.
-	 * otherwise just free p_brand_data and return.
-	 */
-	if (l != NULL) {
-		ASSERT(p->p_tlist == p->p_tlist->t_forw);
-		ASSERT(p->p_tlist->t_lwp == l);
-		(void) brand_solaris_freelwp(l, pbrand);
-	}
-
 	/* upon exit, free our proc brand data */
 	kmem_free(p->p_brand_data, sizeof (brand_proc_data_t));
 	p->p_brand_data = NULL;
@@ -1145,5 +1194,4 @@
 	ASSERT(p->p_tlist == p->p_tlist->t_forw);
 
 	p->p_brand_data = kmem_zalloc(sizeof (brand_proc_data_t), KM_SLEEP);
-	(void) brand_solaris_initlwp(p->p_tlist->t_lwp, pbrand);
 }
--- old/usr/src/uts/common/os/contract.c	Wed Oct  5 00:32:25 2016
+++ new/usr/src/uts/common/os/contract.c	Wed Oct  5 00:32:25 2016
@@ -21,6 +21,7 @@
 /*
  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
  */
 
 /*
@@ -287,7 +288,10 @@
 	avl_index_t where;
 	klwp_t *curlwp = ttolwp(curthread);
 
-	ASSERT(author == curproc);
+	/*
+	 * It's possible that author is not curproc if the zone is creating
+	 * a new process as a child of zsched.
+	 */
 
 	mutex_init(&ct->ct_lock, NULL, MUTEX_DEFAULT, NULL);
 	mutex_init(&ct->ct_reflock, NULL, MUTEX_DEFAULT, NULL);
--- old/usr/src/uts/common/os/exec.c	Wed Oct  5 00:32:25 2016
+++ new/usr/src/uts/common/os/exec.c	Wed Oct  5 00:32:25 2016
@@ -26,7 +26,7 @@
 /*	Copyright (c) 1988 AT&T	*/
 /*	  All Rights Reserved  	*/
 /*
- * Copyright 2014, Joyent, Inc.  All rights reserved.
+ * Copyright 2015, Joyent, Inc.  All rights reserved.
  */
 
 #include <sys/types.h>
@@ -69,6 +69,7 @@
 #include <sys/sdt.h>
 #include <sys/brand.h>
 #include <sys/klpd.h>
+#include <sys/random.h>
 
 #include <c2/audit.h>
 
@@ -97,6 +98,7 @@
 #endif
 
 #define	PSUIDFLAGS		(SNOCD|SUGID)
+#define	RANDOM_LEN	16	/* 16 bytes for AT_RANDOM aux entry */
 
 /*
  * exece() - system call wrapper around exec_common()
@@ -297,14 +299,43 @@
 	ua.argp = argp;
 	ua.envp = envp;
 
-	/* If necessary, brand this process before we start the exec. */
-	if (brandme)
-		brand_setbrand(p);
+	/* If necessary, brand this process/lwp before we start the exec. */
+	if (brandme) {
+		void *brand_data = NULL;
 
+		/*
+		 * Process branding may fail if multiple LWPs are present and
+		 * holdlwps() cannot complete successfully.
+		 */
+		error = brand_setbrand(p, B_TRUE);
+
+		if (error == 0 && BROP(p)->b_lwpdata_alloc != NULL) {
+			brand_data = BROP(p)->b_lwpdata_alloc(p);
+			if (brand_data == NULL) {
+				error = 1;
+			}
+		}
+
+		if (error == 0) {
+			mutex_enter(&p->p_lock);
+			BROP(p)->b_initlwp(lwp, brand_data);
+			mutex_exit(&p->p_lock);
+		} else {
+			VN_RELE(vp);
+			if (dir != NULL) {
+				VN_RELE(dir);
+			}
+			pn_free(&resolvepn);
+			goto fail;
+		}
+	}
+
 	if ((error = gexec(&vp, &ua, &args, NULL, 0, &execsz,
-	    exec_file, p->p_cred, brand_action)) != 0) {
-		if (brandme)
-			brand_clearbrand(p, B_FALSE);
+	    exec_file, p->p_cred, &brand_action)) != 0) {
+		if (brandme) {
+			BROP(p)->b_freelwp(lwp);
+			brand_clearbrand(p, B_TRUE);
+		}
 		VN_RELE(vp);
 		if (dir != NULL)
 			VN_RELE(dir);
@@ -336,7 +367,7 @@
 	/*
 	 * Clear contract template state
 	 */
-	lwp_ctmpl_clear(lwp);
+	lwp_ctmpl_clear(lwp, B_TRUE);
 
 	/*
 	 * Save the directory in which we found the executable for expanding
@@ -360,6 +391,8 @@
 	 * pending held signals remain held, so don't clear t_hold.
 	 */
 	mutex_enter(&p->p_lock);
+	DTRACE_PROBE3(oldcontext__set, klwp_t *, lwp,
+	    uintptr_t, lwp->lwp_oldcontext, uintptr_t, 0);
 	lwp->lwp_oldcontext = 0;
 	lwp->lwp_ustack = 0;
 	lwp->lwp_old_stk_ctl = 0;
@@ -419,8 +452,10 @@
 	TRACE_2(TR_FAC_PROC, TR_PROC_EXEC, "proc_exec:p %p up %p", p, up);
 
 	/* Unbrand ourself if necessary. */
-	if (PROC_IS_BRANDED(p) && (brand_action == EBA_NATIVE))
+	if (PROC_IS_BRANDED(p) && (brand_action == EBA_NATIVE)) {
+		BROP(p)->b_freelwp(lwp);
 		brand_clearbrand(p, B_FALSE);
+	}
 
 	setregs(&args);
 
@@ -544,7 +579,7 @@
 	long *execsz,
 	caddr_t exec_file,
 	struct cred *cred,
-	int brand_action)
+	int *brand_action)
 {
 	struct vnode *vp, *execvp = NULL;
 	proc_t *pp = ttoproc(curthread);
@@ -858,8 +893,14 @@
 			if (pp->p_plist || (pp->p_proc_flag & P_PR_TRACE))
 				args->traceinval = 1;
 		}
-		if (pp->p_proc_flag & P_PR_PTRACE)
+
+		/*
+		 * If legacy ptrace is enabled, generate the SIGTRAP.
+		 */
+		if (pp->p_proc_flag & P_PR_PTRACE) {
 			psignal(pp, SIGTRAP);
+		}
+
 		if (args->traceinval)
 			prinvalidate(&pp->p_user);
 	}
@@ -1517,7 +1558,26 @@
 	return (0);
 }
 
+/*
+ * Add a fixed size byte array to the stack (only from kernel space).
+ */
 static int
+stk_byte_add(uarg_t *args, const uint8_t *sp, size_t len)
+{
+	if (STK_AVAIL(args) < sizeof (int))
+		return (E2BIG);
+	*--args->stk_offp = args->stk_strp - args->stk_base;
+
+	if (len > STK_AVAIL(args))
+		return (E2BIG);
+	bcopy(sp, args->stk_strp, len);
+
+	args->stk_strp += len;
+
+	return (0);
+}
+
+static int
 stk_getptr(uarg_t *args, char *src, char **dst)
 {
 	int error;
@@ -1553,6 +1613,7 @@
 	size_t size, pad;
 	char *argv = (char *)uap->argp;
 	char *envp = (char *)uap->envp;
+	uint8_t rdata[RANDOM_LEN];
 
 	/*
 	 * Copy interpreter's name and argument to argv[0] and argv[1].
@@ -1635,8 +1696,9 @@
 	args->ne = args->na - argc;
 
 	/*
-	 * Add AT_SUN_PLATFORM, AT_SUN_EXECNAME, AT_SUN_BRANDNAME, and
-	 * AT_SUN_EMULATOR strings to the stack.
+	 * Add AT_SUN_PLATFORM, AT_SUN_EXECNAME, AT_SUN_BRANDNAME,
+	 * AT_SUN_BRAND_NROOT, and AT_SUN_EMULATOR strings, as well as AT_RANDOM
+	 * array, to the stack.
 	 */
 	if (auxvpp != NULL && *auxvpp != NULL) {
 		if ((error = stk_add(args, platform, UIO_SYSSPACE)) != 0)
@@ -1649,6 +1711,20 @@
 		if (args->emulator != NULL &&
 		    (error = stk_add(args, args->emulator, UIO_SYSSPACE)) != 0)
 			return (error);
+
+		/*
+		 * For the AT_RANDOM aux vector we provide 16 bytes of random
+		 * data.
+		 */
+		(void) random_get_pseudo_bytes(rdata, sizeof (rdata));
+
+		if ((error = stk_byte_add(args, rdata, sizeof (rdata))) != 0)
+			return (error);
+
+		if (args->brand_nroot != NULL &&
+		    (error = stk_add(args, args->brand_nroot,
+		    UIO_SYSSPACE)) != 0)
+			return (error);
 	}
 
 	/*
@@ -1755,7 +1831,7 @@
 	/*
 	 * Fill in the aux vector now that we know the user stack addresses
 	 * for the AT_SUN_PLATFORM, AT_SUN_EXECNAME, AT_SUN_BRANDNAME and
-	 * AT_SUN_EMULATOR strings.
+	 * AT_SUN_EMULATOR strings, as well as the AT_RANDOM array.
 	 */
 	if (auxvpp != NULL && *auxvpp != NULL) {
 		if (args->to_model == DATAMODEL_NATIVE) {
@@ -1768,6 +1844,11 @@
 			if (args->emulator != NULL)
 				ADDAUX(*a,
 				    AT_SUN_EMULATOR, (long)&ustrp[*--offp])
+			ADDAUX(*a, AT_RANDOM, (long)&ustrp[*--offp])
+			if (args->brand_nroot != NULL) {
+				ADDAUX(*a,
+				    AT_SUN_BRAND_NROOT, (long)&ustrp[*--offp])
+			}
 		} else {
 			auxv32_t **a = (auxv32_t **)auxvpp;
 			ADDAUX(*a,
@@ -1780,6 +1861,11 @@
 			if (args->emulator != NULL)
 				ADDAUX(*a, AT_SUN_EMULATOR,
 				    (int)(uintptr_t)&ustrp[*--offp])
+			ADDAUX(*a, AT_RANDOM, (int)(uintptr_t)&ustrp[*--offp])
+			if (args->brand_nroot != NULL) {
+				ADDAUX(*a, AT_SUN_BRAND_NROOT,
+				    (int)(uintptr_t)&ustrp[*--offp])
+			}
 		}
 	}
 
@@ -1868,6 +1954,9 @@
 		usrstack = (char *)USRSTACK32;
 	}
 
+	if (args->maxstack != 0 && (uintptr_t)usrstack > args->maxstack)
+		usrstack = (char *)args->maxstack;
+
 	ASSERT(P2PHASE((uintptr_t)usrstack, args->stk_align) == 0);
 
 #if defined(__sparc)
--- old/usr/src/uts/common/os/exit.c	Wed Oct  5 00:32:25 2016
+++ new/usr/src/uts/common/os/exit.c	Wed Oct  5 00:32:25 2016
@@ -21,7 +21,7 @@
 
 /*
  * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2011, Joyent, Inc. All rights reserved.
+ * Copyright 2014 Joyent, Inc. All rights reserved.
  */
 
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
@@ -230,7 +230,7 @@
 		siginfofree(lwp->lwp_curinfo);
 		lwp->lwp_curinfo = NULL;
 	}
-	lwp_ctmpl_clear(lwp);
+	lwp_ctmpl_clear(lwp, B_FALSE);
 
 	/*
 	 * Reset both the process root directory and the current working
@@ -366,20 +366,7 @@
 	}
 	mutex_exit(&p->p_lock);
 
-	DTRACE_PROC(lwp__exit);
-	DTRACE_PROC1(exit, int, why);
-
 	/*
-	 * Will perform any brand specific proc exit processing, since this
-	 * is always the last lwp, will also perform lwp_exit and free brand
-	 * data
-	 */
-	if (PROC_IS_BRANDED(p)) {
-		lwp_detach_brand_hdlrs(lwp);
-		brand_clearbrand(p, B_FALSE);
-	}
-
-	/*
 	 * Don't let init exit unless zone_start_init() failed its exec, or
 	 * we are shutting down the zone or the machine.
 	 *
@@ -390,12 +377,35 @@
 		if (z->zone_boot_err == 0 &&
 		    zone_status_get(z) < ZONE_IS_SHUTTING_DOWN &&
 		    zone_status_get(global_zone) < ZONE_IS_SHUTTING_DOWN) {
-			if (z->zone_restart_init == B_TRUE) {
-				if (restart_init(what, why) == 0)
-					return (0);
+
+			/*
+			 * If the init process should be restarted, the
+			 * "zone_restart_init" member will be set.  Some init
+			 * programs in branded zones do not tolerate a restart
+			 * in the traditional manner; setting the
+			 * "zone_reboot_on_init_exit" member will cause the
+			 * entire zone to be rebooted instead.  If neither of
+			 * these flags is set the zone will shut down.
+			 */
+			if (z->zone_reboot_on_init_exit == B_TRUE &&
+			    z->zone_restart_init == B_TRUE) {
+				/*
+				 * Trigger a zone reboot and continue
+				 * with exit processing.
+				 */
+				z->zone_init_status = wstat(why, what);
+				(void) zone_kadmin(A_REBOOT, 0, NULL,
+				    zone_kcred());
+
 			} else {
+				if (z->zone_restart_init == B_TRUE) {
+					if (restart_init(what, why) == 0)
+						return (0);
+				}
+
+				z->zone_init_status = wstat(why, what);
 				(void) zone_kadmin(A_SHUTDOWN, AD_HALT, NULL,
-				    CRED());
+				    zone_kcred());
 			}
 		}
 
@@ -407,6 +417,32 @@
 		z->zone_proc_initpid = -1;
 	}
 
+	/*
+	 * Delay firing probes (and performing brand cleanup) until after the
+	 * zone_proc_initpid check. Cases which result in zone shutdown or
+	 * restart via zone_kadmin eventually result in a call back to
+	 * proc_exit.
+	 */
+	DTRACE_PROC(lwp__exit);
+	DTRACE_PROC1(exit, int, why);
+
+	/*
+	 * Will perform any brand specific proc exit processing. Since this
+	 * is always the last lwp, will also perform lwp exit/free and proc
+	 * exit. Brand data will be freed when the process is reaped.
+	 */
+	if (PROC_IS_BRANDED(p)) {
+		BROP(p)->b_lwpexit(lwp);
+		BROP(p)->b_proc_exit(p);
+		/*
+		 * To ensure that b_proc_exit has access to brand-specific data
+		 * contained by the one remaining lwp, call the freelwp hook as
+		 * the last part of this clean-up process.
+		 */
+		BROP(p)->b_freelwp(lwp);
+		lwp_detach_brand_hdlrs(lwp);
+	}
+
 	lwp_pcb_exit();
 
 	/*
@@ -658,11 +694,23 @@
 	if ((q = p->p_child) != NULL && p != proc_init) {
 		struct proc	*np;
 		struct proc	*initp = proc_init;
+		pid_t		zone_initpid = 1;
+		struct proc	*zoneinitp = NULL;
 		boolean_t	setzonetop = B_FALSE;
 
-		if (!INGLOBALZONE(curproc))
-			setzonetop = B_TRUE;
+		if (!INGLOBALZONE(curproc)) {
+			zone_initpid = curproc->p_zone->zone_proc_initpid;
 
+			ASSERT(MUTEX_HELD(&pidlock));
+			zoneinitp = prfind(zone_initpid);
+			if (zoneinitp != NULL) {
+				initp = zoneinitp;
+			} else {
+				zone_initpid = 1;
+				setzonetop = B_TRUE;
+			}
+		}
+
 		pgdetach(p);
 
 		do {
@@ -673,7 +721,8 @@
 			 */
 			delete_ns(q->p_parent, q);
 
-			q->p_ppid = 1;
+			q->p_ppid = zone_initpid;
+
 			q->p_pidflag &= ~(CLDNOSIGCHLD | CLDWAITPID);
 			if (setzonetop) {
 				mutex_enter(&q->p_lock);
@@ -847,8 +896,50 @@
 
 	mutex_exit(&p->p_lock);
 	if (!evaporate) {
-		p->p_pidflag &= ~CLDPEND;
-		sigcld(p, sqp);
+		/*
+		 * The brand specific code only happens when the brand has a
+		 * function to call in place of sigcld and the parent of the
+		 * exiting process is not the global zone init. If the parent
+		 * is the global zone init, then the process was reparented,
+		 * and we don't want brand code delivering possibly strange
+		 * signals to init. Also, init is not branded, so any brand
+		 * specific exit data will not be picked up by init anyway.
+		 */
+		if (PROC_IS_BRANDED(p) &&
+		    BROP(p)->b_exit_with_sig != NULL &&
+		    p->p_ppid != 1) {
+			/*
+			 * The code for _fini that could unload the brand_t
+			 * blocks until the count of zones using the module
+			 * reaches zero. Zones decrement the refcount on their
+			 * brands only after all user tasks in that zone have
+			 * exited and been waited on. The decrement on the
+			 * brand's refcount happen in zone_destroy(). That
+			 * depends on zone_shutdown() having been completed.
+			 * zone_shutdown() includes a call to zone_empty(),
+			 * where the zone waits for itself to reach the state
+			 * ZONE_IS_EMPTY. This state is only set in either
+			 * zone_shutdown(), when there are no user processes as
+			 * the zone enters this function, or in
+			 * zone_task_rele(). zone_task_rele() is called from
+			 * code triggered by waiting on processes, not by the
+			 * processes exiting through proc_exit().  This means
+			 * all the branded processes that could exist for a
+			 * specific brand_t must exit and get reaped before the
+			 * refcount on the brand_t can reach 0. _fini will
+			 * never unload the corresponding brand module before
+			 * proc_exit finishes execution for all processes
+			 * branded with a particular brand_t, which makes the
+			 * operation below safe to do. Brands that wish to use
+			 * this mechanism must wait in _fini as described
+			 * above.
+			 */
+			BROP(p)->b_exit_with_sig(p, sqp);
+		} else {
+			p->p_pidflag &= ~CLDPEND;
+			sigcld(p, sqp);
+		}
+
 	} else {
 		/*
 		 * Do what sigcld() would do if the disposition
@@ -927,10 +1018,9 @@
 int
 waitid(idtype_t idtype, id_t id, k_siginfo_t *ip, int options)
 {
-	int found;
 	proc_t *cp, *pp;
-	int proc_gone;
 	int waitflag = !(options & WNOWAIT);
+	boolean_t have_brand_helper = B_FALSE;
 
 	/*
 	 * Obsolete flag, defined here only for binary compatibility
@@ -958,7 +1048,8 @@
 	pp = ttoproc(curthread);
 
 	/*
-	 * lock parent mutex so that sibling chain can be searched.
+	 * Anytime you are looking for a process, you take pidlock to prevent
+	 * things from changing as you look.
 	 */
 	mutex_enter(&pidlock);
 
@@ -978,10 +1069,37 @@
 		return (ECHILD);
 	}
 
-	while (pp->p_child != NULL) {
+	if (PROC_IS_BRANDED(pp) && BROP(pp)->b_waitid_helper != NULL) {
+		have_brand_helper = B_TRUE;
+	}
 
-		proc_gone = 0;
+	while (pp->p_child != NULL || have_brand_helper) {
+		boolean_t brand_wants_wait = B_FALSE;
+		int proc_gone = 0;
+		int found = 0;
 
+		/*
+		 * Give the brand a chance to return synthetic results from
+		 * this waitid() call before we do the real thing.
+		 */
+		if (have_brand_helper) {
+			int ret;
+
+			if (BROP(pp)->b_waitid_helper(idtype, id, ip, options,
+			    &brand_wants_wait, &ret) == 0) {
+				mutex_exit(&pidlock);
+				return (ret);
+			}
+
+			if (pp->p_child == NULL) {
+				goto no_real_children;
+			}
+		}
+
+		/*
+		 * Look for interesting children in the newstate list.
+		 */
+		VERIFY(pp->p_child != NULL);
 		for (cp = pp->p_child_ns; cp != NULL; cp = cp->p_sibling_ns) {
 			if (idtype != P_PID && (cp->p_pidflag & CLDWAITPID))
 				continue;
@@ -989,6 +1107,11 @@
 				continue;
 			if (idtype == P_PGID && id != cp->p_pgrp)
 				continue;
+			if (PROC_IS_BRANDED(pp)) {
+				if (BROP(pp)->b_wait_filter != NULL &&
+				    BROP(pp)->b_wait_filter(pp, cp) == B_FALSE)
+					continue;
+			}
 
 			switch (cp->p_wcode) {
 
@@ -1033,12 +1156,16 @@
 		 * Wow! None of the threads on the p_sibling_ns list were
 		 * interesting threads. Check all the kids!
 		 */
-		found = 0;
 		for (cp = pp->p_child; cp != NULL; cp = cp->p_sibling) {
 			if (idtype == P_PID && id != cp->p_pid)
 				continue;
 			if (idtype == P_PGID && id != cp->p_pgrp)
 				continue;
+			if (PROC_IS_BRANDED(pp)) {
+				if (BROP(pp)->b_wait_filter != NULL &&
+				    BROP(pp)->b_wait_filter(pp, cp) == B_FALSE)
+					continue;
+			}
 
 			switch (cp->p_wcode) {
 			case CLD_TRAPPED:
@@ -1107,11 +1234,12 @@
 				break;
 		}
 
+no_real_children:
 		/*
 		 * If we found no interesting processes at all,
 		 * break out and return ECHILD.
 		 */
-		if (found + proc_gone == 0)
+		if (!brand_wants_wait && (found + proc_gone == 0))
 			break;
 
 		if (options & WNOHANG) {
@@ -1130,7 +1258,7 @@
 		 * change state while we wait, we don't wait at all.
 		 * Get out with ECHILD according to SVID.
 		 */
-		if (found == proc_gone)
+		if (!brand_wants_wait && (found == proc_gone))
 			break;
 
 		if (!cv_wait_sig_swap(&pp->p_cv, &pidlock)) {
@@ -1226,6 +1354,12 @@
 		p->p_killsqp = NULL;
 	}
 
+	/* Clear any remaining brand data */
+	if (PROC_IS_BRANDED(p)) {
+		brand_clearbrand(p, B_FALSE);
+	}
+
+
 	prfree(p);	/* inform /proc */
 
 	/*
--- old/usr/src/uts/common/os/flock.c	Wed Oct  5 00:32:25 2016
+++ new/usr/src/uts/common/os/flock.c	Wed Oct  5 00:32:25 2016
@@ -523,10 +523,10 @@
  *    file descriptor the application loses its lock and does not know).
  * 2) Locks are not preserved across fork(2).
  *
- * Because these locks are only assoiciated with a pid they are per-process.
- * This is why any close will drop the lock and is also why once the process
- * forks then the lock is no longer related to the new process. These locks can
- * be considered as pid-ful.
+ * Because these locks are only associated with a PID, they are per-process.
+ * This is why any close will drop the lock and is also why, once the process
+ * forks, the lock is no longer related to the new process. These locks can
+ * be considered as PID-ful.
  *
  * See ofdlock() for the implementation of a similar but improved locking
  * scheme.
@@ -1003,7 +1003,7 @@
 
 	ASSERT(IS_DEAD(lock));
 
-	if ((fp = lock->l_ofd) != NULL)
+	if ((fp = lock->l_ofd) != NULL && fp->f_filock == (struct filock *)lock)
 		fp->f_filock = NULL;
 
 	if (IS_REFERENCED(lock)) {
--- old/usr/src/uts/common/os/fork.c	Wed Oct  5 00:32:26 2016
+++ new/usr/src/uts/common/os/fork.c	Wed Oct  5 00:32:26 2016
@@ -21,7 +21,7 @@
 
 /*
  * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2016, Joyent, Inc.
  */
 
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
@@ -84,6 +84,7 @@
 static int getproc(proc_t **, pid_t, uint_t);
 #define	GETPROC_USER	0x0
 #define	GETPROC_KERNEL	0x1
+#define	GETPROC_ZSCHED	0x2
 
 static void fork_fail(proc_t *);
 static void forklwp_fail(proc_t *);
@@ -125,6 +126,29 @@
 	}
 }
 
+/*
+ * Remove the associations of a child process from its parent and siblings.
+ */
+static void
+disown_proc(proc_t *pp, proc_t *cp)
+{
+	proc_t **orphpp;
+
+	ASSERT(MUTEX_HELD(&pidlock));
+
+	orphpp = &pp->p_orphan;
+	while (*orphpp != cp)
+		orphpp = &(*orphpp)->p_nextorph;
+	*orphpp = cp->p_nextorph;
+
+	if (pp->p_child == cp)
+		pp->p_child = cp->p_sibling;
+	if (cp->p_sibling)
+		cp->p_sibling->p_psibling = cp->p_psibling;
+	if (cp->p_psibling)
+		cp->p_psibling->p_sibling = cp->p_sibling;
+}
+
 /* ARGSUSED */
 static int64_t
 cfork(int isvfork, int isfork1, int flags)
@@ -131,7 +155,7 @@
 {
 	proc_t *p = ttoproc(curthread);
 	struct as *as;
-	proc_t *cp, **orphpp;
+	proc_t *cp;
 	klwp_t *clone;
 	kthread_t *t;
 	task_t *tk;
@@ -266,16 +290,7 @@
 			sprunlock(p);
 			fork_fail(cp);
 			mutex_enter(&pidlock);
-			orphpp = &p->p_orphan;
-			while (*orphpp != cp)
-				orphpp = &(*orphpp)->p_nextorph;
-			*orphpp = cp->p_nextorph;
-			if (p->p_child == cp)
-				p->p_child = cp->p_sibling;
-			if (cp->p_sibling)
-				cp->p_sibling->p_psibling = cp->p_psibling;
-			if (cp->p_psibling)
-				cp->p_psibling->p_sibling = cp->p_sibling;
+			disown_proc(p, cp);
 			mutex_enter(&cp->p_lock);
 			tk = cp->p_task;
 			task_detach(cp);
@@ -640,16 +655,7 @@
 	atomic_dec_32(&cp->p_pool->pool_ref);
 	mutex_exit(&cp->p_lock);
 
-	orphpp = &p->p_orphan;
-	while (*orphpp != cp)
-		orphpp = &(*orphpp)->p_nextorph;
-	*orphpp = cp->p_nextorph;
-	if (p->p_child == cp)
-		p->p_child = cp->p_sibling;
-	if (cp->p_sibling)
-		cp->p_sibling->p_psibling = cp->p_psibling;
-	if (cp->p_psibling)
-		cp->p_psibling->p_sibling = cp->p_sibling;
+	disown_proc(p, cp);
 	pid_exit(cp, tk);
 	mutex_exit(&pidlock);
 
@@ -696,7 +702,7 @@
 	if (PTOU(curproc)->u_cwd)
 		refstr_rele(PTOU(curproc)->u_cwd);
 	if (PROC_IS_BRANDED(cp)) {
-		brand_clearbrand(cp, B_TRUE);
+		brand_clearbrand(cp, B_FALSE);
 	}
 }
 
@@ -745,7 +751,7 @@
 			kmem_free(t->t_door, sizeof (door_data_t));
 			t->t_door = NULL;
 		}
-		lwp_ctmpl_clear(ttolwp(t));
+		lwp_ctmpl_clear(ttolwp(t), B_FALSE);
 
 		/*
 		 * Remove the thread from the all threads list.
@@ -782,6 +788,9 @@
 
 /*
  * fork a kernel process.
+ *
+ * Passing a pid argument of -1 indicates that the new process should be
+ * launched as a child of 'zsched' within the zone.
  */
 int
 newproc(void (*pc)(), caddr_t arg, id_t cid, int pri, struct contract **ct,
@@ -800,6 +809,7 @@
 		rctl_set_t *init_set;
 
 		ASSERT(pid != 1);
+		ASSERT(pid >= 0);
 
 		if (getproc(&p, pid, GETPROC_KERNEL) < 0)
 			return (EAGAIN);
@@ -843,8 +853,18 @@
 		rctl_set_t *init_set;
 		task_t *tk, *tk_old;
 		klwp_t *lwp;
+		boolean_t pzsched = B_FALSE;
+		int flag = GETPROC_USER;
 
-		if (getproc(&p, pid, GETPROC_USER) < 0)
+		/* Handle a new user-level thread as child of zsched. */
+		if (pid < 0) {
+			VERIFY(curzone != global_zone);
+			flag = GETPROC_ZSCHED;
+			pzsched = B_TRUE;
+			pid = 0;
+		}
+
+		if (getproc(&p, pid, flag) < 0)
 			return (EAGAIN);
 		/*
 		 * init creates a new task, distinct from the task
@@ -886,8 +906,11 @@
 		if ((lwp = lwp_create(pc, arg, 0, p, TS_STOPPED, pri,
 		    &curthread->t_hold, cid, 1)) == NULL) {
 			task_t *tk;
+
 			fork_fail(p);
 			mutex_enter(&pidlock);
+			disown_proc(p->p_parent, p);
+
 			mutex_enter(&p->p_lock);
 			tk = p->p_task;
 			task_detach(p);
@@ -894,15 +917,16 @@
 			ASSERT(p->p_pool->pool_ref > 0);
 			atomic_add_32(&p->p_pool->pool_ref, -1);
 			mutex_exit(&p->p_lock);
+
 			pid_exit(p, tk);
 			mutex_exit(&pidlock);
 			task_rele(tk);
-
 			return (EAGAIN);
 		}
 		t = lwptot(lwp);
 
-		ctp = contract_process_fork(sys_process_tmpl, p, curproc,
+		ctp = contract_process_fork(sys_process_tmpl, p,
+		    (pzsched ? curproc->p_zone->zone_zsched : curproc),
 		    B_FALSE);
 		ASSERT(ctp != NULL);
 		if (ct != NULL)
@@ -943,7 +967,11 @@
 	if (zone_status_get(curproc->p_zone) >= ZONE_IS_SHUTTING_DOWN)
 		return (-1);	/* no point in starting new processes */
 
-	pp = (flags & GETPROC_KERNEL) ? &p0 : curproc;
+	if (flags & GETPROC_ZSCHED) {
+		pp = curproc->p_zone->zone_zsched;
+	} else {
+		pp = (flags & GETPROC_KERNEL) ? &p0 : curproc;
+	}
 	task = pp->p_task;
 	proj = task->tk_proj;
 	zone = pp->p_zone;
@@ -1004,6 +1032,9 @@
 	cp->p_t1_lgrpid = LGRP_NONE;
 	cp->p_tr_lgrpid = LGRP_NONE;
 
+	/* Default to native brand initially */
+	cp->p_brand = &native_brand;
+
 	if ((newpid = pid_allocate(cp, pid, PID_ALLOC_PROC)) == -1) {
 		if (nproc == v.v_proc) {
 			CPU_STATS_ADDQ(CPU, sys, procovf, 1);
@@ -1071,9 +1102,6 @@
 	cp->p_flag = pp->p_flag & (SJCTL|SNOWAIT|SNOCD);
 	cp->p_sessp = pp->p_sessp;
 	sess_hold(pp);
-	cp->p_brand = pp->p_brand;
-	if (PROC_IS_BRANDED(pp))
-		BROP(pp)->b_copy_procdata(cp, pp);
 	cp->p_bssbase = pp->p_bssbase;
 	cp->p_brkbase = pp->p_brkbase;
 	cp->p_brksize = pp->p_brksize;
@@ -1153,6 +1181,18 @@
 	mutex_exit(&cp->p_lock);
 	mutex_exit(&pidlock);
 
+	if (PROC_IS_BRANDED(pp)) {
+		/*
+		 * The only reason why process branding should fail is when
+		 * the procedure is complicated by multiple LWPs on the scene.
+		 * With an LWP count of 0, this newly allocated process has no
+		 * reason to fail branding.
+		 */
+		VERIFY0(brand_setbrand(cp, B_FALSE));
+
+		BROP(pp)->b_copy_procdata(cp, pp);
+	}
+
 	avl_create(&cp->p_ct_held, contract_compar, sizeof (contract_t),
 	    offsetof(contract_t, ct_ctlist));
 
--- old/usr/src/uts/common/os/grow.c	Wed Oct  5 00:32:26 2016
+++ new/usr/src/uts/common/os/grow.c	Wed Oct  5 00:32:26 2016
@@ -19,7 +19,10 @@
  * CDDL HEADER END
  */
 
-/* Copyright 2013 OmniTI Computer Consulting, Inc. All rights reserved. */
+/*
+ * Copyright 2013 OmniTI Computer Consulting, Inc. All rights reserved.
+ * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ */
 
 /*
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
@@ -52,6 +55,7 @@
 #include <sys/fcntl.h>
 #include <sys/lwpchan_impl.h>
 #include <sys/nbmlock.h>
+#include <sys/brand.h>
 
 #include <vm/hat.h>
 #include <vm/as.h>
@@ -542,7 +546,21 @@
 	return (0);
 }
 
+caddr_t
+map_userlimit(proc_t *pp, struct as *as, int flags)
+{
+	if (flags & _MAP_LOW32) {
+		if (PROC_IS_BRANDED(pp) && BROP(pp)->b_map32limit != NULL) {
+			return ((caddr_t)(uintptr_t)BROP(pp)->b_map32limit(pp));
+		} else {
+			return ((caddr_t)_userlimit32);
+		}
+	}
 
+	return (as->a_userlimit);
+}
+
+
 /*
  * Used for MAP_ANON - fast way to get anonymous pages
  */
@@ -557,8 +575,6 @@
 		return (EACCES);
 
 	if ((flags & MAP_FIXED) != 0) {
-		caddr_t userlimit;
-
 		/*
 		 * Use the user address.  First verify that
 		 * the address to be used is page aligned.
@@ -567,9 +583,8 @@
 		if (((uintptr_t)*addrp & PAGEOFFSET) != 0)
 			return (EINVAL);
 
-		userlimit = flags & _MAP_LOW32 ?
-		    (caddr_t)USERLIMIT32 : as->a_userlimit;
-		switch (valid_usr_range(*addrp, len, uprot, as, userlimit)) {
+		switch (valid_usr_range(*addrp, len, uprot, as,
+		    map_userlimit(as->a_proc, as, flags))) {
 		case RANGE_OKAY:
 			break;
 		case RANGE_BADPROT:
@@ -737,8 +752,6 @@
 	 * If the user specified an address, do some simple checks here
 	 */
 	if ((flags & MAP_FIXED) != 0) {
-		caddr_t userlimit;
-
 		/*
 		 * Use the user address.  First verify that
 		 * the address to be used is page aligned.
@@ -746,10 +759,8 @@
 		 */
 		if (((uintptr_t)*addrp & PAGEOFFSET) != 0)
 			return (EINVAL);
-
-		userlimit = flags & _MAP_LOW32 ?
-		    (caddr_t)USERLIMIT32 : as->a_userlimit;
-		switch (valid_usr_range(*addrp, len, uprot, as, userlimit)) {
+		switch (valid_usr_range(*addrp, len, uprot, as,
+		    map_userlimit(curproc, as, flags))) {
 		case RANGE_OKAY:
 			break;
 		case RANGE_BADPROT:
--- old/usr/src/uts/common/os/ipc.c	Wed Oct  5 00:32:26 2016
+++ new/usr/src/uts/common/os/ipc.c	Wed Oct  5 00:32:26 2016
@@ -20,6 +20,7 @@
  */
 /*
  * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T		*/
@@ -1217,7 +1218,24 @@
 	    (IPC_ZONE_USAGE(perm, service) == 0)));
 }
 
+/*
+ * Perform actual IPC_RMID, either via ipc_rmid or due to a delayed *_RMID.
+ */
+void
+ipc_rmsvc(ipc_service_t *service, kipc_perm_t *perm)
+{
+	ASSERT(service->ipcs_count > 0);
+	ASSERT(MUTEX_HELD(&service->ipcs_lock));
 
+	ipc_remove(service, perm);
+	mutex_exit(&service->ipcs_lock);
+
+	/* perform any per-service removal actions */
+	service->ipcs_rmid(perm);
+
+	ipc_rele(service, perm);
+}
+
 /*
  * Common code to perform an IPC_RMID.  Returns an errno value on
  * failure, 0 on success.
@@ -1247,14 +1265,8 @@
 	/*
 	 * Nothing can fail from this point on.
 	 */
-	ipc_remove(service, perm);
-	mutex_exit(&service->ipcs_lock);
+	ipc_rmsvc(service, perm);
 
-	/* perform any per-service removal actions */
-	service->ipcs_rmid(perm);
-
-	ipc_rele(service, perm);
-
 	return (0);
 }
 
--- old/usr/src/uts/common/os/klpd.c	Wed Oct  5 00:32:26 2016
+++ new/usr/src/uts/common/os/klpd.c	Wed Oct  5 00:32:26 2016
@@ -39,6 +39,8 @@
 #include <sys/pathname.h>
 #include <sys/varargs.h>
 #include <sys/zone.h>
+#include <sys/cmn_err.h>
+#include <sys/sdt.h>
 #include <netinet/in.h>
 
 #define	ROUNDUP(a, n) (((a) + ((n) - 1)) & ~((n) - 1))
@@ -860,8 +862,13 @@
 		klpd_hold(pfd);
 	mutex_exit(&myzone->zone_lock);
 
-	if (pfd == NULL)
+	if (pfd == NULL) {
+		DTRACE_PROBE2(pfexecd__not__running,
+		    zone_t *, myzone, char *, rpnp->pn_path);
+		uprintf("pfexecd not running; pid %d privileges not "
+		    "elevated\n", curproc->p_pid);
 		return (0);
+	}
 
 	if (pfd->klpd_door_pid == curproc->p_pid) {
 		klpd_rele(pfd);
@@ -896,6 +903,9 @@
 		case EINTR:
 			/* FALLTHROUGH */
 		default:
+			DTRACE_PROBE4(pfexecd__failure,
+			    int, dres, zone_t *, myzone,
+			    char *, rpnp->pn_path, klpd_reg_t *, pfd);
 			goto out;
 		}
 	}
--- old/usr/src/uts/common/os/kmem.c	Wed Oct  5 00:32:26 2016
+++ new/usr/src/uts/common/os/kmem.c	Wed Oct  5 00:32:26 2016
@@ -4150,7 +4150,8 @@
 
 	if (kmem_taskq != NULL)
 		taskq_wait(kmem_taskq);
-	if (kmem_move_taskq != NULL)
+
+	if (kmem_move_taskq != NULL && cp->cache_defrag != NULL)
 		taskq_wait(kmem_move_taskq);
 
 	kmem_cache_magazine_purge(cp);
--- old/usr/src/uts/common/os/lwp.c	Wed Oct  5 00:32:27 2016
+++ new/usr/src/uts/common/os/lwp.c	Wed Oct  5 00:32:27 2016
@@ -25,7 +25,7 @@
  */
 
 /*
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2016, Joyent, Inc.
  */
 
 #include <sys/param.h>
@@ -57,6 +57,8 @@
 #include <sys/lgrp.h>
 #include <sys/rctl.h>
 #include <sys/contract_impl.h>
+#include <sys/contract/process.h>
+#include <sys/contract/process_impl.h>
 #include <sys/cpc_impl.h>
 #include <sys/sdt.h>
 #include <sys/cmn_err.h>
@@ -115,7 +117,7 @@
 	ret_tidhash_t *ret_tidhash = NULL;
 	int i;
 	int rctlfail = 0;
-	boolean_t branded = 0;
+	void *brand_data = NULL;
 	struct ctxop *ctx = NULL;
 
 	ASSERT(cid != sysdccid);	/* system threads must start in SYS */
@@ -283,6 +285,19 @@
 	 */
 	lep = kmem_zalloc(sizeof (*lep), KM_SLEEP);
 
+	/*
+	 * If necessary, speculatively allocate lwp brand data.  This is done
+	 * ahead of time so p_lock need not be dropped during lwp branding.
+	 */
+	if (PROC_IS_BRANDED(p) && BROP(p)->b_lwpdata_alloc != NULL) {
+		if ((brand_data = BROP(p)->b_lwpdata_alloc(p)) == NULL) {
+			mutex_enter(&p->p_lock);
+			err = 1;
+			atomic_inc_32(&p->p_zone->zone_ffmisc);
+			goto error;
+		}
+	}
+
 	mutex_enter(&p->p_lock);
 grow:
 	/*
@@ -630,18 +645,6 @@
 		} while (lwp_hash_lookup(p, t->t_tid) != NULL);
 	}
 
-	/*
-	 * If this is a branded process, let the brand do any necessary lwp
-	 * initialization.
-	 */
-	if (PROC_IS_BRANDED(p)) {
-		if (BROP(p)->b_initlwp(lwp)) {
-			err = 1;
-			atomic_inc_32(&p->p_zone->zone_ffmisc);
-			goto error;
-		}
-		branded = 1;
-	}
 
 	if (t->t_tid == 1) {
 		kpreempt_disable();
@@ -654,7 +657,6 @@
 		}
 	}
 
-	p->p_lwpcnt++;
 	t->t_waitfor = -1;
 
 	/*
@@ -696,8 +698,27 @@
 	t->t_post_sys = 1;
 
 	/*
+	 * Perform lwp branding
+	 *
+	 * The b_initlwp hook is _not_ allowed to drop p->p_lock as it must be
+	 * continuously held between when the tidhash is sized and when the lwp
+	 * is inserted into it.  Operations requiring p->p_lock to be
+	 * temporarily dropped can be performed in b_initlwp_post.
+	 */
+	if (PROC_IS_BRANDED(p)) {
+		BROP(p)->b_initlwp(lwp, brand_data);
+		/*
+		 * The b_initlwp hook is expected to consume any preallocated
+		 * brand_data in a way that prepares it for deallocation by the
+		 * b_freelwp hook.
+		 */
+		brand_data = NULL;
+	}
+
+	/*
 	 * Insert the new thread into the list of all threads.
 	 */
+	p->p_lwpcnt++;
 	if ((tx = p->p_tlist) == NULL) {
 		t->t_back = t;
 		t->t_forw = t;
@@ -718,6 +739,13 @@
 	lep->le_start = t->t_start;
 	lwp_hash_in(p, lep, p->p_tidhash, p->p_tidhash_sz, 1);
 
+	/*
+	 * Complete lwp branding
+	 */
+	if (PROC_IS_BRANDED(p) && BROP(p)->b_initlwp_post != NULL) {
+		BROP(p)->b_initlwp_post(lwp);
+	}
+
 	if (state == TS_RUN) {
 		/*
 		 * We set the new lwp running immediately.
@@ -753,8 +781,9 @@
 		if (cid != NOCLASS && bufp != NULL)
 			CL_FREE(cid, bufp);
 
-		if (branded)
-			BROP(p)->b_freelwp(lwp);
+		if (brand_data != NULL) {
+			BROP(p)->b_lwpdata_free(brand_data);
+		}
 
 		mutex_exit(&p->p_lock);
 		t->t_state = TS_FREE;
@@ -827,8 +856,27 @@
 	int i;
 
 	for (i = 0; i < ct_ntypes; i++) {
-		dst->lwp_ct_active[i] = ctmpl_dup(src->lwp_ct_active[i]);
+		ct_template_t *tmpl = src->lwp_ct_active[i];
+
+		/*
+		 * If the process contract template is setup to be preserved
+		 * across exec, then if we're forking, perform an implicit
+		 * template_clear now. This ensures that future children of
+		 * this child will remain in the same contract unless they're
+		 * explicitly setup differently. We know we're forking if the
+		 * two LWPs belong to different processes.
+		 */
+		if (i == CTT_PROCESS && tmpl != NULL) {
+			ctmpl_process_t *ctp = tmpl->ctmpl_data;
+
+			if (dst->lwp_procp != src->lwp_procp &&
+			    (ctp->ctp_params & CT_PR_KEEP_EXEC) != 0)
+				tmpl = NULL;
+		}
+
+		dst->lwp_ct_active[i] = ctmpl_dup(tmpl);
 		dst->lwp_ct_latest[i] = NULL;
+
 	}
 }
 
@@ -836,21 +884,33 @@
  * Clear an LWP's contract template state.
  */
 void
-lwp_ctmpl_clear(klwp_t *lwp)
+lwp_ctmpl_clear(klwp_t *lwp, boolean_t is_exec)
 {
 	ct_template_t *tmpl;
 	int i;
 
 	for (i = 0; i < ct_ntypes; i++) {
-		if ((tmpl = lwp->lwp_ct_active[i]) != NULL) {
-			ctmpl_free(tmpl);
-			lwp->lwp_ct_active[i] = NULL;
-		}
-
 		if (lwp->lwp_ct_latest[i] != NULL) {
 			contract_rele(lwp->lwp_ct_latest[i]);
 			lwp->lwp_ct_latest[i] = NULL;
 		}
+
+		if ((tmpl = lwp->lwp_ct_active[i]) != NULL) {
+			/*
+			 * If we're exec-ing a new program and the process
+			 * contract template is setup to be preserved across
+			 * exec, then don't clear it.
+			 */
+			if (is_exec && i == CTT_PROCESS) {
+				ctmpl_process_t *ctp = tmpl->ctmpl_data;
+
+				if ((ctp->ctp_params & CT_PR_KEEP_EXEC) != 0)
+					continue;
+			}
+
+			ctmpl_free(tmpl);
+			lwp->lwp_ct_active[i] = NULL;
+		}
 	}
 }
 
@@ -891,13 +951,6 @@
 	if (t->t_upimutex != NULL)
 		upimutex_cleanup();
 
-	/*
-	 * Perform any brand specific exit processing, then release any
-	 * brand data associated with the lwp
-	 */
-	if (PROC_IS_BRANDED(p))
-		BROP(p)->b_lwpexit(lwp);
-
 	lwp_pcb_exit();
 
 	mutex_enter(&p->p_lock);
@@ -941,6 +994,18 @@
 	DTRACE_PROC(lwp__exit);
 
 	/*
+	 * Perform any brand specific exit processing, then release any
+	 * brand data associated with the lwp
+	 */
+	if (PROC_IS_BRANDED(p)) {
+		mutex_exit(&p->p_lock);
+		BROP(p)->b_lwpexit(lwp);
+		BROP(p)->b_freelwp(lwp);
+		mutex_enter(&p->p_lock);
+		prbarrier(p);
+	}
+
+	/*
 	 * If the lwp is a detached lwp or if the process is exiting,
 	 * remove (lwp_hash_out()) the lwp from the lwp directory.
 	 * Otherwise null out the lwp's le_thread pointer in the lwp
@@ -1101,7 +1166,7 @@
 	}
 	kpreempt_enable();
 
-	lwp_ctmpl_clear(ttolwp(t));
+	lwp_ctmpl_clear(ttolwp(t), B_FALSE);
 }
 
 int
--- old/usr/src/uts/common/os/main.c	Wed Oct  5 00:32:27 2016
+++ new/usr/src/uts/common/os/main.c	Wed Oct  5 00:32:27 2016
@@ -27,7 +27,7 @@
 /*	  All Rights Reserved  	*/
 
 /*
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2015, Joyent, Inc.
  */
 
 #include <sys/types.h>
@@ -157,7 +157,7 @@
 	int error = 0, count = 0;
 	proc_t *p = ttoproc(curthread);
 	klwp_t *lwp = ttolwp(curthread);
-	int brand_action;
+	int brand_action = EBA_NONE;
 
 	if (args == NULL)
 		args = "";
@@ -268,7 +268,15 @@
 	 */
 	sigemptyset(&curthread->t_hold);
 
-	brand_action = ZONE_IS_BRANDED(p->p_zone) ? EBA_BRAND : EBA_NONE;
+	/*
+	 * Only instruct exec_common to brand the process if necessary.  It is
+	 * possible that the init process is already properly branded due to the
+	 * proc_exit -> restart_init -> exec_init call chain.
+	 */
+	if (ZONE_IS_BRANDED(p->p_zone) &&
+	    p->p_brand != p->p_zone->zone_brand) {
+		brand_action = EBA_BRAND;
+	}
 again:
 	error = exec_common((const char *)(uintptr_t)exec_fnamep,
 	    (const char **)(uintptr_t)uap, NULL, brand_action);
--- old/usr/src/uts/common/os/mmapobj.c	Wed Oct  5 00:32:27 2016
+++ new/usr/src/uts/common/os/mmapobj.c	Wed Oct  5 00:32:27 2016
@@ -1360,10 +1360,15 @@
 			}
 			if (num_segs++ == 0) {
 				/*
-				 * The p_vaddr of the first PT_LOAD segment
-				 * must either be NULL or within the first
-				 * page in order to be interpreted.
-				 * Otherwise, its an invalid file.
+				 * While ELF doesn't specify the meaning of
+				 * p_vaddr for PT_LOAD segments in ET_DYN
+				 * objects, we mandate that is either NULL or
+				 * (to accommodate some historical binaries)
+				 * within the first page.  (Note that there
+				 * exist non-native ET_DYN objects that violate
+				 * this constraint that we nonetheless must be
+				 * able to execute; see the ET_DYN handling in
+				 * mapelfexec() for details.)
 				 */
 				if (e_type == ET_DYN &&
 				    ((caddr_t)((uintptr_t)vaddr &
--- old/usr/src/uts/common/os/pid.c	Wed Oct  5 00:32:27 2016
+++ new/usr/src/uts/common/os/pid.c	Wed Oct  5 00:32:27 2016
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
  */
 
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
@@ -112,6 +113,18 @@
 	return (pidp);
 }
 
+struct pid *
+pid_find(pid_t pid)
+{
+	struct pid *pidp;
+
+	mutex_enter(&pidlinklock);
+	pidp = pid_lookup(pid);
+	mutex_exit(&pidlinklock);
+
+	return (pidp);
+}
+
 void
 pid_setmin(void)
 {
@@ -521,6 +534,20 @@
 	mutex_exit(&p->p_lock);
 	THREAD_KPRI_RELEASE();
 }
+
+/*
+ * Undo effects of sprlock but without dropping p->p_lock
+ */
+void
+sprunprlock(proc_t *p)
+{
+	ASSERT(p->p_proc_flag & P_PR_LOCK);
+	ASSERT(MUTEX_HELD(&p->p_lock));
+
+	cv_signal(&pr_pid_cv[p->p_slot]);
+	p->p_proc_flag &= ~P_PR_LOCK;
+	THREAD_KPRI_RELEASE();
+}
 
 void
 pid_init(void)
--- old/usr/src/uts/common/os/policy.c	Wed Oct  5 00:32:27 2016
+++ new/usr/src/uts/common/os/policy.c	Wed Oct  5 00:32:27 2016
@@ -20,7 +20,7 @@
  */
 /*
  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
  */
 
 #include <sys/types.h>
@@ -55,6 +55,7 @@
 #include <sys/mntent.h>
 #include <sys/contract_impl.h>
 #include <sys/dld_ioc.h>
+#include <sys/brand.h>
 
 /*
  * There are two possible layers of privilege routines and two possible
@@ -1243,6 +1244,22 @@
 void
 secpolicy_setid_clear(vattr_t *vap, cred_t *cr)
 {
+	proc_t *p = curproc;
+
+	/*
+	 * Allow the brand to override this behaviour.
+	 */
+	if (PROC_IS_BRANDED(p) && BROP(p)->b_setid_clear != NULL) {
+		/*
+		 * This brand hook will return 0 if handling is complete, or
+		 * some other value if the brand would like us to fall back to
+		 * the usual behaviour.
+		 */
+		if (BROP(p)->b_setid_clear(vap, cr) == 0) {
+			return;
+		}
+	}
+
 	if ((vap->va_mode & (S_ISUID | S_ISGID)) != 0 &&
 	    secpolicy_vnode_setid_retain(cr,
 	    (vap->va_mode & S_ISUID) != 0 &&
--- old/usr/src/uts/common/os/shm.c	Wed Oct  5 00:32:27 2016
+++ new/usr/src/uts/common/os/shm.c	Wed Oct  5 00:32:27 2016
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 1986, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -319,6 +320,7 @@
 		size_t	share_size;
 		struct	shm_data ssd;
 		uintptr_t align_hint;
+		long	curprot;
 
 		/*
 		 * Pick a share pagesize to use, if (!isspt(sp)).
@@ -453,6 +455,7 @@
 			}
 		}
 
+		curprot = sp->shm_opts & SHM_PROT_MASK;
 		if (!isspt(sp)) {
 			error = sptcreate(size, &segspt, sp->shm_amp, prot,
 			    flags, share_szc);
@@ -462,8 +465,8 @@
 			}
 			sp->shm_sptinfo->sptas = segspt->s_as;
 			sp->shm_sptseg = segspt;
-			sp->shm_sptprot = prot;
-		} else if ((prot & sp->shm_sptprot) != sp->shm_sptprot) {
+			sp->shm_opts = (sp->shm_opts & ~SHM_PROT_MASK) | prot;
+		} else if ((prot & curprot) != curprot) {
 			/*
 			 * Ensure we're attaching to an ISM segment with
 			 * fewer or equal permissions than what we're
@@ -748,6 +751,23 @@
 		}
 		break;
 
+	/* Stage segment for removal, but don't remove until last detach */
+	case SHM_RMID:
+		if ((error = secpolicy_ipc_owner(cr, (kipc_perm_t *)sp)) != 0)
+			break;
+
+		/*
+		 * If attached, just mark it as a pending remove, otherwise
+		 * we must perform the normal ipc_rmid now.
+		 */
+		if ((sp->shm_perm.ipc_ref - 1) > 0) {
+			sp->shm_opts |= SHM_RM_PENDING;
+		} else {
+			mutex_exit(lock);
+			return (ipc_rmid(shm_svc, shmid, cr));
+		}
+		break;
+
 	default:
 		error = EINVAL;
 		break;
@@ -778,6 +798,23 @@
 		sp->shm_ismattch--;
 	sp->shm_dtime = gethrestime_sec();
 	sp->shm_lpid = pp->p_pid;
+	if ((sp->shm_opts & SHM_RM_PENDING) != 0 &&
+	    sp->shm_perm.ipc_ref == 2) {
+		/*
+		 * If this is the last detach of the segment across the whole
+		 * system then now we can perform the delayed IPC_RMID.
+		 * The ipc_ref count has 1 for the original 'get' and one for
+		 * each 'attach' (see 'stat' handling in shmctl).
+		 */
+		sp->shm_opts &= ~SHM_RM_PENDING;
+		mutex_enter(&shm_svc->ipcs_lock);
+		ipc_rmsvc(shm_svc, (kipc_perm_t *)sp);	/* Drops lock */
+		ASSERT(!MUTEX_HELD(&shm_svc->ipcs_lock));
+		ASSERT(((kipc_perm_t *)sp)->ipc_ref > 0);
+
+		/* Lock was dropped, need to retake it for following rele. */
+		(void) ipc_lock(shm_svc, sp->shm_perm.ipc_id);
+	}
 	ipc_rele(shm_svc, (kipc_perm_t *)sp);	/* Drops lock */
 
 	kmem_free(sap, sizeof (segacct_t));
--- old/usr/src/uts/common/os/sig.c	Wed Oct  5 00:32:27 2016
+++ new/usr/src/uts/common/os/sig.c	Wed Oct  5 00:32:27 2016
@@ -22,7 +22,7 @@
 /*
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
- * Copyright (c) 2014, Joyent, Inc.  All rights reserved.
+ * Copyright 2015, Joyent, Inc.
  */
 
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
@@ -61,6 +61,7 @@
 #include <sys/dtrace.h>
 #include <sys/sdt.h>
 #include <sys/signalfd.h>
+#include <sys/brand.h>
 
 const k_sigset_t nullsmask = {0, 0, 0};
 
@@ -148,6 +149,21 @@
 }
 
 /*
+ * Return true if the signal can safely be ignored.
+ * That is, if the signal is included in the p_ignore mask and doing so is not
+ * forbidden by any process branding.
+ */
+static int
+sig_ignorable(proc_t *p, klwp_t *lwp, int sig)
+{
+	return (sigismember(&p->p_ignore, sig) &&	/* sig in ignore mask */
+	    !(PROC_IS_BRANDED(p) &&			/* allowed by brand */
+	    BROP(p)->b_sig_ignorable != NULL &&
+	    BROP(p)->b_sig_ignorable(p, lwp, sig) == B_FALSE));
+
+}
+
+/*
  * Return true if the signal can safely be discarded on generation.
  * That is, if there is no need for the signal on the receiving end.
  * The answer is true if the process is a zombie or
@@ -159,12 +175,13 @@
  *	the signal is not being accepted via sigwait()
  */
 static int
-sig_discardable(proc_t *p, int sig)
+sig_discardable(proc_t *p, kthread_t *tp, int sig)
 {
 	kthread_t *t = p->p_tlist;
+	klwp_t *lwp = (tp == NULL) ? NULL : tp->t_lwp;
 
 	return (t == NULL ||		/* if zombie or ... */
-	    (sigismember(&p->p_ignore, sig) &&	/* signal is ignored */
+	    (sig_ignorable(p, lwp, sig) &&		/* signal is ignored */
 	    t->t_forw == t &&			/* and single-threaded */
 	    !tracing(p, sig) &&			/* and no /proc tracing */
 	    !signal_is_blocked(t, sig) &&	/* and signal not blocked */
@@ -200,7 +217,7 @@
 		    !(ttoproc(t)->p_proc_flag & P_PR_LOCK)) {
 			ttoproc(t)->p_stopsig = 0;
 			t->t_dtrace_stop = 0;
-			t->t_schedflag |= TS_XSTART | TS_PSTART;
+			t->t_schedflag |= TS_XSTART | TS_PSTART | TS_BSTART;
 			setrun_locked(t);
 		} else if (t != curthread && t->t_state == TS_ONPROC) {
 			aston(t);	/* make it do issig promptly */
@@ -297,7 +314,7 @@
 		}
 	}
 
-	if (sig_discardable(p, sig)) {
+	if (sig_discardable(p, t, sig)) {
 		DTRACE_PROC3(signal__discard, kthread_t *, p->p_tlist,
 		    proc_t *, p, int, sig);
 		return;
@@ -497,7 +514,7 @@
 			if (sigismember(&set, sig) &&
 			    (tracing(p, sig) ||
 			    sigismember(&t->t_sigwait, sig) ||
-			    !sigismember(&p->p_ignore, sig))) {
+			    !sig_ignorable(p, lwp, sig))) {
 				/*
 				 * Don't promote a signal that will stop
 				 * the process when lwp_nostop is set.
@@ -623,6 +640,21 @@
 		}
 
 		/*
+		 * Allow the brand the chance to alter (or suppress) delivery
+		 * of this signal.
+		 */
+		if (PROC_IS_BRANDED(p) && BROP(p)->b_issig_stop != NULL) {
+			/*
+			 * The brand hook will return 0 if it would like
+			 * us to drive on, or -1 if we should restart
+			 * the loop to check other conditions.
+			 */
+			if (BROP(p)->b_issig_stop(p, lwp) != 0) {
+				continue;
+			}
+		}
+
+		/*
 		 * Honor requested stop before dealing with the
 		 * current signal; a debugger may change it.
 		 * Do not want to go back to loop here since this is a special
@@ -656,7 +688,7 @@
 			lwp->lwp_cursig = 0;
 			lwp->lwp_extsig = 0;
 			if (sigismember(&t->t_sigwait, sig) ||
-			    (!sigismember(&p->p_ignore, sig) &&
+			    (!sig_ignorable(p, lwp, sig) &&
 			    !isjobstop(sig))) {
 				if (p->p_flag & (SEXITLWPS|SKILLED)) {
 					sig = SIGKILL;
@@ -708,7 +740,7 @@
 				toproc = 0;
 				if (tracing(p, sig) ||
 				    sigismember(&t->t_sigwait, sig) ||
-				    !sigismember(&p->p_ignore, sig)) {
+				    !sig_ignorable(p, lwp, sig)) {
 					if (sigismember(&t->t_extsig, sig))
 						ext = 1;
 					break;
@@ -722,7 +754,7 @@
 				toproc = 1;
 				if (tracing(p, sig) ||
 				    sigismember(&t->t_sigwait, sig) ||
-				    !sigismember(&p->p_ignore, sig)) {
+				    !sig_ignorable(p, lwp, sig)) {
 					if (sigismember(&p->p_extsig, sig))
 						ext = 1;
 					break;
@@ -954,6 +986,16 @@
 		}
 		break;
 
+	case PR_BRAND:
+		/*
+		 * We have been stopped by the brand code for a brand-private
+		 * reason.  This is an asynchronous stop affecting only this
+		 * LWP.
+		 */
+		VERIFY(PROC_IS_BRANDED(p));
+		flags &= ~TS_BSTART;
+		break;
+
 	default:	/* /proc stop */
 		flags &= ~TS_PSTART;
 		/*
@@ -1065,7 +1107,7 @@
 		}
 	}
 
-	if (why != PR_JOBCONTROL && why != PR_CHECKPOINT) {
+	if (why != PR_JOBCONTROL && why != PR_CHECKPOINT && why != PR_BRAND) {
 		/*
 		 * Do process-level notification when all lwps are
 		 * either stopped on events of interest to /proc
@@ -1171,6 +1213,13 @@
 	if (why == PR_CHECKPOINT)
 		del_one_utstop();
 
+	/*
+	 * Allow the brand to post notification of this stop condition.
+	 */
+	if (PROC_IS_BRANDED(p) && BROP(p)->b_stop_notify != NULL) {
+		BROP(p)->b_stop_notify(p, lwp, why, what);
+	}
+
 	thread_lock(t);
 	ASSERT((t->t_schedflag & TS_ALLSTART) == 0);
 	t->t_schedflag |= flags;
@@ -1192,7 +1241,7 @@
 		    (p->p_flag & (SEXITLWPS|SKILLED))) {
 			p->p_stopsig = 0;
 			thread_lock(t);
-			t->t_schedflag |= TS_XSTART | TS_PSTART;
+			t->t_schedflag |= TS_XSTART | TS_PSTART | TS_BSTART;
 			setrun_locked(t);
 			thread_unlock_nopreempt(t);
 		} else if (why == PR_JOBCONTROL) {
@@ -1327,7 +1376,7 @@
 	 * this signal from pending to current (we dropped p->p_lock).
 	 * This can happen only in a multi-threaded process.
 	 */
-	if (sigismember(&p->p_ignore, sig) ||
+	if (sig_ignorable(p, lwp, sig) ||
 	    (func == SIG_DFL && sigismember(&stopdefault, sig))) {
 		lwp->lwp_cursig = 0;
 		lwp->lwp_extsig = 0;
@@ -1771,9 +1820,12 @@
 			/*
 			 * This can only happen when the parent is init.
 			 * (See call to sigcld(q, NULL) in exit().)
-			 * Use KM_NOSLEEP to avoid deadlock.
+			 * Use KM_NOSLEEP to avoid deadlock. The child procs
+			 * initpid can be 1 for zlogin.
 			 */
-			ASSERT(pp == proc_init);
+			ASSERT(pp->p_pidp->pid_id ==
+			    cp->p_zone->zone_proc_initpid ||
+			    pp->p_pidp->pid_id == 1);
 			winfo(cp, &info, 0);
 			sigaddq(pp, NULL, &info, KM_NOSLEEP);
 		} else {
@@ -1804,6 +1856,15 @@
 
 	sqp = kmem_zalloc(sizeof (sigqueue_t), KM_SLEEP);
 	mutex_enter(&pidlock);
+	if (PROC_IS_BRANDED(pp) && BROP(pp)->b_sigcld_repost != NULL) {
+		/*
+		 * Allow the brand to inject synthetic SIGCLD signals.
+		 */
+		if (BROP(pp)->b_sigcld_repost(pp, sqp) == 0) {
+			mutex_exit(&pidlock);
+			return;
+		}
+	}
 	for (cp = pp->p_child; cp; cp = cp->p_sibling) {
 		if (cp->p_pidflag & CLDPEND) {
 			post_sigcld(cp, sqp);
@@ -2115,7 +2176,7 @@
 	ASSERT(MUTEX_HELD(&p->p_lock));
 	ASSERT(sig >= 1 && sig < NSIG);
 
-	if (sig_discardable(p, sig))
+	if (sig_discardable(p, t, sig))
 		siginfofree(sigqp);
 	else
 		sigaddqins(p, t, sigqp);
@@ -2141,7 +2202,7 @@
 	 * blocking the signal (it *could* change it's mind while
 	 * the signal is pending) then don't bother creating one.
 	 */
-	if (!sig_discardable(p, sig) &&
+	if (!sig_discardable(p, t, sig) &&
 	    (sigismember(&p->p_siginfo, sig) ||
 	    (curproc->p_ct_process != p->p_ct_process) ||
 	    (sig == SIGCLD && SI_FROMKERNEL(infop))) &&
--- old/usr/src/uts/common/os/streamio.c	Wed Oct  5 00:32:28 2016
+++ new/usr/src/uts/common/os/streamio.c	Wed Oct  5 00:32:28 2016
@@ -24,7 +24,7 @@
 
 /*
  * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ * Copyright 2015, Joyent, Inc. All rights reserved.
  */
 
 #include <sys/types.h>
@@ -77,6 +77,7 @@
 #include <sys/policy.h>
 #include <sys/dld.h>
 #include <sys/zone.h>
+#include <sys/limits.h>
 #include <c2/audit.h>
 
 /*
@@ -985,12 +986,20 @@
 		 * (registered in sd_wakeq).
 		 */
 		struiod_t uiod;
+		struct iovec buf[IOV_MAX_STACK];
+		int iovlen = 0;
 
 		if (first)
 			stp->sd_wakeq &= ~RSLEEP;
 
-		(void) uiodup(uiop, &uiod.d_uio, uiod.d_iov,
-		    sizeof (uiod.d_iov) / sizeof (*uiod.d_iov));
+		if (uiop->uio_iovcnt > IOV_MAX_STACK) {
+			iovlen = uiop->uio_iovcnt * sizeof (iovec_t);
+			uiod.d_iov = kmem_alloc(iovlen, KM_SLEEP);
+		} else {
+			uiod.d_iov = buf;
+		}
+
+		(void) uiodup(uiop, &uiod.d_uio, uiod.d_iov, uiop->uio_iovcnt);
 		uiod.d_mp = 0;
 		/*
 		 * Mark that a thread is in rwnext on the read side
@@ -1029,6 +1038,8 @@
 			if ((bp = uiod.d_mp) != NULL) {
 				*errorp = 0;
 				ASSERT(MUTEX_HELD(&stp->sd_lock));
+				if (iovlen != 0)
+					kmem_free(uiod.d_iov, iovlen);
 				return (bp);
 			}
 			error = 0;
@@ -1048,8 +1059,14 @@
 		} else {
 			*errorp = error;
 			ASSERT(MUTEX_HELD(&stp->sd_lock));
+			if (iovlen != 0)
+				kmem_free(uiod.d_iov, iovlen);
 			return (NULL);
 		}
+
+		if (iovlen != 0)
+			kmem_free(uiod.d_iov, iovlen);
+
 		/*
 		 * Try a getq in case a rwnext() generated mblk
 		 * has bubbled up via strrput().
@@ -2544,6 +2561,8 @@
     int b_flag, int pri, int flags)
 {
 	struiod_t uiod;
+	struct iovec buf[IOV_MAX_STACK];
+	int iovlen = 0;
 	mblk_t *mp;
 	queue_t *wqp = stp->sd_wrq;
 	int error = 0;
@@ -2635,13 +2654,21 @@
 	mp->b_flag |= b_flag;
 	mp->b_band = (uchar_t)pri;
 
-	(void) uiodup(uiop, &uiod.d_uio, uiod.d_iov,
-	    sizeof (uiod.d_iov) / sizeof (*uiod.d_iov));
+	if (uiop->uio_iovcnt > IOV_MAX_STACK) {
+		iovlen = uiop->uio_iovcnt * sizeof (iovec_t);
+		uiod.d_iov = (struct iovec *)kmem_alloc(iovlen, KM_SLEEP);
+	} else {
+		uiod.d_iov = buf;
+	}
+
+	(void) uiodup(uiop, &uiod.d_uio, uiod.d_iov, uiop->uio_iovcnt);
 	uiod.d_uio.uio_offset = 0;
 	uiod.d_mp = mp;
 	error = rwnext(wqp, &uiod);
 	if (! uiod.d_mp) {
 		uioskip(uiop, *iosize);
+		if (iovlen != 0)
+			kmem_free(uiod.d_iov, iovlen);
 		return (error);
 	}
 	ASSERT(mp == uiod.d_mp);
@@ -2659,6 +2686,8 @@
 		error = 0;
 	} else {
 		freemsg(mp);
+		if (iovlen != 0)
+			kmem_free(uiod.d_iov, iovlen);
 		return (error);
 	}
 	/* Have to check canput before consuming data from the uio */
@@ -2665,11 +2694,15 @@
 	if (pri == 0) {
 		if (!canputnext(wqp) && !(flags & MSG_IGNFLOW)) {
 			freemsg(mp);
+			if (iovlen != 0)
+				kmem_free(uiod.d_iov, iovlen);
 			return (EWOULDBLOCK);
 		}
 	} else {
 		if (!bcanputnext(wqp, pri) && !(flags & MSG_IGNFLOW)) {
 			freemsg(mp);
+			if (iovlen != 0)
+				kmem_free(uiod.d_iov, iovlen);
 			return (EWOULDBLOCK);
 		}
 	}
@@ -2677,6 +2710,8 @@
 	/* Copyin data from the uio */
 	if ((error = struioget(wqp, mp, &uiod, 0)) != 0) {
 		freemsg(mp);
+		if (iovlen != 0)
+			kmem_free(uiod.d_iov, iovlen);
 		return (error);
 	}
 	uioskip(uiop, *iosize);
@@ -2693,6 +2728,8 @@
 		putnext(wqp, mp);
 		stream_runservice(stp);
 	}
+	if (iovlen != 0)
+		kmem_free(uiod.d_iov, iovlen);
 	return (0);
 }
 
@@ -3178,6 +3215,7 @@
 	case JAGENT:	/* Obsolete */
 	case JTRUN:	/* Obsolete */
 	case JXTPROTO:	/* Obsolete */
+	case TIOCSETLD:
 		return (JCSETP);
 	}
 
--- old/usr/src/uts/common/os/sysent.c	Wed Oct  5 00:32:28 2016
+++ new/usr/src/uts/common/os/sysent.c	Wed Oct  5 00:32:28 2016
@@ -23,7 +23,7 @@
  * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2012 Milan Jurik. All rights reserved.
  * Copyright (c) 2013, OmniTI Computer Consulting, Inc. All rights reserved.
- * Copyright (c) 2015, Joyent, Inc.
+ * Copyright 2016 Joyent, Inc.
  */
 
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
@@ -61,8 +61,7 @@
 int	access(char *, int);
 int	alarm(int);
 int	auditsys(struct auditcalls *, rval_t *);
-int64_t	brandsys(int, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t,
-    uintptr_t);
+int64_t	brandsys(int, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
 int	brk(caddr_t);
 int	chdir(char *);
 int	chmod(char *, int);
@@ -646,7 +645,7 @@
 			SYSENT_NOSYS(),
 			SYSENT_C("llseek",	llseek32,	4)),
 	/* 176 */ SYSENT_LOADABLE(),		/* inst_sync */
-	/* 177 */ SYSENT_CI("brandsys",		brandsys,	6),
+	/* 177 */ SYSENT_CI("brandsys",		brandsys,	5),
 	/* 178 */ SYSENT_LOADABLE(),		/* kaio */
 	/* 179 */ SYSENT_LOADABLE(),		/* cpc */
 	/* 180 */ SYSENT_CI("lgrpsys",		lgrpsys,	3),
@@ -1001,7 +1000,7 @@
 	/* 174 */ SYSENT_CI("pwrite",		pwrite32,		4),
 	/* 175 */ SYSENT_C("llseek",		llseek32,	4),
 	/* 176 */ SYSENT_LOADABLE32(),		/* inst_sync */
-	/* 177 */ SYSENT_CI("brandsys",		brandsys,	6),
+	/* 177 */ SYSENT_CI("brandsys",		brandsys,	5),
 	/* 178 */ SYSENT_LOADABLE32(),		/* kaio */
 	/* 179 */ SYSENT_LOADABLE32(),		/* cpc */
 	/* 180 */ SYSENT_CI("lgrpsys",		lgrpsys,	3),
@@ -1093,18 +1092,20 @@
 
 systrace_sysent_t *systrace_sysent;
 void (*systrace_probe)(dtrace_id_t, uintptr_t, uintptr_t,
-    uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+    uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
 
 /*ARGSUSED*/
 void
 systrace_stub(dtrace_id_t id, uintptr_t arg0, uintptr_t arg1,
-    uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, uintptr_t arg5)
+    uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, uintptr_t arg5,
+    uintptr_t arg6, uintptr_t arg7)
 {}
 
 /*ARGSUSED*/
 int64_t
 dtrace_systrace_syscall(uintptr_t arg0, uintptr_t arg1, uintptr_t arg2,
-    uintptr_t arg3, uintptr_t arg4, uintptr_t arg5)
+    uintptr_t arg3, uintptr_t arg4, uintptr_t arg5, uintptr_t arg6,
+    uintptr_t arg7)
 {
 	systrace_sysent_t *sy = &systrace_sysent[curthread->t_sysnum];
 	dtrace_id_t id;
@@ -1112,7 +1113,8 @@
 	proc_t *p;
 
 	if ((id = sy->stsy_entry) != DTRACE_IDNONE)
-		(*systrace_probe)(id, arg0, arg1, arg2, arg3, arg4, arg5);
+		(*systrace_probe)(id, arg0, arg1, arg2, arg3, arg4, arg5,
+		    arg6, arg7);
 
 	/*
 	 * We want to explicitly allow DTrace consumers to stop a process
@@ -1126,7 +1128,8 @@
 	}
 	mutex_exit(&p->p_lock);
 
-	rval = (*sy->stsy_underlying)(arg0, arg1, arg2, arg3, arg4, arg5);
+	rval = (*sy->stsy_underlying)(arg0, arg1, arg2, arg3, arg4, arg5,
+	    arg6, arg7);
 
 	if (ttolwp(curthread)->lwp_errno != 0)
 		rval = -1;
@@ -1133,7 +1136,7 @@
 
 	if ((id = sy->stsy_return) != DTRACE_IDNONE)
 		(*systrace_probe)(id, (uintptr_t)rval, (uintptr_t)rval,
-		    (uintptr_t)((int64_t)rval >> 32), 0, 0, 0);
+		    (uintptr_t)((int64_t)rval >> 32), 0, 0, 0, 0, 0);
 
 	return (rval);
 }
@@ -1145,7 +1148,8 @@
 /*ARGSUSED*/
 int64_t
 dtrace_systrace_syscall32(uintptr_t arg0, uintptr_t arg1, uintptr_t arg2,
-    uintptr_t arg3, uintptr_t arg4, uintptr_t arg5)
+    uintptr_t arg3, uintptr_t arg4, uintptr_t arg5, uintptr_t arg6,
+    uintptr_t arg7)
 {
 	systrace_sysent_t *sy = &systrace_sysent32[curthread->t_sysnum];
 	dtrace_id_t id;
@@ -1153,7 +1157,8 @@
 	proc_t *p;
 
 	if ((id = sy->stsy_entry) != DTRACE_IDNONE)
-		(*systrace_probe)(id, arg0, arg1, arg2, arg3, arg4, arg5);
+		(*systrace_probe)(id, arg0, arg1, arg2, arg3, arg4, arg5, arg6,
+		    arg7);
 
 	/*
 	 * We want to explicitly allow DTrace consumers to stop a process
@@ -1167,7 +1172,8 @@
 	}
 	mutex_exit(&p->p_lock);
 
-	rval = (*sy->stsy_underlying)(arg0, arg1, arg2, arg3, arg4, arg5);
+	rval = (*sy->stsy_underlying)(arg0, arg1, arg2, arg3, arg4, arg5, arg6,
+	    arg7);
 
 	if (ttolwp(curthread)->lwp_errno != 0)
 		rval = -1;
@@ -1174,7 +1180,7 @@
 
 	if ((id = sy->stsy_return) != DTRACE_IDNONE)
 		(*systrace_probe)(id, (uintptr_t)rval, (uintptr_t)rval,
-		    (uintptr_t)((uint64_t)rval >> 32), 0, 0, 0);
+		    (uintptr_t)((uint64_t)rval >> 32), 0, 0, 0, 0, 0);
 
 	return (rval);
 }
@@ -1202,5 +1208,5 @@
 	}
 
 	if ((id = sy->stsy_return) != DTRACE_IDNONE)
-		(*systrace_probe)(id, 0, 0, 0, 0, 0, 0);
+		(*systrace_probe)(id, 0, 0, 0, 0, 0, 0, 0, 0);
 }
--- old/usr/src/uts/common/os/zone.c	Wed Oct  5 00:32:28 2016
+++ new/usr/src/uts/common/os/zone.c	Wed Oct  5 00:32:28 2016
@@ -21,7 +21,7 @@
 
 /*
  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2015, Joyent Inc. All rights reserved.
+ * Copyright 2016, Joyent Inc.
  */
 
 /*
@@ -250,6 +250,8 @@
 #include <sys/cpucaps.h>
 #include <vm/seg.h>
 #include <sys/mac.h>
+#include <sys/rt.h>
+#include <sys/fx.h>
 
 /*
  * This constant specifies the number of seconds that threads waiting for
@@ -370,6 +372,7 @@
 rctl_hndl_t rc_zone_cpu_shares;
 rctl_hndl_t rc_zone_locked_mem;
 rctl_hndl_t rc_zone_max_swap;
+rctl_hndl_t rc_zone_phys_mem;
 rctl_hndl_t rc_zone_max_lofi;
 rctl_hndl_t rc_zone_cpu_cap;
 rctl_hndl_t rc_zone_zfs_io_pri;
@@ -1742,6 +1745,39 @@
 
 /*ARGSUSED*/
 static rctl_qty_t
+zone_phys_mem_usage(rctl_t *rctl, struct proc *p)
+{
+	rctl_qty_t q;
+	zone_t *z = p->p_zone;
+
+	ASSERT(MUTEX_HELD(&p->p_lock));
+	/* No additional lock because not enforced in the kernel */
+	q = z->zone_phys_mem;
+	return (q);
+}
+
+/*ARGSUSED*/
+static int
+zone_phys_mem_set(rctl_t *rctl, struct proc *p, rctl_entity_p_t *e,
+    rctl_qty_t nv)
+{
+	ASSERT(MUTEX_HELD(&p->p_lock));
+	ASSERT(e->rcep_t == RCENTITY_ZONE);
+	if (e->rcep_p.zone == NULL)
+		return (0);
+	e->rcep_p.zone->zone_phys_mem_ctl = nv;
+	return (0);
+}
+
+static rctl_ops_t zone_phys_mem_ops = {
+	rcop_no_action,
+	zone_phys_mem_usage,
+	zone_phys_mem_set,
+	rcop_no_test
+};
+
+/*ARGSUSED*/
+static rctl_qty_t
 zone_max_lofi_usage(rctl_t *rctl, struct proc *p)
 {
 	rctl_qty_t q;
@@ -1835,6 +1871,20 @@
 }
 
 static int
+zone_physmem_kstat_update(kstat_t *ksp, int rw)
+{
+	zone_t *zone = ksp->ks_private;
+	zone_kstat_t *zk = ksp->ks_data;
+
+	if (rw == KSTAT_WRITE)
+		return (EACCES);
+
+	zk->zk_usage.value.ui64 = zone->zone_phys_mem;
+	zk->zk_value.value.ui64 = zone->zone_phys_mem_ctl;
+	return (0);
+}
+
+static int
 zone_nprocs_kstat_update(kstat_t *ksp, int rw)
 {
 	zone_t *zone = ksp->ks_private;
@@ -1888,8 +1938,162 @@
 	return (ksp);
 }
 
+static int
+zone_vfs_kstat_update(kstat_t *ksp, int rw)
+{
+	zone_t *zone = ksp->ks_private;
+	zone_vfs_kstat_t *zvp = ksp->ks_data;
+	kstat_io_t *kiop = &zone->zone_vfs_rwstats;
 
+	if (rw == KSTAT_WRITE)
+		return (EACCES);
+
+	/*
+	 * Extract the VFS statistics from the kstat_io_t structure used by
+	 * kstat_runq_enter() and related functions.  Since the slow ops
+	 * counters are updated directly by the VFS layer, there's no need to
+	 * copy those statistics here.
+	 *
+	 * Note that kstat_runq_enter() and the related functions use
+	 * gethrtime_unscaled(), so scale the time here.
+	 */
+	zvp->zv_nread.value.ui64 = kiop->nread;
+	zvp->zv_reads.value.ui64 = kiop->reads;
+	zvp->zv_rtime.value.ui64 = kiop->rtime;
+	zvp->zv_rcnt.value.ui64 = kiop->rcnt;
+	zvp->zv_rlentime.value.ui64 = kiop->rlentime;
+	zvp->zv_nwritten.value.ui64 = kiop->nwritten;
+	zvp->zv_writes.value.ui64 = kiop->writes;
+	zvp->zv_wtime.value.ui64 = kiop->wtime;
+	zvp->zv_wcnt.value.ui64 = kiop->wcnt;
+	zvp->zv_wlentime.value.ui64 = kiop->wlentime;
+
+	scalehrtime((hrtime_t *)&zvp->zv_rtime.value.ui64);
+	scalehrtime((hrtime_t *)&zvp->zv_rlentime.value.ui64);
+	scalehrtime((hrtime_t *)&zvp->zv_wtime.value.ui64);
+	scalehrtime((hrtime_t *)&zvp->zv_wlentime.value.ui64);
+
+	return (0);
+}
+
+static kstat_t *
+zone_vfs_kstat_create(zone_t *zone)
+{
+	kstat_t *ksp;
+	zone_vfs_kstat_t *zvp;
+
+	if ((ksp = kstat_create_zone("zone_vfs", zone->zone_id,
+	    zone->zone_name, "zone_vfs", KSTAT_TYPE_NAMED,
+	    sizeof (zone_vfs_kstat_t) / sizeof (kstat_named_t),
+	    KSTAT_FLAG_VIRTUAL, zone->zone_id)) == NULL)
+		return (NULL);
+
+	if (zone->zone_id != GLOBAL_ZONEID)
+		kstat_zone_add(ksp, GLOBAL_ZONEID);
+
+	zvp = ksp->ks_data = kmem_zalloc(sizeof (zone_vfs_kstat_t), KM_SLEEP);
+	ksp->ks_data_size += strlen(zone->zone_name) + 1;
+	ksp->ks_lock = &zone->zone_vfs_lock;
+	zone->zone_vfs_stats = zvp;
+
+	/* The kstat "name" field is not large enough for a full zonename */
+	kstat_named_init(&zvp->zv_zonename, "zonename", KSTAT_DATA_STRING);
+	kstat_named_setstr(&zvp->zv_zonename, zone->zone_name);
+	kstat_named_init(&zvp->zv_nread, "nread", KSTAT_DATA_UINT64);
+	kstat_named_init(&zvp->zv_reads, "reads", KSTAT_DATA_UINT64);
+	kstat_named_init(&zvp->zv_rtime, "rtime", KSTAT_DATA_UINT64);
+	kstat_named_init(&zvp->zv_rcnt, "rcnt", KSTAT_DATA_UINT64);
+	kstat_named_init(&zvp->zv_rlentime, "rlentime", KSTAT_DATA_UINT64);
+	kstat_named_init(&zvp->zv_nwritten, "nwritten", KSTAT_DATA_UINT64);
+	kstat_named_init(&zvp->zv_writes, "writes", KSTAT_DATA_UINT64);
+	kstat_named_init(&zvp->zv_wtime, "wtime", KSTAT_DATA_UINT64);
+	kstat_named_init(&zvp->zv_wcnt, "wcnt", KSTAT_DATA_UINT64);
+	kstat_named_init(&zvp->zv_wlentime, "wlentime", KSTAT_DATA_UINT64);
+	kstat_named_init(&zvp->zv_10ms_ops, "10ms_ops", KSTAT_DATA_UINT64);
+	kstat_named_init(&zvp->zv_100ms_ops, "100ms_ops", KSTAT_DATA_UINT64);
+	kstat_named_init(&zvp->zv_1s_ops, "1s_ops", KSTAT_DATA_UINT64);
+	kstat_named_init(&zvp->zv_10s_ops, "10s_ops", KSTAT_DATA_UINT64);
+	kstat_named_init(&zvp->zv_delay_cnt, "delay_cnt", KSTAT_DATA_UINT64);
+	kstat_named_init(&zvp->zv_delay_time, "delay_time", KSTAT_DATA_UINT64);
+
+	ksp->ks_update = zone_vfs_kstat_update;
+	ksp->ks_private = zone;
+
+	kstat_install(ksp);
+	return (ksp);
+}
+
 static int
+zone_zfs_kstat_update(kstat_t *ksp, int rw)
+{
+	zone_t *zone = ksp->ks_private;
+	zone_zfs_kstat_t *zzp = ksp->ks_data;
+	kstat_io_t *kiop = &zone->zone_zfs_rwstats;
+
+	if (rw == KSTAT_WRITE)
+		return (EACCES);
+
+	/*
+	 * Extract the ZFS statistics from the kstat_io_t structure used by
+	 * kstat_runq_enter() and related functions.  Since the I/O throttle
+	 * counters are updated directly by the ZFS layer, there's no need to
+	 * copy those statistics here.
+	 *
+	 * Note that kstat_runq_enter() and the related functions use
+	 * gethrtime_unscaled(), so scale the time here.
+	 */
+	zzp->zz_nread.value.ui64 = kiop->nread;
+	zzp->zz_reads.value.ui64 = kiop->reads;
+	zzp->zz_rtime.value.ui64 = kiop->rtime;
+	zzp->zz_rlentime.value.ui64 = kiop->rlentime;
+	zzp->zz_nwritten.value.ui64 = kiop->nwritten;
+	zzp->zz_writes.value.ui64 = kiop->writes;
+
+	scalehrtime((hrtime_t *)&zzp->zz_rtime.value.ui64);
+	scalehrtime((hrtime_t *)&zzp->zz_rlentime.value.ui64);
+
+	return (0);
+}
+
+static kstat_t *
+zone_zfs_kstat_create(zone_t *zone)
+{
+	kstat_t *ksp;
+	zone_zfs_kstat_t *zzp;
+
+	if ((ksp = kstat_create_zone("zone_zfs", zone->zone_id,
+	    zone->zone_name, "zone_zfs", KSTAT_TYPE_NAMED,
+	    sizeof (zone_zfs_kstat_t) / sizeof (kstat_named_t),
+	    KSTAT_FLAG_VIRTUAL, zone->zone_id)) == NULL)
+		return (NULL);
+
+	if (zone->zone_id != GLOBAL_ZONEID)
+		kstat_zone_add(ksp, GLOBAL_ZONEID);
+
+	zzp = ksp->ks_data = kmem_zalloc(sizeof (zone_zfs_kstat_t), KM_SLEEP);
+	ksp->ks_data_size += strlen(zone->zone_name) + 1;
+	ksp->ks_lock = &zone->zone_zfs_lock;
+	zone->zone_zfs_stats = zzp;
+
+	/* The kstat "name" field is not large enough for a full zonename */
+	kstat_named_init(&zzp->zz_zonename, "zonename", KSTAT_DATA_STRING);
+	kstat_named_setstr(&zzp->zz_zonename, zone->zone_name);
+	kstat_named_init(&zzp->zz_nread, "nread", KSTAT_DATA_UINT64);
+	kstat_named_init(&zzp->zz_reads, "reads", KSTAT_DATA_UINT64);
+	kstat_named_init(&zzp->zz_rtime, "rtime", KSTAT_DATA_UINT64);
+	kstat_named_init(&zzp->zz_rlentime, "rlentime", KSTAT_DATA_UINT64);
+	kstat_named_init(&zzp->zz_nwritten, "nwritten", KSTAT_DATA_UINT64);
+	kstat_named_init(&zzp->zz_writes, "writes", KSTAT_DATA_UINT64);
+	kstat_named_init(&zzp->zz_waittime, "waittime", KSTAT_DATA_UINT64);
+
+	ksp->ks_update = zone_zfs_kstat_update;
+	ksp->ks_private = zone;
+
+	kstat_install(ksp);
+	return (ksp);
+}
+
+static int
 zone_mcap_kstat_update(kstat_t *ksp, int rw)
 {
 	zone_t *zone = ksp->ks_private;
@@ -1898,11 +2102,19 @@
 	if (rw == KSTAT_WRITE)
 		return (EACCES);
 
+	zmp->zm_rss.value.ui64 = zone->zone_phys_mem;
+	zmp->zm_phys_cap.value.ui64 = zone->zone_phys_mem_ctl;
+	zmp->zm_swap.value.ui64 = zone->zone_max_swap;
+	zmp->zm_swap_cap.value.ui64 = zone->zone_max_swap_ctl;
+	zmp->zm_nover.value.ui64 = zone->zone_mcap_nover;
+	zmp->zm_pagedout.value.ui64 = zone->zone_mcap_pagedout;
 	zmp->zm_pgpgin.value.ui64 = zone->zone_pgpgin;
 	zmp->zm_anonpgin.value.ui64 = zone->zone_anonpgin;
 	zmp->zm_execpgin.value.ui64 = zone->zone_execpgin;
 	zmp->zm_fspgin.value.ui64 = zone->zone_fspgin;
 	zmp->zm_anon_alloc_fail.value.ui64 = zone->zone_anon_alloc_fail;
+	zmp->zm_pf_throttle.value.ui64 = zone->zone_pf_throttle;
+	zmp->zm_pf_throttle_usec.value.ui64 = zone->zone_pf_throttle_usec;
 
 	return (0);
 }
@@ -1930,6 +2142,13 @@
 	/* The kstat "name" field is not large enough for a full zonename */
 	kstat_named_init(&zmp->zm_zonename, "zonename", KSTAT_DATA_STRING);
 	kstat_named_setstr(&zmp->zm_zonename, zone->zone_name);
+	kstat_named_setstr(&zmp->zm_zonename, zone->zone_name);
+	kstat_named_init(&zmp->zm_rss, "rss", KSTAT_DATA_UINT64);
+	kstat_named_init(&zmp->zm_phys_cap, "physcap", KSTAT_DATA_UINT64);
+	kstat_named_init(&zmp->zm_swap, "swap", KSTAT_DATA_UINT64);
+	kstat_named_init(&zmp->zm_swap_cap, "swapcap", KSTAT_DATA_UINT64);
+	kstat_named_init(&zmp->zm_nover, "nover", KSTAT_DATA_UINT64);
+	kstat_named_init(&zmp->zm_pagedout, "pagedout", KSTAT_DATA_UINT64);
 	kstat_named_init(&zmp->zm_pgpgin, "pgpgin", KSTAT_DATA_UINT64);
 	kstat_named_init(&zmp->zm_anonpgin, "anonpgin", KSTAT_DATA_UINT64);
 	kstat_named_init(&zmp->zm_execpgin, "execpgin", KSTAT_DATA_UINT64);
@@ -1936,6 +2155,10 @@
 	kstat_named_init(&zmp->zm_fspgin, "fspgin", KSTAT_DATA_UINT64);
 	kstat_named_init(&zmp->zm_anon_alloc_fail, "anon_alloc_fail",
 	    KSTAT_DATA_UINT64);
+	kstat_named_init(&zmp->zm_pf_throttle, "n_pf_throttle",
+	    KSTAT_DATA_UINT64);
+	kstat_named_init(&zmp->zm_pf_throttle_usec, "n_pf_throttle_usec",
+	    KSTAT_DATA_UINT64);
 
 	ksp->ks_update = zone_mcap_kstat_update;
 	ksp->ks_private = zone;
@@ -2035,9 +2258,16 @@
 	    "lockedmem", zone_lockedmem_kstat_update);
 	zone->zone_swapresv_kstat = zone_kstat_create_common(zone,
 	    "swapresv", zone_swapresv_kstat_update);
+	zone->zone_physmem_kstat = zone_kstat_create_common(zone,
+	    "physicalmem", zone_physmem_kstat_update);
 	zone->zone_nprocs_kstat = zone_kstat_create_common(zone,
 	    "nprocs", zone_nprocs_kstat_update);
 
+	if ((zone->zone_vfs_ksp = zone_vfs_kstat_create(zone)) == NULL) {
+		zone->zone_vfs_stats = kmem_zalloc(
+		    sizeof (zone_vfs_kstat_t), KM_SLEEP);
+	}
+
 	if ((zone->zone_mcap_ksp = zone_mcap_kstat_create(zone)) == NULL) {
 		zone->zone_mcap_stats = kmem_zalloc(
 		    sizeof (zone_mcap_kstat_t), KM_SLEEP);
@@ -2047,6 +2277,7 @@
 		zone->zone_misc_stats = kmem_zalloc(
 		    sizeof (zone_misc_kstat_t), KM_SLEEP);
 	}
+
 }
 
 static void
@@ -2069,12 +2300,18 @@
 	    sizeof (zone_kstat_t));
 	zone_kstat_delete_common(&zone->zone_swapresv_kstat,
 	    sizeof (zone_kstat_t));
+	zone_kstat_delete_common(&zone->zone_physmem_kstat,
+	    sizeof (zone_kstat_t));
 	zone_kstat_delete_common(&zone->zone_nprocs_kstat,
 	    sizeof (zone_kstat_t));
+
+	zone_kstat_delete_common(&zone->zone_vfs_ksp,
+	    sizeof (zone_vfs_kstat_t));
 	zone_kstat_delete_common(&zone->zone_mcap_ksp,
 	    sizeof (zone_mcap_kstat_t));
 	zone_kstat_delete_common(&zone->zone_misc_ksp,
 	    sizeof (zone_misc_kstat_t));
+
 }
 
 /*
@@ -2108,6 +2345,8 @@
 	zone0.zone_locked_mem_ctl = UINT64_MAX;
 	ASSERT(zone0.zone_max_swap == 0);
 	zone0.zone_max_swap_ctl = UINT64_MAX;
+	zone0.zone_phys_mem = 0;
+	zone0.zone_phys_mem_ctl = UINT64_MAX;
 	zone0.zone_max_lofi = 0;
 	zone0.zone_max_lofi_ctl = UINT64_MAX;
 	zone0.zone_shmmax = 0;
@@ -2131,6 +2370,7 @@
 	zone0.zone_initname = initname;
 	zone0.zone_lockedmem_kstat = NULL;
 	zone0.zone_swapresv_kstat = NULL;
+	zone0.zone_physmem_kstat = NULL;
 	zone0.zone_nprocs_kstat = NULL;
 	zone0.zone_zfs_io_pri = 1;
 
@@ -2247,7 +2487,7 @@
 	rc_zone_zfs_io_pri = rctl_register("zone.zfs-io-priority",
 	    RCENTITY_ZONE, RCTL_GLOBAL_SIGNAL_NEVER | RCTL_GLOBAL_DENY_NEVER |
 	    RCTL_GLOBAL_NOBASIC | RCTL_GLOBAL_COUNT | RCTL_GLOBAL_SYSLOG_NEVER,
-	    1024, 1024, &zone_zfs_io_pri_ops);
+	    16384, 16384, &zone_zfs_io_pri_ops);
 
 	rc_zone_nlwps = rctl_register("zone.max-lwps", RCENTITY_ZONE,
 	    RCTL_GLOBAL_NOACTION | RCTL_GLOBAL_NOBASIC | RCTL_GLOBAL_COUNT,
@@ -2300,6 +2540,11 @@
 	    RCTL_GLOBAL_DENY_ALWAYS, UINT64_MAX, UINT64_MAX,
 	    &zone_max_swap_ops);
 
+	rc_zone_phys_mem = rctl_register("zone.max-physical-memory",
+	    RCENTITY_ZONE, RCTL_GLOBAL_NOBASIC | RCTL_GLOBAL_BYTES |
+	    RCTL_GLOBAL_DENY_ALWAYS, UINT64_MAX, UINT64_MAX,
+	    &zone_phys_mem_ops);
+
 	rc_zone_max_lofi = rctl_register("zone.max-lofi",
 	    RCENTITY_ZONE, RCTL_GLOBAL_NOBASIC | RCTL_GLOBAL_COUNT |
 	    RCTL_GLOBAL_DENY_ALWAYS, UINT64_MAX, UINT64_MAX,
@@ -2321,6 +2566,8 @@
 	zone0.zone_ntasks = 1;
 	mutex_exit(&p0.p_lock);
 	zone0.zone_restart_init = B_TRUE;
+	zone0.zone_reboot_on_init_exit = B_FALSE;
+	zone0.zone_init_status = -1;
 	zone0.zone_brand = &native_brand;
 	rctl_prealloc_destroy(gp);
 	/*
@@ -2400,6 +2647,8 @@
 static void
 zone_free(zone_t *zone)
 {
+	zone_dl_t *zdl;
+
 	ASSERT(zone != global_zone);
 	ASSERT(zone->zone_ntasks == 0);
 	ASSERT(zone->zone_nlwps == 0);
@@ -2428,6 +2677,19 @@
 	list_destroy(&zone->zone_ref_list);
 	zone_free_zsd(zone);
 	zone_free_datasets(zone);
+
+	/*
+	 * While dlmgmtd should have removed all of these, it could have left
+	 * something behind or crashed. In which case it's not safe for us to
+	 * assume that the list is empty which list_destroy() will ASSERT. We
+	 * clean up for our userland comrades which may have crashed, or worse,
+	 * been disabled by SMF.
+	 */
+	while ((zdl = list_remove_head(&zone->zone_dl_list)) != NULL) {
+		if (zdl->zdl_net != NULL)
+			nvlist_free(zdl->zdl_net);
+		kmem_free(zdl, sizeof (zone_dl_t));
+	}
 	list_destroy(&zone->zone_dl_list);
 
 	if (zone->zone_rootvp != NULL)
@@ -2563,9 +2825,14 @@
 		return (EINVAL);
 	}
 
-	/* set up the brand specific data */
+	/*
+	 * Set up the brand specific data.
+	 * Note that it's possible that the hook has to drop the
+	 * zone_status_lock and reaquire it before returning so we can't
+	 * assume the lock has been held the entire time.
+	 */
 	zone->zone_brand = bp;
-	ZBROP(zone)->b_init_brand_data(zone);
+	ZBROP(zone)->b_init_brand_data(zone, &zone_status_lock);
 
 	mutex_exit(&zone_status_lock);
 	return (0);
@@ -2611,19 +2878,70 @@
 	return (0);
 }
 
+/*
+ * The zone_set_mcap_nover and zone_set_mcap_pageout functions are used
+ * to provide the physical memory capping kstats.  Since physical memory
+ * capping is currently implemented in userland, that code uses the setattr
+ * entry point to increment the kstats.  We always simply increment nover
+ * every time that setattr is called and we always add in the input value
+ * to zone_mcap_pagedout every time that is called.
+ */
+/*ARGSUSED*/
 static int
-zone_set_phys_mcap(zone_t *zone, const uint64_t *zone_mcap)
+zone_set_mcap_nover(zone_t *zone, const uint64_t *zone_nover)
 {
-	uint64_t mcap;
-	int err = 0;
+	zone->zone_mcap_nover++;
 
-	if ((err = copyin(zone_mcap, &mcap, sizeof (uint64_t))) == 0)
-		zone->zone_phys_mcap = mcap;
+	return (0);
+}
 
+static int
+zone_set_mcap_pageout(zone_t *zone, const uint64_t *zone_pageout)
+{
+	uint64_t pageout;
+	int err;
+
+	if ((err = copyin(zone_pageout, &pageout, sizeof (uint64_t))) == 0)
+		zone->zone_mcap_pagedout += pageout;
+
 	return (err);
 }
 
+/*
+ * The zone_set_page_fault_delay function is used to set the number of usecs
+ * to throttle page faults.  This is normally 0 but can be set to a non-0 value
+ * by the user-land memory capping code when the zone is over its physcial
+ * memory cap.
+ */
 static int
+zone_set_page_fault_delay(zone_t *zone, const uint32_t *pfdelay)
+{
+	uint32_t dusec;
+	int err;
+
+	if ((err = copyin(pfdelay, &dusec, sizeof (uint32_t))) == 0)
+		zone->zone_pg_flt_delay = dusec;
+
+	return (err);
+}
+
+/*
+ * The zone_set_rss function is used to set the zone's RSS when we do the
+ * fast, approximate calculation in user-land.
+ */
+static int
+zone_set_rss(zone_t *zone, const uint64_t *prss)
+{
+	uint64_t rss;
+	int err;
+
+	if ((err = copyin(prss, &rss, sizeof (uint64_t))) == 0)
+		zone->zone_phys_mem = rss;
+
+	return (err);
+}
+
+static int
 zone_set_sched_class(zone_t *zone, const char *new_class)
 {
 	char sched_class[PC_CLNMSZ];
@@ -3773,6 +4091,17 @@
 	 */
 	z->zone_proc_initpid = p->p_pid;
 
+	if (z->zone_setup_app_contract == B_TRUE) {
+		/*
+		 * Normally a process cannot modify its own contract, but we're
+		 * just starting the zone's init process and its contract is
+		 * always initialized from the sys_process_tmpl template, so
+		 * this is the simplest way to setup init's contract to kill
+		 * the process if any other process in the contract exits.
+		 */
+		p->p_ct_process->conp_ev_fatal |= CT_PR_EV_EXIT;
+	}
+
 	/*
 	 * We maintain zone_boot_err so that we can return the cause of the
 	 * failure back to the caller of the zone_boot syscall.
@@ -3801,9 +4130,54 @@
 			lwp_exit();
 		}
 	} else {
+		id_t cid = curthread->t_cid;
+
 		if (zone_status_get(z) == ZONE_IS_BOOTING)
 			zone_status_set(z, ZONE_IS_RUNNING);
 		mutex_exit(&zone_status_lock);
+
+		mutex_enter(&class_lock);
+		ASSERT(cid < loaded_classes);
+		if (strcmp(sclass[cid].cl_name, "FX") == 0 &&
+		    z->zone_fixed_hipri) {
+			/*
+			 * If the zone is using FX then by default all
+			 * processes start at the lowest priority and stay
+			 * there. We provide a mechanism for the zone to
+			 * indicate that it should run at "high priority". In
+			 * this case we setup init to run at the highest FX
+			 * priority (which is one level higher than the
+			 * non-fixed scheduling classes can use).
+			 */
+			pcparms_t pcparms;
+
+			pcparms.pc_cid = cid;
+			((fxkparms_t *)pcparms.pc_clparms)->fx_upri = FXMAXUPRI;
+			((fxkparms_t *)pcparms.pc_clparms)->fx_uprilim =
+			    FXMAXUPRI;
+			((fxkparms_t *)pcparms.pc_clparms)->fx_cflags =
+			    FX_DOUPRILIM | FX_DOUPRI;
+
+			mutex_enter(&pidlock);
+			mutex_enter(&curproc->p_lock);
+
+			(void) parmsset(&pcparms, curthread);
+
+			mutex_exit(&curproc->p_lock);
+			mutex_exit(&pidlock);
+		} else if (strcmp(sclass[cid].cl_name, "RT") == 0) {
+			/*
+			 * zsched always starts the init lwp at priority
+			 * minclsyspri - 1. This priority gets set in t_pri and
+			 * is invalid for RT, but RT never uses t_pri. However
+			 * t_pri is used by procfs, so we always see processes
+			 * within an RT zone with an invalid priority value.
+			 * We fix that up now.
+			 */
+			curthread->t_pri = RTGPPRIO0;
+		}
+		mutex_exit(&class_lock);
+
 		/* cause the process to return to userland. */
 		lwp_rtt();
 	}
@@ -3845,6 +4219,7 @@
 	PTOU(pp)->u_argc = 0;
 	PTOU(pp)->u_argv = NULL;
 	PTOU(pp)->u_envp = NULL;
+	PTOU(pp)->u_commpagep = NULL;
 	closeall(P_FINFO(pp));
 
 	/*
@@ -4287,8 +4662,9 @@
 
 		error = EINVAL;
 		name = nvpair_name(nvp);
-		if (strncmp(nvpair_name(nvp), "zone.", sizeof ("zone.") - 1)
-		    != 0 || nvpair_type(nvp) != DATA_TYPE_NVLIST_ARRAY) {
+		if ((strncmp(name, "zone.", sizeof ("zone.") - 1) != 0 &&
+		    strncmp(name, "project.", sizeof ("project.") - 1) != 0) ||
+		    nvpair_type(nvp) != DATA_TYPE_NVLIST_ARRAY) {
 			goto out;
 		}
 		if ((hndl = rctl_hndl_lookup(name)) == -1) {
@@ -4436,6 +4812,8 @@
 	zone->zone_ncpus = 0;
 	zone->zone_ncpus_online = 0;
 	zone->zone_restart_init = B_TRUE;
+	zone->zone_reboot_on_init_exit = B_FALSE;
+	zone->zone_init_status = -1;
 	zone->zone_brand = &native_brand;
 	zone->zone_initname = NULL;
 	mutex_init(&zone->zone_lock, NULL, MUTEX_DEFAULT, NULL);
@@ -4497,10 +4875,13 @@
 	zone->zone_locked_mem_ctl = UINT64_MAX;
 	zone->zone_max_swap = 0;
 	zone->zone_max_swap_ctl = UINT64_MAX;
+	zone->zone_phys_mem = 0;
+	zone->zone_phys_mem_ctl = UINT64_MAX;
 	zone->zone_max_lofi = 0;
 	zone->zone_max_lofi_ctl = UINT64_MAX;
 	zone->zone_lockedmem_kstat = NULL;
 	zone->zone_swapresv_kstat = NULL;
+	zone->zone_physmem_kstat = NULL;
 	zone->zone_zfs_io_pri = 1;
 
 	/*
@@ -4656,8 +5037,8 @@
 	/*
 	 * The process, task, and project rctls are probably wrong;
 	 * we need an interface to get the default values of all rctls,
-	 * and initialize zsched appropriately.  I'm not sure that that
-	 * makes much of a difference, though.
+	 * and initialize zsched appropriately. However, we allow zoneadmd
+	 * to pass down both zone and project rctls for the zone's init.
 	 */
 	error = newproc(zsched, (void *)&zarg, syscid, minclsyspri, NULL, 0);
 	if (error != 0) {
@@ -5557,14 +5938,6 @@
 				error = EFAULT;
 		}
 		break;
-	case ZONE_ATTR_PHYS_MCAP:
-		size = sizeof (zone->zone_phys_mcap);
-		if (bufsize > size)
-			bufsize = size;
-		if (buf != NULL &&
-		    copyout(&zone->zone_phys_mcap, buf, bufsize) != 0)
-			error = EFAULT;
-		break;
 	case ZONE_ATTR_SCHED_CLASS:
 		mutex_enter(&class_lock);
 
@@ -5619,6 +5992,15 @@
 		}
 		kmem_free(zbuf, bufsize);
 		break;
+	case ZONE_ATTR_SCHED_FIXEDHI:
+		size = sizeof (boolean_t);
+		if (bufsize > size)
+			bufsize = size;
+
+		if (buf != NULL && copyout(&zone->zone_fixed_hipri, buf,
+		    bufsize) != 0)
+			error = EFAULT;
+		break;
 	default:
 		if ((attr >= ZONE_ATTR_BRAND_ATTRS) && ZONE_IS_BRANDED(zone)) {
 			size = bufsize;
@@ -5650,10 +6032,11 @@
 		return (set_errno(EPERM));
 
 	/*
-	 * Only the ZONE_ATTR_PHYS_MCAP attribute can be set on the
-	 * global zone.
+	 * Only the ZONE_ATTR_PMCAP_NOVER and ZONE_ATTR_PMCAP_PAGEOUT
+	 * attributes can be set on the global zone.
 	 */
-	if (zoneid == GLOBAL_ZONEID && attr != ZONE_ATTR_PHYS_MCAP) {
+	if (zoneid == GLOBAL_ZONEID &&
+	    attr != ZONE_ATTR_PMCAP_NOVER && attr != ZONE_ATTR_PMCAP_PAGEOUT) {
 		return (set_errno(EINVAL));
 	}
 
@@ -5670,7 +6053,9 @@
 	 * non-global zones.
 	 */
 	zone_status = zone_status_get(zone);
-	if (attr != ZONE_ATTR_PHYS_MCAP && zone_status > ZONE_IS_READY) {
+	if (attr != ZONE_ATTR_PMCAP_NOVER && attr != ZONE_ATTR_PMCAP_PAGEOUT &&
+	    attr != ZONE_ATTR_PG_FLT_DELAY && attr != ZONE_ATTR_RSS &&
+	    zone_status > ZONE_IS_READY) {
 		err = EINVAL;
 		goto done;
 	}
@@ -5692,9 +6077,18 @@
 	case ZONE_ATTR_FS_ALLOWED:
 		err = zone_set_fs_allowed(zone, (const char *)buf);
 		break;
-	case ZONE_ATTR_PHYS_MCAP:
-		err = zone_set_phys_mcap(zone, (const uint64_t *)buf);
+	case ZONE_ATTR_PMCAP_NOVER:
+		err = zone_set_mcap_nover(zone, (const uint64_t *)buf);
 		break;
+	case ZONE_ATTR_PMCAP_PAGEOUT:
+		err = zone_set_mcap_pageout(zone, (const uint64_t *)buf);
+		break;
+	case ZONE_ATTR_PG_FLT_DELAY:
+		err = zone_set_page_fault_delay(zone, (const uint32_t *)buf);
+		break;
+	case ZONE_ATTR_RSS:
+		err = zone_set_rss(zone, (const uint64_t *)buf);
+		break;
 	case ZONE_ATTR_SCHED_CLASS:
 		err = zone_set_sched_class(zone, (const char *)buf);
 		break;
@@ -5722,6 +6116,22 @@
 		err = zone_set_network(zoneid, zbuf);
 		kmem_free(zbuf, bufsize);
 		break;
+	case ZONE_ATTR_APP_SVC_CT:
+		if (bufsize != sizeof (boolean_t)) {
+			err = EINVAL;
+		} else {
+			zone->zone_setup_app_contract = (boolean_t)buf;
+			err = 0;
+		}
+		break;
+	case ZONE_ATTR_SCHED_FIXEDHI:
+		if (bufsize != sizeof (boolean_t)) {
+			err = EINVAL;
+		} else {
+			zone->zone_fixed_hipri = (boolean_t)buf;
+			err = 0;
+		}
+		break;
 	default:
 		if ((attr >= ZONE_ATTR_BRAND_ATTRS) && ZONE_IS_BRANDED(zone))
 			err = ZBROP(zone)->b_setattr(zone, attr, buf, bufsize);
@@ -6525,6 +6935,7 @@
 	bcopy(zone->zone_name, zone_name, zone_namelen);
 	zoneid = zone->zone_id;
 	uniqid = zone->zone_uniqid;
+	arg.status = zone->zone_init_status;
 	/*
 	 * zoneadmd may be down, but at least we can empty out the zone.
 	 * We can ignore the return value of zone_empty() since we're called
@@ -6765,16 +7176,15 @@
 }
 
 /*
- * Returns true if the named dataset is visible in the current zone.
+ * Returns true if the named dataset is visible in the specified zone.
  * The 'write' parameter is set to 1 if the dataset is also writable.
  */
 int
-zone_dataset_visible(const char *dataset, int *write)
+zone_dataset_visible_inzone(zone_t *zone, const char *dataset, int *write)
 {
 	static int zfstype = -1;
 	zone_dataset_t *zd;
 	size_t len;
-	zone_t *zone = curproc->p_zone;
 	const char *name = NULL;
 	vfs_t *vfsp = NULL;
 
@@ -6842,7 +7252,8 @@
 	vfs_list_read_lock();
 	vfsp = zone->zone_vfslist;
 	do {
-		ASSERT(vfsp);
+		if (vfsp == NULL)
+			break;
 		if (vfsp->vfs_fstype == zfstype) {
 			name = refstr_value(vfsp->vfs_resource);
 
@@ -6878,6 +7289,18 @@
 	return (0);
 }
 
+/*
+ * Returns true if the named dataset is visible in the current zone.
+ * The 'write' parameter is set to 1 if the dataset is also writable.
+ */
+int
+zone_dataset_visible(const char *dataset, int *write)
+{
+	zone_t *zone = curproc->p_zone;
+
+	return (zone_dataset_visible_inzone(zone, dataset, write));
+}
+
 /*
  * zone_find_by_any_path() -
  *
--- old/usr/src/uts/common/sys/Makefile	Wed Oct  5 00:32:29 2016
+++ new/usr/src/uts/common/sys/Makefile	Wed Oct  5 00:32:29 2016
@@ -25,6 +25,7 @@
 # Copyright 2013 Garrett D'Amore <garrett@damore.org>
 # Copyright 2013 Saso Kiselkov. All rights reserved.
 # Copyright 2015 Igor Kozhukhov <ikozhukhov@gmail.com>
+# Copyright 2015, Joyent, Inc. All rights reserved.
 # Copyright 2016 Nexenta Systems, Inc.
 #
 
@@ -275,6 +276,7 @@
 	idmap.h 		\
 	ieeefp.h		\
 	id_space.h		\
+	inotify.h		\
 	instance.h		\
 	int_const.h		\
 	int_fmtio.h		\
@@ -343,6 +345,7 @@
 	lgrp.h			\
 	lgrp_user.h		\
 	libc_kernel.h		\
+	limits.h		\
 	link.h			\
 	list.h			\
 	list_impl.h		\
@@ -663,6 +666,7 @@
 	watchpoint.h		\
 	winlockio.h		\
 	zcons.h			\
+	zfd.h			\
 	zone.h			\
 	xti_inet.h		\
 	xti_osi.h		\
--- old/usr/src/uts/common/sys/auxv.h	Wed Oct  5 00:32:29 2016
+++ new/usr/src/uts/common/sys/auxv.h	Wed Oct  5 00:32:29 2016
@@ -29,7 +29,7 @@
  * Use is subject to license terms.
  */
 /*
- * Copyright (c) 2012, Joyent, Inc.  All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 
 #ifndef	_SYS_AUXV_H
@@ -78,6 +78,9 @@
 #define	AT_FLAGS	8	/* processor flags */
 #define	AT_ENTRY	9	/* a.out entry point */
 
+/* First introduced on Linux */
+#define	AT_RANDOM	25	/* address of 16 random bytes */
+
 /*
  * These relate to the original PPC ABI document; Linux reused
  * the values for other things (see below), so disambiguation of
@@ -90,19 +93,18 @@
  * These are the values from LSB 1.3, the first five are also described
  * in the draft amd64 ABI.
  *
- * At the time of writing, Solaris doesn't place any of these values into
- * the aux vector, except AT_CLKTCK which is placed on the aux vector for
- * lx branded processes; also, we do similar things via AT_SUN_ values.
+ * At the time of writing, illumos doesn't place any of these values into the
+ * aux vector, except where noted. We do similar things via AT_SUN_ values.
  *
  * AT_NOTELF		10	program is not ELF?
- * AT_UID		11	real user id
- * AT_EUID		12	effective user id
- * AT_GID		13	real group id
- * AT_EGID		14	effective group id
+ * AT_UID		11	real user id (provided in LX)
+ * AT_EUID		12	effective user id (provided in LX)
+ * AT_GID		13	real group id (provided in LX)
+ * AT_EGID		14	effective group id (provided in LX)
  *
  * AT_PLATFORM		15
  * AT_HWCAP		16
- * AT_CLKTCK		17	c.f. _SC_CLK_TCK
+ * AT_CLKTCK		17	c.f. _SC_CLK_TCK (provided in LX)
  * AT_FPUCW		18
  *
  * AT_DCACHEBSIZE	19	(moved from 10)
@@ -110,6 +112,16 @@
  * AT_UCACHEBSIZE	21	(moved from 12)
  *
  * AT_IGNOREPPC		22
+ *
+ * On Linux:
+ * AT_* values 18 through 22 are reserved
+ * AT_SECURE		23	secure mode boolean (provided in LX)
+ * AT_BASE_PLATFORM	24	string identifying real platform, may
+ *				differ from AT_PLATFORM.
+ * AT_HWCAP2		26	extension of AT_HWCAP
+ * AT_EXECFN		31	filename of program
+ * AT_SYSINFO		32
+ * AT_SYSINFO_EHDR	33	The vDSO location
  */
 
 /*
@@ -186,8 +198,15 @@
 #define	AT_SUN_BRAND_AUX1	2020
 #define	AT_SUN_BRAND_AUX2	2021
 #define	AT_SUN_BRAND_AUX3	2022
+#define	AT_SUN_BRAND_AUX4	2025
+#define	AT_SUN_BRAND_NROOT	2024
 
 /*
+ * Aux vector for comm page
+ */
+#define	AT_SUN_COMMPAGE		2026
+
+/*
  * Note that 2023 is reserved for the AT_SUN_HWCAP2 word defined above.
  */
 
--- old/usr/src/uts/common/sys/brand.h	Wed Oct  5 00:32:29 2016
+++ new/usr/src/uts/common/sys/brand.h	Wed Oct  5 00:32:29 2016
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2016, Joyent, Inc.
  */
 
 #ifndef _SYS_BRAND_H
@@ -102,19 +103,69 @@
 struct intpdata;
 struct execa;
 
+/*
+ * Common structure to define hooks for brand operation.
+ *
+ * Required Fields:
+ * b_init_brand_data - Setup zone brand data during zone_setbrand
+ * b_free_brand_data - Free zone brand data during zone_destroy
+ * b_brandsys - Syscall handler for brandsys
+ * b_setbrand - Initialize process brand data
+ * b_getattr - Get brand-custom zone attribute
+ * b_setattr - Set brand-custom zone attribute
+ * b_copy_procdata - Copy process brand data during fork
+ * b_proc_exit - Perform process brand exit processing
+ * b_exec - Reset branded process state on exec
+ * b_lwp_setrval - Set return code for forked child
+ * b_initlwp - Initialize lwp brand data (cannot drop p->p_lock)
+ * b_forklwp - Copy lwp brand data during fork
+ * b_freelwp - Free lwp brand data
+ * b_lwpexit - Perform lwp-specific brand exit processing
+ * b_elfexec - Load and execute ELF binary
+ * b_sigset_native_to_brand - Convert sigset native->brand
+ * b_sigset_brand_to_native - Convert sigset brand->native
+ * b_nsig - Maxiumum signal number
+ * b_sendsig - Update process state after sendsig
+ *
+ * Optional Fields:
+ * b_lwpdata_alloc - Speculatively allocate data for use in b_initlwp
+ * b_lwpdata_free - Free data from allocated by b_lwpdata_alloc if errors occur
+ *                  during lwp creation before b_initlwp could be called.
+ * b_initlwp_post - Complete lwp branding (can temporarily drop p->p_lock)
+ * b_exit_with_sig - Instead of sending SIGCLD, exit with custom behavior
+ * b_psig_to_proc - Custom additional behavior during psig
+ * b_wait_filter - Filter processes from being matched by waitid
+ * b_native_exec - Provide interpreter path prefix for executables
+ * b_ptrace_exectrap - Custom behavior for legacy ptrace traps
+ * b_map32limit - Specify alternate limit for MAP_32BIT mappings
+ * b_stop_notify - Hook process stop events
+ * b_waitid_helper - Generate synthetic results for waitid
+ * b_sigcld_repost - Post synthetic SIGCLD signals
+ * b_issig_stop - Alter/suppress signal delivery during issig
+ * b_sig_ignorable - Disallow discarding of signals
+ * b_savecontext - Alter context during savecontext
+ * b_restorecontext - Alter context during restorecontext
+ * b_sendsig_stack - Override stack used for signal delivery
+ * b_setid_clear - Override setid_clear behavior
+ * b_pagefault - Trap pagefault events
+ * b_intp_parse_arg - Controls interpreter argument handling (allow 1 or all)
+ */
 struct brand_ops {
-	void	(*b_init_brand_data)(zone_t *);
+	void	(*b_init_brand_data)(zone_t *, kmutex_t *);
 	void	(*b_free_brand_data)(zone_t *);
 	int	(*b_brandsys)(int, int64_t *, uintptr_t, uintptr_t, uintptr_t,
-		uintptr_t, uintptr_t, uintptr_t);
+		uintptr_t);
 	void	(*b_setbrand)(struct proc *);
 	int	(*b_getattr)(zone_t *, int, void *, size_t *);
 	int	(*b_setattr)(zone_t *, int, void *, size_t);
 	void	(*b_copy_procdata)(struct proc *, struct proc *);
-	void	(*b_proc_exit)(struct proc *, klwp_t *);
+	void	(*b_proc_exit)(struct proc *);
 	void	(*b_exec)();
 	void	(*b_lwp_setrval)(klwp_t *, int, int);
-	int	(*b_initlwp)(klwp_t *);
+	void	*(*b_lwpdata_alloc)(struct proc *);
+	void	(*b_lwpdata_free)(void *);
+	void	(*b_initlwp)(klwp_t *, void *);
+	void	(*b_initlwp_post)(klwp_t *);
 	void	(*b_forklwp)(klwp_t *, klwp_t *);
 	void	(*b_freelwp)(klwp_t *);
 	void	(*b_lwpexit)(klwp_t *);
@@ -121,10 +172,32 @@
 	int	(*b_elfexec)(struct vnode *vp, struct execa *uap,
 	    struct uarg *args, struct intpdata *idata, int level,
 	    long *execsz, int setid, caddr_t exec_file,
-	    struct cred *cred, int brand_action);
+	    struct cred *cred, int *brand_action);
 	void	(*b_sigset_native_to_brand)(sigset_t *);
 	void	(*b_sigset_brand_to_native)(sigset_t *);
+	void	(*b_sigfd_translate)(k_siginfo_t *);
 	int	b_nsig;
+	void	(*b_exit_with_sig)(proc_t *, sigqueue_t *);
+	boolean_t (*b_wait_filter)(proc_t *, proc_t *);
+	boolean_t (*b_native_exec)(uint8_t, const char **);
+	uint32_t (*b_map32limit)(proc_t *);
+	void	(*b_stop_notify)(proc_t *, klwp_t *, ushort_t, ushort_t);
+	int	(*b_waitid_helper)(idtype_t, id_t, k_siginfo_t *, int,
+	    boolean_t *, int *);
+	int	(*b_sigcld_repost)(proc_t *, sigqueue_t *);
+	int	(*b_issig_stop)(proc_t *, klwp_t *);
+	boolean_t (*b_sig_ignorable)(proc_t *, klwp_t *, int);
+	void	(*b_savecontext)(ucontext_t *);
+#if defined(_SYSCALL32_IMPL)
+	void	(*b_savecontext32)(ucontext32_t *);
+#endif
+	void	(*b_restorecontext)(ucontext_t *);
+	caddr_t	(*b_sendsig_stack)(int);
+	void	(*b_sendsig)(int);
+	int	(*b_setid_clear)(vattr_t *vap, cred_t *cr);
+	int	(*b_pagefault)(proc_t *, klwp_t *, caddr_t, enum fault_type,
+	    enum seg_rw);
+	boolean_t b_intp_parse_arg;
 };
 
 /*
@@ -135,6 +208,7 @@
 	char    		*b_name;
 	struct brand_ops	*b_ops;
 	struct brand_mach_ops	*b_machops;
+	size_t			b_data_size;
 } brand_t;
 
 extern brand_t native_brand;
@@ -165,7 +239,7 @@
 extern brand_t	*brand_find_name(char *);
 extern void	brand_unregister_zone(brand_t *);
 extern int	brand_zone_count(brand_t *);
-extern void	brand_setbrand(proc_t *);
+extern int	brand_setbrand(proc_t *, boolean_t);
 extern void	brand_clearbrand(proc_t *, boolean_t);
 
 /*
@@ -178,17 +252,16 @@
 extern void	brand_solaris_copy_procdata(proc_t *, proc_t *,
 		    struct brand *);
 extern int	brand_solaris_elfexec(vnode_t *, execa_t *, uarg_t *,
-		    intpdata_t *, int, long *, int, caddr_t, cred_t *, int,
-		    struct brand *, char *, char *, char *, char *, char *);
+		    intpdata_t *, int, long *, int, caddr_t, cred_t *, int *,
+		    struct brand *, char *, char *, char *);
 extern void	brand_solaris_exec(struct brand *);
 extern int	brand_solaris_fini(char **, struct modlinkage *,
 		    struct brand *);
 extern void	brand_solaris_forklwp(klwp_t *, klwp_t *, struct brand *);
 extern void	brand_solaris_freelwp(klwp_t *, struct brand *);
-extern int	brand_solaris_initlwp(klwp_t *, struct brand *);
+extern void	brand_solaris_initlwp(klwp_t *, struct brand *);
 extern void	brand_solaris_lwpexit(klwp_t *, struct brand *);
-extern void	brand_solaris_proc_exit(struct proc *, klwp_t *,
-		    struct brand *);
+extern void	brand_solaris_proc_exit(struct proc *, struct brand *);
 extern void	brand_solaris_setbrand(proc_t *, struct brand *);
 
 #if defined(_SYSCALL32)
--- old/usr/src/uts/common/sys/buf.h	Wed Oct  5 00:32:29 2016
+++ new/usr/src/uts/common/sys/buf.h	Wed Oct  5 00:32:29 2016
@@ -21,6 +21,7 @@
 /*
  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2012 Joyent, Inc.  All rights reserved.
  */
 
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
@@ -186,6 +187,7 @@
 #define	B_STARTED	0x2000000	/* io:::start probe called for buf */
 #define	B_ABRWRITE	0x4000000	/* Application based recovery active */
 #define	B_PAGE_NOWAIT	0x8000000	/* Skip the page if it is locked */
+#define	B_INVALCURONLY	0x10000000	/* invalidate only for curproc */
 
 /*
  * There is some confusion over the meaning of B_FREE and B_INVAL and what
@@ -198,6 +200,12 @@
  * between the sole use of these two flags.  In both cases, IO will be done
  * if the page is not yet committed to storage.
  *
+ * The B_INVALCURONLY flag modifies the behavior of the B_INVAL flag and is
+ * intended to be used in conjunction with B_INVAL.  B_INVALCURONLY has no
+ * meaning on its own.  When both B_INVALCURONLY and B_INVAL are set, then
+ * the mapping for the page is only invalidated for the current process.
+ * In this case, the page is not destroyed unless this was the final mapping.
+ *
  * In order to discard pages without writing them back, (B_INVAL | B_TRUNC)
  * should be used.
  *
--- old/usr/src/uts/common/sys/contract/process.h	Wed Oct  5 00:32:29 2016
+++ new/usr/src/uts/common/sys/contract/process.h	Wed Oct  5 00:32:29 2016
@@ -21,13 +21,12 @@
 /*
  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
  */
 
 #ifndef	_SYS_CONTRACT_PROCESS_H
 #define	_SYS_CONTRACT_PROCESS_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <sys/contract.h>
 #include <sys/time.h>
 
@@ -55,7 +54,8 @@
 #define	CT_PR_NOORPHAN	0x2	/* kill when contract is abandoned */
 #define	CT_PR_PGRPONLY	0x4	/* only kill process group on fatal errors */
 #define	CT_PR_REGENT	0x8	/* automatically detach inherited contracts */
-#define	CT_PR_ALLPARAM	0xf
+#define	CT_PR_KEEP_EXEC	0x10	/* preserve template accross exec */
+#define	CT_PR_ALLPARAM	0x1f
 
 /*
  * ctr_ev_* flags
--- old/usr/src/uts/common/sys/exec.h	Wed Oct  5 00:32:29 2016
+++ new/usr/src/uts/common/sys/exec.h	Wed Oct  5 00:32:29 2016
@@ -26,6 +26,10 @@
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
 /*	  All Rights Reserved  	*/
 
+/*
+ * Copyright 2016, Joyent, Inc.
+ */
+
 #ifndef _SYS_EXEC_H
 #define	_SYS_EXEC_H
 
@@ -102,10 +106,14 @@
 	vnode_t	*ex_vp;
 	char	*emulator;
 	char	*brandname;
+	const char *brand_nroot;
 	char	*auxp_auxflags; /* addr of auxflags auxv on the user stack */
 	char	*auxp_brand; /* address of first brand auxv on user stack */
 	cred_t	*pfcred;
 	boolean_t scrubenv;
+	uintptr_t maxstack;
+	boolean_t stk_prot_override;
+	uintptr_t commpage;
 } uarg_t;
 
 /*
@@ -175,7 +183,7 @@
 	int	(*exec_func)(struct vnode *vp, struct execa *uap,
 		    struct uarg *args, struct intpdata *idata, int level,
 		    long *execsz, int setid, caddr_t exec_file,
-		    struct cred *cred, int brand_action);
+		    struct cred *cred, int *brand_action);
 	int	(*exec_core)(struct vnode *vp, struct proc *p,
 		    struct cred *cred, rlim64_t rlimit, int sig,
 		    core_content_t content);
@@ -213,7 +221,7 @@
     const char **envp, int brand_action);
 extern int gexec(vnode_t **vp, struct execa *uap, struct uarg *args,
     struct intpdata *idata, int level, long *execsz, caddr_t exec_file,
-    struct cred *cred, int brand_action);
+    struct cred *cred, int *brand_action);
 extern struct execsw *allocate_execsw(char *name, char *magic,
     size_t magic_size);
 extern struct execsw *findexecsw(char *magic);
@@ -238,16 +246,22 @@
  * when compiling the 32-bit compatability elf code in the elfexec module.
  */
 extern int elfexec(vnode_t *, execa_t *, uarg_t *, intpdata_t *, int,
-    long *, int, caddr_t, cred_t *, int);
+    long *, int, caddr_t, cred_t *, int *);
 extern int mapexec_brand(vnode_t *, uarg_t *, Ehdr *, Addr *,
-    intptr_t *, caddr_t, int *, caddr_t *, caddr_t *, size_t *, uintptr_t *);
+    intptr_t *, caddr_t, char **, caddr_t *, caddr_t *, size_t *,
+    uintptr_t *, uintptr_t *);
+extern int elfreadhdr(vnode_t *, cred_t *, Ehdr *, int *, caddr_t *,
+    ssize_t *);
 #endif /* !_ELF32_COMPAT */
 
 #if defined(_LP64)
 extern int elf32exec(vnode_t *, execa_t *, uarg_t *, intpdata_t *, int,
-    long *, int, caddr_t, cred_t *, int);
+    long *, int, caddr_t, cred_t *, int *);
 extern int mapexec32_brand(vnode_t *, uarg_t *, Elf32_Ehdr *, Elf32_Addr *,
-    intptr_t *, caddr_t, int *, caddr_t *, caddr_t *, size_t *, uintptr_t *);
+    intptr_t *, caddr_t, char **, caddr_t *, caddr_t *, size_t *,
+    uintptr_t *, uintptr_t *);
+extern int elf32readhdr(vnode_t *, cred_t *, Elf32_Ehdr *, int *, caddr_t *,
+    ssize_t *);
 #endif  /* _LP64 */
 
 /*
--- old/usr/src/uts/common/sys/fs/tmp.h	Wed Oct  5 00:32:29 2016
+++ new/usr/src/uts/common/sys/fs/tmp.h	Wed Oct  5 00:32:29 2016
@@ -22,12 +22,13 @@
  * Copyright 2007 Sun Microsystems, Inc.
  * All rights reserved.  Use is subject to license terms.
  */
+/*
+ * Copyright 2015 Joyent, Inc.
+ */
 
 #ifndef	_SYS_FS_TMP_H
 #define	_SYS_FS_TMP_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #ifdef	__cplusplus
 extern "C" {
 #endif
@@ -108,6 +109,7 @@
 extern	int	tmp_sticky_remove_access(struct tmpnode *, struct tmpnode *,
 	struct cred *);
 extern	int	tmp_convnum(char *, pgcnt_t *);
+extern	int	tmp_convmode(char *, mode_t *);
 extern	int	tdirenter(struct tmount *, struct tmpnode *, char *,
 	enum de_op, struct tmpnode *, struct tmpnode *, struct vattr *,
 	struct tmpnode **, struct cred *, caller_context_t *);
--- old/usr/src/uts/common/sys/fx.h	Wed Oct  5 00:32:30 2016
+++ new/usr/src/uts/common/sys/fx.h	Wed Oct  5 00:32:30 2016
@@ -21,13 +21,12 @@
 /*
  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
  */
 
 #ifndef _SYS_FX_H
 #define	_SYS_FX_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <sys/types.h>
 #include <sys/thread.h>
 #include <sys/ddi.h>
@@ -145,7 +144,14 @@
 	uint_t	fx_cflags;
 } fxkparms_t;
 
+/*
+ * control flags (kparms->fx_cflags).
+ */
+#define	FX_DOUPRILIM	0x01	/* change user priority limit */
+#define	FX_DOUPRI	0x02	/* change user priority */
+#define	FX_DOTQ		0x04	/* change FX time quantum */
 
+#define	FXMAXUPRI	60	/* maximum user priority setting */
 
 /*
  * Interface for partner private code. This is not a public interface.
--- /dev/null	Wed Oct  5 00:32:30 2016
+++ new/usr/src/uts/common/sys/inotify.h	Wed Oct  5 00:32:30 2016
@@ -0,0 +1,153 @@
+/*
+ * 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 (c) 2014 Joyent, Inc.  All rights reserved.
+ */
+
+/*
+ * Header file to support for the inotify facility.  Note that this facility
+ * is designed to be binary compatible with the Linux inotify facility; values
+ * for constants here should therefore exactly match those found in Linux, and
+ * this facility shouldn't be extended independently of Linux.
+ */
+
+#ifndef _SYS_INOTIFY_H
+#define	_SYS_INOTIFY_H
+
+#include <sys/types.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/*
+ * Events that can be explicitly requested on any inotify watch.
+ */
+#define	IN_ACCESS		0x00000001
+#define	IN_MODIFY		0x00000002
+#define	IN_ATTRIB		0x00000004
+#define	IN_CLOSE_WRITE		0x00000008
+#define	IN_CLOSE_NOWRITE	0x00000010
+#define	IN_OPEN			0x00000020
+#define	IN_MOVED_FROM		0x00000040
+#define	IN_MOVED_TO		0x00000080
+#define	IN_CREATE		0x00000100
+#define	IN_DELETE		0x00000200
+#define	IN_DELETE_SELF		0x00000400
+#define	IN_MOVE_SELF		0x00000800
+
+/*
+ * Events that can be sent to an inotify watch -- requested or not.
+ */
+#define	IN_UNMOUNT		0x00002000
+#define	IN_Q_OVERFLOW		0x00004000
+#define	IN_IGNORED		0x00008000
+
+/*
+ * Flags that can modify an inotify event.
+ */
+#define	IN_ONLYDIR		0x01000000
+#define	IN_DONT_FOLLOW		0x02000000
+#define	IN_EXCL_UNLINK		0x04000000
+#define	IN_MASK_ADD		0x20000000
+#define	IN_ISDIR		0x40000000
+#define	IN_ONESHOT		0x80000000
+
+/*
+ * Helpful constants.
+ */
+#define	IN_CLOSE		(IN_CLOSE_WRITE | IN_CLOSE_NOWRITE)
+#define	IN_MOVE			(IN_MOVED_FROM | IN_MOVED_TO)
+#define	IN_ALL_EVENTS		\
+	(IN_ACCESS | IN_MODIFY | IN_ATTRIB | IN_CLOSE_WRITE | \
+	IN_CLOSE_NOWRITE | IN_OPEN | IN_MOVED_FROM | IN_MOVED_TO | \
+	IN_DELETE | IN_CREATE | IN_DELETE_SELF | IN_MOVE_SELF)
+
+#define	IN_CHILD_EVENTS		\
+	(IN_ACCESS | IN_MODIFY | IN_ATTRIB | IN_CLOSE_WRITE | \
+	IN_CLOSE_NOWRITE | IN_MODIFY | IN_OPEN)
+
+/*
+ * To assure binary compatibility with Linux, these values are fixed at their
+ * Linux equivalents, not their native ones.
+ */
+#define	IN_CLOEXEC		02000000		/* LX_O_CLOEXEC */
+#define	IN_NONBLOCK		04000			/* LX_O_NONBLOCK */
+
+struct inotify_event {
+	int32_t		wd;		/* watch descriptor */
+	uint32_t	mask;		/* mask of events */
+	uint32_t	cookie;		/* event association cookie, if any */
+	uint32_t	len;		/* size of name field */
+	char		name[];		/* optional NUL-terminated name */
+};
+
+/*
+ * These ioctl values are specific to the native implementation; applications
+ * shouldn't be using them directly, and they should therefore be safe to
+ * change without breaking apps.
+ */
+#define	INOTIFYIOC		(('i' << 24) | ('n' << 16) | ('y' << 8))
+#define	INOTIFYIOC_ADD_WATCH	(INOTIFYIOC | 1)	/* add watch */
+#define	INOTIFYIOC_RM_WATCH	(INOTIFYIOC | 2)	/* remove watch */
+#define	INOTIFYIOC_ADD_CHILD	(INOTIFYIOC | 3)	/* add child watch */
+#define	INOTIFYIOC_ACTIVATE	(INOTIFYIOC | 4)	/* activate watch */
+
+#ifndef _LP64
+#ifndef _LITTLE_ENDIAN
+#define	INOTIFY_PTR(type, name)	uint32_t name##pad; type *name
+#else
+#define	INOTIFY_PTR(type, name)	type *name; uint32_t name##pad
+#endif
+#else
+#define	INOTIFY_PTR(type, name)	type *name
+#endif
+
+typedef struct inotify_addwatch {
+	int inaw_fd;			/* open fd for object */
+	uint32_t inaw_mask;		/* desired mask */
+} inotify_addwatch_t;
+
+typedef struct inotify_addchild {
+	INOTIFY_PTR(char, inac_name);	/* pointer to name */
+	int inac_fd;			/* open fd for parent */
+} inotify_addchild_t;
+
+#ifndef _KERNEL
+
+extern int inotify_init(void);
+extern int inotify_init1(int);
+extern int inotify_add_watch(int, const char *, uint32_t);
+extern int inotify_rm_watch(int, int);
+
+#else
+
+#define	IN_UNMASKABLE \
+	(IN_UNMOUNT | IN_Q_OVERFLOW | IN_IGNORED | IN_ISDIR)
+
+#define	IN_MODIFIERS \
+	(IN_EXCL_UNLINK | IN_ONESHOT)
+
+#define	IN_FLAGS \
+	(IN_ONLYDIR | IN_DONT_FOLLOW | IN_MASK_ADD)
+
+#define	IN_REMOVAL		(1ULL << 32)
+#define	INOTIFYMNRN_INOTIFY	0
+#define	INOTIFYMNRN_CLONE	1
+
+#endif /* _KERNEL */
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_INOTIFY_H */
--- old/usr/src/uts/common/sys/ipc_impl.h	Wed Oct  5 00:32:30 2016
+++ new/usr/src/uts/common/sys/ipc_impl.h	Wed Oct  5 00:32:30 2016
@@ -20,6 +20,7 @@
  */
 /*
  * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2016, Joyent, Inc.
  */
 
 #ifndef	_IPC_IMPL_H
@@ -226,6 +227,7 @@
 kmutex_t *ipc_commit_end(ipc_service_t *, kipc_perm_t *);
 void ipc_cleanup(ipc_service_t *, kipc_perm_t *);
 
+void ipc_rmsvc(ipc_service_t *, kipc_perm_t *);
 int ipc_rmid(ipc_service_t *, int, cred_t *);
 int ipc_ids(ipc_service_t *, int *, uint_t, uint_t *);
 
--- old/usr/src/uts/common/sys/iso/signal_iso.h	Wed Oct  5 00:32:30 2016
+++ new/usr/src/uts/common/sys/iso/signal_iso.h	Wed Oct  5 00:32:30 2016
@@ -22,6 +22,7 @@
 /*
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2015, Joyent, Inc.
  */
 
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
@@ -95,7 +96,7 @@
 
 /* insert new signals here, and move _SIGRTM* appropriately */
 #define	_SIGRTMIN 42	/* first (highest-priority) realtime signal */
-#define	_SIGRTMAX 73	/* last (lowest-priority) realtime signal */
+#define	_SIGRTMAX 74	/* last (lowest-priority) realtime signal */
 extern long _sysconf(int);	/* System Private interface to sysconf() */
 #define	SIGRTMIN ((int)_sysconf(_SC_SIGRT_MIN))	/* first realtime signal */
 #define	SIGRTMAX ((int)_sysconf(_SC_SIGRT_MAX))	/* last realtime signal */
--- old/usr/src/uts/common/sys/klwp.h	Wed Oct  5 00:32:30 2016
+++ new/usr/src/uts/common/sys/klwp.h	Wed Oct  5 00:32:30 2016
@@ -24,7 +24,7 @@
  */
 
 /*
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 
 #ifndef	_SYS_KLWP_H
@@ -191,7 +191,14 @@
 	struct ct_template *lwp_ct_active[CTT_MAXTYPE]; /* active templates */
 	struct contract	*lwp_ct_latest[CTT_MAXTYPE]; /* last created contract */
 
-	void	*lwp_brand;		/* per-lwp brand data */
+	/*
+	 * Branding:
+	 * lwp_brand			- per-lwp brand data
+	 * lwp_brand_syscall		- brand syscall interposer
+	 */
+	void	*lwp_brand;
+	int	(*lwp_brand_syscall)(void);
+
 	struct psinfo *lwp_spymaster;	/* if an agent LWP, our spymaster */
 } klwp_t;
 
--- /dev/null	Wed Oct  5 00:32:30 2016
+++ new/usr/src/uts/common/sys/limits.h	Wed Oct  5 00:32:30 2016
@@ -0,0 +1,32 @@
+/*
+ * 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 2015 Joyent, Inc.  All rights reserved.
+ */
+
+#ifndef _SYS_LIMITS_H
+#define	_SYS_LIMITS_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#define	IOV_MAX	1024
+
+#ifdef _KERNEL
+#define	IOV_MAX_STACK	16	/* max. IOV on-stack allocation */
+#endif /* _KERNEL */
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_LIMITS_H */
--- old/usr/src/uts/common/sys/mman.h	Wed Oct  5 00:32:30 2016
+++ new/usr/src/uts/common/sys/mman.h	Wed Oct  5 00:32:30 2016
@@ -339,6 +339,7 @@
 #define	MS_SYNC		0x4		/* wait for msync */
 #define	MS_ASYNC	0x1		/* return immediately */
 #define	MS_INVALIDATE	0x2		/* invalidate caches */
+#define	MS_INVALCURPROC	0x8		/* invalidate cache for curproc only */
 
 #if	(_POSIX_C_SOURCE <= 2) && !defined(_XPG4_2) || defined(__EXTENSIONS__)
 /* functions to mctl */
--- old/usr/src/uts/common/sys/netconfig.h	Wed Oct  5 00:32:30 2016
+++ new/usr/src/uts/common/sys/netconfig.h	Wed Oct  5 00:32:30 2016
@@ -28,6 +28,7 @@
  *
  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
  */
 
 #ifndef	_SYS_NETCONFIG_H
@@ -147,6 +148,8 @@
 extern struct netconfig *getnetpath(void *);
 extern void		nc_perror(const char *);
 extern char		*nc_sperror(void);
+extern void		_nsl_brand_set_hooks(int (*)(void),
+			    struct netconfig *(*)(int));
 
 #ifdef	__cplusplus
 }
--- old/usr/src/uts/common/sys/proc.h	Wed Oct  5 00:32:31 2016
+++ new/usr/src/uts/common/sys/proc.h	Wed Oct  5 00:32:31 2016
@@ -21,6 +21,7 @@
 
 /*
  * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014 Joyent, Inc.  All rights reserved.
  */
 
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
@@ -348,8 +349,10 @@
 	struct zone	*p_zone;	/* zone in which process lives */
 	struct vnode	*p_execdir;	/* directory that p_exec came from */
 	struct brand	*p_brand;	/* process's brand  */
-	void		*p_brand_data;	/* per-process brand state */
 
+	/* per-process brand state */
+	void		*p_brand_data;
+
 	/* additional lock to protect p_sessp (but not its contents) */
 	kmutex_t p_splock;
 	rctl_qty_t	p_locked_mem;	/* locked memory charged to proc */
@@ -363,7 +366,6 @@
 	 */
 	struct user p_user;		/* (see sys/user.h) */
 } proc_t;
-
 #define	PROC_T				/* headers relying on proc_t are OK */
 
 #ifdef _KERNEL
@@ -629,6 +631,7 @@
 extern int sigcheck(proc_t *, kthread_t *);
 extern void sigdefault(proc_t *);
 
+extern struct pid *pid_find(pid_t pid);
 extern void pid_setmin(void);
 extern pid_t pid_allocate(proc_t *, pid_t, int);
 extern int pid_rele(struct pid *);
@@ -644,6 +647,7 @@
 extern void sprwaitlock_proc(proc_t *);
 extern void sprlock_proc(proc_t *);
 extern void sprunlock(proc_t *);
+extern void sprunprlock(proc_t *);
 extern void pid_init(void);
 extern proc_t *pid_entry(int);
 extern int pid_slot(proc_t *);
@@ -759,7 +763,7 @@
 extern	void	continuelwps(proc_t *);
 extern	int	exitlwps(int);
 extern	void	lwp_ctmpl_copy(klwp_t *, klwp_t *);
-extern	void	lwp_ctmpl_clear(klwp_t *);
+extern	void	lwp_ctmpl_clear(klwp_t *, boolean_t);
 extern	klwp_t	*forklwp(klwp_t *, proc_t *, id_t);
 extern	void	lwp_load(klwp_t *, gregset_t, uintptr_t);
 extern	void	lwp_setrval(klwp_t *, int, int);
--- old/usr/src/uts/common/sys/procfs.h	Wed Oct  5 00:32:31 2016
+++ new/usr/src/uts/common/sys/procfs.h	Wed Oct  5 00:32:31 2016
@@ -25,6 +25,7 @@
  */
 /*
  * Copyright 2012 DEY Storage Systems, Inc.  All rights reserved.
+ * Copyright 2015, Joyent, Inc.
  */
 
 #ifndef _SYS_PROCFS_H
@@ -233,6 +234,7 @@
 #define	PR_FAULTED	6
 #define	PR_SUSPENDED	7
 #define	PR_CHECKPOINT	8
+#define	PR_BRAND	9
 
 /*
  * lwp ps(1) information file.  /proc/<pid>/lwp/<lwpid>/lwpsinfo
--- old/usr/src/uts/common/sys/ptms.h	Wed Oct  5 00:32:31 2016
+++ new/usr/src/uts/common/sys/ptms.h	Wed Oct  5 00:32:31 2016
@@ -126,6 +126,12 @@
 #define	DDBGP(a, b)
 #endif
 
+typedef struct __ptmptsopencb_arg *ptmptsopencb_arg_t;
+typedef struct ptmptsopencb {
+	boolean_t		(*ppocb_func)(ptmptsopencb_arg_t);
+	ptmptsopencb_arg_t	ppocb_arg;
+} ptmptsopencb_t;
+
 #endif /* _KERNEL */
 
 typedef struct pt_own {
@@ -157,6 +163,19 @@
 #define	ZONEPT		(('P'<<8)|4)	/* set zone of master/slave pair */
 #define	OWNERPT		(('P'<<8)|5)	/* set owner/group for slave device */
 
+#ifdef _KERNEL
+/*
+ * kernel ioctl commands
+ *
+ * PTMPTSOPENCB: Returns a callback function pointer and opaque argument.
+ *	      The return value of the callback function when it's invoked
+ *	      with the opaque argument passed to it will indicate if the
+ *	      pts slave device is currently open.
+ */
+#define	PTMPTSOPENCB	(('P'<<8)|6)	/* check if the slave is open */
+
+#endif /* _KERNEL */
+
 #ifdef	__cplusplus
 }
 #endif
--- old/usr/src/uts/common/sys/resource.h	Wed Oct  5 00:32:31 2016
+++ new/usr/src/uts/common/sys/resource.h	Wed Oct  5 00:32:31 2016
@@ -23,6 +23,7 @@
  *
  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2014 Joyent, Inc.  All rights reserved.
  */
 
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
@@ -191,6 +192,7 @@
 #define	_RUSAGESYS_GETRUSAGE_CHLD	1	/* rusage child process */
 #define	_RUSAGESYS_GETRUSAGE_LWP	2	/* rusage lwp */
 #define	_RUSAGESYS_GETVMUSAGE		3	/* getvmusage */
+#define	_RUSAGESYS_INVALMAP		4	/* vm_map_inval */
 
 #if defined(_SYSCALL32)
 
--- old/usr/src/uts/common/sys/rt.h	Wed Oct  5 00:32:31 2016
+++ new/usr/src/uts/common/sys/rt.h	Wed Oct  5 00:32:31 2016
@@ -22,6 +22,7 @@
 /*
  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.
  */
 
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
@@ -31,8 +32,6 @@
 #ifndef _SYS_RT_H
 #define	_SYS_RT_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"	/* SVr4.0 1.4 */
-
 #include <sys/types.h>
 #include <sys/thread.h>
 
@@ -77,6 +76,16 @@
 	int	rt_tqsig;	/* real-time time quantum signal */
 	uint_t	rt_cflags;	/* real-time control flags */
 } rtkparms_t;
+
+#define	RTGPPRIO0	100	/* Global priority for RT priority 0 */
+
+/*
+ * control flags (kparms->rt_cflags).
+ */
+#define	RT_DOPRI	0x01	/* change priority */
+#define	RT_DOTQ		0x02	/* change RT time quantum */
+#define	RT_DOSIG	0x04	/* change RT time quantum signal */
+
 #endif	/* _KERNEL */
 
 #ifdef	__cplusplus
--- old/usr/src/uts/common/sys/shm.h	Wed Oct  5 00:32:31 2016
+++ new/usr/src/uts/common/sys/shm.h	Wed Oct  5 00:32:31 2016
@@ -21,6 +21,7 @@
  */
 /*
  * Copyright 2014 Garrett D'Amore <garrett@damore.org>
+ * Copyright 2016 Joyent, Inc.
  *
  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
@@ -120,6 +121,10 @@
 #define	SHM_LOCK	3	/* Lock segment in core */
 #define	SHM_UNLOCK	4	/* Unlock segment */
 
+#if defined(_KERNEL)
+#define	SHM_RMID	5	/* Private RMID for lx support */
+#endif
+
 #if !defined(_KERNEL)
 int shmget(key_t, size_t, int);
 int shmids(int *, uint_t, uint_t *);
--- old/usr/src/uts/common/sys/shm_impl.h	Wed Oct  5 00:32:31 2016
+++ new/usr/src/uts/common/sys/shm_impl.h	Wed Oct  5 00:32:31 2016
@@ -21,13 +21,12 @@
 /*
  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
  */
 
 #ifndef	_SYS_SHM_IMPL_H
 #define	_SYS_SHM_IMPL_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <sys/ipc_impl.h>
 #if defined(_KERNEL) || defined(_KMEMUSER)
 #include <sys/shm.h>
@@ -70,7 +69,11 @@
 	time_t		shm_ctime;	/* last change time */
 	struct sptinfo	*shm_sptinfo;	/* info about ISM segment */
 	struct seg	*shm_sptseg;	/* pointer to ISM segment */
-	long		shm_sptprot;	/* was reserved (still a "long") */
+	ulong_t		shm_opts;
+					/*
+					 * Composed of: sptprot (uchar_t) and
+					 * RM_PENDING flag (1 bit).
+					 */
 } kshmid_t;
 
 /*
@@ -78,6 +81,14 @@
  */
 #define	SHMSA_ISM	1	/* uses shared page table */
 
+/*
+ * shm_opts definitions
+ * Low byte in shm_opts is used for sptprot (see PROT_ALL). The upper bits are
+ * used for additional options.
+ */
+#define	SHM_PROT_MASK	0xff
+#define	SHM_RM_PENDING	0x100
+
 typedef struct sptinfo {
 	struct as	*sptas;		/* dummy as ptr. for spt segment */
 } sptinfo_t;
--- old/usr/src/uts/common/sys/signal.h	Wed Oct  5 00:32:31 2016
+++ new/usr/src/uts/common/sys/signal.h	Wed Oct  5 00:32:31 2016
@@ -22,6 +22,7 @@
 /*
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2015, Joyent, Inc.
  */
 
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
@@ -158,8 +159,8 @@
  * use of these symbols by applications is injurious
  *	to binary compatibility
  */
-#define	NSIG	74	/* valid signals range from 1 to NSIG-1 */
-#define	MAXSIG	73	/* size of u_signal[], NSIG-1 <= MAXSIG */
+#define	NSIG	75	/* valid signals range from 1 to NSIG-1 */
+#define	MAXSIG	74	/* size of u_signal[], NSIG-1 <= MAXSIG */
 #endif /* defined(__EXTENSIONS__) || !defined(_XPG4_2) */
 
 #define	MINSIGSTKSZ	2048
--- old/usr/src/uts/common/sys/signalfd.h	Wed Oct  5 00:32:32 2016
+++ new/usr/src/uts/common/sys/signalfd.h	Wed Oct  5 00:32:32 2016
@@ -10,7 +10,7 @@
  */
 
 /*
- * Copyright 2015 Joyent, Inc.
+ * Copyright 2016 Joyent, Inc.
  */
 
 /*
@@ -75,13 +75,9 @@
 #define	SIGNALFDMNRN_SIGNALFD	0
 #define	SIGNALFDMNRN_CLONE	1
 
-typedef struct sigfd_wake_list {
-	list_node_t sigfd_wl_lst;
-	void *sigfd_wl_state;
-} sigfd_wake_list_t;
-
 /*
  * This holds the proc_t state for a process which is using signalfd.
+ * Its presence and contents are protected by p_lock.
  */
 typedef struct sigfd_proc_state {
 	void (*sigfd_pollwake_cb)(void *, int);
--- old/usr/src/uts/common/sys/socket.h	Wed Oct  5 00:32:32 2016
+++ new/usr/src/uts/common/sys/socket.h	Wed Oct  5 00:32:32 2016
@@ -22,6 +22,7 @@
  * Copyright 2014 Garrett D'Amore <garrett@damore.org>
  *
  * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015, Joyent, Inc. All rights reserved.
  */
 
 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
@@ -39,6 +40,9 @@
 
 /* Copyright (c) 2013, OmniTI Computer Consulting, Inc. All rights reserved. */
 
+/*
+ * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ */
 #ifndef	_SYS_SOCKET_H
 #define	_SYS_SOCKET_H
 
@@ -194,6 +198,7 @@
 #define	SO_SRCADDR	0x2001		/* Internal: AF_UNIX source address */
 #define	SO_FILEP	0x2002		/* Internal: AF_UNIX file pointer */
 #define	SO_UNIX_CLOSE	0x2003		/* Internal: AF_UNIX peer closed */
+#define	SO_REUSEPORT	0x2004		/* allow simultaneous port reuse */
 #endif	/* _KERNEL */
 
 /*
@@ -293,8 +298,9 @@
 #define	AF_INET_OFFLOAD	30		/* Sun private; do not use */
 #define	AF_TRILL	31		/* TRILL interface */
 #define	AF_PACKET	32		/* PF_PACKET Linux socket interface */
+#define	AF_LX_NETLINK	33		/* Linux-compatible netlink */
 
-#define	AF_MAX		32
+#define	AF_MAX		33
 
 /*
  * Protocol families, same as address families for now.
@@ -334,6 +340,7 @@
 #define	PF_INET_OFFLOAD	AF_INET_OFFLOAD	/* Sun private; do not use */
 #define	PF_TRILL	AF_TRILL
 #define	PF_PACKET	AF_PACKET
+#define	PF_LX_NETLINK	AF_LX_NETLINK
 
 #define	PF_MAX		AF_MAX
 
@@ -420,6 +427,7 @@
 #define	MSG_NOTIFICATION 0x100		/* Notification, not data */
 #define	MSG_XPG4_2	0x8000		/* Private: XPG4.2 flag */
 
+/* Obsolete but kept for compilation compatability. Use IOV_MAX. */
 #define	MSG_MAXIOVLEN	16
 
 #ifdef _KERNEL
--- old/usr/src/uts/common/sys/socketvar.h	Wed Oct  5 00:32:32 2016
+++ new/usr/src/uts/common/sys/socketvar.h	Wed Oct  5 00:32:32 2016
@@ -297,15 +297,16 @@
 #define	SS_OOBPEND		0x00002000 /* OOB pending or present - poll */
 #define	SS_HAVEOOBDATA		0x00004000 /* OOB data present */
 #define	SS_HADOOBDATA		0x00008000 /* OOB data consumed */
-#define	SS_CLOSING		0x00010000 /* in process of closing */
 
+#define	SS_CLOSING		0x00010000 /* in process of closing */
 #define	SS_FIL_DEFER		0x00020000 /* filter deferred notification */
 #define	SS_FILOP_OK		0x00040000 /* socket can attach filters */
 #define	SS_FIL_RCV_FLOWCTRL	0x00080000 /* filter asserted rcv flow ctrl */
+
 #define	SS_FIL_SND_FLOWCTRL	0x00100000 /* filter asserted snd flow ctrl */
 #define	SS_FIL_STOP		0x00200000 /* no more filter actions */
-
 #define	SS_SODIRECT		0x00400000 /* transport supports sodirect */
+#define	SS_FILOP_UNSF		0x00800000 /* block attaching unsafe filters */
 
 #define	SS_SENTLASTREADSIG	0x01000000 /* last rx signal has been sent */
 #define	SS_SENTLASTWRITESIG	0x02000000 /* last tx signal has been sent */
@@ -321,7 +322,8 @@
 
 /*
  * Sockets that can fall back to TPI must ensure that fall back is not
- * initiated while a thread is using a socket.
+ * initiated while a thread is using a socket. Otherwise this disables all
+ * future filter attachment.
  */
 #define	SO_BLOCK_FALLBACK(so, fn)				\
 	ASSERT(MUTEX_NOT_HELD(&(so)->so_lock));			\
@@ -337,6 +339,24 @@
 		}						\
 	}
 
+/*
+ * Sockets that can fall back to TPI must ensure that fall back is not
+ * initiated while a thread is using a socket. Otherwise this disables all
+ * future unsafe filter attachment. Safe filters can still attach after
+ * we execute the function in which this macro is used.
+ */
+#define	SO_BLOCK_FALLBACK_SAFE(so, fn)				\
+	ASSERT(MUTEX_NOT_HELD(&(so)->so_lock));			\
+	rw_enter(&(so)->so_fallback_rwlock, RW_READER);		\
+	if ((so)->so_state & SS_FALLBACK_COMP) {		\
+		rw_exit(&(so)->so_fallback_rwlock);		\
+		return (fn);					\
+	} else if (((so)->so_state & SS_FILOP_UNSF) == 0) {	\
+		mutex_enter(&(so)->so_lock);			\
+		(so)->so_state |= SS_FILOP_UNSF;		\
+		mutex_exit(&(so)->so_lock);			\
+	}
+
 #define	SO_UNBLOCK_FALLBACK(so)	{			\
 	rw_exit(&(so)->so_fallback_rwlock);		\
 }
@@ -368,6 +388,7 @@
 /* The modes below are only for non-streams sockets */
 #define	SM_ACCEPTSUPP		0x400	/* can handle accept() */
 #define	SM_SENDFILESUPP		0x800	/* Private: proto supp sendfile  */
+#define	SM_DEFERERR		0x1000	/* Private: defer so_error delivery */
 
 /*
  * Socket versions. Used by the socket library when calling _so_socket().
--- old/usr/src/uts/common/sys/sockfilter.h	Wed Oct  5 00:32:32 2016
+++ new/usr/src/uts/common/sys/sockfilter.h	Wed Oct  5 00:32:32 2016
@@ -20,6 +20,7 @@
  */
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
  */
 
 #ifndef	_SYS_SOCKFILTER_H
@@ -129,6 +130,15 @@
 
 #define	SOF_VERSION	1
 
+/*
+ * Flag indicating that the filter module is safe to attach after bind,
+ * getsockname, getsockopt or setsockopt calls. By default filters are unsafe
+ * so may not be attached after any socket operation. However, a safe filter
+ * can still be attached after one of the above calls. This makes attaching
+ * the filter less dependent on the initial socket setup order.
+ */
+#define	SOF_ATT_SAFE	0x1
+
 extern int	sof_register(int, const char *, const sof_ops_t *, int);
 extern int	sof_unregister(const char *);
 
--- old/usr/src/uts/common/sys/stream.h	Wed Oct  5 00:32:32 2016
+++ new/usr/src/uts/common/sys/stream.h	Wed Oct  5 00:32:32 2016
@@ -21,6 +21,7 @@
 /*
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2015 Joyent, Inc.  All rights reserved.
  */
 
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
@@ -628,16 +629,11 @@
 /*
  * Structure for rw (read/write) procedure calls. A pointer
  * to a struiod_t is passed as a parameter to the rwnext() call.
- *
- * Note: DEF_IOV_MAX is defined and used as it is in "fs/vncalls.c"
- *	 as there isn't a formal definition of IOV_MAX ???
  */
-#define	DEF_IOV_MAX	16
-
 typedef struct struiod {
 	mblk_t		*d_mp;		/* pointer to mblk (chain) */
 	uio_t		d_uio;		/* uio info */
-	iovec_t d_iov[DEF_IOV_MAX];	/* iov referenced by uio */
+	iovec_t 	*d_iov;		/* iov referenced by uio */
 } struiod_t;
 
 /*
--- old/usr/src/uts/common/sys/systrace.h	Wed Oct  5 00:32:32 2016
+++ new/usr/src/uts/common/sys/systrace.h	Wed Oct  5 00:32:32 2016
@@ -22,13 +22,12 @@
 /*
  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2014 Joyent, Inc.  All rights reserved.
  */
 
 #ifndef _SYS_SYSTRACE_H
 #define	_SYS_SYSTRACE_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #include <sys/dtrace.h>
 
 #ifdef	__cplusplus
@@ -47,16 +46,18 @@
 extern systrace_sysent_t *systrace_sysent32;
 
 extern void (*systrace_probe)(dtrace_id_t, uintptr_t, uintptr_t,
-    uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+    uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
 extern void systrace_stub(dtrace_id_t, uintptr_t, uintptr_t,
-    uintptr_t, uintptr_t, uintptr_t, uintptr_t);
+    uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
 
 extern int64_t dtrace_systrace_syscall(uintptr_t arg0, uintptr_t arg1,
-    uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, uintptr_t arg5);
+    uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, uintptr_t arg5,
+    uintptr_t arg6, uintptr_t arg7);
 
 #ifdef _SYSCALL32_IMPL
 extern int64_t dtrace_systrace_syscall32(uintptr_t arg0, uintptr_t arg1,
-    uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, uintptr_t arg5);
+    uintptr_t arg2, uintptr_t arg3, uintptr_t arg4, uintptr_t arg5,
+    uintptr_t arg6, uintptr_t arg7);
 #endif
 
 #endif
--- old/usr/src/uts/common/sys/termios.h	Wed Oct  5 00:32:32 2016
+++ new/usr/src/uts/common/sys/termios.h	Wed Oct  5 00:32:32 2016
@@ -361,6 +361,24 @@
 #define	TCSETSF		(_TIOC|16)
 
 /*
+ * linux terminal ioctls we need to be aware of
+ */
+#define	TIOCSETLD	(_TIOC|123)	/* set line discipline parms */
+#define	TIOCGETLD	(_TIOC|124)	/* get line discipline parms */
+
+/*
+ * The VMIN and VTIME and solaris overlap with VEOF and VEOL - This is
+ * perfectly legal except, linux expects them to be separate. So we keep
+ * them separately.
+ */
+struct lx_cc {
+	unsigned char veof;	/* veof value */
+	unsigned char veol;	/* veol value */
+	unsigned char vmin;	/* vmin value */
+	unsigned char vtime;	/* vtime value */
+};
+
+/*
  * NTP PPS ioctls
  */
 #define	TIOCGPPS	(_TIOC|125)
--- old/usr/src/uts/common/sys/thread.h	Wed Oct  5 00:32:32 2016
+++ new/usr/src/uts/common/sys/thread.h	Wed Oct  5 00:32:32 2016
@@ -414,8 +414,9 @@
 #define	TS_RESUME	0x1000	/* setrun() by CPR resume process */
 #define	TS_CREATE	0x2000	/* setrun() by syslwp_create() */
 #define	TS_RUNQMATCH	0x4000	/* exact run queue balancing by setbackdq() */
+#define	TS_BSTART	0x8000	/* setrun() by brand */
 #define	TS_ALLSTART	\
-	(TS_CSTART|TS_UNPAUSE|TS_XSTART|TS_PSTART|TS_RESUME|TS_CREATE)
+	(TS_CSTART|TS_UNPAUSE|TS_XSTART|TS_PSTART|TS_RESUME|TS_CREATE|TS_BSTART)
 #define	TS_ANYWAITQ	(TS_PROJWAITQ|TS_ZONEWAITQ)
 
 /*
@@ -443,6 +444,10 @@
 #define	ISTOPPED(t) ((t)->t_state == TS_STOPPED && \
 			!((t)->t_schedflag & TS_PSTART))
 
+/* True if thread is stopped for a brand-specific reason */
+#define	BSTOPPED(t)	((t)->t_state == TS_STOPPED && \
+			    !((t)->t_schedflag & TS_BSTART))
+
 /* True if thread is asleep and wakeable */
 #define	ISWAKEABLE(t) (((t)->t_state == TS_SLEEP && \
 			((t)->t_flag & T_WAKEABLE)))
--- old/usr/src/uts/common/sys/time.h	Wed Oct  5 00:32:32 2016
+++ new/usr/src/uts/common/sys/time.h	Wed Oct  5 00:32:32 2016
@@ -15,6 +15,7 @@
  * Use is subject to license terms.
  *
  * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 
 /*
@@ -261,6 +262,14 @@
 
 #if defined(_KERNEL) || defined(_FAKE_KERNEL)
 
+/*
+ * Unsigned counterpart to hrtime_t
+ */
+typedef	u_longlong_t	uhrtime_t;
+
+#define	HRTIME_MAX	LLONG_MAX
+#define	UHRTIME_MAX	ULLONG_MAX
+
 #include <sys/time_impl.h>
 #include <sys/mutex.h>
 
--- old/usr/src/uts/common/sys/uio.h	Wed Oct  5 00:32:33 2016
+++ new/usr/src/uts/common/sys/uio.h	Wed Oct  5 00:32:33 2016
@@ -145,7 +145,8 @@
  */
 typedef enum xuio_type {
 	UIOTYPE_ASYNCIO,
-	UIOTYPE_ZEROCOPY
+	UIOTYPE_ZEROCOPY,
+	UIOTYPE_PEEKSIZE
 } xuio_type_t;
 
 typedef struct xuio {
@@ -175,6 +176,15 @@
 			int xu_zc_rw;	/* read or write buffer */
 			void *xu_zc_priv;	/* fs specific */
 		} xu_zc;
+
+		/*
+		 * Peek Size Support -- facilitate peeking at the size of a
+		 * waiting message on a socket.
+		 */
+		struct {
+			ssize_t xu_ps_size;	/* size of waiting msg */
+			boolean_t xu_ps_set;	/* was size calculated? */
+		} xu_ps;
 	} xu_ext;
 } xuio_t;
 
--- old/usr/src/uts/common/sys/user.h	Wed Oct  5 00:32:33 2016
+++ new/usr/src/uts/common/sys/user.h	Wed Oct  5 00:32:33 2016
@@ -26,7 +26,7 @@
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
 /*	  All Rights Reserved	*/
 /*
- * Copyright (c) 2012 Joyent, Inc.  All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 
 
@@ -185,9 +185,9 @@
  * This value should not be changed in a patch.
  */
 #if defined(__sparc)
-#define	__KERN_NAUXV_IMPL 20
+#define	__KERN_NAUXV_IMPL 24
 #elif defined(__i386) || defined(__amd64)
-#define	__KERN_NAUXV_IMPL 22
+#define	__KERN_NAUXV_IMPL 26
 #endif
 
 struct execsw;
@@ -211,6 +211,7 @@
 	int	u_argc;			/* value of argc passed to main() */
 	uintptr_t u_argv;		/* value of argv passed to main() */
 	uintptr_t u_envp;		/* value of envp passed to main() */
+	uintptr_t u_commpagep;		/* address of mapped comm page */
 
 	/*
 	 * These fields are protected by p_lock:
--- old/usr/src/uts/common/sys/vm_usage.h	Wed Oct  5 00:32:33 2016
+++ new/usr/src/uts/common/sys/vm_usage.h	Wed Oct  5 00:32:33 2016
@@ -21,6 +21,7 @@
 /*
  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2014 Joyent, Inc.  All rights reserved.
  */
 
 #ifndef	_SYS_VM_USAGE_H
@@ -79,8 +80,9 @@
 					/* zoneid */
 #define	VMUSAGE_COL_EUSERS	0x2000	/* same as VMUSAGE_COL_RUSERS, but by */
 					/* euser */
+#define	VMUSAGE_A_ZONE		0x4000	/* rss/swap for a specified zone */
 
-#define	VMUSAGE_MASK		0x3fff  /* all valid flags for getvmusage() */
+#define	VMUSAGE_MASK		0x7fff  /* all valid flags for getvmusage() */
 
 typedef struct vmusage {
 	id_t	vmu_zoneid;		/* zoneid, or ALL_ZONES for */
@@ -108,6 +110,7 @@
 
 int vm_getusage(uint_t, time_t, vmusage_t *, size_t *, int);
 void vm_usage_init();
+int vm_map_inval(pid_t, caddr_t, size_t);
 
 #endif	/* _KERNEL */
 
--- old/usr/src/uts/common/sys/vmsystm.h	Wed Oct  5 00:32:33 2016
+++ new/usr/src/uts/common/sys/vmsystm.h	Wed Oct  5 00:32:33 2016
@@ -19,6 +19,9 @@
  * CDDL HEADER END
  */
 /*
+ * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ */
+/*
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
@@ -159,6 +162,8 @@
 
 extern	size_t	exec_get_spslew(void);
 
+extern	caddr_t	map_userlimit(proc_t *pp, struct as *as, int flags);
+
 #endif	/* _KERNEL */
 
 #ifdef	__cplusplus
--- old/usr/src/uts/common/sys/vnode.h	Wed Oct  5 00:32:33 2016
+++ new/usr/src/uts/common/sys/vnode.h	Wed Oct  5 00:32:33 2016
@@ -21,7 +21,7 @@
 
 /*
  * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2014, Joyent, Inc. All rights reserved.
  */
 
 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
@@ -743,7 +743,9 @@
 	VE_TRUNCATE = 9,	/* Truncate */
 	VE_PRE_RENAME_SRC = 10,	/* Pre-rename, with vnode as source */
 	VE_PRE_RENAME_DEST = 11, /* Pre-rename, with vnode as target/dest. */
-	VE_PRE_RENAME_DEST_DIR = 12 /* Pre-rename with vnode as target dir */
+	VE_PRE_RENAME_DEST_DIR = 12, /* Pre-rename with vnode as target dir */
+	VE_RENAME_SRC_DIR = 13,	/* Rename with vnode as source dir */
+	VE_RESIZE	= 14	/* Resize/truncate to non-zero offset */
 } vnevent_t;
 
 /*
@@ -1298,7 +1300,8 @@
 void	vnevent_rmdir(vnode_t *, vnode_t *, char *, caller_context_t *);
 void	vnevent_create(vnode_t *, caller_context_t *);
 void	vnevent_link(vnode_t *, caller_context_t *);
-void	vnevent_rename_dest_dir(vnode_t *, caller_context_t *ct);
+void	vnevent_rename_dest_dir(vnode_t *, vnode_t *, char *,
+    caller_context_t *ct);
 void	vnevent_mountedover(vnode_t *, caller_context_t *);
 void	vnevent_truncate(vnode_t *, caller_context_t *);
 int	vnevent_support(vnode_t *, caller_context_t *);
@@ -1308,6 +1311,7 @@
 	    caller_context_t *);
 void	vnevent_pre_rename_dest_dir(vnode_t *, vnode_t *, char *,
 	    caller_context_t *);
+void	vnevent_resize(vnode_t *, caller_context_t *);
 
 /* Vnode specific data */
 void vsd_create(uint_t *, void (*)(void *));
--- /dev/null	Wed Oct  5 00:32:33 2016
+++ new/usr/src/uts/common/sys/zfd.h	Wed Oct  5 00:32:33 2016
@@ -0,0 +1,78 @@
+/*
+ * 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 2015 Joyent, Inc.
+ */
+
+#ifndef _SYS_ZFD_H
+#define	_SYS_ZFD_H
+
+#include <sys/types.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/*
+ * Minor node name of the global zone side (often called the "master" side)
+ * of the zfd dev.
+ */
+#define	ZFD_MASTER_NAME	"master"
+
+/*
+ * Minor node name of the non-global zone side (often called the "slave"
+ * side) of the zfd dev.
+ */
+#define	ZFD_SLAVE_NAME	"slave"
+
+#define	ZFD_NAME_LEN	16
+
+/*
+ * ZFD_IOC forms the base for all zfd ioctls.
+ */
+#define	ZFD_IOC		(('Z' << 24) | ('f' << 16) | ('d' << 8))
+
+/*
+ * This ioctl tells the slave side it should push the TTY stream modules
+ * so that the fd looks like a tty.
+ */
+#define	ZFD_MAKETTY		(ZFD_IOC | 0)
+
+/*
+ * This ioctl puts a hangup into the stream so that the slave side sees EOF.
+ */
+#define	ZFD_EOF			(ZFD_IOC | 1)
+
+/*
+ * This ioctl succeeds if the slave side is open.
+ */
+#define	ZFD_HAS_SLAVE		(ZFD_IOC | 2)
+
+/*
+ * This ioctl links two streams into a multiplexer configuration for in-zone
+ * logging.
+ */
+#define	ZFD_MUX			(ZFD_IOC | 3)
+
+/*
+ * This ioctl controls the flow control setting for the log multiplexer stream
+ * (1 = true, 0 = false). The default is false which implies teeing into the
+ * log stream is "best-effort" but data will be discarded if the stream
+ * becomes full. If set and the log stream begins to fill up, the primary
+ * stream will stop flowing.
+ */
+#define	ZFD_MUX_FLOWCON		(ZFD_IOC | 4)
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_ZFD_H */
--- old/usr/src/uts/common/sys/zone.h	Wed Oct  5 00:32:33 2016
+++ new/usr/src/uts/common/sys/zone.h	Wed Oct  5 00:32:33 2016
@@ -23,6 +23,7 @@
  * Copyright 2015 Joyent, Inc. All rights reserved.
  * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
  * Copyright 2014 Igor Kozhukhov <ikozhukhov@gmail.com>.
+ * Copyright 2016, Joyent, Inc.
  */
 
 #ifndef _SYS_ZONE_H
@@ -97,13 +98,19 @@
 #define	ZONE_ATTR_INITNAME	9
 #define	ZONE_ATTR_BOOTARGS	10
 #define	ZONE_ATTR_BRAND		11
-#define	ZONE_ATTR_PHYS_MCAP	12
+#define	ZONE_ATTR_PMCAP_NOVER	12
 #define	ZONE_ATTR_SCHED_CLASS	13
 #define	ZONE_ATTR_FLAGS		14
 #define	ZONE_ATTR_HOSTID	15
 #define	ZONE_ATTR_FS_ALLOWED	16
 #define	ZONE_ATTR_NETWORK	17
+#define	ZONE_ATTR_DID		18
+#define	ZONE_ATTR_PMCAP_PAGEOUT	19
 #define	ZONE_ATTR_INITNORESTART	20
+#define	ZONE_ATTR_PG_FLT_DELAY	21
+#define	ZONE_ATTR_RSS		22
+#define	ZONE_ATTR_APP_SVC_CT	23
+#define	ZONE_ATTR_SCHED_FIXEDHI	24
 
 /* Start of the brand-specific attribute namespace */
 #define	ZONE_ATTR_BRAND_ATTRS	32768
@@ -244,9 +251,12 @@
 typedef struct zone_cmd_arg {
 	uint64_t	uniqid;		/* unique "generation number" */
 	zone_cmd_t	cmd;		/* requested action */
-	uint32_t	_pad;		/* need consistent 32/64 bit alignmt */
+	int		status;		/* init status on shutdown */
+	uint32_t	debug;		/* enable brand hook debug */
 	char locale[MAXPATHLEN];	/* locale in which to render messages */
 	char bootbuf[BOOTARGS_MAX];	/* arguments passed to zone_boot() */
+	/* Needed for 32/64 zoneadm -> zoneadmd door arg size check. */
+	int		pad;
 } zone_cmd_arg_t;
 
 /*
@@ -389,12 +399,51 @@
 } sys_zio_cntr_t;
 
 typedef struct {
+	kstat_named_t	zv_zonename;
+	kstat_named_t	zv_nread;
+	kstat_named_t	zv_reads;
+	kstat_named_t	zv_rtime;
+	kstat_named_t	zv_rlentime;
+	kstat_named_t	zv_rcnt;
+	kstat_named_t	zv_nwritten;
+	kstat_named_t	zv_writes;
+	kstat_named_t	zv_wtime;
+	kstat_named_t	zv_wlentime;
+	kstat_named_t	zv_wcnt;
+	kstat_named_t	zv_10ms_ops;
+	kstat_named_t	zv_100ms_ops;
+	kstat_named_t	zv_1s_ops;
+	kstat_named_t	zv_10s_ops;
+	kstat_named_t 	zv_delay_cnt;
+	kstat_named_t	zv_delay_time;
+} zone_vfs_kstat_t;
+
+typedef struct {
+	kstat_named_t	zz_zonename;
+	kstat_named_t	zz_nread;
+	kstat_named_t	zz_reads;
+	kstat_named_t	zz_rtime;
+	kstat_named_t	zz_rlentime;
+	kstat_named_t	zz_nwritten;
+	kstat_named_t	zz_writes;
+	kstat_named_t	zz_waittime;
+} zone_zfs_kstat_t;
+
+typedef struct {
 	kstat_named_t	zm_zonename;
+	kstat_named_t	zm_rss;
+	kstat_named_t	zm_phys_cap;
+	kstat_named_t	zm_swap;
+	kstat_named_t	zm_swap_cap;
+	kstat_named_t	zm_nover;
+	kstat_named_t	zm_pagedout;
 	kstat_named_t	zm_pgpgin;
 	kstat_named_t	zm_anonpgin;
 	kstat_named_t	zm_execpgin;
 	kstat_named_t	zm_fspgin;
 	kstat_named_t	zm_anon_alloc_fail;
+	kstat_named_t	zm_pf_throttle;
+	kstat_named_t	zm_pf_throttle_usec;
 } zone_mcap_kstat_t;
 
 typedef struct {
@@ -505,10 +554,11 @@
 	kcondvar_t	zone_cv;	/* used to signal state changes */
 	struct proc	*zone_zsched;	/* Dummy kernel "zsched" process */
 	pid_t		zone_proc_initpid; /* pid of "init" for this zone */
-	char		*zone_initname;	/* fs path to 'init' */
+	char		*zone_initname;		/* fs path to 'init' */
+	int		zone_init_status;	/* init's exit status */
 	int		zone_boot_err;  /* for zone_boot() if boot fails */
 	char		*zone_bootargs;	/* arguments passed via zone_boot() */
-	uint64_t	zone_phys_mcap;	/* physical memory cap */
+	rctl_qty_t	zone_phys_mem_ctl;	/* current phys. memory limit */
 	/*
 	 * zone_kthreads is protected by zone_status_lock.
 	 */
@@ -546,9 +596,12 @@
 	tsol_mlp_list_t zone_mlps;	/* MLPs on zone-private addresses */
 
 	boolean_t	zone_restart_init;	/* Restart init if it dies? */
+	boolean_t	zone_reboot_on_init_exit; /* Reboot if init dies? */
+	boolean_t	zone_setup_app_contract; /* setup contract? */
 	struct brand	*zone_brand;		/* zone's brand */
 	void 		*zone_brand_data;	/* store brand specific data */
 	id_t		zone_defaultcid;	/* dflt scheduling class id */
+	boolean_t	zone_fixed_hipri;	/* fixed sched. hi prio */
 	kstat_t		*zone_swapresv_kstat;
 	kstat_t		*zone_lockedmem_kstat;
 	/*
@@ -573,6 +626,28 @@
 	sys_zio_cntr_t	zone_lwr_ops;		/* logical writes. */
 
 	/*
+	 * kstats and counters for I/O ops and bytes.
+	 */
+	kmutex_t	zone_io_lock;		/* protects I/O statistics */
+	kstat_t		*zone_io_ksp;
+	kstat_io_t	*zone_io_kiop;
+
+	/*
+	 * kstats and counters for VFS ops and bytes.
+	 */
+	kmutex_t	zone_vfs_lock;		/* protects VFS statistics */
+	kstat_t		*zone_vfs_ksp;
+	kstat_io_t	zone_vfs_rwstats;
+	zone_vfs_kstat_t *zone_vfs_stats;
+
+	/*
+	 * kstats for ZFS I/O ops and bytes.
+	 */
+	kmutex_t	zone_zfs_lock;		/* protects ZFS statistics */
+	kstat_io_t	zone_zfs_rwstats;
+	zone_zfs_kstat_t *zone_zfs_stats;
+
+	/*
 	 * Solaris Auditing per-zone audit context
 	 */
 	struct au_kcontext	*zone_audit_kctxt;
@@ -590,6 +665,13 @@
 						/* zone_rctls->rcs_lock */
 	kstat_t		*zone_nprocs_kstat;
 
+	/*
+	 * kstats and counters for physical memory capping.
+	 */
+	rctl_qty_t	zone_phys_mem;	/* current bytes of phys. mem. (RSS) */
+	kstat_t		*zone_physmem_kstat;
+	uint64_t	zone_mcap_nover;	/* # of times over phys. cap */
+	uint64_t	zone_mcap_pagedout;	/* bytes of mem. paged out */
 	kmutex_t	zone_mcap_lock;	/* protects mcap statistics */
 	kstat_t		*zone_mcap_ksp;
 	zone_mcap_kstat_t *zone_mcap_stats;
@@ -598,7 +680,12 @@
 	uint64_t	zone_execpgin;		/* exec pages paged in */
 	uint64_t	zone_fspgin;		/* fs pages paged in */
 	uint64_t	zone_anon_alloc_fail;	/* cnt of anon alloc fails */
+	uint64_t	zone_pf_throttle;	/* cnt of page flt throttles */
+	uint64_t	zone_pf_throttle_usec;	/* time of page flt throttles */
 
+	/* Num usecs to throttle page fault when zone is over phys. mem cap */
+	uint32_t	zone_pg_flt_delay;
+
 	/*
 	 * Misc. kstats and counters for zone cpu-usage aggregation.
 	 * The zone_Xtime values are the sum of the micro-state accounting
@@ -859,6 +946,7 @@
  * Returns true if the named pool/dataset is visible in the current zone.
  */
 extern int zone_dataset_visible(const char *, int *);
+extern int zone_dataset_visible_inzone(zone_t *, const char *, int *);
 
 /*
  * zone version of kadmin()
@@ -873,6 +961,7 @@
 
 extern rctl_hndl_t rc_zone_locked_mem;
 extern rctl_hndl_t rc_zone_max_swap;
+extern rctl_hndl_t rc_zone_phys_mem;
 extern rctl_hndl_t rc_zone_max_lofi;
 
 #endif	/* _KERNEL */
--- old/usr/src/uts/common/syscall/brandsys.c	Wed Oct  5 00:32:33 2016
+++ new/usr/src/uts/common/syscall/brandsys.c	Wed Oct  5 00:32:33 2016
@@ -23,7 +23,9 @@
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
+/*
+ * Copyright 2016 Joyent, Inc.
+ */
 
 #include <sys/brand.h>
 #include <sys/systm.h>
@@ -35,7 +37,7 @@
  */
 int64_t
 brandsys(int cmd, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3,
-    uintptr_t arg4, uintptr_t arg5, uintptr_t arg6)
+    uintptr_t arg4)
 {
 	struct proc *p = curthread->t_procp;
 	int64_t rval = 0;
@@ -49,7 +51,7 @@
 		return (set_errno(ENOSYS));
 
 	if ((err = ZBROP(p->p_zone)->b_brandsys(cmd, &rval, arg1, arg2, arg3,
-	    arg4, arg5, arg6)) != 0)
+	    arg4)) != 0)
 		return (set_errno(err));
 
 	return (rval);
--- old/usr/src/uts/common/syscall/fcntl.c	Wed Oct  5 00:32:33 2016
+++ new/usr/src/uts/common/syscall/fcntl.c	Wed Oct  5 00:32:34 2016
@@ -54,7 +54,8 @@
 
 #include <sys/cmn_err.h>
 
-static int flock_check(vnode_t *, flock64_t *, offset_t, offset_t);
+/* This is global so that it can be used by brand emulation. */
+int flock_check(vnode_t *, flock64_t *, offset_t, offset_t);
 static int flock_get_start(vnode_t *, flock64_t *, offset_t, u_offset_t *);
 static void fd_too_big(proc_t *);
 
@@ -365,7 +366,8 @@
 			}
 		}
 
-		if (cmd == F_OFD_SETLK || cmd == F_OFD_SETLKW) {
+		if (cmd == F_OFD_GETLK || cmd == F_OFD_SETLK ||
+		    cmd == F_OFD_SETLKW) {
 			/*
 			 * TBD OFD-style locking is currently limited to
 			 * covering the entire file.
--- old/usr/src/uts/common/syscall/memcntl.c	Wed Oct  5 00:32:34 2016
+++ new/usr/src/uts/common/syscall/memcntl.c	Wed Oct  5 00:32:34 2016
@@ -115,13 +115,17 @@
 		 * MS_SYNC used to be defined to be zero but is now non-zero.
 		 * For binary compatibility we still accept zero
 		 * (the absence of MS_ASYNC) to mean the same thing.
+		 * Binary compatibility is not an issue for MS_INVALCURPROC.
 		 */
 		iarg = (uintptr_t)arg;
 		if ((iarg & ~MS_INVALIDATE) == 0)
 			iarg |= MS_SYNC;
 
-		if (((iarg & ~(MS_SYNC|MS_ASYNC|MS_INVALIDATE)) != 0) ||
-		    ((iarg & (MS_SYNC|MS_ASYNC)) == (MS_SYNC|MS_ASYNC))) {
+		if (((iarg &
+		    ~(MS_SYNC|MS_ASYNC|MS_INVALIDATE|MS_INVALCURPROC)) != 0) ||
+		    ((iarg & (MS_SYNC|MS_ASYNC)) == (MS_SYNC|MS_ASYNC)) ||
+		    ((iarg & (MS_INVALIDATE|MS_INVALCURPROC)) ==
+		    (MS_INVALIDATE|MS_INVALCURPROC))) {
 			error = set_errno(EINVAL);
 		} else {
 			error = as_ctl(as, addr, len, cmd, attr, iarg, NULL, 0);
--- old/usr/src/uts/common/syscall/open.c	Wed Oct  5 00:32:34 2016
+++ new/usr/src/uts/common/syscall/open.c	Wed Oct  5 00:32:34 2016
@@ -74,12 +74,12 @@
 
 	if (filemode & (FSEARCH|FEXEC)) {
 		/*
-		 * Must be one or the other and neither FREAD nor FWRITE
+		 * Must be one or the other.
 		 * Must not be any of FAPPEND FCREAT FTRUNC FXATTR FXATTRDIROPEN
-		 * XXX: Should these just be silently ignored?
+		 * XXX: Should these just be silently ignored like we
+		 * silently ignore FREAD|FWRITE?
 		 */
-		if ((filemode & (FREAD|FWRITE)) ||
-		    (filemode & (FSEARCH|FEXEC)) == (FSEARCH|FEXEC) ||
+		if ((filemode & (FSEARCH|FEXEC)) == (FSEARCH|FEXEC) ||
 		    (filemode & (FAPPEND|FCREAT|FTRUNC|FXATTR|FXATTRDIROPEN)))
 			return (set_errno(EINVAL));
 	}
--- old/usr/src/uts/common/syscall/poll.c	Wed Oct  5 00:32:34 2016
+++ new/usr/src/uts/common/syscall/poll.c	Wed Oct  5 00:32:34 2016
@@ -29,7 +29,7 @@
 
 /*
  * Copyright (c) 2012 by Delphix. All rights reserved.
- * Copyright 2015, Joyent, Inc.
+ * Copyright 2016, Joyent, Inc.
  */
 
 /*
@@ -317,20 +317,57 @@
 	return (0);
 }
 
-static int
-poll_common(pollfd_t *fds, nfds_t nfds, timespec_t *tsp, k_sigset_t *ksetp)
+int
+poll_copyin(pollstate_t *ps, pollfd_t *fds, nfds_t nfds)
 {
+	pollfd_t *pollfdp;
+	nfds_t old_nfds;
+
+	/*
+	 * NOTE: for performance, buffers are saved across poll() calls.
+	 * The theory is that if a process polls heavily, it tends to poll
+	 * on the same set of descriptors.  Therefore, we only reallocate
+	 * buffers when nfds changes.  There is no hysteresis control,
+	 * because there is no data to suggest that this is necessary;
+	 * the penalty of reallocating is not *that* great in any event.
+	 */
+	old_nfds = ps->ps_nfds;
+	if (nfds != old_nfds) {
+		kmem_free(ps->ps_pollfd, old_nfds * sizeof (pollfd_t));
+		pollfdp = kmem_alloc(nfds * sizeof (pollfd_t), KM_SLEEP);
+		ps->ps_pollfd = pollfdp;
+		ps->ps_nfds = nfds;
+	}
+
+	pollfdp = ps->ps_pollfd;
+	if (copyin(fds, pollfdp, nfds * sizeof (pollfd_t))) {
+		return (EFAULT);
+	}
+
+	if (fds == NULL) {
+		/*
+		 * If the process has page 0 mapped, then the copyin() above
+		 * will succeed even if fds is NULL.  However, our cached
+		 * poll lists are keyed by the address of the passed-in fds
+		 * structure, and we use the value NULL to indicate an unused
+		 * poll cache list entry.  As such, we elect not to support
+		 * NULL as a valid (user) memory address and fail the poll()
+		 * call.
+		 */
+		return (EFAULT);
+	}
+	return (0);
+}
+
+int
+poll_common(pollstate_t *ps, pollfd_t *fds, nfds_t nfds, timespec_t *tsp,
+    int *fdcnt)
+{
 	kthread_t *t = curthread;
-	klwp_t *lwp = ttolwp(t);
-	proc_t *p = ttoproc(t);
-	int fdcnt = 0;
-	int i;
 	hrtime_t deadline; /* hrtime value when we want to return */
 	pollfd_t *pollfdp;
-	pollstate_t *ps;
 	pollcache_t *pcp;
 	int error = 0;
-	nfds_t old_nfds;
 	int cacheindex = 0;	/* which cache set is used */
 
 	/*
@@ -340,40 +377,42 @@
 		deadline = -1;
 	} else if (tsp->tv_sec == 0 && tsp->tv_nsec == 0) {
 		deadline = 0;
+	} else if (tsp->tv_sec >= HRTIME_MAX/NANOSEC) {
+		/* Use an indefinite timeout if tv_sec would cause overflow */
+		deadline = -1;
 	} else {
+		/*
+		 * The above check, when combined with the protections offered
+		 * by itimerspecfix (ensuring that neither field is negative
+		 * and that tv_nsec represents less than a whole second), will
+		 * prevent overflow during the conversion from timespec_t to
+		 * uhrtime_t.
+		 */
+		uhrtime_t utime = tsp->tv_sec * NANOSEC;
+		utime += tsp->tv_nsec;
+
 		/* They must wait at least a tick. */
-		deadline = ((hrtime_t)tsp->tv_sec * NANOSEC) + tsp->tv_nsec;
-		deadline = MAX(deadline, nsec_per_tick);
-		deadline += gethrtime();
-	}
+		utime = MAX(utime, nsec_per_tick);
 
-	/*
-	 * Reset our signal mask, if requested.
-	 */
-	if (ksetp != NULL) {
-		mutex_enter(&p->p_lock);
-		schedctl_finish_sigblock(t);
-		lwp->lwp_sigoldmask = t->t_hold;
-		t->t_hold = *ksetp;
-		t->t_flag |= T_TOMASK;
 		/*
-		 * Call cv_reltimedwait_sig() just to check for signals.
-		 * We will return immediately with either 0 or -1.
+		 * Since utime has an upper bound of HRTIME_MAX, adding the
+		 * gethrtime() result cannot incur an overflow as the unsigned
+		 * type has an adequate bound.
 		 */
-		if (!cv_reltimedwait_sig(&t->t_delay_cv, &p->p_lock, 0,
-		    TR_CLOCK_TICK)) {
-			mutex_exit(&p->p_lock);
-			error = EINTR;
-			goto pollout;
+		utime += (uhrtime_t)gethrtime();
+		if (utime > HRTIME_MAX) {
+			deadline = -1;
+		} else {
+			deadline = (hrtime_t)utime;
 		}
-		mutex_exit(&p->p_lock);
 	}
 
 	/*
-	 * Check to see if this guy just wants to use poll() as a timeout.
+	 * Check to see if the caller just wants to use poll() as a timeout.
 	 * If yes then bypass all the other stuff and make him sleep.
 	 */
 	if (nfds == 0) {
+		*fdcnt = 0;
 		/*
 		 * Sleep until we have passed the requested future
 		 * time or until interrupted by a signal.
@@ -385,67 +424,15 @@
 			    &t->t_delay_lock, deadline)) > 0)
 				continue;
 			mutex_exit(&t->t_delay_lock);
-			error = (error == 0) ? EINTR : 0;
+			return ((error == 0) ? EINTR : 0);
 		}
-		goto pollout;
+		return (0);
 	}
 
-	if (nfds > p->p_fno_ctl) {
-		mutex_enter(&p->p_lock);
-		(void) rctl_action(rctlproc_legacy[RLIMIT_NOFILE],
-		    p->p_rctls, p, RCA_SAFE);
-		mutex_exit(&p->p_lock);
-		error = EINVAL;
-		goto pollout;
-	}
-
-	/*
-	 * Need to allocate memory for pollstate before anything because
-	 * the mutex and cv are created in this space
-	 */
-	ps = pollstate_create();
-
-	if (ps->ps_pcache == NULL)
-		ps->ps_pcache = pcache_alloc();
-	pcp = ps->ps_pcache;
-
-	/*
-	 * NOTE: for performance, buffers are saved across poll() calls.
-	 * The theory is that if a process polls heavily, it tends to poll
-	 * on the same set of descriptors.  Therefore, we only reallocate
-	 * buffers when nfds changes.  There is no hysteresis control,
-	 * because there is no data to suggest that this is necessary;
-	 * the penalty of reallocating is not *that* great in any event.
-	 */
-	old_nfds = ps->ps_nfds;
-	if (nfds != old_nfds) {
-
-		kmem_free(ps->ps_pollfd, old_nfds * sizeof (pollfd_t));
-		pollfdp = kmem_alloc(nfds * sizeof (pollfd_t), KM_SLEEP);
-		ps->ps_pollfd = pollfdp;
-		ps->ps_nfds = nfds;
-	}
-
+	VERIFY(ps != NULL);
 	pollfdp = ps->ps_pollfd;
-	if (copyin(fds, pollfdp, nfds * sizeof (pollfd_t))) {
-		error = EFAULT;
-		goto pollout;
-	}
+	VERIFY(pollfdp != NULL);
 
-	if (fds == NULL) {
-		/*
-		 * If the process has page 0 mapped, then the copyin() above
-		 * will succeed even if fds is NULL.  However, our cached
-		 * poll lists are keyed by the address of the passed-in fds
-		 * structure, and we use the value NULL to indicate an unused
-		 * poll cache list entry.  As such, we elect not to support
-		 * NULL as a valid (user) memory address and fail the poll()
-		 * call.
-		 */
-		error = EINVAL;
-		goto pollout;
-	}
-
 	/*
 	 * If this thread polls for the first time, allocate ALL poll
 	 * cache data structures and cache the poll fd list. This
@@ -460,10 +447,10 @@
 		/*
 		 * poll and cache this poll fd list in ps_pcacheset[0].
 		 */
-		error = pcacheset_cache_list(ps, fds, &fdcnt, cacheindex);
-		if (fdcnt || error) {
+		error = pcacheset_cache_list(ps, fds, fdcnt, cacheindex);
+		if (error || *fdcnt) {
 			mutex_exit(&ps->ps_lock);
-			goto pollout;
+			return (error);
 		}
 	} else {
 		pollcacheset_t	*pcset = ps->ps_pcacheset;
@@ -488,11 +475,11 @@
 				 * the callee will guarantee the consistency
 				 * of cached poll list and cache content.
 				 */
-				error = pcacheset_resolve(ps, nfds, &fdcnt,
+				error = pcacheset_resolve(ps, nfds, fdcnt,
 				    cacheindex);
 				if (error) {
 					mutex_exit(&ps->ps_lock);
-					goto pollout;
+					return (error);
 				}
 				break;
 			}
@@ -509,11 +496,11 @@
 				 * found an unused entry. Use it to cache
 				 * this poll list.
 				 */
-				error = pcacheset_cache_list(ps, fds, &fdcnt,
+				error = pcacheset_cache_list(ps, fds, fdcnt,
 				    cacheindex);
-				if (fdcnt || error) {
+				if (error || *fdcnt) {
 					mutex_exit(&ps->ps_lock);
-					goto pollout;
+					return (error);
 				}
 				break;
 			}
@@ -527,10 +514,10 @@
 			cacheindex = pcacheset_replace(ps);
 			ASSERT(cacheindex < ps->ps_nsets);
 			pcset[cacheindex].pcs_usradr = (uintptr_t)fds;
-			error = pcacheset_resolve(ps, nfds, &fdcnt, cacheindex);
+			error = pcacheset_resolve(ps, nfds, fdcnt, cacheindex);
 			if (error) {
 				mutex_exit(&ps->ps_lock);
-				goto pollout;
+				return (error);
 			}
 		}
 	}
@@ -548,8 +535,8 @@
 	mutex_enter(&pcp->pc_lock);
 	for (;;) {
 		pcp->pc_flag = 0;
-		error = pcache_poll(pollfdp, ps, nfds, &fdcnt, cacheindex);
-		if (fdcnt || error) {
+		error = pcache_poll(pollfdp, ps, nfds, fdcnt, cacheindex);
+		if (error || *fdcnt) {
 			mutex_exit(&pcp->pc_lock);
 			mutex_exit(&ps->ps_lock);
 			break;
@@ -595,13 +582,116 @@
 		mutex_enter(&pcp->pc_lock);
 	}
 
+	return (error);
+}
+
+/*
+ * This is the system call trap that poll(),
+ * select() and pselect() are built upon.
+ * It is a private interface between libc and the kernel.
+ */
+int
+pollsys(pollfd_t *fds, nfds_t nfds, timespec_t *timeoutp, sigset_t *setp)
+{
+	kthread_t *t = curthread;
+	klwp_t *lwp = ttolwp(t);
+	proc_t *p = ttoproc(t);
+	timespec_t ts;
+	timespec_t *tsp;
+	k_sigset_t kset;
+	pollstate_t *ps = NULL;
+	pollfd_t *pollfdp = NULL;
+	int error = 0, fdcnt = 0;
+
+	/*
+	 * Copy in timeout
+	 */
+	if (timeoutp == NULL) {
+		tsp = NULL;
+	} else {
+		if (get_udatamodel() == DATAMODEL_NATIVE) {
+			if (copyin(timeoutp, &ts, sizeof (ts)))
+				return (set_errno(EFAULT));
+		} else {
+			timespec32_t ts32;
+
+			if (copyin(timeoutp, &ts32, sizeof (ts32)))
+				return (set_errno(EFAULT));
+			TIMESPEC32_TO_TIMESPEC(&ts, &ts32)
+		}
+
+		if (itimerspecfix(&ts))
+			return (set_errno(EINVAL));
+		tsp = &ts;
+	}
+
+	/*
+	 * Copy in and reset signal mask, if requested.
+	 */
+	if (setp != NULL) {
+		sigset_t set;
+
+		if (copyin(setp, &set, sizeof (set)))
+			return (set_errno(EFAULT));
+		sigutok(&set, &kset);
+
+		mutex_enter(&p->p_lock);
+		schedctl_finish_sigblock(t);
+		lwp->lwp_sigoldmask = t->t_hold;
+		t->t_hold = kset;
+		t->t_flag |= T_TOMASK;
+		/*
+		 * Call cv_reltimedwait_sig() just to check for signals.
+		 * We will return immediately with either 0 or -1.
+		 */
+		if (!cv_reltimedwait_sig(&t->t_delay_cv, &p->p_lock, 0,
+		    TR_CLOCK_TICK)) {
+			mutex_exit(&p->p_lock);
+			error = EINTR;
+			goto pollout;
+		}
+		mutex_exit(&p->p_lock);
+	}
+
+	/*
+	 * Initialize pollstate and copy in pollfd data if present.
+	 * If nfds == 0, we will skip all of the copying and check steps and
+	 * proceed directly into poll_common to process the supplied timeout.
+	 */
+	if (nfds != 0) {
+		if (nfds > p->p_fno_ctl) {
+			mutex_enter(&p->p_lock);
+			(void) rctl_action(rctlproc_legacy[RLIMIT_NOFILE],
+			    p->p_rctls, p, RCA_SAFE);
+			mutex_exit(&p->p_lock);
+			error = EINVAL;
+			goto pollout;
+		}
+
+		/*
+		 * Need to allocate memory for pollstate before anything
+		 * because the mutex and cv are created in this space
+		 */
+		ps = pollstate_create();
+		if (ps->ps_pcache == NULL)
+			ps->ps_pcache = pcache_alloc();
+
+		if ((error = poll_copyin(ps, fds, nfds)) != 0)
+			goto pollout;
+		pollfdp = ps->ps_pollfd;
+	}
+
+	/*
+	 * Perform the actual poll.
+	 */
+	error = poll_common(ps, fds, nfds, tsp, &fdcnt);
+
 pollout:
 	/*
-	 * If we changed the signal mask but we received
-	 * no signal then restore the signal mask.
-	 * Otherwise psig() will deal with the signal mask.
+	 * If we changed the signal mask but we received no signal then restore
+	 * the signal mask.  Otherwise psig() will deal with the signal mask.
 	 */
-	if (ksetp != NULL) {
+	if (setp != NULL) {
 		mutex_enter(&p->p_lock);
 		if (lwp->lwp_cursig == 0) {
 			t->t_hold = lwp->lwp_sigoldmask;
@@ -612,12 +702,10 @@
 
 	if (error)
 		return (set_errno(error));
-
 	/*
 	 * Copy out the events and return the fdcnt to the user.
 	 */
-	if (nfds != 0 &&
-	    copyout(pollfdp, fds, nfds * sizeof (pollfd_t)))
+	if (nfds != 0 && copyout(pollfdp, fds, nfds * sizeof (pollfd_t)))
 		return (set_errno(EFAULT));
 
 #ifdef DEBUG
@@ -625,7 +713,7 @@
 	 * Another sanity check:
 	 */
 	if (fdcnt) {
-		int	reventcnt = 0;
+		int i, reventcnt = 0;
 
 		for (i = 0; i < nfds; i++) {
 			if (pollfdp[i].fd < 0) {
@@ -638,6 +726,8 @@
 		}
 		ASSERT(fdcnt == reventcnt);
 	} else {
+		int i;
+
 		for (i = 0; i < nfds; i++) {
 			ASSERT(pollfdp[i].revents == 0);
 		}
@@ -647,52 +737,6 @@
 	return (fdcnt);
 }
 
-/*
- * This is the system call trap that poll(),
- * select() and pselect() are built upon.
- * It is a private interface between libc and the kernel.
- */
-int
-pollsys(pollfd_t *fds, nfds_t nfds, timespec_t *timeoutp, sigset_t *setp)
-{
-	timespec_t ts;
-	timespec_t *tsp;
-	sigset_t set;
-	k_sigset_t kset;
-	k_sigset_t *ksetp;
-	model_t datamodel = get_udatamodel();
-
-	if (timeoutp == NULL)
-		tsp = NULL;
-	else {
-		if (datamodel == DATAMODEL_NATIVE) {
-			if (copyin(timeoutp, &ts, sizeof (ts)))
-				return (set_errno(EFAULT));
-		} else {
-			timespec32_t ts32;
-
-			if (copyin(timeoutp, &ts32, sizeof (ts32)))
-				return (set_errno(EFAULT));
-			TIMESPEC32_TO_TIMESPEC(&ts, &ts32)
-		}
-
-		if (itimerspecfix(&ts))
-			return (set_errno(EINVAL));
-		tsp = &ts;
-	}
-
-	if (setp == NULL)
-		ksetp = NULL;
-	else {
-		if (copyin(setp, &set, sizeof (set)))
-			return (set_errno(EFAULT));
-		sigutok(&set, &kset);
-		ksetp = &kset;
-	}
-
-	return (poll_common(fds, nfds, tsp, ksetp));
-}
-
 /*
  * Clean up any state left around by poll(2). Called when a thread exits.
  */
--- old/usr/src/uts/common/syscall/rusagesys.c	Wed Oct  5 00:32:34 2016
+++ new/usr/src/uts/common/syscall/rusagesys.c	Wed Oct  5 00:32:34 2016
@@ -21,6 +21,7 @@
 /*
  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2014 Joyent, Inc.  All rights reserved.
  */
 
 /*
@@ -257,6 +258,19 @@
 	case _RUSAGESYS_GETVMUSAGE:
 		return (vm_getusage((uint_t)(uintptr_t)arg1, (time_t)arg2,
 		    (vmusage_t *)arg3, (size_t *)arg4, 0));
+	case _RUSAGESYS_INVALMAP:
+		/*
+		 * SPARC sfmmu hat does not support HAT_CURPROC_PGUNLOAD
+		 * handling so callers on SPARC should get simple sync
+		 * handling with invalidation to all processes.
+		 */
+#if defined(__sparc)
+		return (memcntl((caddr_t)arg2, (size_t)arg3, MC_SYNC,
+		    (caddr_t)(MS_ASYNC | MS_INVALIDATE), 0, 0));
+#else
+		return (vm_map_inval((pid_t)(uintptr_t)arg1, (caddr_t)arg2,
+		    (size_t)arg3));
+#endif
 	default:
 		return (set_errno(EINVAL));
 	}
--- old/usr/src/uts/common/syscall/rw.c	Wed Oct  5 00:32:34 2016
+++ new/usr/src/uts/common/syscall/rw.c	Wed Oct  5 00:32:34 2016
@@ -22,7 +22,7 @@
 /*
  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
- * Copyright (c) 2015, Joyent, Inc.  All rights reserved.
+ * Copyright 2015, Joyent, Inc.  All rights reserved.
  */
 
 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
@@ -50,6 +50,7 @@
 #include <sys/debug.h>
 #include <sys/rctl.h>
 #include <sys/nbmlock.h>
+#include <sys/limits.h>
 
 #define	COPYOUT_MAX_CACHE	(1<<17)		/* 128K */
 
@@ -607,19 +608,12 @@
 	return (bcount);
 }
 
-/*
- * XXX -- The SVID refers to IOV_MAX, but doesn't define it.  Grrrr....
- * XXX -- However, SVVS expects readv() and writev() to fail if
- * XXX -- iovcnt > 16 (yes, it's hard-coded in the SVVS source),
- * XXX -- so I guess that's the "interface".
- */
-#define	DEF_IOV_MAX	16
-
 ssize_t
 readv(int fdes, struct iovec *iovp, int iovcnt)
 {
 	struct uio auio;
-	struct iovec aiov[DEF_IOV_MAX];
+	struct iovec buf[IOV_MAX_STACK], *aiov = buf;
+	int aiovlen = 0;
 	file_t *fp;
 	register vnode_t *vp;
 	struct cpu *cp;
@@ -630,9 +624,14 @@
 	u_offset_t fileoff;
 	int in_crit = 0;
 
-	if (iovcnt <= 0 || iovcnt > DEF_IOV_MAX)
+	if (iovcnt <= 0 || iovcnt > IOV_MAX)
 		return (set_errno(EINVAL));
 
+	if (iovcnt > IOV_MAX_STACK) {
+		aiovlen = iovcnt * sizeof (iovec_t);
+		aiov = kmem_alloc(aiovlen, KM_SLEEP);
+	}
+
 #ifdef _SYSCALL32_IMPL
 	/*
 	 * 32-bit callers need to have their iovec expanded,
@@ -640,36 +639,63 @@
 	 * of data in a single call.
 	 */
 	if (get_udatamodel() == DATAMODEL_ILP32) {
-		struct iovec32 aiov32[DEF_IOV_MAX];
+		struct iovec32 buf32[IOV_MAX_STACK], *aiov32 = buf32;
+		int aiov32len;
 		ssize32_t count32;
 
-		if (copyin(iovp, aiov32, iovcnt * sizeof (struct iovec32)))
+		aiov32len = iovcnt * sizeof (iovec32_t);
+		if (aiovlen != 0)
+			aiov32 = kmem_alloc(aiov32len, KM_SLEEP);
+
+		if (copyin(iovp, aiov32, aiov32len)) {
+			if (aiovlen != 0) {
+				kmem_free(aiov32, aiov32len);
+				kmem_free(aiov, aiovlen);
+			}
 			return (set_errno(EFAULT));
+		}
 
 		count32 = 0;
 		for (i = 0; i < iovcnt; i++) {
 			ssize32_t iovlen32 = aiov32[i].iov_len;
 			count32 += iovlen32;
-			if (iovlen32 < 0 || count32 < 0)
+			if (iovlen32 < 0 || count32 < 0) {
+				if (aiovlen != 0) {
+					kmem_free(aiov32, aiov32len);
+					kmem_free(aiov, aiovlen);
+				}
 				return (set_errno(EINVAL));
+			}
 			aiov[i].iov_len = iovlen32;
 			aiov[i].iov_base =
 			    (caddr_t)(uintptr_t)aiov32[i].iov_base;
 		}
+
+		if (aiovlen != 0)
+			kmem_free(aiov32, aiov32len);
 	} else
 #endif
-	if (copyin(iovp, aiov, iovcnt * sizeof (struct iovec)))
+	if (copyin(iovp, aiov, iovcnt * sizeof (iovec_t))) {
+		if (aiovlen != 0)
+			kmem_free(aiov, aiovlen);
 		return (set_errno(EFAULT));
+	}
 
 	count = 0;
 	for (i = 0; i < iovcnt; i++) {
 		ssize_t iovlen = aiov[i].iov_len;
 		count += iovlen;
-		if (iovlen < 0 || count < 0)
+		if (iovlen < 0 || count < 0) {
+			if (aiovlen != 0)
+				kmem_free(aiov, aiovlen);
 			return (set_errno(EINVAL));
+		}
 	}
-	if ((fp = getf(fdes)) == NULL)
+	if ((fp = getf(fdes)) == NULL) {
+		if (aiovlen != 0)
+			kmem_free(aiov, aiovlen);
 		return (set_errno(EBADF));
+	}
 	if (((fflag = fp->f_flag) & FREAD) == 0) {
 		error = EBADF;
 		goto out;
@@ -768,6 +794,8 @@
 	if (in_crit)
 		nbl_end_crit(vp);
 	releasef(fdes);
+	if (aiovlen != 0)
+		kmem_free(aiov, aiovlen);
 	if (error)
 		return (set_errno(error));
 	return (count);
@@ -777,7 +805,8 @@
 writev(int fdes, struct iovec *iovp, int iovcnt)
 {
 	struct uio auio;
-	struct iovec aiov[DEF_IOV_MAX];
+	struct iovec buf[IOV_MAX_STACK], *aiov = buf;
+	int aiovlen = 0;
 	file_t *fp;
 	register vnode_t *vp;
 	struct cpu *cp;
@@ -788,9 +817,14 @@
 	u_offset_t fileoff;
 	int in_crit = 0;
 
-	if (iovcnt <= 0 || iovcnt > DEF_IOV_MAX)
+	if (iovcnt <= 0 || iovcnt > IOV_MAX)
 		return (set_errno(EINVAL));
 
+	if (iovcnt > IOV_MAX_STACK) {
+		aiovlen = iovcnt * sizeof (iovec_t);
+		aiov = kmem_alloc(aiovlen, KM_SLEEP);
+	}
+
 #ifdef _SYSCALL32_IMPL
 	/*
 	 * 32-bit callers need to have their iovec expanded,
@@ -798,36 +832,62 @@
 	 * of data in a single call.
 	 */
 	if (get_udatamodel() == DATAMODEL_ILP32) {
-		struct iovec32 aiov32[DEF_IOV_MAX];
+		struct iovec32 buf32[IOV_MAX_STACK], *aiov32 = buf32;
+		int aiov32len;
 		ssize32_t count32;
 
-		if (copyin(iovp, aiov32, iovcnt * sizeof (struct iovec32)))
+		aiov32len = iovcnt * sizeof (iovec32_t);
+		if (aiovlen != 0)
+			aiov32 = kmem_alloc(aiov32len, KM_SLEEP);
+
+		if (copyin(iovp, aiov32, aiov32len)) {
+			if (aiovlen != 0) {
+				kmem_free(aiov32, aiov32len);
+				kmem_free(aiov, aiovlen);
+			}
 			return (set_errno(EFAULT));
+		}
 
 		count32 = 0;
 		for (i = 0; i < iovcnt; i++) {
 			ssize32_t iovlen = aiov32[i].iov_len;
 			count32 += iovlen;
-			if (iovlen < 0 || count32 < 0)
+			if (iovlen < 0 || count32 < 0) {
+				if (aiovlen != 0) {
+					kmem_free(aiov32, aiov32len);
+					kmem_free(aiov, aiovlen);
+				}
 				return (set_errno(EINVAL));
+			}
 			aiov[i].iov_len = iovlen;
 			aiov[i].iov_base =
 			    (caddr_t)(uintptr_t)aiov32[i].iov_base;
 		}
+		if (aiovlen != 0)
+			kmem_free(aiov32, aiov32len);
 	} else
 #endif
-	if (copyin(iovp, aiov, iovcnt * sizeof (struct iovec)))
+	if (copyin(iovp, aiov, iovcnt * sizeof (iovec_t))) {
+		if (aiovlen != 0)
+			kmem_free(aiov, aiovlen);
 		return (set_errno(EFAULT));
+	}
 
 	count = 0;
 	for (i = 0; i < iovcnt; i++) {
 		ssize_t iovlen = aiov[i].iov_len;
 		count += iovlen;
-		if (iovlen < 0 || count < 0)
+		if (iovlen < 0 || count < 0) {
+			if (aiovlen != 0)
+				kmem_free(aiov, aiovlen);
 			return (set_errno(EINVAL));
+		}
 	}
-	if ((fp = getf(fdes)) == NULL)
+	if ((fp = getf(fdes)) == NULL) {
+		if (aiovlen != 0)
+			kmem_free(aiov, aiovlen);
 		return (set_errno(EBADF));
+	}
 	if (((fflag = fp->f_flag) & FWRITE) == 0) {
 		error = EBADF;
 		goto out;
@@ -917,6 +977,8 @@
 	if (in_crit)
 		nbl_end_crit(vp);
 	releasef(fdes);
+	if (aiovlen != 0)
+		kmem_free(aiov, aiovlen);
 	if (error)
 		return (set_errno(error));
 	return (count);
@@ -927,7 +989,8 @@
     off_t extended_offset)
 {
 	struct uio auio;
-	struct iovec aiov[DEF_IOV_MAX];
+	struct iovec buf[IOV_MAX_STACK], *aiov = buf;
+	int aiovlen = 0;
 	file_t *fp;
 	register vnode_t *vp;
 	struct cpu *cp;
@@ -952,9 +1015,14 @@
 
 	int in_crit = 0;
 
-	if (iovcnt <= 0 || iovcnt > DEF_IOV_MAX)
+	if (iovcnt <= 0 || iovcnt > IOV_MAX)
 		return (set_errno(EINVAL));
 
+	if (iovcnt > IOV_MAX_STACK) {
+		aiovlen = iovcnt * sizeof (iovec_t);
+		aiov = kmem_alloc(aiovlen, KM_SLEEP);
+	}
+
 #ifdef _SYSCALL32_IMPL
 	/*
 	 * 32-bit callers need to have their iovec expanded,
@@ -962,39 +1030,68 @@
 	 * of data in a single call.
 	 */
 	if (get_udatamodel() == DATAMODEL_ILP32) {
-		struct iovec32 aiov32[DEF_IOV_MAX];
+		struct iovec32 buf32[IOV_MAX_STACK], *aiov32 = buf32;
+		int aiov32len;
 		ssize32_t count32;
 
-		if (copyin(iovp, aiov32, iovcnt * sizeof (struct iovec32)))
+		aiov32len = iovcnt * sizeof (iovec32_t);
+		if (aiovlen != 0)
+			aiov32 = kmem_alloc(aiov32len, KM_SLEEP);
+
+		if (copyin(iovp, aiov32, aiov32len)) {
+			if (aiovlen != 0) {
+				kmem_free(aiov32, aiov32len);
+				kmem_free(aiov, aiovlen);
+			}
 			return (set_errno(EFAULT));
+		}
 
 		count32 = 0;
 		for (i = 0; i < iovcnt; i++) {
 			ssize32_t iovlen32 = aiov32[i].iov_len;
 			count32 += iovlen32;
-			if (iovlen32 < 0 || count32 < 0)
+			if (iovlen32 < 0 || count32 < 0) {
+				if (aiovlen != 0) {
+					kmem_free(aiov32, aiov32len);
+					kmem_free(aiov, aiovlen);
+				}
 				return (set_errno(EINVAL));
+			}
 			aiov[i].iov_len = iovlen32;
 			aiov[i].iov_base =
 			    (caddr_t)(uintptr_t)aiov32[i].iov_base;
 		}
+		if (aiovlen != 0)
+			kmem_free(aiov32, aiov32len);
 	} else
 #endif /* _SYSCALL32_IMPL */
-		if (copyin(iovp, aiov, iovcnt * sizeof (struct iovec)))
+		if (copyin(iovp, aiov, iovcnt * sizeof (iovec_t))) {
+			if (aiovlen != 0)
+				kmem_free(aiov, aiovlen);
 			return (set_errno(EFAULT));
+		}
 
 	count = 0;
 	for (i = 0; i < iovcnt; i++) {
 		ssize_t iovlen = aiov[i].iov_len;
 		count += iovlen;
-		if (iovlen < 0 || count < 0)
+		if (iovlen < 0 || count < 0) {
+			if (aiovlen != 0)
+				kmem_free(aiov, aiovlen);
 			return (set_errno(EINVAL));
+		}
 	}
 
-	if ((bcount = (ssize_t)count) < 0)
+	if ((bcount = (ssize_t)count) < 0) {
+		if (aiovlen != 0)
+			kmem_free(aiov, aiovlen);
 		return (set_errno(EINVAL));
-	if ((fp = getf(fdes)) == NULL)
+	}
+	if ((fp = getf(fdes)) == NULL) {
+		if (aiovlen != 0)
+			kmem_free(aiov, aiovlen);
 		return (set_errno(EBADF));
+	}
 	if (((fflag = fp->f_flag) & FREAD) == 0) {
 		error = EBADF;
 		goto out;
@@ -1099,6 +1196,8 @@
 	if (in_crit)
 		nbl_end_crit(vp);
 	releasef(fdes);
+	if (aiovlen != 0)
+		kmem_free(aiov, aiovlen);
 	if (error)
 		return (set_errno(error));
 	return (count);
@@ -1109,7 +1208,8 @@
     off_t extended_offset)
 {
 	struct uio auio;
-	struct iovec aiov[DEF_IOV_MAX];
+	struct iovec buf[IOV_MAX_STACK], *aiov = buf;
+	int aiovlen = 0;
 	file_t *fp;
 	register vnode_t *vp;
 	struct cpu *cp;
@@ -1134,9 +1234,14 @@
 
 	int in_crit = 0;
 
-	if (iovcnt <= 0 || iovcnt > DEF_IOV_MAX)
+	if (iovcnt <= 0 || iovcnt > IOV_MAX)
 		return (set_errno(EINVAL));
 
+	if (iovcnt > IOV_MAX_STACK) {
+		aiovlen = iovcnt * sizeof (iovec_t);
+		aiov = kmem_alloc(aiovlen, KM_SLEEP);
+	}
+
 #ifdef _SYSCALL32_IMPL
 	/*
 	 * 32-bit callers need to have their iovec expanded,
@@ -1144,39 +1249,68 @@
 	 * of data in a single call.
 	 */
 	if (get_udatamodel() == DATAMODEL_ILP32) {
-		struct iovec32 aiov32[DEF_IOV_MAX];
+		struct iovec32 buf32[IOV_MAX_STACK], *aiov32 = buf32;
+		int aiov32len;
 		ssize32_t count32;
 
-		if (copyin(iovp, aiov32, iovcnt * sizeof (struct iovec32)))
+		aiov32len = iovcnt * sizeof (iovec32_t);
+		if (aiovlen != 0)
+			aiov32 = kmem_alloc(aiov32len, KM_SLEEP);
+
+		if (copyin(iovp, aiov32, aiov32len)) {
+			if (aiovlen != 0) {
+				kmem_free(aiov32, aiov32len);
+				kmem_free(aiov, aiovlen);
+			}
 			return (set_errno(EFAULT));
+		}
 
 		count32 = 0;
 		for (i = 0; i < iovcnt; i++) {
 			ssize32_t iovlen32 = aiov32[i].iov_len;
 			count32 += iovlen32;
-			if (iovlen32 < 0 || count32 < 0)
+			if (iovlen32 < 0 || count32 < 0) {
+				if (aiovlen != 0) {
+					kmem_free(aiov32, aiov32len);
+					kmem_free(aiov, aiovlen);
+				}
 				return (set_errno(EINVAL));
+			}
 			aiov[i].iov_len = iovlen32;
 			aiov[i].iov_base =
 			    (caddr_t)(uintptr_t)aiov32[i].iov_base;
 		}
+		if (aiovlen != 0)
+			kmem_free(aiov32, aiov32len);
 	} else
 #endif /* _SYSCALL32_IMPL */
-		if (copyin(iovp, aiov, iovcnt * sizeof (struct iovec)))
+		if (copyin(iovp, aiov, iovcnt * sizeof (iovec_t))) {
+			if (aiovlen != 0)
+				kmem_free(aiov, aiovlen);
 			return (set_errno(EFAULT));
+		}
 
 	count = 0;
 	for (i = 0; i < iovcnt; i++) {
 		ssize_t iovlen = aiov[i].iov_len;
 		count += iovlen;
-		if (iovlen < 0 || count < 0)
+		if (iovlen < 0 || count < 0) {
+			if (aiovlen != 0)
+				kmem_free(aiov, aiovlen);
 			return (set_errno(EINVAL));
+		}
 	}
 
-	if ((bcount = (ssize_t)count) < 0)
+	if ((bcount = (ssize_t)count) < 0) {
+		if (aiovlen != 0)
+			kmem_free(aiov, aiovlen);
 		return (set_errno(EINVAL));
-	if ((fp = getf(fdes)) == NULL)
+	}
+	if ((fp = getf(fdes)) == NULL) {
+		if (aiovlen != 0)
+			kmem_free(aiov, aiovlen);
 		return (set_errno(EBADF));
+	}
 	if (((fflag = fp->f_flag) & FWRITE) == 0) {
 		error = EBADF;
 		goto out;
@@ -1308,6 +1442,8 @@
 	if (in_crit)
 		nbl_end_crit(vp);
 	releasef(fdes);
+	if (aiovlen != 0)
+		kmem_free(aiov, aiovlen);
 	if (error)
 		return (set_errno(error));
 	return (count);
--- old/usr/src/uts/common/syscall/sendfile.c	Wed Oct  5 00:32:34 2016
+++ new/usr/src/uts/common/syscall/sendfile.c	Wed Oct  5 00:32:34 2016
@@ -82,7 +82,7 @@
  * 64 bit kernel or 32 bit kernel. For 32 bit apps, we can't transfer
  * more than 2GB of data.
  */
-int
+static int
 sendvec_chunk64(file_t *fp, u_offset_t *fileoff, struct ksendfilevec64 *sfv,
     int copy_cnt, ssize32_t *count)
 {
@@ -343,7 +343,7 @@
 	return (0);
 }
 
-ssize32_t
+static ssize32_t
 sendvec64(file_t *fp, const struct ksendfilevec64 *vec, int sfvcnt,
 	size32_t *xferred, int fildes)
 {
@@ -390,7 +390,7 @@
 }
 #endif
 
-int
+static int
 sendvec_small_chunk(file_t *fp, u_offset_t *fileoff, struct sendfilevec *sfv,
     int copy_cnt, ssize_t total_size, int maxblk, ssize_t *count)
 {
@@ -680,7 +680,7 @@
 }
 
 
-int
+static int
 sendvec_chunk(file_t *fp, u_offset_t *fileoff, struct sendfilevec *sfv,
     int copy_cnt, ssize_t *count)
 {
@@ -1160,6 +1160,17 @@
 		} else {
 			maxblk = (int)vp->v_stream->sd_maxblk;
 		}
+
+		/*
+		 * We need to make sure that the socket that we're sending on
+		 * supports sendfile behavior. sockfs doesn't know that the APIs
+		 * we want to use are coming from sendfile, so we can't rely on
+		 * it to check for us.
+		 */
+		if ((so->so_mode & SM_SENDFILESUPP) == 0) {
+			error = EOPNOTSUPP;
+			goto err;
+		}
 		break;
 	case VREG:
 		break;
--- old/usr/src/uts/common/syscall/stat.c	Wed Oct  5 00:32:35 2016
+++ new/usr/src/uts/common/syscall/stat.c	Wed Oct  5 00:32:35 2016
@@ -61,7 +61,7 @@
  * to VOP_GETATTR
  */
 
-static int
+int
 cstatat_getvp(int fd, char *name, int follow, vnode_t **vp, cred_t **cred)
 {
 	vnode_t *startvp;
--- old/usr/src/uts/common/syscall/sysconfig.c	Wed Oct  5 00:32:35 2016
+++ new/usr/src/uts/common/syscall/sysconfig.c	Wed Oct  5 00:32:35 2016
@@ -22,6 +22,7 @@
 /*
  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2011 Joyent, Inc.  All rights reserved.
  */
 
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
@@ -170,8 +171,8 @@
 		 * even though rcapd can be used on the global zone too.
 		 */
 		if (!INGLOBALZONE(curproc) &&
-		    curproc->p_zone->zone_phys_mcap != 0)
-			return (MIN(btop(curproc->p_zone->zone_phys_mcap),
+		    curproc->p_zone->zone_phys_mem_ctl != UINT64_MAX)
+			return (MIN(btop(curproc->p_zone->zone_phys_mem_ctl),
 			    physinstalled));
 
 		return (physinstalled);
@@ -184,12 +185,12 @@
 		 * though rcapd can be used on the global zone too.
 		 */
 		if (!INGLOBALZONE(curproc) &&
-		    curproc->p_zone->zone_phys_mcap != 0) {
+		    curproc->p_zone->zone_phys_mem_ctl != UINT64_MAX) {
 			pgcnt_t cap, rss, free;
 			vmusage_t in_use;
 			size_t cnt = 1;
 
-			cap = btop(curproc->p_zone->zone_phys_mcap);
+			cap = btop(curproc->p_zone->zone_phys_mem_ctl);
 			if (cap > physinstalled)
 				return (freemem);
 
--- old/usr/src/uts/common/vm/hat.h	Wed Oct  5 00:32:35 2016
+++ new/usr/src/uts/common/vm/hat.h	Wed Oct  5 00:32:35 2016
@@ -21,6 +21,7 @@
 /*
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2014 Joyent, Inc.  All rights reserved.
  */
 
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
@@ -269,8 +270,13 @@
  *	call.
  *
  * int hat_pageunload(pp, forceflag)
- *	unload all translations attached to pp.
+ *	Unload all translations attached to pp. On x86 the bulk of the work is
+ *	done by hat_page_inval.
  *
+ * void	hat_page_inval(pp, pgsz, curhat)
+ *	Unload translations attached to pp. If curhat is provided, only the
+ *	translation for that process is unloaded, otherwise all are unloaded.
+ *
  * uint_t hat_pagesync(pp, flags)
  *	get hw stats from hardware into page struct and reset hw stats
  *	returns attributes of page
@@ -291,6 +297,7 @@
 void	hat_page_clrattr(struct page *, uint_t);
 uint_t	hat_page_getattr(struct page *, uint_t);
 int	hat_pageunload(struct page *, uint_t);
+void	hat_page_inval(struct page *, uint_t, struct hat *);
 uint_t	hat_pagesync(struct page *, uint_t);
 ulong_t	hat_page_getshare(struct page *);
 int	hat_page_checkshare(struct page *, ulong_t);
@@ -460,6 +467,7 @@
  */
 #define	HAT_ADV_PGUNLOAD	0x00
 #define	HAT_FORCE_PGUNLOAD	0x01
+#define	HAT_CURPROC_PGUNLOAD	0x02
 
 /*
  * Attributes for hat_page_*attr, hat_setstats and
--- old/usr/src/uts/common/vm/seg_kmem.c	Wed Oct  5 00:32:35 2016
+++ new/usr/src/uts/common/vm/seg_kmem.c	Wed Oct  5 00:32:35 2016
@@ -20,6 +20,7 @@
  */
 /*
  * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 
 #include <sys/types.h>
@@ -773,7 +774,7 @@
 	return (0);
 }
 
-static struct seg_ops segkmem_ops = {
+struct seg_ops segkmem_ops = {
 	SEGKMEM_BADOP(int),		/* dup */
 	SEGKMEM_BADOP(int),		/* unmap */
 	SEGKMEM_BADOP(void),		/* free */
--- old/usr/src/uts/common/vm/seg_kmem.h	Wed Oct  5 00:32:35 2016
+++ new/usr/src/uts/common/vm/seg_kmem.h	Wed Oct  5 00:32:35 2016
@@ -21,6 +21,7 @@
 /*
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
  */
 
 #ifndef _VM_SEG_KMEM_H
@@ -136,6 +137,8 @@
 #define	IS_KMEM_VA_LARGEPAGE(vaddr)				        \
 	(((vaddr) >= heap_lp_base) && ((vaddr) < heap_lp_end))
 
+extern struct seg_ops segkmem_ops;
+
 #endif	/* _KERNEL */
 
 #ifdef	__cplusplus
--- /dev/null	Wed Oct  5 00:32:35 2016
+++ new/usr/src/uts/common/vm/seg_umap.c	Wed Oct  5 00:32:35 2016
@@ -0,0 +1,453 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+/*
+ * VM - Kernel-to-user mapping segment
+ *
+ * The umap segment driver was primarily designed to facilitate the comm page:
+ * a portion of kernel memory shared with userspace so that certain (namely
+ * clock-related) actions could operate without making an expensive trip into
+ * the kernel.
+ *
+ * Since the initial requirements for the comm page are slim, advanced features
+ * of the segment driver such as per-page protection have been left
+ * unimplemented at this time.
+ */
+
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/cred.h>
+#include <sys/kmem.h>
+#include <sys/lgrp.h>
+#include <sys/mman.h>
+
+#include <vm/hat.h>
+#include <vm/as.h>
+#include <vm/seg.h>
+#include <vm/seg_kmem.h>
+#include <vm/seg_umap.h>
+
+
+static boolean_t segumap_verify_safe(caddr_t, size_t);
+static int segumap_dup(struct seg *, struct seg *);
+static int segumap_unmap(struct seg *, caddr_t, size_t);
+static void segumap_free(struct seg *);
+static faultcode_t segumap_fault(struct hat *, struct seg *, caddr_t, size_t,
+    enum fault_type, enum seg_rw);
+static faultcode_t segumap_faulta(struct seg *, caddr_t);
+static int segumap_setprot(struct seg *, caddr_t, size_t, uint_t);
+static int segumap_checkprot(struct seg *, caddr_t, size_t, uint_t);
+static int segumap_sync(struct seg *, caddr_t, size_t, int, uint_t);
+static size_t segumap_incore(struct seg *, caddr_t, size_t, char *);
+static int segumap_lockop(struct seg *, caddr_t, size_t, int, int, ulong_t *,
+    size_t);
+static int segumap_getprot(struct seg *, caddr_t, size_t, uint_t *);
+static u_offset_t segumap_getoffset(struct seg *, caddr_t);
+static int segumap_gettype(struct seg *, caddr_t);
+static int segumap_getvp(struct seg *, caddr_t, struct vnode **);
+static int segumap_advise(struct seg *, caddr_t, size_t, uint_t);
+static void segumap_dump(struct seg *);
+static int segumap_pagelock(struct seg *, caddr_t, size_t, struct page ***,
+    enum lock_type, enum seg_rw);
+static int segumap_setpagesize(struct seg *, caddr_t, size_t, uint_t);
+static int segumap_getmemid(struct seg *, caddr_t, memid_t *);
+static int segumap_capable(struct seg *, segcapability_t);
+
+static struct seg_ops segumap_ops = {
+	segumap_dup,
+	segumap_unmap,
+	segumap_free,
+	segumap_fault,
+	segumap_faulta,
+	segumap_setprot,
+	segumap_checkprot,
+	NULL,			/* kluster: disabled */
+	NULL,			/* swapout: disabled */
+	segumap_sync,
+	segumap_incore,
+	segumap_lockop,
+	segumap_getprot,
+	segumap_getoffset,
+	segumap_gettype,
+	segumap_getvp,
+	segumap_advise,
+	segumap_dump,
+	segumap_pagelock,
+	segumap_setpagesize,
+	segumap_getmemid,
+	NULL,			/* getpolicy: disabled */
+	segumap_capable,
+	seg_inherit_notsup
+};
+
+
+/*
+ * Create a kernel/user-mapped segment.
+ */
+int
+segumap_create(struct seg *seg, void *argsp)
+{
+	segumap_crargs_t *a = (struct segumap_crargs *)argsp;
+	segumap_data_t *data;
+
+	ASSERT((uintptr_t)a->kaddr > _userlimit);
+
+	/*
+	 * Check several aspects of the mapping request to ensure validity:
+	 * - kernel pages must reside entirely in kernel space
+	 * - target protection must be user-accessible
+	 * - kernel address must be page-aligned
+	 * - kernel address must reside inside a "safe" segment
+	 */
+	if ((uintptr_t)a->kaddr <= _userlimit ||
+	    ((uintptr_t)a->kaddr + seg->s_size) < (uintptr_t)a->kaddr ||
+	    (a->prot & PROT_USER) == 0 ||
+	    ((uintptr_t)a->kaddr & PAGEOFFSET) != 0 ||
+	    !segumap_verify_safe(a->kaddr, seg->s_size)) {
+		return (EINVAL);
+	}
+
+	data = kmem_zalloc(sizeof (*data), KM_SLEEP);
+	rw_init(&data->sud_lock, NULL, RW_DEFAULT, NULL);
+	data->sud_kaddr = a->kaddr;
+	data->sud_prot = a->prot;
+
+	seg->s_ops = &segumap_ops;
+	seg->s_data = data;
+	return (0);
+}
+
+static boolean_t
+segumap_verify_safe(caddr_t kaddr, size_t len)
+{
+	struct seg *seg;
+
+	/*
+	 * Presently, only pages which are backed by segkmem are allowed to be
+	 * shared with userspace.  This prevents nasty paging behavior with
+	 * other drivers such as seg_kp.  Furthermore, the backing kernel
+	 * segment must completely contain the region to be mapped.
+	 *
+	 * Failing these checks is fatal for now since such mappings are done
+	 * in a very limited context from the kernel.
+	 */
+	AS_LOCK_ENTER(&kas, RW_READER);
+	seg = as_segat(&kas, kaddr);
+	VERIFY(seg != NULL);
+	VERIFY(seg->s_base + seg->s_size >= kaddr + len);
+	VERIFY(seg->s_ops == &segkmem_ops);
+	AS_LOCK_EXIT(&kas);
+
+	return (B_TRUE);
+}
+
+static int
+segumap_dup(struct seg *seg, struct seg *newseg)
+{
+	segumap_data_t *sud = (segumap_data_t *)seg->s_data;
+	segumap_data_t *newsud;
+
+	ASSERT(seg->s_as && AS_WRITE_HELD(seg->s_as));
+
+	newsud = kmem_zalloc(sizeof (segumap_data_t), KM_SLEEP);
+	rw_init(&newsud->sud_lock, NULL, RW_DEFAULT, NULL);
+	newsud->sud_kaddr = sud->sud_kaddr;
+	newsud->sud_prot = sud->sud_prot;
+
+	newseg->s_ops = seg->s_ops;
+	newseg->s_data = newsud;
+	return (0);
+}
+
+static int
+segumap_unmap(struct seg *seg, caddr_t addr, size_t len)
+{
+	segumap_data_t *sud = (segumap_data_t *)seg->s_data;
+
+	ASSERT(seg->s_as && AS_WRITE_HELD(seg->s_as));
+
+	/* Only allow unmap of entire segment */
+	if (addr != seg->s_base || len != seg->s_size) {
+		return (EINVAL);
+	}
+	if (sud->sud_softlockcnt != 0) {
+		return (EAGAIN);
+	}
+
+	/*
+	 * Unconditionally unload the entire segment range.
+	 */
+	hat_unload(seg->s_as->a_hat, addr, len, HAT_UNLOAD_UNMAP);
+
+	seg_free(seg);
+	return (0);
+}
+
+static void
+segumap_free(struct seg *seg)
+{
+	segumap_data_t *data = (segumap_data_t *)seg->s_data;
+
+	ASSERT(data != NULL);
+
+	rw_destroy(&data->sud_lock);
+	VERIFY(data->sud_softlockcnt == 0);
+	kmem_free(data, sizeof (*data));
+	seg->s_data = NULL;
+}
+
+/* ARGSUSED */
+static faultcode_t
+segumap_fault(struct hat *hat, struct seg *seg, caddr_t addr, size_t len,
+    enum fault_type type, enum seg_rw tw)
+{
+	segumap_data_t *sud = (segumap_data_t *)seg->s_data;
+
+	ASSERT(seg->s_as && AS_LOCK_HELD(seg->s_as));
+
+	if (type == F_PROT) {
+		/*
+		 * Since protection on the segment is fixed, there is nothing
+		 * to do but report an error for protection faults.
+		 */
+		return (FC_PROT);
+	} else if (type == F_SOFTUNLOCK) {
+		size_t plen = btop(len);
+
+		rw_enter(&sud->sud_lock, RW_WRITER);
+		VERIFY(sud->sud_softlockcnt >= plen);
+		sud->sud_softlockcnt -= plen;
+		rw_exit(&sud->sud_lock);
+		return (0);
+	}
+
+	ASSERT(type == F_INVAL || type == F_SOFTLOCK);
+	rw_enter(&sud->sud_lock, RW_WRITER);
+
+	if (type == F_INVAL ||
+	    (type == F_SOFTLOCK && sud->sud_softlockcnt == 0)) {
+		/*
+		 * Load the (entire) segment into the HAT.
+		 *
+		 * It's possible that threads racing into as_fault will cause
+		 * seg_umap to load the same range multiple times in quick
+		 * succession.  Redundant hat_devload operations are safe.
+		 */
+		for (uintptr_t i = 0; i < seg->s_size; i += PAGESIZE) {
+			pfn_t pfn;
+
+			pfn = hat_getpfnum(kas.a_hat, sud->sud_kaddr + i);
+			VERIFY(pfn != PFN_INVALID);
+			hat_devload(seg->s_as->a_hat, seg->s_base + i,
+			    PAGESIZE, pfn, sud->sud_prot, HAT_LOAD);
+		}
+	}
+	if (type == F_SOFTLOCK) {
+		size_t nval = sud->sud_softlockcnt + btop(len);
+
+		if (sud->sud_softlockcnt >= nval) {
+			rw_exit(&sud->sud_lock);
+			return (FC_MAKE_ERR(EOVERFLOW));
+		}
+		sud->sud_softlockcnt = nval;
+	}
+
+	rw_exit(&sud->sud_lock);
+	return (0);
+}
+
+/* ARGSUSED */
+static faultcode_t
+segumap_faulta(struct seg *seg, caddr_t addr)
+{
+	/* Do nothing since asynch pagefault should not load translation. */
+	return (0);
+}
+
+/* ARGSUSED */
+static int
+segumap_setprot(struct seg *seg, caddr_t addr, size_t len, uint_t prot)
+{
+	ASSERT(seg->s_as && AS_LOCK_HELD(seg->s_as));
+
+	/*
+	 * The seg_umap driver does not yet allow protection to be changed.
+	 */
+	return (EACCES);
+}
+
+/* ARGSUSED */
+static int
+segumap_checkprot(struct seg *seg, caddr_t addr, size_t len, uint_t prot)
+{
+	segumap_data_t *sud = (segumap_data_t *)seg->s_data;
+	int error = 0;
+
+	ASSERT(seg->s_as && AS_LOCK_HELD(seg->s_as));
+
+	rw_enter(&sud->sud_lock, RW_READER);
+	if ((sud->sud_prot & prot) != prot) {
+		error = EACCES;
+	}
+	rw_exit(&sud->sud_lock);
+	return (error);
+}
+
+/* ARGSUSED */
+static int
+segumap_sync(struct seg *seg, caddr_t addr, size_t len, int attr, uint_t flags)
+{
+	/* Always succeed since there are no backing store to sync */
+	return (0);
+}
+
+/* ARGSUSED */
+static size_t
+segumap_incore(struct seg *seg, caddr_t addr, size_t len, char *vec)
+{
+	size_t sz = 0;
+
+	ASSERT(seg->s_as && AS_LOCK_HELD(seg->s_as));
+
+	len = (len + PAGEOFFSET) & PAGEMASK;
+	while (len > 0) {
+		*vec = 1;
+		sz += PAGESIZE;
+		vec++;
+		len -= PAGESIZE;
+	}
+	return (sz);
+}
+
+/* ARGSUSED */
+static int
+segumap_lockop(struct seg *seg, caddr_t addr, size_t len, int attr, int op,
+    ulong_t *lockmap, size_t pos)
+{
+	/* Report success since kernel pages are always in memory. */
+	return (0);
+}
+
+static int
+segumap_getprot(struct seg *seg, caddr_t addr, size_t len, uint_t *protv)
+{
+	segumap_data_t *sud = (segumap_data_t *)seg->s_data;
+	size_t pgno;
+	uint_t prot;
+
+	ASSERT(seg->s_as && AS_LOCK_HELD(seg->s_as));
+
+	rw_enter(&sud->sud_lock, RW_READER);
+	prot = sud->sud_prot;
+	rw_exit(&sud->sud_lock);
+
+	/*
+	 * Reporting protection is simple since it is not tracked per-page.
+	 */
+	pgno = seg_page(seg, addr + len) - seg_page(seg, addr) + 1;
+	while (pgno > 0) {
+		protv[--pgno] = prot;
+	}
+	return (0);
+}
+
+/* ARGSUSED */
+static u_offset_t
+segumap_getoffset(struct seg *seg, caddr_t addr)
+{
+	/*
+	 * To avoid leaking information about the layout of the kernel address
+	 * space, always report '0' as the offset.
+	 */
+	return (0);
+}
+
+/* ARGSUSED */
+static int
+segumap_gettype(struct seg *seg, caddr_t addr)
+{
+	/*
+	 * Since already-existing kernel pages are being mapped into userspace,
+	 * always report the segment type as shared.
+	 */
+	return (MAP_SHARED);
+}
+
+/* ARGSUSED */
+static int
+segumap_getvp(struct seg *seg, caddr_t addr, struct vnode **vpp)
+{
+	ASSERT(seg->s_as && AS_LOCK_HELD(seg->s_as));
+
+	*vpp = NULL;
+	return (0);
+}
+
+/* ARGSUSED */
+static int
+segumap_advise(struct seg *seg, caddr_t addr, size_t len, uint_t behav)
+{
+	if (behav == MADV_PURGE) {
+		/* Purge does not make sense for this mapping */
+		return (EINVAL);
+	}
+	/* Indicate success for everything else. */
+	return (0);
+}
+
+/* ARGSUSED */
+static void
+segumap_dump(struct seg *seg)
+{
+	/*
+	 * Since this is a mapping to share kernel data with userspace, nothing
+	 * additional should be dumped.
+	 */
+}
+
+/* ARGSUSED */
+static int
+segumap_pagelock(struct seg *seg, caddr_t addr, size_t len, struct page ***ppp,
+    enum lock_type type, enum seg_rw rw)
+{
+	return (ENOTSUP);
+}
+
+/* ARGSUSED */
+static int
+segumap_setpagesize(struct seg *seg, caddr_t addr, size_t len, uint_t szc)
+{
+	return (ENOTSUP);
+}
+
+static int
+segumap_getmemid(struct seg *seg, caddr_t addr, memid_t *memidp)
+{
+	segumap_data_t *sud = (segumap_data_t *)seg->s_data;
+
+	memidp->val[0] = (uintptr_t)sud->sud_kaddr;
+	memidp->val[1] = (uintptr_t)(addr - seg->s_base);
+	return (0);
+}
+
+/* ARGSUSED */
+static int
+segumap_capable(struct seg *seg, segcapability_t capability)
+{
+	/* no special capablities */
+	return (0);
+}
--- /dev/null	Wed Oct  5 00:32:35 2016
+++ new/usr/src/uts/common/vm/seg_umap.h	Wed Oct  5 00:32:35 2016
@@ -0,0 +1,42 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#ifndef	_VM_SEG_UMAP_H
+#define	_VM_SEG_UMAP_H
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+typedef struct segumap_crargs {
+	caddr_t	kaddr;
+	uchar_t	prot;		/* protection */
+	uchar_t	maxprot;	/* maximum protection */
+} segumap_crargs_t;
+
+typedef struct segumap_data {
+	krwlock_t	sud_lock;
+	caddr_t		sud_kaddr;
+	uchar_t		sud_prot;
+	size_t		sud_softlockcnt;
+} segumap_data_t;
+
+extern int segumap_create(struct seg *, void *);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _VM_SEG_UMAP_H */
--- old/usr/src/uts/common/vm/seg_vn.c	Wed Oct  5 00:32:35 2016
+++ new/usr/src/uts/common/vm/seg_vn.c	Wed Oct  5 00:32:35 2016
@@ -7308,7 +7308,8 @@
 	vpp = svd->vpage;
 	offset = svd->offset + (uintptr_t)(addr - seg->s_base);
 	bflags = ((flags & MS_ASYNC) ? B_ASYNC : 0) |
-	    ((flags & MS_INVALIDATE) ? B_INVAL : 0);
+	    ((flags & MS_INVALIDATE) ? B_INVAL : 0) |
+	    ((flags & MS_INVALCURPROC) ? (B_INVALCURONLY | B_INVAL) : 0);
 
 	if (attr) {
 		pageprot = attr & ~(SHARED|PRIVATE);
@@ -7333,11 +7334,11 @@
 			vpp = &svd->vpage[seg_page(seg, addr)];
 
 	} else if (svd->vp && svd->amp == NULL &&
-	    (flags & MS_INVALIDATE) == 0) {
+	    (flags & (MS_INVALIDATE | MS_INVALCURPROC)) == 0) {
 
 		/*
-		 * No attributes, no anonymous pages and MS_INVALIDATE flag
-		 * is not on, just use one big request.
+		 * No attributes, no anonymous pages and MS_INVAL* flags
+		 * are not on, just use one big request.
 		 */
 		err = VOP_PUTPAGE(svd->vp, (offset_t)offset, len,
 		    bflags, svd->cred, NULL);
@@ -7389,7 +7390,7 @@
 		 * might race in and lock the page after we unlock and before
 		 * we do the PUTPAGE, then PUTPAGE simply does nothing.
 		 */
-		if (flags & MS_INVALIDATE) {
+		if (flags & (MS_INVALIDATE | MS_INVALCURPROC)) {
 			if ((pp = page_lookup(vp, off, SE_SHARED)) != NULL) {
 				if (pp->p_lckcnt != 0 || pp->p_cowcnt != 0) {
 					page_unlock(pp);
--- old/usr/src/uts/common/vm/vm_as.c	Wed Oct  5 00:32:36 2016
+++ new/usr/src/uts/common/vm/vm_as.c	Wed Oct  5 00:32:36 2016
@@ -58,6 +58,7 @@
 #include <sys/debug.h>
 #include <sys/tnf_probe.h>
 #include <sys/vtrace.h>
+#include <sys/ddi.h>
 
 #include <vm/hat.h>
 #include <vm/as.h>
@@ -850,9 +851,8 @@
 	struct seg *segsav;
 	int as_lock_held;
 	klwp_t *lwp = ttolwp(curthread);
+	zone_t *zonep = curzone;
 
-
-
 retry:
 	/*
 	 * Indicate that the lwp is not to be stopped while waiting for a
@@ -887,6 +887,22 @@
 		if (as == &kas)
 			CPU_STATS_ADDQ(CPU, vm, kernel_asflt, 1);
 		CPU_STATS_EXIT_K();
+		if (zonep->zone_pg_flt_delay != 0) {
+			/*
+			 * The zone in which this process is running
+			 * is currently over it's physical memory cap.
+			 * Throttle page faults to help the user-land
+			 * memory capper catch up. Note that
+			 * drv_usectohz() rounds up.
+			 */
+			atomic_add_64(&zonep->zone_pf_throttle, 1);
+			atomic_add_64(&zonep->zone_pf_throttle_usec,
+			    zonep->zone_pg_flt_delay);
+			if (zonep->zone_pg_flt_delay < TICK_TO_USEC(1))
+				drv_usecwait(zonep->zone_pg_flt_delay);
+			else
+				delay(drv_usectohz(zonep->zone_pg_flt_delay));
+		}
 		break;
 	}
 
--- old/usr/src/uts/common/vm/vm_pvn.c	Wed Oct  5 00:32:36 2016
+++ new/usr/src/uts/common/vm/vm_pvn.c	Wed Oct  5 00:32:36 2016
@@ -21,6 +21,7 @@
 /*
  * Copyright (c) 1986, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
  */
 
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
@@ -432,8 +433,15 @@
 				page_io_unlock(pp);
 				page_unlock(pp);
 			}
-		} else if (flags & B_INVAL) {
+		} else if ((flags & (B_INVAL | B_INVALCURONLY)) == B_INVAL) {
 			/*
+			 * If B_INVALCURONLY is set, then we handle that case
+			 * in the next conditional if hat_page_is_mapped()
+			 * indicates that there are no additional mappings
+			 * to the page.
+			 */
+
+			/*
 			 * XXX - Failed writes with B_INVAL set are
 			 * not handled appropriately.
 			 */
@@ -573,8 +581,9 @@
 }
 
 /*
- * Flags are composed of {B_ASYNC, B_INVAL, B_FREE, B_DONTNEED, B_DELWRI,
- * B_TRUNC, B_FORCE}.  B_DELWRI indicates that this page is part of a kluster
+ * Flags are composed of {B_ASYNC, B_INVAL, B_INVALCURONLY, B_FREE,
+ * B_DONTNEED, B_DELWRI, B_TRUNC, B_FORCE}.
+ * B_DELWRI indicates that this page is part of a kluster
  * operation and is only to be considered if it doesn't involve any
  * waiting here.  B_TRUNC indicates that the file is being truncated
  * and so no i/o needs to be done. B_FORCE indicates that the page
@@ -628,6 +637,8 @@
 	 * If we want to free or invalidate the page then
 	 * we need to unload it so that anyone who wants
 	 * it will have to take a minor fault to get it.
+	 * If we are only invalidating the page for the
+	 * current process, then pass in a different flag.
 	 * Otherwise, we're just writing the page back so we
 	 * need to sync up the hardwre and software mod bit to
 	 * detect any future modifications.  We clear the
@@ -634,7 +645,9 @@
 	 * software mod bit when we put the page on the dirty
 	 * list.
 	 */
-	if (flags & (B_INVAL | B_FREE)) {
+	if (flags & B_INVALCURONLY) {
+		(void) hat_pageunload(pp, HAT_CURPROC_PGUNLOAD);
+	} else if (flags & (B_INVAL | B_FREE)) {
 		(void) hat_pageunload(pp, HAT_FORCE_PGUNLOAD);
 	} else {
 		(void) hat_pagesync(pp, HAT_SYNC_ZERORM);
@@ -646,7 +659,7 @@
 		 * list after all.
 		 */
 		page_io_unlock(pp);
-		if (flags & B_INVAL) {
+		if ((flags & (B_INVAL | B_INVALCURONLY)) == B_INVAL) {
 			/*LINTED: constant in conditional context*/
 			VN_DISPOSE(pp, B_INVAL, 0, kcred);
 		} else if (flags & B_FREE) {
@@ -658,6 +671,9 @@
 			 * of VOP_PUTPAGE() who prefer freeing the
 			 * page _only_ if no one else is accessing it.
 			 * E.g. segmap_release()
+			 * We also take this path for B_INVALCURONLY and
+			 * let page_release call VN_DISPOSE if no one else is
+			 * using the page.
 			 *
 			 * The above hat_ismod() check is useless because:
 			 * (1) we may not be holding SE_EXCL lock;
@@ -682,7 +698,7 @@
 	 * We'll detect the fact that they used it when the
 	 * i/o is done and avoid freeing the page.
 	 */
-	if (flags & B_FREE)
+	if (flags & (B_FREE | B_INVALCURONLY))
 		page_downgrade(pp);
 
 
--- old/usr/src/uts/common/vm/vm_usage.c	Wed Oct  5 00:32:36 2016
+++ new/usr/src/uts/common/vm/vm_usage.c	Wed Oct  5 00:32:36 2016
@@ -25,6 +25,10 @@
  */
 
 /*
+ * Copyright 2016, Joyent, Inc.
+ */
+
+/*
  * vm_usage
  *
  * This file implements the getvmusage() private system call.
@@ -518,7 +522,8 @@
 
 	zone->vmz_id = id;
 
-	if ((vmu_data.vmu_calc_flags & (VMUSAGE_ZONE | VMUSAGE_ALL_ZONES)) != 0)
+	if ((vmu_data.vmu_calc_flags &
+	    (VMUSAGE_ZONE | VMUSAGE_ALL_ZONES | VMUSAGE_A_ZONE)) != 0)
 		zone->vmz_zone = vmu_alloc_entity(id, VMUSAGE_ZONE, id);
 
 	if ((vmu_data.vmu_calc_flags & (VMUSAGE_PROJECTS |
@@ -918,6 +923,8 @@
 			next = AVL_NEXT(tree, next);
 			continue;
 		}
+
+		ASSERT(next->vmb_type == VMUSAGE_BOUND_UNKNOWN);
 		bound_type = next->vmb_type;
 		index = next->vmb_start;
 		while (index <= next->vmb_end) {
@@ -937,7 +944,10 @@
 
 			if (ap != NULL && vn != NULL && vn->v_pages != NULL &&
 			    (page = page_exists(vn, off)) != NULL) {
-				page_type = VMUSAGE_BOUND_INCORE;
+				if (PP_ISFREE(page))
+					page_type = VMUSAGE_BOUND_NOT_INCORE;
+				else
+					page_type = VMUSAGE_BOUND_INCORE;
 				if (page->p_szc > 0) {
 					pgcnt = page_get_pagecnt(page->p_szc);
 					pgshft = page_get_shift(page->p_szc);
@@ -947,8 +957,10 @@
 			} else {
 				page_type = VMUSAGE_BOUND_NOT_INCORE;
 			}
+
 			if (bound_type == VMUSAGE_BOUND_UNKNOWN) {
 				next->vmb_type = page_type;
+				bound_type = page_type;
 			} else if (next->vmb_type != page_type) {
 				/*
 				 * If current bound type does not match page
@@ -1009,6 +1021,7 @@
 			continue;
 		}
 
+		ASSERT(next->vmb_type == VMUSAGE_BOUND_UNKNOWN);
 		bound_type = next->vmb_type;
 		index = next->vmb_start;
 		while (index <= next->vmb_end) {
@@ -1024,7 +1037,10 @@
 
 			if (vnode->v_pages != NULL &&
 			    (page = page_exists(vnode, ptob(index))) != NULL) {
-				page_type = VMUSAGE_BOUND_INCORE;
+				if (PP_ISFREE(page))
+					page_type = VMUSAGE_BOUND_NOT_INCORE;
+				else
+					page_type = VMUSAGE_BOUND_INCORE;
 				if (page->p_szc > 0) {
 					pgcnt = page_get_pagecnt(page->p_szc);
 					pgshft = page_get_shift(page->p_szc);
@@ -1034,8 +1050,10 @@
 			} else {
 				page_type = VMUSAGE_BOUND_NOT_INCORE;
 			}
+
 			if (bound_type == VMUSAGE_BOUND_UNKNOWN) {
 				next->vmb_type = page_type;
+				bound_type = page_type;
 			} else if (next->vmb_type != page_type) {
 				/*
 				 * If current bound type does not match page
@@ -1304,6 +1322,12 @@
 			}
 
 			/*
+			 * Pages on the free list aren't counted for the rss.
+			 */
+			if (PP_ISFREE(page))
+				continue;
+
+			/*
 			 * Assume anon structs with a refcnt
 			 * of 1 are not COW shared, so there
 			 * is no reason to track them per entity.
@@ -1461,8 +1485,9 @@
 		entities = tmp;
 	}
 	if (vmu_data.vmu_calc_flags &
-	    (VMUSAGE_ZONE | VMUSAGE_ALL_ZONES | VMUSAGE_PROJECTS |
-	    VMUSAGE_ALL_PROJECTS | VMUSAGE_TASKS | VMUSAGE_ALL_TASKS |
+	    (VMUSAGE_ZONE | VMUSAGE_ALL_ZONES | VMUSAGE_A_ZONE |
+	    VMUSAGE_PROJECTS | VMUSAGE_ALL_PROJECTS |
+	    VMUSAGE_TASKS | VMUSAGE_ALL_TASKS |
 	    VMUSAGE_RUSERS | VMUSAGE_ALL_RUSERS | VMUSAGE_EUSERS |
 	    VMUSAGE_ALL_EUSERS)) {
 		ret = i_mod_hash_find_nosync(vmu_data.vmu_zones_hash,
@@ -1739,12 +1764,34 @@
 }
 
 /*
+ * When new data is calculated, update the phys_mem rctl usage value in the
+ * zones.
+ */
+static void
+vmu_update_zone_rctls(vmu_cache_t *cache)
+{
+	vmusage_t	*rp;
+	size_t		i = 0;
+	zone_t		*zp;
+
+	for (rp = cache->vmc_results; i < cache->vmc_nresults; rp++, i++) {
+		if (rp->vmu_type == VMUSAGE_ZONE &&
+		    rp->vmu_zoneid != ALL_ZONES) {
+			if ((zp = zone_find_by_id(rp->vmu_zoneid)) != NULL) {
+			        zp->zone_phys_mem = rp->vmu_rss_all;
+			        zone_rele(zp);
+			}
+		}
+	}
+}
+
+/*
  * Copy out the cached results to a caller.  Inspect the callers flags
  * and zone to determine which cached results should be copied.
  */
 static int
 vmu_copyout_results(vmu_cache_t *cache, vmusage_t *buf, size_t *nres,
-    uint_t flags, int cpflg)
+    uint_t flags, id_t req_zone_id, int cpflg)
 {
 	vmusage_t *result, *out_result;
 	vmusage_t dummy;
@@ -1763,7 +1810,7 @@
 	/* figure out what results the caller is interested in. */
 	if ((flags & VMUSAGE_SYSTEM) && curproc->p_zone == global_zone)
 		types |= VMUSAGE_SYSTEM;
-	if (flags & (VMUSAGE_ZONE | VMUSAGE_ALL_ZONES))
+	if (flags & (VMUSAGE_ZONE | VMUSAGE_ALL_ZONES | VMUSAGE_A_ZONE))
 		types |= VMUSAGE_ZONE;
 	if (flags & (VMUSAGE_PROJECTS | VMUSAGE_ALL_PROJECTS |
 	    VMUSAGE_COL_PROJECTS))
@@ -1826,26 +1873,33 @@
 				continue;
 		}
 
-		/* Skip "other zone" results if not requested */
-		if (result->vmu_zoneid != curproc->p_zone->zone_id) {
-			if (result->vmu_type == VMUSAGE_ZONE &&
-			    (flags & VMUSAGE_ALL_ZONES) == 0)
+		if (result->vmu_type == VMUSAGE_ZONE &&
+		    flags & VMUSAGE_A_ZONE) {
+			/* Skip non-requested zone results */
+			if (result->vmu_zoneid != req_zone_id)
 				continue;
-			if (result->vmu_type == VMUSAGE_PROJECTS &&
-			    (flags & (VMUSAGE_ALL_PROJECTS |
-			    VMUSAGE_COL_PROJECTS)) == 0)
-				continue;
-			if (result->vmu_type == VMUSAGE_TASKS &&
-			    (flags & VMUSAGE_ALL_TASKS) == 0)
-				continue;
-			if (result->vmu_type == VMUSAGE_RUSERS &&
-			    (flags & (VMUSAGE_ALL_RUSERS |
-			    VMUSAGE_COL_RUSERS)) == 0)
-				continue;
-			if (result->vmu_type == VMUSAGE_EUSERS &&
-			    (flags & (VMUSAGE_ALL_EUSERS |
-			    VMUSAGE_COL_EUSERS)) == 0)
-				continue;
+		} else {
+			/* Skip "other zone" results if not requested */
+			if (result->vmu_zoneid != curproc->p_zone->zone_id) {
+				if (result->vmu_type == VMUSAGE_ZONE &&
+				    (flags & VMUSAGE_ALL_ZONES) == 0)
+					continue;
+				if (result->vmu_type == VMUSAGE_PROJECTS &&
+				    (flags & (VMUSAGE_ALL_PROJECTS |
+				    VMUSAGE_COL_PROJECTS)) == 0)
+					continue;
+				if (result->vmu_type == VMUSAGE_TASKS &&
+				    (flags & VMUSAGE_ALL_TASKS) == 0)
+					continue;
+				if (result->vmu_type == VMUSAGE_RUSERS &&
+				    (flags & (VMUSAGE_ALL_RUSERS |
+				    VMUSAGE_COL_RUSERS)) == 0)
+					continue;
+				if (result->vmu_type == VMUSAGE_EUSERS &&
+				    (flags & (VMUSAGE_ALL_EUSERS |
+				    VMUSAGE_COL_EUSERS)) == 0)
+					continue;
+			}
 		}
 		count++;
 		if (out_result != NULL) {
@@ -1901,10 +1955,12 @@
 	int cacherecent = 0;
 	hrtime_t now;
 	uint_t flags_orig;
+	id_t req_zone_id;
 
 	/*
 	 * Non-global zones cannot request system wide and/or collated
-	 * results, or the system result, so munge the flags accordingly.
+	 * results, or the system result, or usage of another zone, so munge
+	 * the flags accordingly.
 	 */
 	flags_orig = flags;
 	if (curproc->p_zone != global_zone) {
@@ -1924,6 +1980,10 @@
 			flags &= ~VMUSAGE_SYSTEM;
 			flags |= VMUSAGE_ZONE;
 		}
+		if (flags & VMUSAGE_A_ZONE) {
+			flags &= ~VMUSAGE_A_ZONE;
+			flags |= VMUSAGE_ZONE;
+		}
 	}
 
 	/* Check for unknown flags */
@@ -1934,6 +1994,21 @@
 	if ((flags & VMUSAGE_MASK) == 0)
 		return (set_errno(EINVAL));
 
+	/* If requesting results for a specific zone, get the zone ID */
+	if (flags & VMUSAGE_A_ZONE) {
+		size_t bufsize;
+		vmusage_t zreq;
+
+		if (ddi_copyin((caddr_t)nres, &bufsize, sizeof (size_t), cpflg))
+			return (set_errno(EFAULT));
+		/* Requested zone ID is passed in buf, so 0 len not allowed */
+		if (bufsize == 0)
+			return (set_errno(EINVAL));
+		if (ddi_copyin((caddr_t)buf, &zreq, sizeof (vmusage_t), cpflg))
+			return (set_errno(EFAULT));
+		req_zone_id = zreq.vmu_id;
+	}
+
 	mutex_enter(&vmu_data.vmu_lock);
 	now = gethrtime();
 
@@ -1953,7 +2028,7 @@
 			mutex_exit(&vmu_data.vmu_lock);
 
 			ret = vmu_copyout_results(cache, buf, nres, flags_orig,
-			    cpflg);
+			    req_zone_id, cpflg);
 			mutex_enter(&vmu_data.vmu_lock);
 			vmu_cache_rele(cache);
 			if (vmu_data.vmu_pending_waiters > 0)
@@ -2009,8 +2084,11 @@
 
 		mutex_exit(&vmu_data.vmu_lock);
 
+		/* update zone's phys. mem. rctl usage */
+		vmu_update_zone_rctls(cache);
 		/* copy cache */
-		ret = vmu_copyout_results(cache, buf, nres, flags_orig, cpflg);
+		ret = vmu_copyout_results(cache, buf, nres, flags_orig,
+		    req_zone_id, cpflg);
 		mutex_enter(&vmu_data.vmu_lock);
 		vmu_cache_rele(cache);
 		mutex_exit(&vmu_data.vmu_lock);
@@ -2030,3 +2108,185 @@
 	vmu_data.vmu_pending_waiters--;
 	goto start;
 }
+
+#if defined(__x86)
+/*
+ * Attempt to invalidate all of the pages in the mapping for the given process.
+ */
+static void
+map_inval(proc_t *p, struct seg *seg, caddr_t addr, size_t size)
+{
+	page_t		*pp;
+	size_t		psize;
+	u_offset_t	off;
+	caddr_t		eaddr;
+	struct vnode	*vp;
+	struct segvn_data *svd;
+	struct hat	*victim_hat;
+
+	ASSERT((addr + size) <= (seg->s_base + seg->s_size));
+
+	victim_hat = p->p_as->a_hat;
+	svd = (struct segvn_data *)seg->s_data;
+	vp = svd->vp;
+	psize = page_get_pagesize(seg->s_szc);
+
+	off = svd->offset + (uintptr_t)(addr - seg->s_base);
+
+	for (eaddr = addr + size; addr < eaddr; addr += psize, off += psize) {
+		pp = page_lookup_nowait(vp, off, SE_SHARED);
+
+		if (pp != NULL) {
+			/* following logic based on pvn_getdirty() */
+
+			if (pp->p_lckcnt != 0 || pp->p_cowcnt != 0) {
+				page_unlock(pp);
+				continue;
+			}
+
+			page_io_lock(pp);
+			hat_page_inval(pp, 0, victim_hat);
+			page_io_unlock(pp);
+
+			/*
+			 * For B_INVALCURONLY-style handling we let
+			 * page_release call VN_DISPOSE if no one else is using
+			 * the page.
+			 *
+			 * A hat_ismod() check would be useless because:
+			 * (1) we are not be holding SE_EXCL lock
+			 * (2) we've not unloaded _all_ translations
+			 *
+			 * Let page_release() do the heavy-lifting.
+			 */
+			(void) page_release(pp, 1);
+		}
+	}
+}
+
+/*
+ * vm_map_inval()
+ *
+ * Invalidate as many pages as possible within the given mapping for the given
+ * process. addr is expected to be the base address of the mapping and size is
+ * the length of the mapping. In some cases a mapping will encompass an
+ * entire segment, but at least for anon or stack mappings, these will be
+ * regions within a single large segment. Thus, the invalidation is oriented
+ * around a single mapping and not an entire segment.
+ *
+ * SPARC sfmmu hat does not support HAT_CURPROC_PGUNLOAD-style handling so
+ * this code is only applicable to x86.
+ */
+int
+vm_map_inval(pid_t pid, caddr_t addr, size_t size)
+{
+	int ret;
+	int error = 0;
+	proc_t *p;		/* target proc */
+	struct as *as;		/* target proc's address space */
+	struct seg *seg;	/* working segment */
+
+	if (curproc->p_zone != global_zone || crgetruid(curproc->p_cred) != 0)
+		return (set_errno(EPERM));
+
+	/* If not a valid mapping address, return an error */
+	if ((caddr_t)((uintptr_t)addr & (uintptr_t)PAGEMASK) != addr)
+		return (set_errno(EINVAL));
+
+again:
+	mutex_enter(&pidlock);
+	p = prfind(pid);
+	if (p == NULL) {
+		mutex_exit(&pidlock);
+		return (set_errno(ESRCH));
+	}
+
+	mutex_enter(&p->p_lock);
+	mutex_exit(&pidlock);
+
+	if (panicstr != NULL) {
+		mutex_exit(&p->p_lock);
+		return (0);
+	}
+
+	as = p->p_as;
+
+	/*
+	 * Try to set P_PR_LOCK - prevents process "changing shape"
+	 * - blocks fork
+	 * - blocks sigkill
+	 * - cannot be a system proc
+	 * - must be fully created proc
+	 */
+	ret = sprtrylock_proc(p);
+	if (ret == -1) {
+		/* Process in invalid state */
+		mutex_exit(&p->p_lock);
+		return (set_errno(ESRCH));
+	}
+
+	if (ret == 1) {
+		/*
+		 * P_PR_LOCK is already set. Wait and try again. This also
+		 * drops p_lock so p may no longer be valid since the proc may
+		 * have exited.
+		 */
+		sprwaitlock_proc(p);
+		goto again;
+	}
+
+	/* P_PR_LOCK is now set */
+	mutex_exit(&p->p_lock);
+
+	AS_LOCK_ENTER(as, RW_READER);
+	if ((seg = as_segat(as, addr)) == NULL) {
+		AS_LOCK_EXIT(as);
+		mutex_enter(&p->p_lock);
+		sprunlock(p);
+		return (set_errno(ENOMEM));
+	}
+
+	/*
+	 * The invalidation behavior only makes sense for vnode-backed segments.
+	 */
+	if (seg->s_ops != &segvn_ops) {
+		AS_LOCK_EXIT(as);
+		mutex_enter(&p->p_lock);
+		sprunlock(p);
+		return (0);
+	}
+
+	/*
+	 * If the mapping is out of bounds of the segement return an error.
+	 */
+	if ((addr + size) > (seg->s_base + seg->s_size)) {
+		AS_LOCK_EXIT(as);
+		mutex_enter(&p->p_lock);
+		sprunlock(p);
+		return (set_errno(EINVAL));
+	}
+
+	/*
+	 * Don't use MS_INVALCURPROC flag here since that would eventually
+	 * initiate hat invalidation based on curthread. Since we're doing this
+	 * on behalf of a different process, that would erroneously invalidate
+	 * our own process mappings.
+	 */
+	error = SEGOP_SYNC(seg, addr, size, 0, (uint_t)MS_ASYNC);
+	if (error == 0) {
+		/*
+		 * Since we didn't invalidate during the sync above, we now
+		 * try to invalidate all of the pages in the mapping.
+		 */
+		map_inval(p, seg, addr, size);
+	}
+	AS_LOCK_EXIT(as);
+
+	mutex_enter(&p->p_lock);
+	sprunlock(p);
+
+	if (error)
+		(void) set_errno(error);
+	return (error);
+}
+#endif
--- old/usr/src/uts/i86pc/Makefile.files	Wed Oct  5 00:32:36 2016
+++ new/usr/src/uts/i86pc/Makefile.files	Wed Oct  5 00:32:36 2016
@@ -23,6 +23,7 @@
 # Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
 #
 # Copyright (c) 2010, Intel Corporation.
+# Copyright 2016 Joyent, Inc.
 #
 #	This Makefile defines file modules in the directory uts/i86pc
 #	and its children. These are the source files which are i86pc
@@ -40,6 +41,7 @@
 	cmi.o			\
 	cmi_hw.o		\
 	cms.o			\
+	comm_page.o		\
 	confunix.o		\
 	cpu_idle.o		\
 	cpuid.o			\
--- /dev/null	Wed Oct  5 00:32:36 2016
+++ new/usr/src/uts/i86pc/ml/comm_page.s	Wed Oct  5 00:32:36 2016
@@ -0,0 +1,88 @@
+
+/*
+ * 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 Joyent, Inc.
+ */
+
+#include <sys/asm_linkage.h>
+#include <sys/asm_misc.h>
+#include <sys/param.h>
+#include <sys/comm_page.h>
+#include <sys/tsc.h>
+
+#if defined(_GENCTF) || defined(__lint)
+
+hrtime_t tsc_last;
+hrtime_t tsc_resume_cap;
+hrtime_t tsc_hrtime_base;
+uint32_t tsc_max_delta;
+volatile uint32_t hres_lock;
+uint32_t tsc_type;
+uint32_t nsec_scale;
+int64_t hrestime_adj;
+hrtime_t hres_last_tick;
+uint32_t tsc_ncpu;
+volatile timestruc_t hrestime;
+hrtime_t tsc_sync_tick_delta[NCPU];
+
+comm_page_t comm_page;
+
+#else /* defined(_GENCTF) || defined(__lint) */
+
+#include "assym.h"
+
+/*
+ * x86 Comm Page
+ *
+ * This is the definition for the comm page on x86.  The purpose of this struct
+ * is to consolidate certain pieces of kernel state into one contiguous section
+ * of memory in order for it to be exposed (read-only) to userspace.  The
+ * struct contents are defined by hand so that member variables will maintain
+ * their original symbols for use throughout the rest of the kernel.  This
+ * layout must exactly match the C definition of comm_page_t.
+ * See: "uts/i86pc/sys/comm_page.h"
+ */
+
+	.data
+	DGDEF3(comm_page, COMM_PAGE_S_SIZE, 4096)
+	DGDEF2(tsc_last, 8)
+	.fill	1, 8, 0
+	DGDEF2(tsc_hrtime_base, 8)
+	.fill	1, 8, 0
+	DGDEF2(tsc_resume_cap, 8)
+	.fill	1, 8, 0
+	DGDEF2(tsc_type, 4);
+	.fill	1, 4, _CONST(TSC_RDTSC_CPUID)
+	DGDEF2(tsc_max_delta, 4);
+	.fill	1, 4, 0
+	DGDEF2(hres_lock, 4);
+	.fill	1, 4, 0
+	DGDEF2(nsec_scale, 4);
+	.fill	1, 4, 0
+	DGDEF2(hrestime_adj, 8)
+	.fill	1, 8, 0
+	DGDEF2(hres_last_tick, 8)
+	.fill	1, 8, 0
+	DGDEF2(tsc_ncpu, 4)
+	.fill	1, 4, 0
+	/* _cp_pad */
+	.fill	1, 4, 0
+	DGDEF2(hrestime, _MUL(2, 8))
+	.fill	2, 8, 0
+	DGDEF2(tsc_sync_tick_delta, _MUL(NCPU, 8))
+	.fill	_CONST(NCPU), 8, 0
+
+	/* pad out the rest of the page from the struct end */
+	.fill	_CONST(COMM_PAGE_SIZE - COMM_PAGE_S_SIZE), 1, 0
+
+#endif /* defined(_GENCTF) || defined(__lint) */
--- old/usr/src/uts/i86pc/ml/offsets.in	Wed Oct  5 00:32:36 2016
+++ new/usr/src/uts/i86pc/ml/offsets.in	Wed Oct  5 00:32:36 2016
@@ -1,6 +1,7 @@
 \
 \ Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
 \ Copyright 2012 Garrett D'Amore <garrett@damore.org>.  All rights reserved.
+\ Copyright 2016 Joyent, Inc.
 \
 \ CDDL HEADER START
 \
@@ -61,6 +62,7 @@
 #include <sys/brand.h>
 #include <sys/fastboot.h>
 #include <sys/cpr_wakecode.h>
+#include <sys/comm_page.h>
 
 proc		PROCSIZE
 	p_link
@@ -150,6 +152,7 @@
 	lwp_thread
 	lwp_procp
 	lwp_brand
+	lwp_brand_syscall
 	lwp_eosys
 	lwp_regs
 	lwp_arg
@@ -467,3 +470,5 @@
 
 wc_wakecode
 	wc_cpu
+
+comm_page_s	COMM_PAGE_S_SIZE
--- old/usr/src/uts/i86pc/ml/syscall_asm.s	Wed Oct  5 00:32:37 2016
+++ new/usr/src/uts/i86pc/ml/syscall_asm.s	Wed Oct  5 00:32:37 2016
@@ -631,8 +631,38 @@
 	sysexit
 	SET_SIZE(sys_sysenter)
 	SET_SIZE(brand_sys_sysenter)
+#endif	/* __lint */
 
+#if defined(__lint)
 /*
+ * System call via an int80.  This entry point is only used by the Linux
+ * application environment.  Unlike the sysenter path, there is no default
+ * action to take if no callback is registered for this process.
+ */
+void
+sys_int80()
+{}
+
+#else	/* __lint */
+
+	ENTRY_NP(brand_sys_int80)
+	BRAND_CALLBACK(BRAND_CB_INT80)
+
+	ALTENTRY(sys_int80)
+	/*
+	 * We hit an int80, but this process isn't of a brand with an int80
+	 * handler.  Bad process!  Make it look as if the INT failed.
+	 * Modify %eip to point before the INT, push the expected error
+	 * code and fake a GP fault.
+	 * 
+	 */
+	subl	$2, (%esp)	/* int insn 2-bytes */
+	pushl	$_CONST(_MUL(T_INT80, GATE_DESC_SIZE) + 2)
+	jmp	gptrap			/ GP fault
+	SET_SIZE(sys_int80)
+	SET_SIZE(brand_sys_int80)
+
+/*
  * Declare a uintptr_t which covers the entire pc range of syscall
  * handlers for the stack walkers that need this.
  */
--- old/usr/src/uts/i86pc/ml/syscall_asm_amd64.s	Wed Oct  5 00:32:37 2016
+++ new/usr/src/uts/i86pc/ml/syscall_asm_amd64.s	Wed Oct  5 00:32:37 2016
@@ -20,7 +20,7 @@
  */
 /*
  * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2015 Joyent, Inc.
+ * Copyright 2016 Joyent, Inc.
  */
 
 #include <sys/asm_linkage.h>
@@ -28,7 +28,6 @@
 #include <sys/regset.h>
 #include <sys/privregs.h>
 #include <sys/psw.h>
-#include <sys/machbrand.h>
 
 #if defined(__lint)
 
@@ -38,6 +37,7 @@
 
 #else	/* __lint */
 
+#include <sys/machbrand.h>
 #include <sys/segments.h>
 #include <sys/pcb.h>
 #include <sys/trap.h>
@@ -503,6 +503,7 @@
 	
 	movq	T_LWP(%r15), %r14
 	ASSERT_NO_RUPDATE_PENDING(%r14)
+
 	ENABLE_INTR_FLAGS
 
 	MSTATE_TRANSITION(LMS_USER, LMS_SYSTEM)
@@ -516,6 +517,28 @@
 
 	incq	%gs:CPU_STATS_SYS_SYSCALL
 
+	/*
+	 * If our LWP has an alternate system call handler, run that instead of
+	 * the regular system call path.
+	 */
+	movq	LWP_BRAND_SYSCALL(%r14), %rdi
+	testq	%rdi, %rdi
+	jz	_syscall_no_brand
+
+	pushq	%rax
+	subq	$8, %rsp	/* align stack for call to C */
+	call	*%rdi
+	addq	$8, %rsp
+
+	/*
+	 * If the alternate handler returns 0, we skip straight to the return to
+	 * usermode.  Otherwise, we resume regular system call processing.
+	 */
+	testl	%eax, %eax
+	popq	%rax
+	jz	_syscall_after_brand
+
+_syscall_no_brand:
 	movw	%ax, T_SYSNUM(%r15)
 	movzbl	T_PRE_SYS(%r15), %ebx
 	ORL_SYSCALLTRACE(%ebx)
@@ -550,6 +573,8 @@
 	shrq	$32, %r13	/* upper 32-bits into %edx */
 	movl	%r12d, %r12d	/* lower 32-bits into %eax */
 5:
+
+_syscall_after_brand:
 	/*
 	 * Optimistically assume that there's no post-syscall
 	 * work to do.  (This is to avoid having to call syscall_mstate()
@@ -795,11 +820,37 @@
 	incq	 %gs:CPU_STATS_SYS_SYSCALL
 
 	/*
+	 * If our lwp has an alternate system call handler, run that instead
+	 * of the regular system call path.
+	 */
+	movq	LWP_BRAND_SYSCALL(%r14), %rax
+	testq	%rax, %rax
+	jz	_syscall32_no_brand
+
+	movb	$LWP_SYS, LWP_STATE(%r14)
+	call	*%rax
+
+	/*
+	 * If the alternate handler returns 0, we skip straight to the return
+	 * to usermode.  Otherwise, we resume regular system call processing.
+	 */
+	testl	%eax, %eax
+	jz	_syscall32_after_brand
+
+_syscall32_no_brand:
+	/*
 	 * Make some space for MAXSYSARGS (currently 8) 32-bit args placed
 	 * into 64-bit (long) arg slots, maintaining 16 byte alignment.  Or
 	 * more succinctly:
 	 *
 	 *	SA(MAXSYSARGS * sizeof (long)) == 64
+	 *
+	 * Note, this space is used both to copy in the arguments from user
+	 * land, but also to as part of the old UNIX style syscall_ap() method.
+	 * syscall_entry expects that we do not change the values of this space
+	 * that we give it. However, this means that when we end up in the more
+	 * recent model of passing the arguments based on the calling
+	 * conventions, we'll need to save an additional 16 bytes of stack.
 	 */
 #define	SYS_DROP	64			/* drop for args */
 	subq	$SYS_DROP, %rsp
@@ -827,12 +878,16 @@
 	 */
 
 	movq	%rax, %rbx
-	movl	0(%rsp), %edi
-	movl	8(%rsp), %esi
-	movl	0x10(%rsp), %edx
-	movl	0x18(%rsp), %ecx
-	movl	0x20(%rsp), %r8d
-	movl	0x28(%rsp), %r9d
+	movl	0x0(%rsp), %edi		/* arg0 */
+	movl	0x8(%rsp), %esi		/* arg1 */
+	movl	0x10(%rsp), %edx	/* arg2 */
+	movl	0x38(%rsp), %eax	/* arg7 load */
+	movl	0x18(%rsp), %ecx	/* arg3 */
+	pushq	%rax			/* arg7 saved to stack */
+	movl	0x28(%rsp), %r8d	/* arg4 */
+	movl	0x38(%rsp), %eax	/* arg6 load */
+	movl	0x30(%rsp), %r9d	/* arg5 */
+	pushq	%rax			/* arg6 saved to stack */
 
 	call	*SY_CALLC(%rbx)
 
@@ -850,6 +905,8 @@
 	shrq	$32, %r13	/* upper 32-bits into %edx */
 	movl	%eax, %r12d	/* lower 32-bits into %eax */
 
+_syscall32_after_brand:
+
 	/*
 	 * Optimistically assume that there's no post-syscall
 	 * work to do.  (This is to avoid having to call syscall_mstate()
@@ -1079,15 +1136,20 @@
 	/*
 	 * Fetch the arguments copied onto the kernel stack and put
 	 * them in the right registers to invoke a C-style syscall handler.
-	 * %rax contains the handler address.
+	 * %rax contains the handler address. For the last two arguments, we
+	 * push them onto the stack -- we can't clobber the old arguments.
 	 */
 	movq	%rax, %rbx
-	movl	0(%rsp), %edi
-	movl	8(%rsp), %esi
-	movl	0x10(%rsp), %edx
-	movl	0x18(%rsp), %ecx
-	movl	0x20(%rsp), %r8d
-	movl	0x28(%rsp), %r9d
+	movl	0x0(%rsp), %edi		/* arg0 */
+	movl	0x8(%rsp), %esi		/* arg1 */
+	movl	0x10(%rsp), %edx	/* arg2 */
+	movl	0x38(%rsp), %eax	/* arg7 load */
+	movl	0x18(%rsp), %ecx	/* arg3 */
+	pushq	%rax			/* arg7 saved to stack */
+	movl	0x28(%rsp), %r8d	/* arg4 */
+	movl	0x38(%rsp), %eax	/* arg6 load */
+	movl	0x30(%rsp), %r9d	/* arg5 */
+	pushq	%rax			/* arg6 saved to stack */
 
 	call	*SY_CALLC(%rbx)
 
@@ -1159,7 +1221,73 @@
 	SET_SIZE(brand_sys_sysenter)
 
 #endif	/* __lint */
+ 
+#if defined(__lint)
+/*
+ * System call via an int80.  This entry point is only used by the Linux
+ * application environment.  Unlike the other entry points, there is no
+ * default action to take if no callback is registered for this process.
+ */
+void
+sys_int80()
+{}
 
+#else	/* __lint */
+
+	ENTRY_NP(brand_sys_int80)
+	SWAPGS				/* kernel gsbase */
+	XPV_TRAP_POP
+
+	/*
+	 * We first attempt to call the "b_int80" handler from the "struct
+	 * brand_mach_ops" for this brand.  If no handler function is installed
+	 * for this brand, the BRAND_CALLBACK() macro returns here and we
+	 * check the lwp for a "lwp_brand_syscall" handler.
+	 */
+	BRAND_CALLBACK(BRAND_CB_INT80, BRAND_URET_FROM_INTR_STACK())
+
+	/*
+	 * Check to see if this lwp provides "lwp_brand_syscall".  If so, we
+	 * will route this int80 through the regular system call handling path.
+	 */
+	movq	%r15, %gs:CPU_RTMP_R15
+	movq	%gs:CPU_THREAD, %r15
+	movq	T_LWP(%r15), %r15
+	movq	LWP_BRAND_SYSCALL(%r15), %r15
+	testq	%r15, %r15
+	movq	%gs:CPU_RTMP_R15, %r15
+	jnz	nopop_syscall_int
+
+	/*
+	 * The brand provided neither a "b_int80", nor a "lwp_brand_syscall"
+	 * function, and has thus opted out of handling this trap.
+	 */
+	SWAPGS				/* user gsbase */
+	jmp	nopop_int80
+
+	ENTRY_NP(sys_int80)
+	/*
+	 * We hit an int80, but this process isn't of a brand with an int80
+	 * handler.  Bad process!  Make it look as if the INT failed.
+	 * Modify %rip to point before the INT, push the expected error
+	 * code and fake a GP fault. Note on 64-bit hypervisor we need
+	 * to undo the XPV_TRAP_POP and push rcx and r11 back on the stack
+	 * because gptrap will pop them again with its own XPV_TRAP_POP.
+	 */
+	XPV_TRAP_POP
+nopop_int80:
+	subq	$2, (%rsp)	/* int insn 2-bytes */
+	pushq	$_CONST(_MUL(T_INT80, GATE_DESC_SIZE) + 2)
+#if defined(__xpv)
+	push	%r11
+	push	%rcx
+#endif
+	jmp	gptrap			/ GP fault
+	SET_SIZE(sys_int80)
+	SET_SIZE(brand_sys_int80)
+#endif	/* __lint */
+
+
 /*
  * This is the destination of the "int $T_SYSCALLINT" interrupt gate, used by
  * the generic i386 libc to do system calls. We do a small amount of setup
--- old/usr/src/uts/i86pc/os/cpuid.c	Wed Oct  5 00:32:37 2016
+++ new/usr/src/uts/i86pc/os/cpuid.c	Wed Oct  5 00:32:37 2016
@@ -32,7 +32,7 @@
  * Portions Copyright 2009 Advanced Micro Devices, Inc.
  */
 /*
- * Copyright (c) 2015, Joyent, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 /*
  * Various routines to handle identification
@@ -57,6 +57,8 @@
 #include <sys/auxv_386.h>
 #include <sys/memnode.h>
 #include <sys/pci_cfgspace.h>
+#include <sys/comm_page.h>
+#include <sys/tsc.h>
 
 #ifdef __xpv
 #include <sys/hypervisor.h>
@@ -2826,6 +2828,13 @@
 
 	}
 
+	/* Detect systems with a potential CPUID limit  */
+	if (cpi->cpi_vendor == X86_VENDOR_Intel && cpi->cpi_maxeax < 4) {
+		cmn_err(CE_NOTE, "CPUID limit detected, "
+		    "see the CPUID(7D) man page for details\n");
+	}
+
+
 	if (cpi->cpi_xmaxeax < 0x80000001)
 		goto pass4_done;
 
@@ -4607,27 +4616,30 @@
 	size_t cnt;
 
 	switch (flag) {
-	case X86_NO_TSC:
+	case TSC_NONE:
 		cnt = &_no_rdtsc_end - &_no_rdtsc_start;
 		(void) memcpy((void *)tsc_read, (void *)&_no_rdtsc_start, cnt);
 		break;
-	case X86_HAVE_TSCP:
-		cnt = &_tscp_end - &_tscp_start;
-		(void) memcpy((void *)tsc_read, (void *)&_tscp_start, cnt);
-		break;
-	case X86_TSC_MFENCE:
+	case TSC_RDTSC_MFENCE:
 		cnt = &_tsc_mfence_end - &_tsc_mfence_start;
 		(void) memcpy((void *)tsc_read,
 		    (void *)&_tsc_mfence_start, cnt);
 		break;
-	case X86_TSC_LFENCE:
+	case TSC_RDTSC_LFENCE:
 		cnt = &_tsc_lfence_end - &_tsc_lfence_start;
 		(void) memcpy((void *)tsc_read,
 		    (void *)&_tsc_lfence_start, cnt);
 		break;
+	case TSC_TSCP:
+		cnt = &_tscp_end - &_tscp_start;
+		(void) memcpy((void *)tsc_read, (void *)&_tscp_start, cnt);
+		break;
 	default:
+		/* Bail for unexpected TSC types. (TSC_NONE covers 0) */
+		cmn_err(CE_PANIC, "Unrecogized TSC type: %d", flag);
 		break;
 	}
+	tsc_type = flag;
 }
 
 int
--- old/usr/src/uts/i86pc/os/mlsetup.c	Wed Oct  5 00:32:37 2016
+++ new/usr/src/uts/i86pc/os/mlsetup.c	Wed Oct  5 00:32:37 2016
@@ -23,6 +23,7 @@
  *
  * Copyright (c) 1993, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2011 by Delphix. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 /*
  * Copyright (c) 2010, Intel Corporation.
@@ -61,6 +62,7 @@
 #include <sys/promif.h>
 #include <sys/pci_cfgspace.h>
 #include <sys/bootvfs.h>
+#include <sys/tsc.h>
 #ifdef __xpv
 #include <sys/hypervisor.h>
 #else
@@ -227,15 +229,15 @@
 	 */
 	if ((get_hwenv() & HW_XEN_HVM) == 0 &&
 	    is_x86_feature(x86_featureset, X86FSET_TSCP))
-		patch_tsc_read(X86_HAVE_TSCP);
+		patch_tsc_read(TSC_TSCP);
 	else if (cpuid_getvendor(CPU) == X86_VENDOR_AMD &&
 	    cpuid_getfamily(CPU) <= 0xf &&
 	    is_x86_feature(x86_featureset, X86FSET_SSE2))
-		patch_tsc_read(X86_TSC_MFENCE);
+		patch_tsc_read(TSC_RDTSC_MFENCE);
 	else if (cpuid_getvendor(CPU) == X86_VENDOR_Intel &&
 	    cpuid_getfamily(CPU) <= 6 &&
 	    is_x86_feature(x86_featureset, X86FSET_SSE2))
-		patch_tsc_read(X86_TSC_LFENCE);
+		patch_tsc_read(TSC_RDTSC_LFENCE);
 
 #endif	/* !__xpv */
 
@@ -246,7 +248,7 @@
 	 * return 0.
 	 */
 	if (!is_x86_feature(x86_featureset, X86FSET_TSC))
-		patch_tsc_read(X86_NO_TSC);
+		patch_tsc_read(TSC_NONE);
 #endif	/* __i386 && !__xpv */
 
 #if defined(__amd64) && !defined(__xpv)
--- old/usr/src/uts/i86pc/os/mp_startup.c	Wed Oct  5 00:32:37 2016
+++ new/usr/src/uts/i86pc/os/mp_startup.c	Wed Oct  5 00:32:37 2016
@@ -27,7 +27,7 @@
  * All rights reserved.
  */
 /*
- * Copyright 2015 Joyent, Inc.
+ * Copyright 2016 Joyent, Inc.
  * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
  */
 
@@ -247,7 +247,25 @@
 	kpreempt_enable();
 }
 
+#if !defined(__xpv)
 /*
+ * Configure per-cpu ID GDT
+ */
+static void
+init_cpu_id_gdt(struct cpu *cp)
+{
+	/* Write cpu_id into limit field of GDT for usermode retrieval */
+#if defined(__amd64)
+	set_usegd(&cp->cpu_gdt[GDT_CPUID], SDP_SHORT, NULL, cp->cpu_id,
+	    SDT_MEMRODA, SEL_UPL, SDP_BYTES, SDP_OP32);
+#elif defined(__i386)
+	set_usegd(&cp->cpu_gdt[GDT_CPUID], NULL, cp->cpu_id, SDT_MEMRODA,
+	    SEL_UPL, SDP_BYTES, SDP_OP32);
+#endif
+}
+#endif /* !defined(__xpv) */
+
+/*
  * Multiprocessor initialization.
  *
  * Allocate and initialize the cpu structure, TRAPTRACE buffer, and the
@@ -430,6 +448,10 @@
 
 	init_cpu_info(cp);
 
+#if !defined(__xpv)
+	init_cpu_id_gdt(cp);
+#endif
+
 	/*
 	 * alloc space for ucode_info
 	 */
@@ -1486,6 +1508,10 @@
 	 */
 	init_cpu_info(CPU);
 
+#if !defined(__xpv)
+	init_cpu_id_gdt(CPU);
+#endif
+
 	cmn_err(CE_CONT, "?cpu%d: %s\n", CPU->cpu_id, CPU->cpu_idstr);
 	cmn_err(CE_CONT, "?cpu%d: %s\n", CPU->cpu_id, CPU->cpu_brandstr);
 
--- old/usr/src/uts/i86pc/os/timestamp.c	Wed Oct  5 00:32:38 2016
+++ new/usr/src/uts/i86pc/os/timestamp.c	Wed Oct  5 00:32:38 2016
@@ -25,6 +25,7 @@
  *
  * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
  * Copyright (c) 2014, 2016 by Delphix. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 
 #include <sys/types.h>
@@ -47,6 +48,7 @@
 #include <sys/panic.h>
 #include <sys/cpu.h>
 #include <sys/sdt.h>
+#include <sys/comm_page.h>
 
 /*
  * Using the Pentium's TSC register for gethrtime()
@@ -99,7 +101,6 @@
 
 #define	NSEC_SHIFT 5
 
-static uint_t nsec_scale;
 static uint_t nsec_unscale;
 
 /*
@@ -140,8 +141,6 @@
 
 int tsc_master_slave_sync_needed = 1;
 
-static int	tsc_max_delta;
-static hrtime_t tsc_sync_tick_delta[NCPU];
 typedef struct tsc_sync {
 	volatile hrtime_t master_tsc, slave_tsc;
 } tsc_sync_t;
@@ -149,9 +148,7 @@
 static hrtime_t largest_tsc_delta = 0;
 static ulong_t shortest_write_time = ~0UL;
 
-static hrtime_t	tsc_last = 0;
 static hrtime_t	tsc_last_jumped = 0;
-static hrtime_t	tsc_hrtime_base = 0;
 static int	tsc_jumped = 0;
 static uint32_t	tsc_wayback = 0;
 /*
@@ -159,7 +156,6 @@
  * tsc_tick() function runs which means that when gethrtime() is called it
  * should never be more than 1 second since tsc_last was updated.
  */
-static hrtime_t tsc_resume_cap;
 static hrtime_t tsc_resume_cap_ns = NANOSEC;	 /* 1s */
 
 static hrtime_t	shadow_tsc_hrtime_base;
@@ -546,6 +542,7 @@
 	if (largest_tsc_delta > shortest_write_time) {
 		gethrtimef = tsc_gethrtime_delta;
 		gethrtimeunscaledf = tsc_gethrtimeunscaled_delta;
+		tsc_ncpu = NCPU;
 	}
 	restore_int_flag(flags);
 }
@@ -688,6 +685,12 @@
 	hrtime_tick = tsc_tick;
 	gethrtime_hires = 1;
 	/*
+	 * Being part of the comm page, tsc_ncpu communicates the published
+	 * length of the tsc_sync_tick_delta array.  This is kept zeroed to
+	 * ignore the absent delta data while the TSCs are synced.
+	 */
+	tsc_ncpu = 0;
+	/*
 	 * Allocate memory for the structure used in the tsc sync logic.
 	 * This structure should be aligned on a multiple of cache line size.
 	 */
@@ -726,6 +729,7 @@
 
 	gethrtimef = tsc_gethrtime_delta;
 	gethrtimeunscaledf = tsc_gethrtimeunscaled_delta;
+	tsc_ncpu = NCPU;
 }
 
 /*
--- old/usr/src/uts/i86pc/os/trap.c	Wed Oct  5 00:32:38 2016
+++ new/usr/src/uts/i86pc/os/trap.c	Wed Oct  5 00:32:38 2016
@@ -32,7 +32,7 @@
 /*								*/
 
 /*
- * Copyright 2012 Joyent, Inc. All rights reserved.
+ * Copyright 2015 Joyent, Inc.
  */
 
 #include <sys/types.h>
@@ -101,6 +101,7 @@
 #include <sys/hypervisor.h>
 #endif
 #include <sys/contract/process_impl.h>
+#include <sys/brand.h>
 
 #define	USER	0x10000		/* user-mode flag added to trap type */
 
@@ -862,6 +863,17 @@
 			fault_type = F_INVAL;
 		}
 
+		/*
+		 * Allow the brand to interpose on invalid memory accesses
+		 * prior to running the native pagefault handler.  If this
+		 * brand hook returns zero, it was able to handle the fault
+		 * completely.  Otherwise, drive on and call pagefault().
+		 */
+		if (PROC_IS_BRANDED(p) && BROP(p)->b_pagefault != NULL &&
+		    BROP(p)->b_pagefault(p, lwp, addr, fault_type, rw) == 0) {
+			goto out;
+		}
+
 		res = pagefault(addr, fault_type, rw, 0);
 
 		/*
--- old/usr/src/uts/i86pc/sys/apic.h	Wed Oct  5 00:32:38 2016
+++ new/usr/src/uts/i86pc/sys/apic.h	Wed Oct  5 00:32:38 2016
@@ -382,7 +382,7 @@
 /* special or reserve vectors */
 #define	APIC_CHECK_RESERVE_VECTORS(v) \
 	(((v) == T_FASTTRAP) || ((v) == APIC_SPUR_INTR) || \
-	((v) == T_SYSCALLINT) || ((v) == T_DTRACE_RET))
+	((v) == T_SYSCALLINT) || ((v) == T_DTRACE_RET) || ((v) == 0x80))
 
 /* cmos shutdown code for BIOS						*/
 #define	BIOS_SHUTDOWN		0x0a
--- /dev/null	Wed Oct  5 00:32:38 2016
+++ new/usr/src/uts/i86pc/sys/comm_page.h	Wed Oct  5 00:32:38 2016
@@ -0,0 +1,100 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#ifndef _COMM_PAGE_H
+#define	_COMM_PAGE_H
+
+#ifndef _ASM
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#endif /* _ASM */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define	COMM_PAGE_SIZE	PAGESIZE
+#define	COMM_PAGE_ALIGN	0x4000
+
+#ifndef _ASM
+
+/*
+ * x86 comm page
+ *
+ * This struct defines the data format for the "comm page": kernel data made
+ * directly available to userspace for read-only operations.  This enables
+ * facilities such as clock_gettime to operate entirely in userspace without
+ * the need for a trap or fasttrap.
+ *
+ * A note about 32-bit/64-bit compatibility:
+ * The current format of the comm page is designed to be consistent for both
+ * 32-bit and 64-bit programs running in a 64-bit kernel.  On 32-bit kernels,
+ * the comm page is not exposed to userspace due to the difference in
+ * timespec_t sizing.
+ *
+ * This struct is instantiated "by hand" in assembly to preserve the global
+ * symbols it contains.  That layout must be kept in sync with the structure
+ * defined here.
+ * See: "uts/i86pc/ml/comm_page.s"
+ */
+typedef struct comm_page_s {
+	hrtime_t		cp_tsc_last;
+	hrtime_t		cp_tsc_hrtime_base;
+	hrtime_t		cp_tsc_resume_cap;
+	uint32_t		cp_tsc_type;
+	uint32_t		cp_tsc_max_delta;
+
+	volatile uint32_t	cp_hres_lock;	/* must be 8-byte aligned */
+	uint32_t		cp_nsec_scale;
+	int64_t			cp_hrestime_adj;
+	hrtime_t		cp_hres_last_tick;
+	uint32_t		cp_tsc_ncpu;
+	uint32_t		_cp_pad;
+	volatile int64_t	cp_hrestime[2];
+#if defined(_MACHDEP)
+	hrtime_t		cp_tsc_sync_tick_delta[NCPU];
+#else
+	/* length resides in cp_ncpu */
+	hrtime_t		cp_tsc_sync_tick_delta[];
+#endif /* defined(_MACHDEP) */
+} comm_page_t;
+
+#if defined(_KERNEL)
+extern comm_page_t comm_page;
+
+#if defined(_MACHDEP)
+extern hrtime_t tsc_last;
+extern hrtime_t tsc_hrtime_base;
+extern hrtime_t tsc_resume_cap;
+extern uint32_t tsc_type;
+extern uint32_t tsc_max_delta;
+extern volatile uint32_t hres_lock;
+extern uint32_t nsec_scale;
+extern int64_t hrestime_adj;
+extern hrtime_t hres_last_tick;
+extern uint32_t tsc_ncpu;
+extern volatile timestruc_t hrestime;
+extern hrtime_t tsc_sync_tick_delta[NCPU];
+#endif /* defined(_MACHDEP) */
+#endif /* defined(_KERNEL) */
+
+#endif  /* _ASM */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _COMM_PAGE_H */
--- old/usr/src/uts/i86pc/sys/machparam.h	Wed Oct  5 00:32:38 2016
+++ new/usr/src/uts/i86pc/sys/machparam.h	Wed Oct  5 00:32:38 2016
@@ -21,6 +21,7 @@
 /*
  * Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2015 by Delphix. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 
 /*	Copyright (c) 1988 AT&T	*/
@@ -54,6 +55,10 @@
  */
 
 #if defined(__amd64)
+/*
+ * If NCPU grows beyond 256, sizing for the x86 comm page will require
+ * adjustment.
+ */
 #define	NCPU	256
 #define	NCPU_LOG2	8
 #elif defined(__i386)
--- /dev/null	Wed Oct  5 00:32:38 2016
+++ new/usr/src/uts/i86pc/sys/tsc.h	Wed Oct  5 00:32:38 2016
@@ -0,0 +1,28 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#ifndef _TSC_H
+#define	_TSC_H
+
+/*
+ * flags to patch tsc_read routine.
+ */
+#define	TSC_NONE		0x0
+#define	TSC_RDTSC_CPUID		0x1
+#define	TSC_RDTSC_MFENCE	0x2
+#define	TSC_RDTSC_LFENCE	0x3
+#define	TSC_TSCP		0x4
+
+#endif /* _TSC_H */
--- old/usr/src/uts/i86pc/unix/Makefile	Wed Oct  5 00:32:38 2016
+++ new/usr/src/uts/i86pc/unix/Makefile	Wed Oct  5 00:32:38 2016
@@ -64,7 +64,7 @@
 
 LIBOPTS		= -L $(GENUNIX_DIR)/$(OBJS_DIR) -l $(GENUNIX)
 
-CTFEXTRAOBJS	= $(OBJS_DIR)/vers.o
+CTFEXTRAOBJS	= $(OBJS_DIR)/vers.o $(OBJS_DIR)/comm_page_ctf.o
 
 DBOOT_OBJS_DIR	= dboot/$(OBJS_DIR)
 DBOOT_OBJECTS	= $(DBOOT_OBJS:%=$(DBOOT_OBJS_DIR)/%)
@@ -97,9 +97,10 @@
 #
 CLEANFILES	+=		\
 	$(UNIX_O) $(MODSTUBS_O)	\
-	$(OBJS_DIR)/vers.c $(OBJS_DIR)/vers.o \
+	$(OBJS_DIR)/vers.c \
 	$(OBJS_DIR)/dtracestubs.s \
-	$(DTRACESTUBS_O) $(DTRACESTUBS)
+	$(DTRACESTUBS_O) $(DTRACESTUBS) \
+	$(CTFEXTRAOBJS)
 
 CLEANFILES	+=		\
 	$(DBOOT_O) $(DBOOT_S)	\
@@ -188,7 +189,7 @@
 	$(CTFMERGE_UNIQUIFY_AGAINST_GENUNIX)
 	$(POST_PROCESS)
 
-$(UNIX_O):	$(OBJECTS) $(OBJS_DIR)/vers.o
+$(UNIX_O):	$(OBJECTS) $(CTFEXTRAOBJS)
 	$(LD) -r -o $@ $(OBJECTS) $(OBJS_DIR)/vers.o
 
 $(DBOOT_BIN):	$(DBOOT_OBJS_DIR) $(DBOOT_OBJECTS) dboot/Mapfile.dboot
@@ -205,6 +206,10 @@
 $(DBOOT_OBJS_DIR):
 	-@mkdir -p $@ 2> /dev/null
 
+$(OBJS_DIR)/comm_page_ctf.o:	$(UTSBASE)/i86pc/ml/comm_page.s
+	$(COMPILE.c) -_gcc=-xc -D_GENCTF -o $@ $(UTSBASE)/i86pc/ml/comm_page.s
+	$(CTFCONVERT_O)
+
 #
 # dboot is built as an intermediate target in dboot.o, so just make
 # dboot.o the dependency here.
--- old/usr/src/uts/i86pc/vm/hat_i86.c	Wed Oct  5 00:32:38 2016
+++ new/usr/src/uts/i86pc/vm/hat_i86.c	Wed Oct  5 00:32:38 2016
@@ -28,6 +28,7 @@
 /*
  * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
  * Copyright (c) 2014, 2015 by Delphix. All rights reserved.
+ * Copyright 2014 Joyent, Inc.  All rights reserved.
  */
 
 /*
@@ -3323,7 +3324,7 @@
 
 
 /*
- * common code used by hat_pageunload() and hment_steal()
+ * common code used by hat_page_inval() and hment_steal()
  */
 hment_t *
 hati_page_unmap(page_t *pp, htable_t *ht, uint_t entry)
@@ -3379,15 +3380,13 @@
 
 extern int	vpm_enable;
 /*
- * Unload all translations to a page. If the page is a subpage of a large
+ * Unload translations to a page. If the page is a subpage of a large
  * page, the large page mappings are also removed.
- *
- * The forceflags are unused.
+ * If curhat is not NULL, then we only unload the translation
+ * for the given process, otherwise all translations are unloaded.
  */
-
-/*ARGSUSED*/
-static int
-hati_pageunload(struct page *pp, uint_t pg_szcd, uint_t forceflag)
+void
+hat_page_inval(struct page *pp, uint_t pg_szcd, struct hat *curhat)
 {
 	page_t		*cur_pp = pp;
 	hment_t		*hm;
@@ -3395,15 +3394,10 @@
 	htable_t	*ht;
 	uint_t		entry;
 	level_t		level;
+	ulong_t		cnt;
 
 	XPV_DISALLOW_MIGRATE();
 
-	/*
-	 * prevent recursion due to kmem_free()
-	 */
-	++curthread->t_hatdepth;
-	ASSERT(curthread->t_hatdepth < 16);
-
 #if defined(__amd64)
 	/*
 	 * clear the vpm ref.
@@ -3416,6 +3410,8 @@
 	 * The loop with next_size handles pages with multiple pagesize mappings
 	 */
 next_size:
+	if (curhat != NULL)
+		cnt = hat_page_getshare(cur_pp);
 	for (;;) {
 
 		/*
@@ -3427,14 +3423,13 @@
 			if (hm == NULL) {
 				x86_hm_exit(cur_pp);
 
+curproc_done:
 				/*
 				 * If not part of a larger page, we're done.
 				 */
 				if (cur_pp->p_szc <= pg_szcd) {
-					ASSERT(curthread->t_hatdepth > 0);
-					--curthread->t_hatdepth;
 					XPV_ALLOW_MIGRATE();
-					return (0);
+					return;
 				}
 
 				/*
@@ -3453,8 +3448,20 @@
 			 * If this mapping size matches, remove it.
 			 */
 			level = ht->ht_level;
-			if (level == pg_szcd)
-				break;
+			if (level == pg_szcd) {
+				if (curhat == NULL || ht->ht_hat == curhat)
+					break;
+				/*
+				 * Unloading only the given process but it's
+				 * not the hat for the current process. Leave
+				 * entry in place. Also do a safety check to
+				 * ensure we don't get in an infinite loop
+				 */
+				if (cnt-- == 0) {
+					x86_hm_exit(cur_pp);
+					goto curproc_done;
+				}
+			}
 		}
 
 		/*
@@ -3464,14 +3471,44 @@
 		hm = hati_page_unmap(cur_pp, ht, entry);
 		if (hm != NULL)
 			hment_free(hm);
+
+		/* Perform check above for being part of a larger page. */
+		if (curhat != NULL)
+			goto curproc_done;
 	}
 }
 
+/*
+ * Unload translations to a page. If unloadflag is HAT_CURPROC_PGUNLOAD, then
+ * we only unload the translation for the current process, otherwise all
+ * translations are unloaded.
+ */
+static int
+hati_pageunload(struct page *pp, uint_t pg_szcd, uint_t unloadflag)
+{
+	struct hat	*curhat = NULL;
+
+	/*
+	 * prevent recursion due to kmem_free()
+	 */
+	++curthread->t_hatdepth;
+	ASSERT(curthread->t_hatdepth < 16);
+
+	if (unloadflag == HAT_CURPROC_PGUNLOAD)
+		curhat = curthread->t_procp->p_as->a_hat;
+
+	hat_page_inval(pp, pg_szcd, curhat);
+
+	ASSERT(curthread->t_hatdepth > 0);
+	--curthread->t_hatdepth;
+	return (0);
+}
+
 int
-hat_pageunload(struct page *pp, uint_t forceflag)
+hat_pageunload(struct page *pp, uint_t unloadflag)
 {
 	ASSERT(PAGE_EXCL(pp));
-	return (hati_pageunload(pp, 0, forceflag));
+	return (hati_pageunload(pp, 0, unloadflag));
 }
 
 /*
--- old/usr/src/uts/i86pc/vm/vm_machdep.c	Wed Oct  5 00:32:39 2016
+++ new/usr/src/uts/i86pc/vm/vm_machdep.c	Wed Oct  5 00:32:39 2016
@@ -24,6 +24,7 @@
 /*
  * Copyright (c) 2010, Intel Corporation.
  * All rights reserved.
+ * Copyright (c) 2014, Joyent, Inc. All rights reserved.
  */
 
 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -623,10 +624,8 @@
 map_addr(caddr_t *addrp, size_t len, offset_t off, int vacalign, uint_t flags)
 {
 	struct proc *p = curproc;
-	caddr_t userlimit = (flags & _MAP_LOW32) ?
-	    (caddr_t)_userlimit32 : p->p_as->a_userlimit;
-
-	map_addr_proc(addrp, len, off, vacalign, userlimit, curproc, flags);
+	map_addr_proc(addrp, len, off, vacalign,
+	    map_userlimit(p, p->p_as, flags), curproc, flags);
 }
 
 /*ARGSUSED*/
--- old/usr/src/uts/i86xpv/os/xpv_timestamp.c	Wed Oct  5 00:32:39 2016
+++ new/usr/src/uts/i86xpv/os/xpv_timestamp.c	Wed Oct  5 00:32:39 2016
@@ -22,6 +22,7 @@
 /*
  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
  */
 
 #include <sys/types.h>
@@ -71,6 +72,11 @@
 static hrtime_t hrtime_suspend_time;
 static hrtime_t hrtime_addend;
 
+volatile uint32_t hres_lock;
+hrtime_t hres_last_tick;
+int64_t hrestime_adj;
+volatile timestruc_t hrestime;
+
 /*
  * These functions are used in DTrace probe context, and must be removed from
  * fbt consideration.  Currently fbt ignores all weak symbols, so this will
--- old/usr/src/uts/intel/Makefile	Wed Oct  5 00:32:39 2016
+++ new/usr/src/uts/intel/Makefile	Wed Oct  5 00:32:39 2016
@@ -65,7 +65,7 @@
 
 .PARALLEL:	$(PARALLEL_KMODS) $(XMODS) config $(LINT_DEPS)
 
-def all install clean clobber modlist: $(KMODS) $(XMODS) config
+def all install clean clobber modlist: genassym $(KMODS) $(XMODS) config
 
 clobber: clobber.targ
 
@@ -107,7 +107,7 @@
 # intel/dtrace depends on i86pc/genassym, so we need to build both
 # i86pc/genassym and intel/genassym.
 #
-all.prereq install.prereq def.prereq: genunix FRC
+all.prereq install.prereq def.prereq: genassym genunix FRC
 	@cd ../i86pc/genassym; pwd; $(MAKE) $(@:%.prereq=%)
 
 #
@@ -125,7 +125,7 @@
 
 modlintlib clean.lint: $(LINT_KMODS) $(XMODS)
 
-$(KMODS) $(SUBDIRS) config:	FRC
+genassym $(KMODS) $(SUBDIRS) config:	FRC
 	@cd $@; pwd; $(MAKE) $(NO_STATE) $(TARGET)
 
 $(XMODS):	FRC
--- old/usr/src/uts/intel/Makefile.files	Wed Oct  5 00:32:39 2016
+++ new/usr/src/uts/intel/Makefile.files	Wed Oct  5 00:32:39 2016
@@ -21,7 +21,7 @@
 
 #
 # Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
-# Copyright (c) 2013, Joyent, Inc. All rights reserved.
+# Copyright 2016, Joyent, Inc.
 # Copyright 2016 Nexenta Systems, Inc.
 #
 
@@ -36,6 +36,7 @@
 #
 CORE_OBJS +=		\
 	arch_kdi.o	\
+	comm_page_util.o \
 	copy.o		\
 	copy_subr.o	\
 	cpc_subr.o	\
@@ -100,7 +101,31 @@
 #
 CORE_OBJS +=		\
 	prmachdep.o
+ 
+LX_CGROUP_OBJS +=	\
+	cgrps_node.o	\
+	cgrps_vfsops.o	\
+	cgrps_vnops.o
 
+LX_DEVFS_OBJS +=	\
+	lxd_attrdb.o	\
+	lxd_node.o	\
+	lxd_vfsops.o	\
+	lxd_vnops.o
+
+LX_PROC_OBJS +=	\
+	lx_prsubr.o	\
+	lx_prvfsops.o	\
+	lx_prvnops.o
+
+LX_SYS_OBJS +=	\
+	lx_syssubr.o	\
+	lx_sysvfsops.o	\
+	lx_sysvnops.o
+
+LX_AUTOFS_OBJS +=	\
+	lx_autofs.o
+
 #
 #	ZFS file system module
 #
@@ -264,6 +289,65 @@
 #
 SN1_BRAND_OBJS	=	sn1_brand.o sn1_brand_asm.o
 S10_BRAND_OBJS	=	s10_brand.o s10_brand_asm.o
+LX_BRAND_OBJS  =		\
+	lx_access.o		\
+	lx_aio.o		\
+	lx_archdep.o		\
+	lx_auxv.o		\
+	lx_brand.o		\
+	lx_brk.o		\
+	lx_chmod.o		\
+	lx_chown.o		\
+	lx_clone.o		\
+	lx_close.o		\
+	lx_cpu.o		\
+	lx_dup.o		\
+	lx_errno.o		\
+	lx_epoll.o		\
+	lx_fadvise.o		\
+	lx_fallocate.o		\
+	lx_fcntl.o		\
+	lx_futex.o		\
+	lx_getcwd.o		\
+	lx_getdents.o		\
+	lx_getpid.o		\
+	lx_getrandom.o		\
+	lx_id.o			\
+	lx_ioctl.o		\
+	lx_ioprio.o		\
+	lx_kill.o		\
+	lx_link.o		\
+	lx_lseek.o		\
+	lx_misc.o		\
+	lx_miscsys.o		\
+	lx_mkdir.o		\
+	lx_modify_ldt.o		\
+	lx_mount.o		\
+	lx_open.o		\
+	lx_personality.o	\
+	lx_pgrp.o		\
+	lx_pid.o		\
+	lx_pipe.o		\
+	lx_poll.o		\
+	lx_prctl.o		\
+	lx_ptrace.o		\
+	lx_rename.o		\
+	lx_rlimit.o		\
+	lx_rw.o			\
+	lx_sched.o		\
+	lx_signal.o		\
+	lx_signum.o		\
+	lx_socket.o		\
+	lx_stat.o		\
+	lx_sync.o		\
+	lx_syscall.o		\
+	lx_sysinfo.o		\
+	lx_thread_area.o	\
+	lx_timer.o		\
+	lx_umask.o		\
+	lx_uname.o		\
+	lx_wait.o		\
+	lx_xattr.o
 
 #
 #	special files
--- old/usr/src/uts/intel/Makefile.intel	Wed Oct  5 00:32:39 2016
+++ new/usr/src/uts/intel/Makefile.intel	Wed Oct  5 00:32:39 2016
@@ -46,6 +46,7 @@
 #
 UNIX_DIR	 = $(UTSBASE)/i86pc/unix
 GENLIB_DIR	 = $(UTSBASE)/intel/genunix
+GENASSYM_DIR	 = $(UTSBASE)/intel/genassym
 IPDRV_DIR	 = $(UTSBASE)/intel/ip
 MODSTUBS_DIR	 = $(UNIX_DIR)
 DSF_DIR		 = $(UTSBASE)/$(PLATFORM)/genassym
@@ -139,6 +140,7 @@
 ASFLAGS_XARCH		= $(ASFLAGS_XARCH_$(CLASS))
 
 ASFLAGS			+= $(ASFLAGS_XARCH)
+AS_INC_PATH		+= -I$(GENASSYM_DIR)/$(OBJS_DIR)
 
 #
 #	Define the base directory for installation.
@@ -253,6 +255,7 @@
 DRV_KMODS	+= i915
 DRV_KMODS	+= icmp
 DRV_KMODS	+= icmp6
+DRV_KMODS	+= inotify
 DRV_KMODS	+= intel_nb5000
 DRV_KMODS	+= intel_nhm
 DRV_KMODS	+= ip
@@ -364,6 +367,7 @@
 DRV_KMODS	+= xge
 DRV_KMODS	+= yge
 DRV_KMODS	+= zcons
+DRV_KMODS	+= zfd
 DRV_KMODS	+= zyd
 DRV_KMODS	+= simnet
 DRV_KMODS	+= stmf
@@ -516,7 +520,8 @@
 #
 #	Brand modules
 #
-BRAND_KMODS	+= sn1_brand s10_brand
+BRAND_KMODS	+= sn1_brand s10_brand lx_brand
+DRV_KMODS	+= lx_systrace lx_ptm lx_netlink
 
 #
 #	Exec Class Modules (/kernel/exec):
@@ -532,9 +537,9 @@
 #	File System Modules (/kernel/fs):
 #
 FS_KMODS	+= autofs ctfs dcfs dev devfs fdfs fifofs hsfs lofs
-FS_KMODS	+= mntfs namefs nfs objfs zfs zut
-FS_KMODS	+= pcfs procfs sockfs specfs tmpfs udfs ufs sharefs
-FS_KMODS	+= smbfs bootfs
+FS_KMODS	+= lxautofs lx_proc mntfs namefs nfs objfs zfs zut
+FS_KMODS	+= pcfs procfs sockfs specfs tmpfs udfs ufs sharefs lx_sysfs
+FS_KMODS	+= smbfs bootfs lx_cgroup lx_devfs
 
 #
 #	Streams Modules (/kernel/strmod):
--- old/usr/src/uts/intel/bpf/Makefile	Wed Oct  5 00:32:39 2016
+++ new/usr/src/uts/intel/bpf/Makefile	Wed Oct  5 00:32:39 2016
@@ -60,7 +60,7 @@
 #
 #
 CFLAGS		+= $(CCVERBOSE)
-LDFLAGS		+= -dy -Nmisc/mac -Nmisc/dls -Ndrv/ipnet -Nmisc/neti
+LDFLAGS		+= -dy -Nmisc/mac -Nmisc/dls -Ndrv/ipnet -Nmisc/neti -Ndrv/ip
 INC_PATH	+= -I$(UTSBASE)/common/io/bpf
 
 #
--- old/usr/src/uts/intel/brand/common/brand_asm.h	Wed Oct  5 00:32:39 2016
+++ new/usr/src/uts/intel/brand/common/brand_asm.h	Wed Oct  5 00:32:39 2016
@@ -161,7 +161,7 @@
 
 #define	GET_P_BRAND_DATA(sp, pcnt, reg)					\
 	GET_PROCP(sp, pcnt, reg);					\
-	mov	P_BRAND_DATA(reg), reg		/* get p_brand_data */
+	mov	__P_BRAND_DATA(reg), reg	/* get p_brand_data */
 
 /*
  * Each of the following macros returns to the standard syscall codepath if
--- /dev/null	Wed Oct  5 00:32:40 2016
+++ new/usr/src/uts/intel/brand/lx/lx_archdep.c	Wed Oct  5 00:32:40 2016
@@ -0,0 +1,1720 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+/*
+ * LX brand Intel-specific routines.
+ */
+
+#include <sys/types.h>
+#include <sys/sunddi.h>
+#include <sys/ddi.h>
+#include <sys/brand.h>
+#include <sys/lx_brand.h>
+#include <sys/lx_misc.h>
+#include <sys/privregs.h>
+#include <sys/pcb.h>
+#include <sys/archsystm.h>
+#include <sys/stack.h>
+#include <sys/sdt.h>
+#include <sys/sysmacros.h>
+#include <sys/psw.h>
+#include <lx_errno.h>
+
+/*
+ * Argument constants for fix_segreg.
+ * See usr/src/uts/intel/ia32/os/archdep.c for the originals.
+ */
+#define	IS_CS		1
+#define	IS_NOT_CS	0
+
+extern greg_t fix_segreg(greg_t, int, model_t);
+
+
+#define	LX_REG(ucp, r)	((ucp)->uc_mcontext.gregs[(r)])
+
+#define	PSLMERGE(oldval, newval)	\
+	(((oldval) & ~PSL_USERMASK) | ((newval) & PSL_USERMASK))
+
+#ifdef __amd64
+/* 64-bit native user_regs_struct */
+typedef struct lx_user_regs64 {
+	int64_t	lxur_r15;
+	int64_t	lxur_r14;
+	int64_t	lxur_r13;
+	int64_t	lxur_r12;
+	int64_t	lxur_rbp;
+	int64_t	lxur_rbx;
+	int64_t	lxur_r11;
+	int64_t	lxur_r10;
+	int64_t	lxur_r9;
+	int64_t	lxur_r8;
+	int64_t	lxur_rax;
+	int64_t	lxur_rcx;
+	int64_t	lxur_rdx;
+	int64_t	lxur_rsi;
+	int64_t	lxur_rdi;
+	int64_t	lxur_orig_rax;
+	int64_t	lxur_rip;
+	int64_t	lxur_xcs;
+	int64_t	lxur_rflags;
+	int64_t	lxur_rsp;
+	int64_t	lxur_xss;
+	int64_t	lxur_xfs_base;
+	int64_t	lxur_xgs_base;
+	int64_t	lxur_xds;
+	int64_t	lxur_xes;
+	int64_t	lxur_xfs;
+	int64_t	lxur_xgs;
+} lx_user_regs64_t;
+
+/* 64-bit native user_fpregs_struct */
+typedef struct lx_user_fpregs64 {
+	uint16_t	lxufp_cwd;
+	uint16_t	lxufp_swd;
+	uint16_t	lxufp_ftw;
+	uint16_t	lxufp_fop;
+	uint64_t	lxufp_rip;
+	uint64_t	lxufp_rdp;
+	uint32_t	lxufp_mxcsr;
+	uint32_t	lxufp_mxcr_mask;
+	/* 8*16 bytes for each FP-reg = 128 bytes */
+	uint32_t	lxufp_st_space[32];
+	/* 16*16 bytes for each XMM-reg = 256 bytes */
+	uint32_t	lxufp_xmm_space[64];
+	uint32_t	lxufp_padding[24];
+} lx_user_fpregs64_t;
+
+/* 64-bit native user_struct */
+typedef struct lx_user64 {
+	lx_user_regs64_t	lxu_regs;
+	int32_t			lxu_fpvalid;
+	int32_t			lxu_pad0;
+	lx_user_fpregs64_t	lxu_i387;
+	uint64_t		lxu_tsize;
+	uint64_t		lxu_dsize;
+	uint64_t		lxu_ssize;
+	uint64_t		lxu_start_code;
+	uint64_t		lxu_start_stack;
+	int64_t			lxu_signal;
+	int32_t			lxu_reserved;
+	int32_t			lxu_pad1;
+	/* help gdb to locate user_regs structure */
+	caddr_t			lxu_ar0;
+	/* help gdb to locate user_fpregs structure */
+	caddr_t			lxu_fpstate;
+	uint64_t		lxu_magic;
+	char			lxu_comm[32];
+	uint64_t		lxu_debugreg[8];
+	uint64_t		lxu_error_code;
+	uint64_t		lxu_fault_address;
+} lx_user64_t;
+
+#endif /* __amd64 */
+
+/* 32-bit native user_regs_struct */
+typedef struct lx_user_regs32 {
+	int32_t	lxur_ebx;
+	int32_t	lxur_ecx;
+	int32_t	lxur_edx;
+	int32_t	lxur_esi;
+	int32_t	lxur_edi;
+	int32_t	lxur_ebp;
+	int32_t	lxur_eax;
+	int32_t	lxur_xds;
+	int32_t	lxur_xes;
+	int32_t	lxur_xfs;
+	int32_t	lxur_xgs;
+	int32_t	lxur_orig_eax;
+	int32_t	lxur_eip;
+	int32_t	lxur_xcs;
+	int32_t	lxur_eflags;
+	int32_t	lxur_esp;
+	int32_t	lxur_xss;
+} lx_user_regs32_t;
+
+/* 32-bit native user_fpregs_struct */
+typedef struct lx_user_fpregs32 {
+	int32_t		lxufp_cwd;
+	int32_t		lxufp_swd;
+	int32_t		lxufp_twd;
+	int32_t		lxufp_fip;
+	int32_t		lxufp_fcs;
+	int32_t		lxufp_foo;
+	int32_t		lxufp_fos;
+	int32_t		lxufp_st_space[20];
+} lx_user_fpregs32_t;
+
+/* 32-bit native user_fpxregs_struct */
+typedef struct lx_user_fpxregs32 {
+	uint16_t	lxufpx_cwd;
+	uint16_t	lxufpx_swd;
+	uint16_t	lxufpx_twd;
+	uint16_t	lxufpx_fop;
+	int32_t		lxufpx_fip;
+	int32_t		lxufpx_fcs;
+	int32_t		lxufpx_foo;
+	int32_t		lxufpx_fos;
+	int32_t		lxufpx_mxcsr;
+	int32_t		lxufpx_reserved;
+	/* 8*16 bytes for each FP-reg = 128 bytes */
+	int32_t		lxufpx_st_space[32];
+	/* 8*16 bytes for each XMM-reg = 128 bytes */
+	int32_t		lxufpx_xmm_space[32];
+	int32_t		lxufpx_padding[56];
+} lx_user_fpxregs32_t;
+
+/* 32-bit native user_struct */
+typedef struct lx_user32 {
+	lx_user_regs32_t	lxu_regs;
+	int32_t			lxu_fpvalid;
+	lx_user_fpregs32_t	lxu_i387;
+	uint32_t		lxu_tsize;
+	uint32_t		lxu_dsize;
+	uint32_t		lxu_ssize;
+	uint32_t		lxu_start_code;
+	uint32_t		lxu_start_stack;
+	int32_t			lxu_signal;
+	int32_t			lxu_reserved;
+	caddr32_t		lxu_ar0;
+	caddr32_t		lxu_fpstate;
+	uint32_t		lxu_magic;
+	char			lxu_comm[32];
+	int32_t			lxu_debugreg[8];
+} lx_user32_t;
+
+/*
+ * Certain version of strace (on centos6 for example) use the %cs value to
+ * determine what kind of process is being traced. Here is a sample comment:
+ *	Check CS register value. On x86-64 linux it is:
+ *	    0x33	for long mode (64 bit and x32))
+ *	    0x23	for compatibility mode (32 bit)
+ *	%ds = 0x2b for x32 mode (x86-64 in 32 bit)
+ * We can't change the %cs value in the ucp (see setgregs and _sys_rtt) so we
+ * emulate the expected value for ptrace use.
+ */
+#define	LX_CS_64BIT	0x33
+#define	LX_CS_32BIT	0x23
+
+extern int getsetcontext(int, void *);
+#if defined(_SYSCALL32_IMPL)
+extern int getsetcontext32(int, void *);
+#endif
+
+static int
+lx_rw_uc(proc_t *p, void *ucp, void *kucp, size_t ucsz, boolean_t writing)
+{
+	int error = 0;
+	size_t rem = ucsz;
+	off_t pos = 0;
+
+	VERIFY(MUTEX_HELD(&p->p_lock));
+
+	/*
+	 * Grab P_PR_LOCK so that we can drop p_lock while doing I/O.
+	 */
+	sprlock_proc(p);
+
+	/*
+	 * Drop p_lock while we do I/O to avoid deadlock with the clock thread.
+	 */
+	mutex_exit(&p->p_lock);
+	while (rem != 0) {
+		uintptr_t addr = (uintptr_t)ucp + pos;
+		size_t len = MIN(rem, PAGESIZE - (addr & PAGEOFFSET));
+
+		if (writing) {
+			error = uwrite(p, (caddr_t)kucp + pos, len, addr);
+		} else {
+			error = uread(p, (caddr_t)kucp + pos, len, addr);
+		}
+
+		if (error != 0) {
+			break;
+		}
+
+		rem -= len;
+		pos += len;
+	}
+	mutex_enter(&p->p_lock);
+
+	sprunlock(p);
+	mutex_enter(&p->p_lock);
+
+	return (error);
+}
+
+/*
+ * Read a ucontext_t from the target process, which may or may not be
+ * the current process.
+ */
+static int
+lx_read_uc(proc_t *p, void *ucp, void *kucp, size_t ucsz)
+{
+	return (lx_rw_uc(p, ucp, kucp, ucsz, B_FALSE));
+}
+
+/*
+ * Write a ucontext_t to the target process, which may or may not be
+ * the current process.
+ */
+static int
+lx_write_uc(proc_t *p, void *ucp, void *kucp, size_t ucsz)
+{
+	return (lx_rw_uc(p, ucp, kucp, ucsz, B_TRUE));
+}
+
+static void
+lx_getfpregs32(lx_lwp_data_t *lwpd, lx_user_fpregs32_t *lfp)
+{
+#ifdef __amd64
+	fpregset32_t fp;
+	getfpregs32(lwpd->br_lwp, &fp);
+#else /* __i386 */
+	fpregset_t fp;
+	getfpregs(lwpd->br_lwp, &fp);
+#endif /* __amd64 */
+
+	/*
+	 * The fpchip_state.state field should correspond to all 27 fields in
+	 * the 32-bit structure.
+	 */
+	bcopy(&fp.fp_reg_set.fpchip_state.state, lfp, sizeof (*lfp));
+}
+
+static void
+lx_setfpregs32(lx_lwp_data_t *lwpd, lx_user_fpregs32_t *lfp)
+{
+#ifdef __amd64
+	fpregset32_t fp;
+#else /* __i386 */
+	fpregset_t fp;
+#endif /* __amd64 */
+
+	/*
+	 * The fpchip_state field should correspond to all 27 fields in the
+	 * native 32-bit structure.
+	 */
+	bcopy(lfp, &fp.fp_reg_set.fpchip_state.state, sizeof (*lfp));
+
+#ifdef __amd64
+	setfpregs32(lwpd->br_lwp, &fp);
+#else /* __i386 */
+	setfpregs(lwpd->br_lwp, &fp);
+#endif /* __amd64 */
+}
+
+static int
+lx_get_user_regs32_uc(klwp_t *lwp, void *ucp, lx_user_regs32_t *lxrp)
+{
+	proc_t *p = lwptoproc(lwp);
+	ucontext32_t uc;
+
+	if (lx_read_uc(p, ucp, &uc, sizeof (uc)) != 0) {
+		return (-1);
+	}
+
+	lxrp->lxur_ebx = LX_REG(&uc, EBX);
+	lxrp->lxur_ecx = LX_REG(&uc, ECX);
+	lxrp->lxur_edx = LX_REG(&uc, EDX);
+	lxrp->lxur_esi = LX_REG(&uc, ESI);
+	lxrp->lxur_edi = LX_REG(&uc, EDI);
+	lxrp->lxur_ebp = LX_REG(&uc, EBP);
+	lxrp->lxur_eax = LX_REG(&uc, EAX);
+	lxrp->lxur_orig_eax = 0;
+
+	lxrp->lxur_eip = LX_REG(&uc, EIP);
+	lxrp->lxur_eflags = LX_REG(&uc, EFL);
+	lxrp->lxur_esp = LX_REG(&uc, UESP);
+	lxrp->lxur_xss = LX_REG(&uc, SS);
+
+	/* emulated %cs, see defines */
+	lxrp->lxur_xcs = LX_CS_32BIT;
+	lxrp->lxur_xds = LX_REG(&uc, DS);
+	lxrp->lxur_xes = LX_REG(&uc, ES);
+	lxrp->lxur_xfs = LX_REG(&uc, FS);
+	lxrp->lxur_xgs = LX_REG(&uc, GS);
+	return (0);
+}
+
+static int
+lx_get_user_regs32(lx_lwp_data_t *lwpd, lx_user_regs32_t *lxrp)
+{
+	klwp_t *lwp = lwpd->br_lwp;
+	struct regs *rp = lwptoregs(lwp);
+	void *ucp;
+#ifdef __amd64
+	struct pcb *pcb = &lwp->lwp_pcb;
+#endif
+
+	VERIFY(lwp_getdatamodel(lwp) == DATAMODEL_ILP32);
+
+	switch (lx_regs_location(lwpd, &ucp, B_FALSE)) {
+	case LX_REG_LOC_UNAVAIL:
+		return (-1);
+
+	case LX_REG_LOC_UCP:
+		return (lx_get_user_regs32_uc(lwp, ucp, lxrp));
+
+	case LX_REG_LOC_LWP:
+		/* transformation below */
+		break;
+
+	default:
+		VERIFY(0);
+		break;
+	}
+
+#ifdef __amd64
+	lxrp->lxur_ebx = (int32_t)rp->r_rbx;
+	lxrp->lxur_ecx = (int32_t)rp->r_rcx;
+	lxrp->lxur_edx = (int32_t)rp->r_rdx;
+	lxrp->lxur_esi = (int32_t)rp->r_rsi;
+	lxrp->lxur_edi = (int32_t)rp->r_rdi;
+	lxrp->lxur_ebp = (int32_t)rp->r_rbp;
+	lxrp->lxur_eax = (int32_t)rp->r_rax;
+	lxrp->lxur_orig_eax = 0;
+	lxrp->lxur_eip = (int32_t)rp->r_rip;
+	lxrp->lxur_eflags = (int32_t)rp->r_rfl;
+	lxrp->lxur_esp = (int32_t)rp->r_rsp;
+	lxrp->lxur_xss = (int32_t)rp->r_ss;
+
+	kpreempt_disable();
+	if (pcb->pcb_rupdate == 1) {
+		lxrp->lxur_xds = pcb->pcb_ds;
+		lxrp->lxur_xes = pcb->pcb_es;
+		lxrp->lxur_xfs = pcb->pcb_fs;
+		lxrp->lxur_xgs = pcb->pcb_gs;
+	} else {
+		lxrp->lxur_xds = rp->r_ds;
+		lxrp->lxur_xes = rp->r_es;
+		lxrp->lxur_xfs = rp->r_fs;
+		lxrp->lxur_xgs = rp->r_gs;
+	}
+	kpreempt_enable();
+#else /* __i386 */
+	lxrp->lxur_ebx = rp->r_ebx;
+	lxrp->lxur_ecx = rp->r_ecx;
+	lxrp->lxur_edx = rp->r_edx;
+	lxrp->lxur_esi = rp->r_esi;
+	lxrp->lxur_edi = rp->r_edi;
+	lxrp->lxur_ebp = rp->r_ebp;
+	lxrp->lxur_eax = rp->r_eax;
+	lxrp->lxur_orig_eax = 0;
+	lxrp->lxur_eip = rp->r_eip;
+	lxrp->lxur_eflags = rp->r_efl;
+	lxrp->lxur_esp = rp->r_esp;
+	lxrp->lxur_xss = rp->r_ss;
+
+	lxrp->lxur_xds = rp->r_ds;
+	lxrp->lxur_xes = rp->r_es;
+	lxrp->lxur_xfs = rp->r_fs;
+	lxrp->lxur_xgs = rp->r_gs;
+#endif /* __amd64 */
+
+	/* emulated %cs, see defines */
+	lxrp->lxur_xcs = LX_CS_32BIT;
+
+	if (lwpd->br_ptrace_whatstop == LX_PR_SYSENTRY) {
+		lxrp->lxur_eax = (int32_t)-lx_errno(ENOTSUP, EINVAL);
+		lxrp->lxur_orig_eax = (int32_t)lwpd->br_syscall_num;
+	} else if (lwpd->br_ptrace_whatstop == LX_PR_SYSEXIT) {
+		lxrp->lxur_orig_eax = (int32_t)lwpd->br_syscall_num;
+	}
+
+	return (0);
+}
+
+static int
+lx_set_user_regs32_uc(klwp_t *lwp, void *ucp, lx_user_regs32_t *lxrp)
+{
+	proc_t *p = lwptoproc(lwp);
+	ucontext32_t uc;
+
+	if (lx_read_uc(p, ucp, &uc, sizeof (uc)) != 0) {
+		return (-1);
+	}
+
+	/*
+	 * Note: we currently ignore "lxur_orig_rax" here since this
+	 * path should not be used for system call stops.
+	 */
+	LX_REG(&uc, EBP) = lxrp->lxur_ebp;
+	LX_REG(&uc, EBX) = lxrp->lxur_ebx;
+	LX_REG(&uc, EAX) = lxrp->lxur_eax;
+	LX_REG(&uc, ECX) = lxrp->lxur_ecx;
+	LX_REG(&uc, EDX) = lxrp->lxur_edx;
+	LX_REG(&uc, ESI) = lxrp->lxur_esi;
+	LX_REG(&uc, EDI) = lxrp->lxur_edi;
+	LX_REG(&uc, EIP) = lxrp->lxur_eip;
+	LX_REG(&uc, EFL) = PSLMERGE(LX_REG(&uc, EFL), lxrp->lxur_eflags);
+	LX_REG(&uc, UESP) = lxrp->lxur_esp;
+	LX_REG(&uc, SS) = fix_segreg(lxrp->lxur_xss, IS_NOT_CS,
+	    DATAMODEL_ILP32);
+
+	/* %cs is ignored because of our lies */
+	LX_REG(&uc, DS) = fix_segreg(lxrp->lxur_xds, IS_NOT_CS,
+	    DATAMODEL_ILP32);
+	LX_REG(&uc, ES) = fix_segreg(lxrp->lxur_xes, IS_NOT_CS,
+	    DATAMODEL_ILP32);
+	LX_REG(&uc, FS) = fix_segreg(lxrp->lxur_xfs, IS_NOT_CS,
+	    DATAMODEL_ILP32);
+	LX_REG(&uc, GS) = fix_segreg(lxrp->lxur_xgs, IS_NOT_CS,
+	    DATAMODEL_ILP32);
+
+	if (lx_write_uc(p, ucp, &uc, sizeof (uc)) != 0) {
+		return (-1);
+	}
+	return (0);
+}
+
+static int
+lx_set_user_regs32(lx_lwp_data_t *lwpd, lx_user_regs32_t *lxrp)
+{
+	klwp_t *lwp = lwpd->br_lwp;
+	struct regs *rp = lwptoregs(lwp);
+	void *ucp;
+#ifdef __amd64
+	struct pcb *pcb = &lwp->lwp_pcb;
+#endif
+
+	VERIFY(lwp_getdatamodel(lwp) == DATAMODEL_ILP32);
+
+	switch (lx_regs_location(lwpd, &ucp, B_TRUE)) {
+	case LX_REG_LOC_UNAVAIL:
+		return (-1);
+
+	case LX_REG_LOC_UCP:
+		return (lx_set_user_regs32_uc(lwp, ucp, lxrp));
+
+	case LX_REG_LOC_LWP:
+		/* transformation below */
+		break;
+
+	default:
+		VERIFY(0);
+		break;
+	}
+
+#ifdef __amd64
+	rp->r_rbx = (int32_t)lxrp->lxur_ebx;
+	rp->r_rcx = (int32_t)lxrp->lxur_ecx;
+	rp->r_rdx = (int32_t)lxrp->lxur_edx;
+	rp->r_rsi = (int32_t)lxrp->lxur_esi;
+	rp->r_rdi = (int32_t)lxrp->lxur_edi;
+	rp->r_rbp = (int32_t)lxrp->lxur_ebp;
+	rp->r_rax = (int32_t)lxrp->lxur_eax;
+	lwpd->br_syscall_num = (int)lxrp->lxur_orig_eax;
+	rp->r_rip = (int32_t)lxrp->lxur_eip;
+	rp->r_rfl = (int32_t)PSLMERGE(rp->r_rfl, lxrp->lxur_eflags);
+	rp->r_rsp = (int32_t)lxrp->lxur_esp;
+	rp->r_ss = (int32_t)fix_segreg(lxrp->lxur_xss, IS_NOT_CS,
+	    DATAMODEL_ILP32);
+
+	kpreempt_disable();
+	pcb->pcb_rupdate = 1;
+	pcb->pcb_ds = fix_segreg(lxrp->lxur_xds, IS_NOT_CS, DATAMODEL_ILP32);
+	pcb->pcb_es = fix_segreg(lxrp->lxur_xes, IS_NOT_CS, DATAMODEL_ILP32);
+	pcb->pcb_fs = fix_segreg(lxrp->lxur_xfs, IS_NOT_CS, DATAMODEL_ILP32);
+	pcb->pcb_gs = fix_segreg(lxrp->lxur_xgs, IS_NOT_CS, DATAMODEL_ILP32);
+	kpreempt_enable();
+#else /* __i386 */
+	rp->r_ebx = lxrp->lxur_ebx;
+	rp->r_ecx = lxrp->lxur_ecx;
+	rp->r_edx = lxrp->lxur_edx;
+	rp->r_esi = lxrp->lxur_esi;
+	rp->r_edi = lxrp->lxur_edi;
+	rp->r_ebp = lxrp->lxur_ebp;
+	rp->r_eax = lxrp->lxur_eax;
+	lwpd->br_syscall_num = (int)lxrp->lxur_orig_eax;
+	rp->r_eip = lxrp->lxur_eip;
+	rp->r_efl = PSLMERGE(rp->r_efl, lxrp->lxur_eflags);
+	rp->r_esp = lxrp->lxur_esp;
+	rp->r_ss = fix_segreg(lxrp->lxur_xss, IS_NOT_CS, DATAMODEL_ILP32);
+
+	rp->r_ds = fix_segreg(lxrp->lxur_xds, IS_NOT_CS, DATAMODEL_ILP32);
+	rp->r_es = fix_segreg(lxrp->lxur_xes, IS_NOT_CS, DATAMODEL_ILP32);
+	rp->r_fs = fix_segreg(lxrp->lxur_xfs, IS_NOT_CS, DATAMODEL_ILP32);
+	rp->r_gs = fix_segreg(lxrp->lxur_xgs, IS_NOT_CS, DATAMODEL_ILP32);
+#endif /* __amd64 */
+
+	return (0);
+}
+
+#ifdef __amd64
+
+static void
+lx_getfpregs64(lx_lwp_data_t *lwpd, lx_user_fpregs64_t *lfp)
+{
+	fpregset_t fp;
+
+	getfpregs(lwpd->br_lwp, &fp);
+	/* Drop the extra illumos status/xstatus fields when copying state */
+	bcopy(&fp.fp_reg_set.fpchip_state, lfp, sizeof (*lfp));
+}
+
+static void
+lx_setfpregs64(lx_lwp_data_t *lwpd, lx_user_fpregs64_t *lfp)
+{
+	fpregset_t fp;
+
+	/*
+	 * Since the Linux fpregs structure does not contain the same
+	 * additional status register which illumos contains, we simply
+	 * preserve the existing values when setting fp state.
+	 */
+	getfpregs(lwpd->br_lwp, &fp);
+
+	/* Copy the identically formatted state */
+	bcopy(lfp, &fp.fp_reg_set.fpchip_state, sizeof (*lfp));
+
+	setfpregs(lwpd->br_lwp, &fp);
+}
+
+static int
+lx_get_user_regs64_uc(klwp_t *lwp, void *ucp, lx_user_regs64_t *lxrp)
+{
+	proc_t *p = lwptoproc(lwp);
+
+	switch (lwp_getdatamodel(lwp)) {
+	case DATAMODEL_LP64: {
+		ucontext_t uc;
+
+		if (lx_read_uc(p, ucp, &uc, sizeof (uc)) != 0) {
+			return (-1);
+		}
+
+		lxrp->lxur_r15 = LX_REG(&uc, REG_R15);
+		lxrp->lxur_r14 = LX_REG(&uc, REG_R14);
+		lxrp->lxur_r13 = LX_REG(&uc, REG_R13);
+		lxrp->lxur_r12 = LX_REG(&uc, REG_R12);
+		lxrp->lxur_rbp = LX_REG(&uc, REG_RBP);
+		lxrp->lxur_rbx = LX_REG(&uc, REG_RBX);
+		lxrp->lxur_r11 = LX_REG(&uc, REG_R11);
+		lxrp->lxur_r10 = LX_REG(&uc, REG_R10);
+		lxrp->lxur_r9 = LX_REG(&uc, REG_R9);
+		lxrp->lxur_r8 = LX_REG(&uc, REG_R8);
+		lxrp->lxur_rax = LX_REG(&uc, REG_RAX);
+		lxrp->lxur_rcx = LX_REG(&uc, REG_RCX);
+		lxrp->lxur_rdx = LX_REG(&uc, REG_RDX);
+		lxrp->lxur_rsi = LX_REG(&uc, REG_RSI);
+		lxrp->lxur_rdi = LX_REG(&uc, REG_RDI);
+		lxrp->lxur_orig_rax = 0;
+		lxrp->lxur_rip = LX_REG(&uc, REG_RIP);
+		lxrp->lxur_rflags = LX_REG(&uc, REG_RFL);
+		lxrp->lxur_rsp = LX_REG(&uc, REG_RSP);
+		lxrp->lxur_xss = LX_REG(&uc, REG_SS);
+		lxrp->lxur_xfs_base = LX_REG(&uc, REG_FSBASE);
+		lxrp->lxur_xgs_base = LX_REG(&uc, REG_GSBASE);
+
+		lxrp->lxur_xds = LX_REG(&uc, REG_DS);
+		lxrp->lxur_xes = LX_REG(&uc, REG_ES);
+		lxrp->lxur_xfs = LX_REG(&uc, REG_FS);
+		lxrp->lxur_xgs = LX_REG(&uc, REG_GS);
+
+		/* emulated %cs, see defines */
+		lxrp->lxur_xcs = LX_CS_64BIT;
+		return (0);
+	}
+
+	case DATAMODEL_ILP32: {
+		ucontext32_t uc;
+
+		if (lx_read_uc(p, ucp, &uc, sizeof (uc)) != 0) {
+			return (-1);
+		}
+
+		lxrp->lxur_r15 = 0;
+		lxrp->lxur_r14 = 0;
+		lxrp->lxur_r13 = 0;
+		lxrp->lxur_r12 = 0;
+		lxrp->lxur_r11 = 0;
+		lxrp->lxur_r10 = 0;
+		lxrp->lxur_r9 = 0;
+		lxrp->lxur_r8 = 0;
+		lxrp->lxur_rbp = LX_REG(&uc, EBP);
+		lxrp->lxur_rbx = LX_REG(&uc, EBX);
+		lxrp->lxur_rax = LX_REG(&uc, EAX);
+		lxrp->lxur_orig_rax = 0;
+		lxrp->lxur_rcx = LX_REG(&uc, ECX);
+		lxrp->lxur_rdx = LX_REG(&uc, EDX);
+		lxrp->lxur_rsi = LX_REG(&uc, ESI);
+		lxrp->lxur_rdi = LX_REG(&uc, EDI);
+		lxrp->lxur_rip = LX_REG(&uc, EIP);
+
+		lxrp->lxur_rflags = LX_REG(&uc, EFL);
+		lxrp->lxur_rsp = LX_REG(&uc, UESP);
+		lxrp->lxur_xss = LX_REG(&uc, SS);
+		lxrp->lxur_xfs_base = 0;
+		lxrp->lxur_xgs_base = 0;
+
+		lxrp->lxur_xds = LX_REG(&uc, DS);
+		lxrp->lxur_xes = LX_REG(&uc, ES);
+		lxrp->lxur_xfs = LX_REG(&uc, FS);
+		lxrp->lxur_xgs = LX_REG(&uc, GS);
+
+		/* See comment above re: %cs register */
+		lxrp->lxur_xcs = LX_CS_32BIT;
+		return (0);
+	}
+
+	default:
+		break;
+	}
+
+	return (-1);
+}
+
+static int
+lx_get_user_regs64(lx_lwp_data_t *lwpd, lx_user_regs64_t *lxrp)
+{
+	klwp_t *lwp = lwpd->br_lwp;
+	struct regs *rp = lwptoregs(lwp);
+	struct pcb *pcb = &lwp->lwp_pcb;
+	void *ucp;
+
+	switch (lx_regs_location(lwpd, &ucp, B_FALSE)) {
+	case LX_REG_LOC_UNAVAIL:
+		return (-1);
+
+	case LX_REG_LOC_UCP:
+		return (lx_get_user_regs64_uc(lwp, ucp, lxrp));
+
+	case LX_REG_LOC_LWP:
+		/* transformation below */
+		break;
+
+	default:
+		VERIFY(0);
+		break;
+	}
+
+	lxrp->lxur_r15 = rp->r_r15;
+	lxrp->lxur_r14 = rp->r_r14;
+	lxrp->lxur_r13 = rp->r_r13;
+	lxrp->lxur_r12 = rp->r_r12;
+	lxrp->lxur_rbp = rp->r_rbp;
+	lxrp->lxur_rbx = rp->r_rbx;
+	lxrp->lxur_r11 = rp->r_r11;
+	lxrp->lxur_r10 = rp->r_r10;
+	lxrp->lxur_r9 = rp->r_r9;
+	lxrp->lxur_r8 = rp->r_r8;
+	lxrp->lxur_rax = rp->r_rax;
+	lxrp->lxur_rcx = rp->r_rcx;
+	lxrp->lxur_rdx = rp->r_rdx;
+	lxrp->lxur_rsi = rp->r_rsi;
+	lxrp->lxur_rdi = rp->r_rdi;
+	lxrp->lxur_orig_rax = 0;
+	lxrp->lxur_rip = rp->r_rip;
+
+	lxrp->lxur_rflags = rp->r_rfl;
+	lxrp->lxur_rsp = rp->r_rsp;
+	lxrp->lxur_xss = rp->r_ss;
+	lxrp->lxur_xfs_base = pcb->pcb_fsbase;
+	lxrp->lxur_xgs_base = pcb->pcb_gsbase;
+
+	/* emulated %cs, see defines */
+	switch (lwp_getdatamodel(lwp)) {
+	case DATAMODEL_LP64:
+		lxrp->lxur_xcs = LX_CS_64BIT;
+		break;
+	case DATAMODEL_ILP32:
+		lxrp->lxur_xcs = LX_CS_32BIT;
+		break;
+	default:
+		VERIFY(0);
+		break;
+	}
+
+	kpreempt_disable();
+	if (pcb->pcb_rupdate == 1) {
+		lxrp->lxur_xds = pcb->pcb_ds;
+		lxrp->lxur_xes = pcb->pcb_es;
+		lxrp->lxur_xfs = pcb->pcb_fs;
+		lxrp->lxur_xgs = pcb->pcb_gs;
+	} else {
+		lxrp->lxur_xds = rp->r_ds;
+		lxrp->lxur_xes = rp->r_es;
+		lxrp->lxur_xfs = rp->r_fs;
+		lxrp->lxur_xgs = rp->r_gs;
+	}
+	kpreempt_enable();
+
+	if (lwpd->br_ptrace_whatstop == LX_PR_SYSENTRY) {
+		lxrp->lxur_rax = -lx_errno(ENOTSUP, EINVAL);
+		lxrp->lxur_orig_rax = lwpd->br_syscall_num;
+	} else if (lwpd->br_ptrace_whatstop == LX_PR_SYSEXIT) {
+		lxrp->lxur_orig_rax = lwpd->br_syscall_num;
+	}
+
+	return (0);
+}
+
+static int
+lx_set_user_regs64_uc(klwp_t *lwp, void *ucp, lx_user_regs64_t *lxrp)
+{
+	proc_t *p = lwptoproc(lwp);
+
+	switch (lwp_getdatamodel(lwp)) {
+	case DATAMODEL_LP64: {
+		ucontext_t uc;
+
+		if (lx_read_uc(p, ucp, &uc, sizeof (uc)) != 0) {
+			return (-1);
+		}
+
+		/*
+		 * Note: we currently ignore "lxur_orig_rax" here since this
+		 * path should not be used for system call stops.
+		 */
+		LX_REG(&uc, REG_R15) = lxrp->lxur_r15;
+		LX_REG(&uc, REG_R14) = lxrp->lxur_r14;
+		LX_REG(&uc, REG_R13) = lxrp->lxur_r13;
+		LX_REG(&uc, REG_R12) = lxrp->lxur_r12;
+		LX_REG(&uc, REG_RBP) = lxrp->lxur_rbp;
+		LX_REG(&uc, REG_RBX) = lxrp->lxur_rbx;
+		LX_REG(&uc, REG_R11) = lxrp->lxur_r11;
+		LX_REG(&uc, REG_R10) = lxrp->lxur_r10;
+		LX_REG(&uc, REG_R9) = lxrp->lxur_r9;
+		LX_REG(&uc, REG_R8) = lxrp->lxur_r8;
+		LX_REG(&uc, REG_RAX) = lxrp->lxur_rax;
+		LX_REG(&uc, REG_RCX) = lxrp->lxur_rcx;
+		LX_REG(&uc, REG_RDX) = lxrp->lxur_rdx;
+		LX_REG(&uc, REG_RSI) = lxrp->lxur_rsi;
+		LX_REG(&uc, REG_RDI) = lxrp->lxur_rdi;
+		LX_REG(&uc, REG_RIP) = lxrp->lxur_rip;
+		LX_REG(&uc, REG_RFL) = PSLMERGE(LX_REG(&uc, REG_RFL),
+		    lxrp->lxur_rflags);
+		LX_REG(&uc, REG_RSP) = lxrp->lxur_rsp;
+		LX_REG(&uc, REG_SS) = fix_segreg(lxrp->lxur_xss, IS_NOT_CS,
+		    DATAMODEL_LP64);
+		LX_REG(&uc, REG_FSBASE) = lxrp->lxur_xfs_base;
+		LX_REG(&uc, REG_GSBASE) = lxrp->lxur_xgs_base;
+
+		/* %cs is ignored because of our lies */
+		LX_REG(&uc, REG_DS) = fix_segreg(lxrp->lxur_xds, IS_NOT_CS,
+		    DATAMODEL_LP64);
+		LX_REG(&uc, REG_ES) = fix_segreg(lxrp->lxur_xes, IS_NOT_CS,
+		    DATAMODEL_LP64);
+		LX_REG(&uc, REG_FS) = fix_segreg(lxrp->lxur_xfs, IS_NOT_CS,
+		    DATAMODEL_LP64);
+		LX_REG(&uc, REG_GS) = fix_segreg(lxrp->lxur_xgs, IS_NOT_CS,
+		    DATAMODEL_LP64);
+
+		if (lx_write_uc(p, ucp, &uc, sizeof (uc)) != 0) {
+			return (-1);
+		}
+
+		return (0);
+	}
+
+	case DATAMODEL_ILP32: {
+		ucontext32_t uc;
+
+		if (lx_read_uc(p, ucp, &uc, sizeof (uc)) != 0) {
+			return (-1);
+		}
+
+		/*
+		 * Note: we currently ignore "lxur_orig_rax" here since this
+		 * path should not be used for system call stops.
+		 */
+		LX_REG(&uc, EBP) = (int32_t)lxrp->lxur_rbp;
+		LX_REG(&uc, EBX) = (int32_t)lxrp->lxur_rbx;
+		LX_REG(&uc, EAX) = (int32_t)lxrp->lxur_rax;
+		LX_REG(&uc, ECX) = (int32_t)lxrp->lxur_rcx;
+		LX_REG(&uc, EDX) = (int32_t)lxrp->lxur_rdx;
+		LX_REG(&uc, ESI) = (int32_t)lxrp->lxur_rsi;
+		LX_REG(&uc, EDI) = (int32_t)lxrp->lxur_rdi;
+		LX_REG(&uc, EIP) = (int32_t)lxrp->lxur_rip;
+		LX_REG(&uc, EFL) = (int32_t)PSLMERGE(LX_REG(&uc, EFL),
+		    lxrp->lxur_rflags);
+		LX_REG(&uc, UESP) = (int32_t)lxrp->lxur_rsp;
+		LX_REG(&uc, SS) = (int32_t)fix_segreg(lxrp->lxur_xss,
+		    IS_NOT_CS, DATAMODEL_ILP32);
+
+		/* %cs is ignored because of our lies */
+		LX_REG(&uc, DS) = (int32_t)fix_segreg(lxrp->lxur_xds,
+		    IS_NOT_CS, DATAMODEL_ILP32);
+		LX_REG(&uc, ES) = (int32_t)fix_segreg(lxrp->lxur_xes,
+		    IS_NOT_CS, DATAMODEL_ILP32);
+		LX_REG(&uc, FS) = (int32_t)fix_segreg(lxrp->lxur_xfs,
+		    IS_NOT_CS, DATAMODEL_ILP32);
+		LX_REG(&uc, GS) = (int32_t)fix_segreg(lxrp->lxur_xgs,
+		    IS_NOT_CS, DATAMODEL_ILP32);
+
+		if (lx_write_uc(p, ucp, &uc, sizeof (uc)) != 0) {
+			return (-1);
+		}
+		return (0);
+	}
+
+	default:
+		break;
+	}
+
+	return (-1);
+}
+
+static int
+lx_set_user_regs64(lx_lwp_data_t *lwpd, lx_user_regs64_t *lxrp)
+{
+	klwp_t *lwp = lwpd->br_lwp;
+	struct regs *rp = lwptoregs(lwp);
+	struct pcb *pcb = &lwp->lwp_pcb;
+	void *ucp;
+
+	switch (lx_regs_location(lwpd, &ucp, B_TRUE)) {
+	case LX_REG_LOC_UNAVAIL:
+		return (-1);
+
+	case LX_REG_LOC_UCP:
+		return (lx_set_user_regs64_uc(lwp, ucp, lxrp));
+
+	case LX_REG_LOC_LWP:
+		/* transformation below */
+		break;
+
+	default:
+		VERIFY(0);
+		break;
+	}
+
+	rp->r_r15 = lxrp->lxur_r15;
+	rp->r_r14 = lxrp->lxur_r14;
+	rp->r_r13 = lxrp->lxur_r13;
+	rp->r_r12 = lxrp->lxur_r12;
+	rp->r_rbp = lxrp->lxur_rbp;
+	rp->r_rbx = lxrp->lxur_rbx;
+	rp->r_r11 = lxrp->lxur_r11;
+	rp->r_r10 = lxrp->lxur_r10;
+	rp->r_r9 = lxrp->lxur_r9;
+	rp->r_r8 = lxrp->lxur_r8;
+	rp->r_rax = lxrp->lxur_rax;
+	rp->r_rcx = lxrp->lxur_rcx;
+	rp->r_rdx = lxrp->lxur_rdx;
+	rp->r_rsi = lxrp->lxur_rsi;
+	rp->r_rdi = lxrp->lxur_rdi;
+	lwpd->br_syscall_num = (int)lxrp->lxur_orig_rax;
+	rp->r_rip = lxrp->lxur_rip;
+	rp->r_rfl = PSLMERGE(rp->r_rfl, lxrp->lxur_rflags);
+	rp->r_rsp = lxrp->lxur_rsp;
+	rp->r_ss = fix_segreg(lxrp->lxur_xss, IS_NOT_CS, DATAMODEL_LP64);
+	pcb->pcb_fsbase = lxrp->lxur_xfs_base;
+	pcb->pcb_gsbase = lxrp->lxur_xgs_base;
+
+	kpreempt_disable();
+	pcb->pcb_rupdate = 1;
+	pcb->pcb_ds = fix_segreg(lxrp->lxur_xds, IS_NOT_CS, DATAMODEL_LP64);
+	pcb->pcb_es = fix_segreg(lxrp->lxur_xes, IS_NOT_CS, DATAMODEL_LP64);
+	pcb->pcb_fs = fix_segreg(lxrp->lxur_xfs, IS_NOT_CS, DATAMODEL_LP64);
+	pcb->pcb_gs = fix_segreg(lxrp->lxur_xgs, IS_NOT_CS, DATAMODEL_LP64);
+	kpreempt_enable();
+
+	return (0);
+}
+
+#endif /* __amd64 */
+
+static int
+lx_peekuser32(lx_lwp_data_t *lwpd, uintptr_t offset, uint32_t *res)
+{
+	lx_user32_t lxu;
+	boolean_t valid = B_FALSE;
+
+	bzero(&lxu, sizeof (lxu));
+	if (offset < sizeof (lx_user_regs32_t)) {
+		if (lx_get_user_regs32(lwpd, &lxu.lxu_regs) == 0) {
+			valid = B_TRUE;
+		}
+	}
+	if (valid) {
+		uint32_t *data = (uint32_t *)&lxu;
+		*res = data[offset / sizeof (uint32_t)];
+		return (0);
+	}
+	return (-1);
+}
+
+#ifdef __amd64
+static int
+lx_peekuser64(lx_lwp_data_t *lwpd, uintptr_t offset, uintptr_t *res)
+{
+	lx_user64_t lxu;
+	boolean_t valid = B_FALSE;
+
+	bzero(&lxu, sizeof (lxu));
+	if (offset < sizeof (lx_user_regs64_t)) {
+		if (lx_get_user_regs64(lwpd, &lxu.lxu_regs) == 0) {
+			valid = B_TRUE;
+		}
+	}
+	if (valid) {
+		uintptr_t *data = (uintptr_t *)&lxu;
+		*res = data[offset / sizeof (uintptr_t)];
+		return (0);
+	}
+	return (-1);
+}
+#endif /* __amd64 */
+
+int
+lx_user_regs_copyin(lx_lwp_data_t *lwpd, void *uregsp)
+{
+	model_t target_model = lwp_getdatamodel(lwpd->br_lwp);
+
+	switch (get_udatamodel()) {
+	case DATAMODEL_ILP32:
+		if (target_model == DATAMODEL_ILP32) {
+			lx_user_regs32_t regs;
+
+			if (copyin(uregsp, &regs, sizeof (regs)) != 0) {
+				return (EFAULT);
+			}
+			if (lx_set_user_regs32(lwpd, &regs) != 0) {
+				return (EIO);
+			}
+			return (0);
+		}
+		break;
+
+#ifdef __amd64
+	case DATAMODEL_LP64:
+		if (target_model == DATAMODEL_ILP32 ||
+		    target_model == DATAMODEL_LP64) {
+			lx_user_regs64_t regs;
+
+			if (copyin(uregsp, &regs, sizeof (regs)) != 0) {
+				return (EFAULT);
+			}
+			if (lx_set_user_regs64(lwpd, &regs) != 0) {
+				return (EIO);
+			}
+			return (0);
+		}
+		break;
+#endif /* __amd64 */
+
+	default:
+		break;
+	}
+	return (EIO);
+}
+
+int
+lx_user_regs_copyout(lx_lwp_data_t *lwpd, void *uregsp)
+{
+	model_t target_model = lwp_getdatamodel(lwpd->br_lwp);
+
+	switch (get_udatamodel()) {
+	case DATAMODEL_ILP32:
+		if (target_model == DATAMODEL_ILP32) {
+			lx_user_regs32_t regs;
+
+			if (lx_get_user_regs32(lwpd, &regs) != 0) {
+				return (EIO);
+			}
+			if (copyout(&regs, uregsp, sizeof (regs)) != 0) {
+				return (EFAULT);
+			}
+			return (0);
+		}
+		break;
+
+#ifdef __amd64
+	case DATAMODEL_LP64:
+		if (target_model == DATAMODEL_ILP32 ||
+		    target_model == DATAMODEL_LP64) {
+			lx_user_regs64_t regs;
+
+			if (lx_get_user_regs64(lwpd, &regs) != 0) {
+				return (EIO);
+			}
+			if (copyout(&regs, uregsp, sizeof (regs)) != 0) {
+				return (EFAULT);
+			}
+			return (0);
+		}
+		break;
+#endif /* __amd64 */
+
+	default:
+		break;
+	}
+	return (EIO);
+}
+
+int
+lx_user_fpregs_copyin(lx_lwp_data_t *lwpd, void *uregsp)
+{
+	model_t target_model = lwp_getdatamodel(lwpd->br_lwp);
+
+	switch (get_udatamodel()) {
+	case DATAMODEL_ILP32:
+		if (target_model == DATAMODEL_ILP32) {
+			lx_user_fpregs32_t regs;
+
+			if (copyin(uregsp, &regs, sizeof (regs)) != 0) {
+				return (EFAULT);
+			}
+			lx_setfpregs32(lwpd, &regs);
+			return (0);
+		}
+		break;
+
+#ifdef __amd64
+	case DATAMODEL_LP64:
+		if (target_model == DATAMODEL_ILP32 ||
+		    target_model == DATAMODEL_LP64) {
+			lx_user_fpregs64_t regs;
+
+			if (copyin(uregsp, &regs, sizeof (regs)) != 0) {
+				return (EFAULT);
+			}
+			lx_setfpregs64(lwpd, &regs);
+			return (0);
+		}
+		break;
+#endif /* __amd64 */
+
+	default:
+		break;
+	}
+	return (EIO);
+}
+
+int
+lx_user_fpregs_copyout(lx_lwp_data_t *lwpd, void *uregsp)
+{
+	model_t target_model = lwp_getdatamodel(lwpd->br_lwp);
+
+	switch (get_udatamodel()) {
+	case DATAMODEL_ILP32:
+		if (target_model == DATAMODEL_ILP32) {
+			lx_user_fpregs32_t regs;
+
+			lx_getfpregs32(lwpd, &regs);
+			if (copyout(&regs, uregsp, sizeof (regs)) != 0) {
+				return (EFAULT);
+			}
+			return (0);
+		}
+		break;
+
+#ifdef __amd64
+	case DATAMODEL_LP64:
+		if (target_model == DATAMODEL_ILP32 ||
+		    target_model == DATAMODEL_LP64) {
+			lx_user_fpregs64_t regs;
+
+			lx_getfpregs64(lwpd, &regs);
+			if (copyout(&regs, uregsp, sizeof (regs)) != 0) {
+				return (EFAULT);
+			}
+			return (0);
+		}
+		break;
+#endif /* __amd64 */
+
+	default:
+		break;
+	}
+	return (EIO);
+}
+
+/* ARGSUSED */
+int
+lx_user_fpxregs_copyin(lx_lwp_data_t *lwpd, void *uregsp)
+{
+	/* Punt on fpxregs for now */
+	return (EIO);
+}
+
+/* ARGSUSED */
+int
+lx_user_fpxregs_copyout(lx_lwp_data_t *lwpd, void *uregsp)
+{
+	/* Punt on fpxregs for now */
+	return (EIO);
+}
+
+int
+lx_ptrace_peekuser(lx_lwp_data_t *lwpd, uintptr_t offset, void *uptr)
+{
+	model_t target_model = lwp_getdatamodel(lwpd->br_lwp);
+
+	switch (get_udatamodel()) {
+	case DATAMODEL_ILP32:
+		if ((offset & (sizeof (uint32_t) - 1)) != 0) {
+			/* Must be aligned to 32bit boundary */
+			break;
+		}
+		if (target_model == DATAMODEL_ILP32) {
+			uint32_t res;
+
+			if (lx_peekuser32(lwpd, offset, &res) != 0) {
+				return (EIO);
+			}
+			if (copyout(&res, uptr, sizeof (res)) != 0) {
+				return (EFAULT);
+			}
+			return (0);
+		}
+		break;
+
+#ifdef __amd64
+	case DATAMODEL_LP64:
+		if ((offset & (sizeof (uintptr_t) - 1)) != 0) {
+			/* Must be aligned to 64bit boundary */
+			break;
+		}
+		if (target_model == DATAMODEL_ILP32 ||
+		    target_model == DATAMODEL_LP64) {
+			uintptr_t res;
+
+			if (lx_peekuser64(lwpd, offset, &res) != 0) {
+				return (EIO);
+			}
+			if (copyout(&res, uptr, sizeof (res)) != 0) {
+				return (EFAULT);
+			}
+			return (0);
+		}
+		break;
+#endif /* __amd64 */
+
+	default:
+		break;
+	}
+	return (EIO);
+}
+
+/* ARGSUSED */
+int
+lx_ptrace_pokeuser(lx_lwp_data_t *lwpd, uintptr_t offset, void *uptr)
+{
+	return (EIO);
+}
+
+
+/*
+ * Load registers and repoint the stack and program counter.  This function is
+ * used by the B_JUMP_TO_LINUX brand system call to revector to a Linux
+ * entrypoint.
+ */
+int
+lx_runexe(klwp_t *lwp, void *ucp)
+{
+	lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+
+	/*
+	 * We should only make it here when transitioning to Linux from
+	 * the NATIVE or INIT mode.
+	 */
+	VERIFY(lwpd->br_stack_mode == LX_STACK_MODE_NATIVE ||
+	    lwpd->br_stack_mode == LX_STACK_MODE_INIT);
+
+#if defined(__amd64)
+	if (lwp_getdatamodel(lwp) == DATAMODEL_NATIVE) {
+		struct pcb *pcb = &lwp->lwp_pcb;
+
+		/*
+		 * Preserve the %fs/%gsbase value for this LWP, as set and used
+		 * by native illumos code.
+		 */
+		lwpd->br_ntv_fsbase = pcb->pcb_fsbase;
+		lwpd->br_ntv_gsbase = pcb->pcb_gsbase;
+
+		return (getsetcontext(SETCONTEXT, ucp));
+	} else {
+		return (getsetcontext32(SETCONTEXT, ucp));
+	}
+#else
+	return (getsetcontext(SETCONTEXT, ucp));
+#endif
+}
+
+/*
+ * The usermode emulation code is illumos library code.  This routine ensures
+ * the segment registers are set up correctly for native illumos code.  It
+ * should be called _after_ we have stored the outgoing Linux machine state
+ * but _before_ we return from the kernel to any illumos native code; e.g. the
+ * usermode emulation library, or any interposed signal handlers.
+ *
+ * See the comment on lwp_segregs_save() for how we handle the usermode
+ * registers when we come into the kernel and see update_sregs() for how we
+ * restore.
+ */
+void
+lx_switch_to_native(klwp_t *lwp)
+{
+#if defined(__amd64)
+	model_t datamodel = lwp_getdatamodel(lwp);
+
+	switch (datamodel) {
+	case DATAMODEL_ILP32: {
+		struct pcb *pcb = &lwp->lwp_pcb;
+
+		/*
+		 * For 32-bit processes, we ensure that the correct %gs value
+		 * is loaded:
+		 */
+		kpreempt_disable();
+		if (pcb->pcb_rupdate == 1) {
+			/*
+			 * If we are already flushing the segment registers,
+			 * then ensure we are flushing the native %gs.
+			 */
+			pcb->pcb_gs = LWPGS_SEL;
+		} else {
+			struct regs *rp = lwptoregs(lwp);
+
+			/*
+			 * If we are not flushing the segment registers yet,
+			 * only do so if %gs is not correct already:
+			 */
+			if (rp->r_gs != LWPGS_SEL) {
+				pcb->pcb_gs = LWPGS_SEL;
+
+				/*
+				 * Ensure we go out via update_sregs.
+				 */
+				pcb->pcb_rupdate = 1;
+			}
+		}
+		kpreempt_enable();
+		break;
+	}
+
+	case DATAMODEL_LP64: {
+		lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+
+		/*
+		 * For 64-bit processes we ensure that the correct %fsbase
+		 * value is loaded:
+		 */
+		if (lwpd->br_ntv_fsbase != 0) {
+			struct pcb *pcb = &lwp->lwp_pcb;
+
+			kpreempt_disable();
+			if (pcb->pcb_fsbase != lwpd->br_ntv_fsbase) {
+				pcb->pcb_fsbase = lwpd->br_ntv_fsbase;
+
+				/*
+				 * Ensure we go out via update_sregs.
+				 */
+				pcb->pcb_rupdate = 1;
+			}
+			kpreempt_enable();
+		}
+		/*
+		 * ... and the correct %gsbase
+		 */
+		if (lwpd->br_ntv_gsbase != 0) {
+			struct pcb *pcb = &lwp->lwp_pcb;
+
+			kpreempt_disable();
+			if (pcb->pcb_gsbase != lwpd->br_ntv_gsbase) {
+				pcb->pcb_gsbase = lwpd->br_ntv_gsbase;
+
+				/*
+				 * Ensure we go out via update_sregs.
+				 */
+				pcb->pcb_rupdate = 1;
+			}
+			kpreempt_enable();
+		}
+		break;
+	}
+
+	default:
+		cmn_err(CE_PANIC, "unknown data model: %d", datamodel);
+	}
+#elif defined(__i386)
+	struct regs *rp = lwptoregs(lwp);
+
+	rp->r_gs = LWPGS_SEL;
+#else
+#error "unknown x86"
+#endif
+}
+
+#if defined(__amd64)
+/*
+ * Call frame for the 64-bit usermode emulation handler:
+ *    lx_emulate(ucontext_t *ucp, int syscall_num, uintptr_t *args)
+ *
+ * old sp: --------------------------------------------------------------
+ *  |      - ucontext_t              (register state for emulation)
+ *  |      - uintptr_t[6]            (system call arguments array)
+ *  V      --------------------------------------------------------------
+ * new sp: - bogus return address
+ *
+ * Arguments are passed in registers, per the AMD64 ABI: %rdi, %rsi and %rdx.
+ */
+void
+lx_emulate_user(klwp_t *lwp, int syscall_num, uintptr_t *args)
+{
+	lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+	struct regs *rp = lwptoregs(lwp);
+	label_t lab;
+	uintptr_t uc_addr;
+	uintptr_t args_addr;
+	uintptr_t top;
+	/*
+	 * Variables used after on_fault() returns for a fault
+	 * must be volatile.
+	 */
+	volatile size_t frsz;
+	volatile uintptr_t sp;
+	volatile proc_t *p = lwptoproc(lwp);
+	volatile int watched;
+
+	/*
+	 * We should not be able to get here unless we are running Linux
+	 * code for a system call we cannot emulate in the kernel.
+	 */
+	VERIFY(lwpd->br_stack_mode == LX_STACK_MODE_BRAND);
+
+	/*
+	 * The AMD64 ABI requires us to align the return address on the stack
+	 * so that when the called function pushes %rbp, the stack is 16-byte
+	 * aligned.
+	 *
+	 * This routine, like the amd64 version of sendsig(), depends on
+	 * STACK_ALIGN being 16 and STACK_ENTRY_ALIGN being 8.
+	 */
+#if STACK_ALIGN != 16 || STACK_ENTRY_ALIGN != 8
+#error "lx_emulate_user() amd64 did not find the expected stack alignments"
+#endif
+
+	/*
+	 * We begin at the current native stack pointer, and reserve space for
+	 * the ucontext_t we are copying onto the stack, as well as the call
+	 * arguments for the usermode emulation handler.
+	 *
+	 * We 16-byte align the entire frame, and then unalign it again by
+	 * adding space for the return address.
+	 */
+	frsz = SA(sizeof (ucontext_t)) + SA(6 * sizeof (uintptr_t)) +
+	    sizeof (uintptr_t);
+	VERIFY((frsz & (STACK_ALIGN - 1UL)) == 8);
+	VERIFY((frsz & (STACK_ENTRY_ALIGN - 1UL)) == 0);
+
+	if (lwpd->br_ntv_stack == lwpd->br_ntv_stack_current) {
+		/*
+		 * Nobody else is using the stack right now, so start at the
+		 * top.
+		 */
+		top = lwpd->br_ntv_stack_current;
+	} else {
+		/*
+		 * Drop below the 128-byte reserved region of the stack frame
+		 * we are interrupting.
+		 */
+		top = lwpd->br_ntv_stack_current - STACK_RESERVE;
+	}
+	top = top & ~(STACK_ALIGN - 1);
+	sp = top - frsz;
+
+	uc_addr = top - SA(sizeof (ucontext_t));
+	args_addr = uc_addr - SA(6 * sizeof (uintptr_t));
+
+	watched = watch_disable_addr((caddr_t)sp, frsz, S_WRITE);
+
+	/*
+	 * Save the register state we preserved on the way into this brand
+	 * system call and drop it on the native stack.
+	 */
+	{
+		/*
+		 * Note: the amd64 ucontext_t is 864 bytes.
+		 */
+		ucontext_t uc;
+
+		/*
+		 * We do not want to save the signal mask for an emulation
+		 * context.  Some emulated system calls alter the signal mask;
+		 * restoring it when the emulation is complete would clobber
+		 * those intentional side effects.
+		 */
+		savecontext(&uc, NULL);
+
+		if (on_fault(&lab)) {
+			goto badstack;
+		}
+
+		/*
+		 * Mark this as a system call emulation context:
+		 */
+		uc.uc_brand_data[0] = (void *)((uintptr_t)
+		    uc.uc_brand_data[0] | LX_UC_FRAME_IS_SYSCALL);
+
+		copyout_noerr(&uc, (void *)(uintptr_t)uc_addr, sizeof (uc));
+	}
+
+	DTRACE_PROBE3(oldcontext__set, klwp_t *, lwp,
+	    uintptr_t, lwp->lwp_oldcontext, uintptr_t, uc_addr);
+	lwp->lwp_oldcontext = (uintptr_t)uc_addr;
+
+	/*
+	 * Copy the system call arguments out to userland:
+	 */
+	copyout_noerr(args, (void *)(uintptr_t)args_addr,
+	    6 * sizeof (uintptr_t));
+
+	/*
+	 * Drop the bogus return address on the stack.
+	 */
+	suword64_noerr((void *)sp, 0);
+
+	no_fault();
+	if (watched) {
+		watch_enable_addr((caddr_t)sp, frsz, S_WRITE);
+	}
+
+	/*
+	 * Pass the arguments to lx_emulate() in the appropriate registers.
+	 */
+	rp->r_rdi = uc_addr;
+	rp->r_rsi = syscall_num;
+	rp->r_rdx = args_addr;
+
+	/*
+	 * In order to be able to restore %edx, we need to JUSTRETURN.
+	 */
+	lwp->lwp_eosys = JUSTRETURN;
+	curthread->t_post_sys = 1;
+	aston(curthread);
+
+	/*
+	 * Set stack pointer and return address to the usermode emulation
+	 * handler:
+	 */
+	lwpd->br_stack_mode = LX_STACK_MODE_NATIVE;
+	lx_lwp_set_native_stack_current(lwpd, sp);
+
+	/*
+	 * Divert execution, on our return, to the usermode emulation stack
+	 * and handler:
+	 */
+	rp->r_fp = 0;
+	rp->r_sp = sp;
+	rp->r_pc = ptolxproc(p)->l_handler;
+
+	/*
+	 * Fix up segment registers, etc.
+	 */
+	lx_switch_to_native(lwp);
+
+	return;
+
+badstack:
+	no_fault();
+	if (watched) {
+		watch_enable_addr((caddr_t)sp, frsz, S_WRITE);
+	}
+
+#ifdef DEBUG
+	printf("lx_emulate_user: bad native stack cmd=%s, pid=%d, sp=0x%lx\n",
+	    PTOU(p)->u_comm, p->p_pid, sp);
+#endif
+
+	exit(CLD_KILLED, SIGSEGV);
+}
+
+#if defined(_SYSCALL32_IMPL)
+/*
+ * Call frame for the 32-bit usermode emulation handler:
+ *    lx_emulate(ucontext_t *ucp, int syscall_num, uintptr_t *args)
+ *
+ * old sp: --------------------------------------------------------------
+ *  |      - ucontext_t              (register state for emulation)
+ *  |      - uintptr_t[6]            (system call arguments array)
+ *  |      --------------------------------------------------------------
+ *  |      - arg2: uintptr_t *       (pointer to arguments array above)
+ *  |      - arg1: int               (system call number)
+ *  V      - arg0: ucontext_t *      (pointer to context above)
+ * new sp: - bogus return address
+ */
+struct lx_emu_frame32 {
+	caddr32_t	retaddr;	/* 0 */
+	caddr32_t	ucontextp;	/* 4 */
+	int32_t		syscall_num;	/* 8 */
+	caddr32_t	argsp;		/* c */
+};
+
+/*
+ * This function arranges for the lwp to execute the usermode emulation handler
+ * for this system call.  The mechanism is similar to signal handling, and this
+ * function is modelled on sendsig32().
+ */
+void
+lx_emulate_user32(klwp_t *lwp, int syscall_num, uintptr_t *args)
+{
+	lx_lwp_data_t *lwpd = lwptolxlwp(lwp);
+	struct regs *rp = lwptoregs(lwp);
+	label_t lab;
+	caddr32_t uc_addr;
+	caddr32_t args_addr;
+	caddr32_t top;
+	/*
+	 * Variables used after on_fault() returns for a fault
+	 * must be volatile.
+	 */
+	volatile size_t frsz;
+	volatile caddr32_t sp;
+	volatile proc_t *p = lwptoproc(lwp);
+	volatile int watched;
+
+	/*
+	 * We should not be able to get here unless we are running Linux
+	 * code for a system call we cannot emulate in the kernel.
+	 */
+	VERIFY(lwpd->br_stack_mode == LX_STACK_MODE_BRAND);
+
+	/*
+	 * We begin at the current native stack pointer, and reserve space for
+	 * the ucontext_t we are copying onto the stack, as well as the call
+	 * arguments for the usermode emulation handler.
+	 */
+	frsz = SA32(sizeof (ucontext32_t)) + SA32(6 * sizeof (uint32_t)) +
+	    SA32(sizeof (struct lx_emu_frame32));
+	VERIFY((frsz & (STACK_ALIGN32 - 1)) == 0);
+
+	top = (caddr32_t)(lwpd->br_ntv_stack_current & ~(STACK_ALIGN32 - 1));
+	sp = top - frsz;
+
+	uc_addr = top - SA32(sizeof (ucontext32_t));
+	args_addr = uc_addr - SA32(6 * sizeof (uint32_t));
+
+	watched = watch_disable_addr((caddr_t)(uintptr_t)sp, frsz, S_WRITE);
+
+	/*
+	 * Save the register state we preserved on the way into this brand
+	 * system call and drop it on the native stack.
+	 */
+	{
+		/*
+		 * Note: ucontext32_t is 512 bytes.
+		 */
+		ucontext32_t uc;
+
+		/*
+		 * We do not want to save the signal mask for an emulation
+		 * context.  Some emulated system calls alter the signal mask;
+		 * restoring it when the emulation is complete would clobber
+		 * those intentional side effects.
+		 */
+		savecontext32(&uc, NULL);
+
+		if (on_fault(&lab)) {
+			goto badstack;
+		}
+
+		/*
+		 * Mark this as a system call emulation context:
+		 */
+		uc.uc_brand_data[0] |= LX_UC_FRAME_IS_SYSCALL;
+		copyout_noerr(&uc, (void *)(uintptr_t)uc_addr, sizeof (uc));
+	}
+
+	DTRACE_PROBE3(oldcontext__set, klwp_t *, lwp,
+	    uintptr_t, lwp->lwp_oldcontext, uintptr_t, uc_addr);
+	lwp->lwp_oldcontext = (uintptr_t)uc_addr;
+
+	/*
+	 * Copy the system call arguments out to userland:
+	 */
+	{
+		uint32_t args32[6];
+
+		args32[0] = args[0];
+		args32[1] = args[1];
+		args32[2] = args[2];
+		args32[3] = args[3];
+		args32[4] = args[4];
+		args32[5] = args[5];
+
+		copyout_noerr(&args32, (void *)(uintptr_t)args_addr,
+		    sizeof (args32));
+	}
+
+	/*
+	 * Assemble the call frame on the stack.
+	 */
+	{
+		struct lx_emu_frame32 frm;
+
+		frm.retaddr = 0;
+		frm.ucontextp = uc_addr;
+		frm.argsp = args_addr;
+		frm.syscall_num = syscall_num;
+
+		copyout_noerr(&frm, (void *)(uintptr_t)sp, sizeof (frm));
+	}
+
+	no_fault();
+	if (watched) {
+		watch_enable_addr((caddr_t)(uintptr_t)sp, frsz, S_WRITE);
+	}
+
+	/*
+	 * Set stack pointer and return address to the usermode emulation
+	 * handler:
+	 */
+	lwpd->br_stack_mode = LX_STACK_MODE_NATIVE;
+	lx_lwp_set_native_stack_current(lwpd, sp);
+
+	/*
+	 * Divert execution, on our return, to the usermode emulation stack
+	 * and handler:
+	 */
+	rp->r_fp = 0;
+	rp->r_sp = sp;
+	rp->r_pc = ptolxproc(p)->l_handler;
+
+	/*
+	 * Fix up segment registers, etc.
+	 */
+	lx_switch_to_native(lwp);
+
+	return;
+
+badstack:
+	no_fault();
+	if (watched) {
+		watch_enable_addr((caddr_t)(uintptr_t)sp, frsz, S_WRITE);
+	}
+
+#ifdef DEBUG
+	printf("lx_emulate_user32: bad native stack cmd=%s, pid=%d, sp=0x%x\n",
+	    PTOU(p)->u_comm, p->p_pid, sp);
+#endif
+
+	exit(CLD_KILLED, SIGSEGV);
+}
+#endif	/* _SYSCALL32_IMPL */
+
+#else	/* !__amd64 (__i386) */
+
+/* ARGSUSED */
+void
+lx_emulate_user(klwp_t *lwp, int syscall_num, uintptr_t *args)
+{
+	cmn_err(CE_WARN, "%s: no 32-bit kernel support", __FUNCTION__);
+	exit(CLD_KILLED, SIGSYS);
+}
+
+#endif	/* __amd64 */
--- old/usr/src/uts/intel/dld/Makefile	Wed Oct  5 00:32:40 2016
+++ new/usr/src/uts/intel/dld/Makefile	Wed Oct  5 00:32:40 2016
@@ -55,7 +55,6 @@
 #
 CFLAGS		+= $(CCVERBOSE)
 LDFLAGS		+= -dy -N misc/dls -N misc/mac
-INC_PATH	+= -I$(UTSBASE)/common/io/bpf
 
 #
 # For now, disable these lint checks; maintainers should endeavor
--- old/usr/src/uts/intel/dls/Makefile	Wed Oct  5 00:32:40 2016
+++ new/usr/src/uts/intel/dls/Makefile	Wed Oct  5 00:32:40 2016
@@ -53,7 +53,6 @@
 #
 CFLAGS		+= $(CCVERBOSE)
 LDFLAGS		+= -dy -N misc/mac
-INC_PATH	+= -I$(UTSBASE)/common/io/bpf
 
 #
 # For now, disable these lint checks; maintainers should endeavor
--- old/usr/src/uts/intel/dtrace/fasttrap_isa.c	Wed Oct  5 00:32:40 2016
+++ new/usr/src/uts/intel/dtrace/fasttrap_isa.c	Wed Oct  5 00:32:40 2016
@@ -24,7 +24,9 @@
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
+/*
+ * Copyright (c) 2015, Joyent, Inc. All rights reserved.
+ */
 
 #include <sys/fasttrap_isa.h>
 #include <sys/fasttrap_impl.h>
@@ -38,6 +40,9 @@
 #include <sys/sysmacros.h>
 #include <sys/trap.h>
 #include <sys/archsystm.h>
+#include <sys/proc.h>
+#include <sys/brand.h>
+#include <sys/machbrand.h>
 
 /*
  * Lossless User-Land Tracing on x86
@@ -1394,6 +1399,14 @@
 #if defined(__amd64)
 		if (p->p_model == DATAMODEL_LP64) {
 			addr = lwp->lwp_pcb.pcb_fsbase;
+
+			/*
+			 * If we're branded, convert the fsbase from the
+			 * brand's fsbase to the native fsbase.
+			 */
+			if (PROC_IS_BRANDED(p) && BRMOP(p)->b_fsbase != NULL)
+				addr = BRMOP(p)->b_fsbase(lwp, addr);
+
 			addr += sizeof (void *);
 		} else {
 			addr = lwp->lwp_pcb.pcb_gsbase;
--- old/usr/src/uts/intel/elfexec/Makefile	Wed Oct  5 00:32:40 2016
+++ new/usr/src/uts/intel/elfexec/Makefile	Wed Oct  5 00:32:40 2016
@@ -23,6 +23,7 @@
 #
 # Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
+# Copyright 2016 Joyent, Inc.
 #
 
 #
@@ -60,6 +61,8 @@
 LINT_TARGET	= $(MODULE).lint
 INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
 
+INC_PATH	+= -I$(UTSBASE)/intel/ia32
+
 #
 # For now, disable these lint checks; maintainers should endeavor
 # to investigate and remove these for maximum lint coverage.
--- /dev/null	Wed Oct  5 00:32:40 2016
+++ new/usr/src/uts/intel/genassym/Makefile	Wed Oct  5 00:32:40 2016
@@ -0,0 +1,85 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+#
+#	This makefile drives the production of genassym.h through
+#	compile time intialized data.
+#
+#	intel architecture dependent
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+GENASSYM_H	= $(GENASSYM_DIR)/$(OBJS_DIR)/genassym.h
+OFFSETS_SRC	= $(GENASSYM_DIR)/offsets.in
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(GENASSYM_H)
+
+INC_PATH	+= -I$(UTSBASE)/common/brand/lx
+
+#
+#	Overrides
+#
+CLEANFILES	= Nothing_to_remove
+CLOBBERFILES	= $(GENASSYM_H) Nothing_to_remove
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+clean.lint:
+
+install:	def
+
+#
+# Create genassym.h
+#
+$(GENASSYM_H): $(OFFSETS_SRC)
+	$(OFFSETS_CREATE) <$(OFFSETS_SRC) >$@
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
--- /dev/null	Wed Oct  5 00:32:40 2016
+++ new/usr/src/uts/intel/genassym/offsets.in	Wed Oct  5 00:32:40 2016
@@ -0,0 +1,43 @@
+\
+\ CDDL HEADER START
+\
+\ The contents of this file are subject to the terms of the
+\ Common Development and Distribution License (the "License").
+\ You may not use this file except in compliance with the License.
+\
+\ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+\ or http://www.opensolaris.org/os/licensing.
+\ See the License for the specific language governing permissions
+\ and limitations under the License.
+\
+\ When distributing Covered Code, include this CDDL HEADER in each
+\ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+\ If applicable, add the following below this CDDL HEADER, with the
+\ fields enclosed by brackets "[]" replaced with your own identifying
+\ information: Portions Copyright [yyyy] [name of copyright owner]
+\
+\ CDDL HEADER END
+\
+\
+\ Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
+\ Use is subject to license terms.
+\ Copyright 2015 Joyent, Inc.
+\
+
+\
+\ offsets.in: input file to produce the architecture-dependent genassym.h
+\ using the ctfstabs program
+\
+
+#ifndef	_GENASSYM
+#define	_GENASSYM
+#endif
+
+#include <sys/lx_brand.h>
+
+lx_proc_data
+	l_handler
+
+lx_lwp_data
+	br_lx_fsbase
+	br_ntv_fsbase
--- old/usr/src/uts/intel/ia32/ml/i86_subr.s	Wed Oct  5 00:32:40 2016
+++ new/usr/src/uts/intel/ia32/ml/i86_subr.s	Wed Oct  5 00:32:40 2016
@@ -23,6 +23,7 @@
  * Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
  * Copyright (c) 2014 by Delphix. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 
 /*
@@ -3590,29 +3591,13 @@
 {}
 
 int64_t timedelta;
-hrtime_t hres_last_tick;
-volatile timestruc_t hrestime;
-int64_t hrestime_adj;
-volatile int hres_lock;
 hrtime_t hrtime_base;
 
 #else	/* __lint */
 
-	DGDEF3(hrestime, _MUL(2, CLONGSIZE), 8)
-	.NWORD	0, 0
-
-	DGDEF3(hrestime_adj, 8, 8)
-	.long	0, 0
-
-	DGDEF3(hres_last_tick, 8, 8)
-	.long	0, 0
-
 	DGDEF3(timedelta, 8, 8)
 	.long	0, 0
 
-	DGDEF3(hres_lock, 4, 8)
-	.long	0
-
 	/*
 	 * initialized to a non zero value to make pc_gethrtime()
 	 * work correctly even before clock is initialized
--- old/usr/src/uts/intel/ia32/ml/modstubs.s	Wed Oct  5 00:32:41 2016
+++ new/usr/src/uts/intel/ia32/ml/modstubs.s	Wed Oct  5 00:32:41 2016
@@ -50,7 +50,7 @@
  *	NOTE: Use NO_UNLOAD_STUBs if the module is NOT unloadable once it is
  *	      loaded.
  */
-#define	MAXNARG	10
+#define	MAXNARG	12
 
 /*
  * WARNING: there is no check for forgetting to write END_MODULE,
@@ -184,7 +184,7 @@
 	pushq	%rcx
 	pushq	%r8
 	pushq	%r9
-	/* (next 4 args, if any, are already on the stack above %rbp) */
+	/* (next 6 args, if any, are already on the stack above %rbp) */
 	movq	%r15, %rdi
 	call	mod_hold_stub		/* mod_hold_stub(mod_stub_info *) */
 	cmpl	$-1, %eax		/* error? */
@@ -195,7 +195,7 @@
 	jmp	.L2
 .L1:
 	/*
-	 * copy MAXNARG == 10 incoming arguments
+	 * copy MAXNARG == 12 incoming arguments
 	 */
 	popq	%r9
 	popq	%r8
@@ -219,8 +219,10 @@
 	pushq	(%rsp, %r11, 8)
 	pushq	(%rsp, %r11, 8)
 	pushq	(%rsp, %r11, 8)
+	pushq	(%rsp, %r11, 8)
+	pushq	(%rsp, %r11, 8)
 	call	*(%r15)			/* call the stub fn(arg, ..) */
-	addq	$0x20, %rsp		/* pop off last 4 args */
+	addq	$0x30, %rsp		/* pop off last 6 args */
 	pushq	%rax			/* save any return values */
 	pushq	%rdx
 	movq	%r15, %rdi
@@ -338,6 +340,8 @@
 	pushl	(%esp, %ecx, 4)		/ push MAXNARG times
 	pushl	(%esp, %ecx, 4)
 	pushl	(%esp, %ecx, 4)
+	pushl	(%esp, %ecx, 4)
+	pushl	(%esp, %ecx, 4)
 	pushl	(%esp, %ecx, 4)
 	pushl	(%esp, %ecx, 4)
 	pushl	(%esp, %ecx, 4)
--- old/usr/src/uts/intel/ia32/os/archdep.c	Wed Oct  5 00:32:41 2016
+++ new/usr/src/uts/intel/ia32/os/archdep.c	Wed Oct  5 00:32:41 2016
@@ -25,7 +25,7 @@
 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
 /*	  All Rights Reserved  	*/
 /*
- * Copyright (c) 2012, Joyent, Inc.  All rights reserved.
+ * Copyright 2015 Joyent, Inc.
  * Copyright 2012 Nexenta Systems, Inc.  All rights reserved.
  */
 
@@ -575,6 +575,13 @@
 	if (src->uc_flags & UC_FPU)
 		fpregset_32ton(&src->uc_mcontext.fpregs,
 		    &dst->uc_mcontext.fpregs);
+
+	/*
+	 * Copy the brand-private data:
+	 */
+	dst->uc_brand_data[0] = (void *)(uintptr_t)src->uc_brand_data[0];
+	dst->uc_brand_data[1] = (void *)(uintptr_t)src->uc_brand_data[1];
+	dst->uc_brand_data[2] = (void *)(uintptr_t)src->uc_brand_data[2];
 }
 
 #endif	/* _SYSCALL32_IMPL */
@@ -629,9 +636,11 @@
 #define	IS_NOT_CS	0
 
 /*ARGSUSED*/
-static greg_t
+greg_t
 fix_segreg(greg_t sr, int iscs, model_t datamodel)
 {
+	kthread_t *t = curthread;
+
 	switch (sr &= 0xffff) {
 
 	case 0:
@@ -667,6 +676,19 @@
 		break;
 	}
 
+	/*
+	 * Allow this process's brand to do any necessary segment register
+	 * manipulation.
+	 */
+	if (PROC_IS_BRANDED(t->t_procp) && BRMOP(t->t_procp)->b_fixsegreg) {
+		greg_t bsr = BRMOP(t->t_procp)->b_fixsegreg(sr, datamodel);
+
+		if (bsr == 0 && iscs == IS_CS)
+			return (0 | SEL_UPL);
+		else
+			return (bsr);
+	}
+
 	/*
 	 * Force it into the LDT in ring 3 for 32-bit processes, which by
 	 * default do not have an LDT, so that any attempt to use an invalid
--- old/usr/src/uts/intel/ia32/os/desctbls.c	Wed Oct  5 00:32:41 2016
+++ new/usr/src/uts/intel/ia32/os/desctbls.c	Wed Oct  5 00:32:41 2016
@@ -161,7 +161,7 @@
  * The brand infrastructure interposes on two handlers, and we use one as a
  * NULL signpost.
  */
-static struct interposing_handler brand_tbl[2];
+static struct interposing_handler brand_tbl[3];
 
 /*
  * software prototypes for default local descriptor table
@@ -976,6 +976,12 @@
 	set_gatesegd(&idt[T_MCE], &mcetrap, KCS_SEL, SDT_SYSIGT, TRP_KPL, 0);
 	set_gatesegd(&idt[T_SIMDFPE], &xmtrap, KCS_SEL, SDT_SYSIGT, TRP_KPL, 0);
 
+ 	/*
+	 * install "int80" handler at, well, 0x80.
+	 */
+	set_gatesegd(&idt0[T_INT80], &sys_int80, KCS_SEL, SDT_SYSIGT, TRP_UPL,
+	    0);
+
 	/*
 	 * install fast trap handler at 210.
 	 */
@@ -1001,21 +1007,27 @@
 	    SDT_SYSIGT, TRP_UPL, 0);
 
 	/*
-	 * Prepare interposing descriptor for the syscall handler
-	 * and cache copy of the default descriptor.
+-	 * Prepare interposing descriptors for the branded "int80"
+-	 * and syscall handlers and cache copies of the default
+-	 * descriptors.
 	 */
-	brand_tbl[0].ih_inum = T_SYSCALLINT;
-	brand_tbl[0].ih_default_desc = idt0[T_SYSCALLINT];
+	brand_tbl[0].ih_inum = T_INT80;
+	brand_tbl[0].ih_default_desc = idt0[T_INT80];
+	set_gatesegd(&(brand_tbl[0].ih_interp_desc), &brand_sys_int80, KCS_SEL,
+	    SDT_SYSIGT, TRP_UPL, 0);
 
+	brand_tbl[1].ih_inum = T_SYSCALLINT;
+	brand_tbl[1].ih_default_desc = idt0[T_SYSCALLINT];
+
 #if defined(__amd64)
-	set_gatesegd(&(brand_tbl[0].ih_interp_desc), &brand_sys_syscall_int,
+	set_gatesegd(&(brand_tbl[1].ih_interp_desc), &brand_sys_syscall_int,
 	    KCS_SEL, SDT_SYSIGT, TRP_UPL, 0);
 #elif defined(__i386)
-	set_gatesegd(&(brand_tbl[0].ih_interp_desc), &brand_sys_call,
+	set_gatesegd(&(brand_tbl[1].ih_interp_desc), &brand_sys_call,
 	    KCS_SEL, SDT_SYSIGT, TRP_UPL, 0);
 #endif	/* __i386 */
 
-	brand_tbl[1].ih_inum = 0;
+	brand_tbl[2].ih_inum = 0;
 }
 
 #if defined(__xpv)
--- old/usr/src/uts/intel/ia32/os/sendsig.c	Wed Oct  5 00:32:41 2016
+++ new/usr/src/uts/intel/ia32/os/sendsig.c	Wed Oct  5 00:32:41 2016
@@ -20,6 +20,9 @@
  */
 
 /*
+ * Copyright 2015 Joyent, Inc.
+ */
+/*
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
@@ -87,6 +90,8 @@
 #include <sys/kdi.h>
 #include <sys/contract_impl.h>
 #include <sys/x86_archext.h>
+#include <sys/brand.h>
+#include <sys/sdt.h>
 
 /*
  * Construct the execution environment for the user's signal
@@ -186,7 +191,18 @@
 	newstack = sigismember(&PTOU(curproc)->u_sigonstack, sig) &&
 	    !(lwp->lwp_sigaltstack.ss_flags & (SS_ONSTACK|SS_DISABLE));
 
-	if (newstack) {
+	/*
+	 * If this is a branded process, the brand may provide an alternate
+	 * stack pointer for signal delivery:
+	 */
+	if (PROC_IS_BRANDED(p) && BROP(p)->b_sendsig_stack != NULL) {
+		/*
+		 * Use the stack pointer value provided by the brand,
+		 * accounting for the 128-byte reserved region.
+		 */
+		newstack = 0;
+		fp = BROP(p)->b_sendsig_stack(sig) - STACK_RESERVE;
+	} else if (newstack) {
 		fp = (caddr_t)(SA((uintptr_t)lwp->lwp_sigaltstack.ss_sp) +
 		    SA(lwp->lwp_sigaltstack.ss_size) - STACK_ALIGN);
 	} else {
@@ -296,6 +312,8 @@
 	kmem_free(tuc, sizeof (*tuc));
 	tuc = NULL;
 
+	DTRACE_PROBE3(oldcontext__set, klwp_t *, lwp,
+	    uintptr_t, lwp->lwp_oldcontext, uintptr_t, (uintptr_t)uc);
 	lwp->lwp_oldcontext = (uintptr_t)uc;
 
 	if (newstack) {
@@ -345,6 +363,14 @@
 	}
 
 	/*
+	 * Allow the brand to perform additional book-keeping once the signal
+	 * handling frame has been fully assembled:
+	 */
+	if (PROC_IS_BRANDED(p) && BROP(p)->b_sendsig != NULL) {
+		BROP(p)->b_sendsig(sig);
+	}
+
+	/*
 	 * Don't set lwp_eosys here.  sendsig() is called via psig() after
 	 * lwp_eosys is handled, so setting it here would affect the next
 	 * system call.
@@ -420,7 +446,17 @@
 	newstack = sigismember(&PTOU(curproc)->u_sigonstack, sig) &&
 	    !(lwp->lwp_sigaltstack.ss_flags & (SS_ONSTACK|SS_DISABLE));
 
-	if (newstack) {
+	/*
+	 * If this is a branded process, the brand may provide an alternate
+	 * stack pointer for signal delivery:
+	 */
+	if (PROC_IS_BRANDED(p) && BROP(p)->b_sendsig_stack != NULL) {
+		/*
+		 * Use the stack pointer value provided by the brand:
+		 */
+		newstack = 0;
+		fp = BROP(p)->b_sendsig_stack(sig);
+	} else if (newstack) {
 		fp = (caddr_t)(SA32((uintptr_t)lwp->lwp_sigaltstack.ss_sp) +
 		    SA32(lwp->lwp_sigaltstack.ss_size) - STACK_ALIGN32);
 	} else if ((rp->r_ss & 0xffff) != UDS_SEL) {
@@ -435,8 +471,9 @@
 			    USEGD_GETBASE(&ldt[SELTOIDX(rp->r_ss)]);
 		else
 			fp = (caddr_t)rp->r_sp;
-	} else
+	} else {
 		fp = (caddr_t)rp->r_sp;
+	}
 
 	/*
 	 * Force proper stack pointer alignment, even in the face of a
@@ -517,6 +554,8 @@
 	kmem_free(tuc, sizeof (*tuc));
 	tuc = NULL;
 
+	DTRACE_PROBE3(oldcontext__set, klwp_t *, lwp,
+	    uintptr_t, lwp->lwp_oldcontext, uintptr_t, (uintptr_t)uc);
 	lwp->lwp_oldcontext = (uintptr_t)uc;
 
 	if (newstack) {
@@ -566,6 +605,14 @@
 	}
 
 	/*
+	 * Allow the brand to perform additional book-keeping once the signal
+	 * handling frame has been fully assembled:
+	 */
+	if (PROC_IS_BRANDED(p) && BROP(p)->b_sendsig != NULL) {
+		BROP(p)->b_sendsig(sig);
+	}
+
+	/*
 	 * Don't set lwp_eosys here.  sendsig() is called via psig() after
 	 * lwp_eosys is handled, so setting it here would affect the next
 	 * system call.
@@ -643,7 +690,17 @@
 	newstack = sigismember(&PTOU(curproc)->u_sigonstack, sig) &&
 	    !(lwp->lwp_sigaltstack.ss_flags & (SS_ONSTACK|SS_DISABLE));
 
-	if (newstack) {
+	/*
+	 * If this is a branded process, the brand may provide an alternate
+	 * stack pointer for signal delivery:
+	 */
+	if (PROC_IS_BRANDED(p) && BROP(p)->b_sendsig_stack != NULL) {
+		/*
+		 * Use the stack pointer value provided by the brand:
+		 */
+		newstack = 0;
+		fp = BROP(p)->b_sendsig_stack(sig);
+	} else if (newstack) {
 		fp = (caddr_t)(SA((uintptr_t)lwp->lwp_sigaltstack.ss_sp) +
 		    SA(lwp->lwp_sigaltstack.ss_size) - STACK_ALIGN);
 	} else if ((rp->r_ss & 0xffff) != UDS_SEL) {
@@ -658,8 +715,9 @@
 			    USEGD_GETBASE(&ldt[SELTOIDX(rp->r_ss)]);
 		else
 			fp = (caddr_t)rp->r_sp;
-	} else
+	} else {
 		fp = (caddr_t)rp->r_sp;
+	}
 
 	/*
 	 * Force proper stack pointer alignment, even in the face of a
@@ -737,6 +795,8 @@
 	kmem_free(tuc, sizeof (*tuc));
 	tuc = NULL;
 
+	DTRACE_PROBE3(oldcontext__set, klwp_t *, lwp,
+	    uintptr_t, lwp->lwp_oldcontext, uintptr_t, (uintptr_t)uc);
 	lwp->lwp_oldcontext = (uintptr_t)uc;
 
 	if (newstack) {
@@ -773,6 +833,14 @@
 		rp->r_ss = UDS_SEL;
 	}
 
+	/*
+	 * Allow the brand to perform additional book-keeping once the signal
+	 * handling frame has been fully assembled:
+	 */
+	if (PROC_IS_BRANDED(p) && BROP(p)->b_sendsig != NULL) {
+		BROP(p)->b_sendsig(sig);
+	}
+
 	/*
 	 * Don't set lwp_eosys here.  sendsig() is called via psig() after
 	 * lwp_eosys is handled, so setting it here would affect the next
--- /dev/null	Wed Oct  5 00:32:41 2016
+++ new/usr/src/uts/intel/ia32/sys/comm_page_util.h	Wed Oct  5 00:32:41 2016
@@ -0,0 +1,33 @@
+/*
+ * 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 Joyent, Inc.
+ */
+
+#ifndef _COMM_PAGE_UTIL_H
+#define	_COMM_PAGE_UTIL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(_KERNEL)
+
+extern caddr_t comm_page_mapin();
+
+#endif /* defined(_KERNEL) */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _COMM_PAGE_UTIL_H */
--- old/usr/src/uts/intel/ia32/syscall/getcontext.c	Wed Oct  5 00:32:41 2016
+++ new/usr/src/uts/intel/ia32/syscall/getcontext.c	Wed Oct  5 00:32:41 2016
@@ -20,6 +20,9 @@
  */
 
 /*
+ * Copyright 2015 Joyent, Inc.
+ */
+/*
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
@@ -46,6 +49,7 @@
 #include <sys/schedctl.h>
 #include <sys/debug.h>
 #include <sys/sysmacros.h>
+#include <sys/sdt.h>
 
 /*
  * Save user context.
@@ -125,7 +129,23 @@
 	else
 		ucp->uc_flags &= ~UC_FPU;
 
-	sigktou(mask, &ucp->uc_sigmask);
+	if (mask != NULL) {
+		/*
+		 * Save signal mask.
+		 */
+		sigktou(mask, &ucp->uc_sigmask);
+	} else {
+		ucp->uc_flags &= ~UC_SIGMASK;
+		bzero(&ucp->uc_sigmask, sizeof (ucp->uc_sigmask));
+	}
+
+	if (PROC_IS_BRANDED(p) && BROP(p)->b_savecontext != NULL) {
+		/*
+		 * Allow the brand the chance to modify the context we
+		 * saved:
+		 */
+		BROP(p)->b_savecontext(ucp);
+	}
 }
 
 /*
@@ -136,7 +156,19 @@
 {
 	kthread_t *t = curthread;
 	klwp_t *lwp = ttolwp(t);
+	proc_t *p = lwptoproc(lwp);
 
+	if (PROC_IS_BRANDED(p) && BROP(p)->b_restorecontext != NULL) {
+		/*
+		 * Allow the brand the chance to modify the context before
+		 * we restore it:
+		 */
+		BROP(p)->b_restorecontext(ucp);
+	}
+
+	DTRACE_PROBE3(oldcontext__set, klwp_t *, lwp,
+	    uintptr_t, lwp->lwp_oldcontext,
+	    uintptr_t, (uintptr_t)ucp->uc_link);
 	lwp->lwp_oldcontext = (uintptr_t)ucp->uc_link;
 
 	if (ucp->uc_flags & UC_STACK) {
@@ -184,6 +216,7 @@
 	ucontext_t *ucp;
 	klwp_t *lwp = ttolwp(curthread);
 	stack_t dummy_stk;
+	proc_t *p = lwptoproc(lwp);
 
 	/*
 	 * In future releases, when the ucontext structure grows,
@@ -228,6 +261,15 @@
 			return (set_errno(EFAULT));
 		}
 
+		/*
+		 * If this is a branded process, copy in the brand-private
+		 * data:
+		 */
+		if (PROC_IS_BRANDED(p) && copyin(&ucp->uc_brand_data,
+		    &uc.uc_brand_data, sizeof (uc.uc_brand_data)) != 0) {
+			return (set_errno(EFAULT));
+		}
+
 		restorecontext(&uc);
 
 		if ((uc.uc_flags & UC_STACK) && (lwp->lwp_ustack != 0))
@@ -311,7 +353,23 @@
 	else
 		ucp->uc_flags &= ~UC_FPU;
 
-	sigktou(mask, &ucp->uc_sigmask);
+	if (mask != NULL) {
+		/*
+		 * Save signal mask.
+		 */
+		sigktou(mask, &ucp->uc_sigmask);
+	} else {
+		ucp->uc_flags &= ~UC_SIGMASK;
+		bzero(&ucp->uc_sigmask, sizeof (ucp->uc_sigmask));
+	}
+
+	if (PROC_IS_BRANDED(p) && BROP(p)->b_savecontext32 != NULL) {
+		/*
+		 * Allow the brand the chance to modify the context we
+		 * saved:
+		 */
+		BROP(p)->b_savecontext32(ucp);
+	}
 }
 
 int
@@ -323,6 +381,7 @@
 	klwp_t *lwp = ttolwp(curthread);
 	caddr32_t ustack32;
 	stack32_t dummy_stk32;
+	proc_t *p = lwptoproc(lwp);
 
 	switch (flag) {
 	default:
@@ -353,6 +412,15 @@
 		    sizeof (uc.uc_mcontext.fpregs))) {
 			return (set_errno(EFAULT));
 		}
+
+		/*
+		 * If this is a branded process, copy in the brand-private
+		 * data:
+		 */
+		if (PROC_IS_BRANDED(p) && copyin(&ucp->uc_brand_data,
+		    &uc.uc_brand_data, sizeof (uc.uc_brand_data)) != 0) {
+			return (set_errno(EFAULT));
+		}
 
 		ucontext_32ton(&uc, &ucnat);
 		restorecontext(&ucnat);
--- old/usr/src/uts/intel/icmp/Makefile	Wed Oct  5 00:32:41 2016
+++ new/usr/src/uts/intel/icmp/Makefile	Wed Oct  5 00:32:41 2016
@@ -23,6 +23,7 @@
 #
 # Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
+# Copyright 2016 Joyent, Inc.
 #
 #	This makefile drives the production of the icmp IP driver
 #
@@ -63,6 +64,8 @@
 LINT_TARGET	= $(MODULE).lint
 INSTALL_TARGET	= $(BINARY) $(ROOTMODULE) $(ROOTLINK) $(ROOT_CONFFILE)
 
+INC_PATH	+= -I$(UTSBASE)/common/io/bpf
+
 #
 #	depends on ip and sockfs
 #
--- /dev/null	Wed Oct  5 00:32:42 2016
+++ new/usr/src/uts/intel/inotify/Makefile	Wed Oct  5 00:32:42 2016
@@ -0,0 +1,70 @@
+#
+# 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 (c) 2014 Joyent, Inc.  All rights reserved.
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= inotify
+OBJECTS		= $(INOTIFY_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(INOTIFY_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(USR_DRV_DIR)/$(MODULE)
+CONF_SRCDIR	= $(UTSBASE)/common/io
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+LINTTAGS	+= -erroff=E_STRUCT_DERIVED_FROM_FLEX_MBR
+CERRWARN	+= -_gcc=-Wno-parentheses
+LDFLAGS		+= -dy -Nfs/specfs
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY) $(SRC_CONFILE)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
--- old/usr/src/uts/intel/iptun/Makefile	Wed Oct  5 00:32:42 2016
+++ new/usr/src/uts/intel/iptun/Makefile	Wed Oct  5 00:32:42 2016
@@ -54,7 +54,6 @@
 #
 CFLAGS		+= $(CCVERBOSE)
 LDFLAGS		+= -dy -Ndrv/dld -Nmisc/dls -Nmisc/mac -Ndrv/ip
-INC_PATH        += -I$(UTSBASE)/common/io/bpf
 
 LINTTAGS	+= -erroff=E_BAD_PTR_CAST_ALIGN
 LINTTAGS	+= -erroff=E_PTRDIFF_OVERFLOW
--- /dev/null	Wed Oct  5 00:32:42 2016
+++ new/usr/src/uts/intel/lx_brand/Makefile	Wed Oct  5 00:32:42 2016
@@ -0,0 +1,112 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+# Copyright 2016 Joyent, Inc.
+#
+#	This makefile drives the production of the kernel component of
+#	the lx brand
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE		= ../..
+
+#
+#	Path to where brand common sources live
+#
+LX_CMN		= $(SRC)/common/brand/lx
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= lx_brand
+OBJECTS		= $(LX_BRAND_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(LX_BRAND_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(USR_BRAND_DIR)/$(MODULE)
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+INC_PATH	+= -I$(UTSBASE)/common/brand/lx -I$(LX_CMN) -I$(SRC)/common
+INC_PATH	+= -I$(UTSBASE)/common/inet/sockmods -I$(UTSBASE)/common/io/bpf
+INC_PATH	+= -I$(UTSBASE)/common/fs/sockfs
+INC_PATH	+= -I$(UTSBASE)/common/fs/zfs
+AS_INC_PATH	+= -I$(UTSBASE)/i86pc/genassym/$(OBJS_DIR)
+
+#
+# lint pass one enforcement
+#
+CFLAGS		+= $(CCVERBOSE)
+
+LDFLAGS		+= -dy -Nexec/elfexec -Nfs/fifofs -Nfs/sockfs -Ndrv/ip -Nfs/zfs
+
+#
+# For now, disable these lint checks; maintainers should endeavor
+# to investigate and remove these for maximum lint coverage.
+# Please do not carry these forward to new Makefiles.
+#
+LINTTAGS	+= -erroff=E_ASSIGN_NARROW_CONV
+LINTTAGS	+= -erroff=E_SUSPICIOUS_COMPARISON
+LINTTAGS	+= -erroff=E_TRUE_LOGICAL_EXPR
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
+
+#
+#	Include brand-specific rules
+#
+
+include $(UTSBASE)/intel/lx_brand/Makefile.rules
--- /dev/null	Wed Oct  5 00:32:42 2016
+++ new/usr/src/uts/intel/lx_brand/Makefile.rules	Wed Oct  5 00:32:42 2016
@@ -0,0 +1,103 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+# Copyright 2016 Joyent, Inc.
+#
+#
+
+#
+#	Section 1a: C object build rules
+#
+$(OBJS_DIR_OBJ64)/%.o:		$(UTSBASE)/common/brand/lx/os/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
+$(OBJS_DIR_DBG64)/%.o:		$(UTSBASE)/common/brand/lx/os/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
+$(OBJS_DIR_OBJ64)/%.o:		$(UTSBASE)/common/brand/lx/syscall/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
+$(OBJS_DIR_DBG64)/%.o:		$(UTSBASE)/common/brand/lx/syscall/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
+$(OBJS_DIR_OBJ64)/%.o:		$(UTSBASE)/intel/brand/lx/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
+$(OBJS_DIR_DBG64)/%.o:		$(UTSBASE)/intel/brand/lx/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
+$(OBJS_DIR_OBJ64)/%.o:		$(UTSBASE)/intel/brand/lx/%.s
+	$(COMPILE.s) -I$(UTSBASE)/i86pc -o $@ $<
+
+$(OBJS_DIR_OBJ64)/%.o:		$(LX_CMN)/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
+$(OBJS_DIR_DBG64)/%.o:		$(UTSBASE)/intel/brand/lx/%.s
+	$(COMPILE.s) -I$(UTSBASE)/i86pc -o $@ $<
+
+$(OBJS_DIR)/%.o:		$(UTSBASE)/common/brand/lx/os/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
+$(OBJS_DIR)/%.o:		$(UTSBASE)/common/brand/lx/syscall/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
+$(OBJS_DIR)/%.o:		$(UTSBASE)/intel/brand/lx/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
+$(OBJS_DIR)/%.o:		$(LX_CMN)/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
+$(OBJS_DIR)/%.o:		$(UTSBASE)/intel/brand/lx/%.s
+	$(COMPILE.s) -I$(UTSBASE)/i86pc -o $@ $<
+
+#
+#	Section 1b: Lint `object' build rules.
+#
+$(LINTS_DIR)/%.ln:		$(UTSBASE)/common/brand/lx/os/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
+
+$(LINTS_DIR)/%.ln:		$(UTSBASE)/common/brand/lx/syscall/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
+
+$(LINTS_DIR)/%.ln:		$(LX_CMN)/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
+
+$(LINTS_DIR)/%.ln:		$(UTSBASE)/intel/brand/lx/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
+
+$(LINTS_DIR)/%.ln:		$(UTSBASE)/intel/brand/lx/%.s
+	@($(LHEAD) $(LINT.s) $< $(LTAIL))
+
+$(LINTS_DIR)/%.ln:		$(UTSBASE)/intel/brand/lx/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
--- /dev/null	Wed Oct  5 00:32:42 2016
+++ new/usr/src/uts/intel/lx_cgroup/Makefile	Wed Oct  5 00:32:42 2016
@@ -0,0 +1,57 @@
+#
+# 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 2015 Joyent, Inc.
+#
+
+UTSBASE	= ../..
+
+LX_CMN		= $(SRC)/common/brand/lx
+
+MODULE		= lx_cgroup
+OBJECTS		= $(LX_CGROUP_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(LX_CGROUP_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(USR_FS_DIR)/$(MODULE)
+
+INC_PATH	+= -I$(UTSBASE)/common/brand/lx -I$(LX_CMN)
+
+include $(UTSBASE)/intel/Makefile.intel
+
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+CFLAGS		+= $(CCVERBOSE)
+
+LDFLAGS		+= -dy -Nbrand/lx_brand
+
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+include $(UTSBASE)/intel/Makefile.targ
+
+include $(UTSBASE)/intel/lx_cgroup/Makefile.rules
--- /dev/null	Wed Oct  5 00:32:42 2016
+++ new/usr/src/uts/intel/lx_cgroup/Makefile.rules	Wed Oct  5 00:32:42 2016
@@ -0,0 +1,21 @@
+#
+# 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 2015 Joyent, Inc.  All rights reserved.
+#
+
+$(OBJS_DIR)/%.o:		$(UTSBASE)/common/brand/lx/cgroups/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
+$(LINTS_DIR)/%.ln:		$(UTSBASE)/common/brand/lx/cgroups/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
--- /dev/null	Wed Oct  5 00:32:42 2016
+++ new/usr/src/uts/intel/lx_devfs/Makefile	Wed Oct  5 00:32:42 2016
@@ -0,0 +1,57 @@
+#
+# 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 2015 Joyent, Inc.
+#
+
+UTSBASE	= ../..
+
+LX_CMN		= $(SRC)/common/brand/lx
+
+MODULE		= lx_devfs
+OBJECTS		= $(LX_DEVFS_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(LX_DEVFS_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(USR_FS_DIR)/$(MODULE)
+
+INC_PATH	+= -I$(UTSBASE)/common/brand/lx -I$(LX_CMN)
+
+include $(UTSBASE)/intel/Makefile.intel
+
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+CFLAGS		+= $(CCVERBOSE)
+
+LDFLAGS		+= -dy -Nbrand/lx_brand
+
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+include $(UTSBASE)/intel/Makefile.targ
+
+include $(UTSBASE)/intel/lx_devfs/Makefile.rules
--- /dev/null	Wed Oct  5 00:32:42 2016
+++ new/usr/src/uts/intel/lx_devfs/Makefile.rules	Wed Oct  5 00:32:42 2016
@@ -0,0 +1,21 @@
+#
+# 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 2015 Joyent, Inc.  All rights reserved.
+#
+
+$(OBJS_DIR)/%.o:		$(UTSBASE)/common/brand/lx/devfs/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
+$(LINTS_DIR)/%.ln:		$(UTSBASE)/common/brand/lx/devfs/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
--- /dev/null	Wed Oct  5 00:32:42 2016
+++ new/usr/src/uts/intel/lx_netlink/Makefile	Wed Oct  5 00:32:42 2016
@@ -0,0 +1,77 @@
+#
+# 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 (c) 2014 Joyent, Inc.  All rights reserved.
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= lx_netlink
+OBJECTS		= $(LX_NETLINK_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(LX_NETLINK_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(ROOT_SOCK_DIR)/$(MODULE)
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+LINTTAGS	+= -erroff=E_BAD_PTR_CAST_ALIGN
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+CPPFLAGS	+= -I$(UTSBASE)/common/brand/lx
+LDFLAGS		+= -dy -Ndrv/ip -Nfs/sockfs -Nbrand/lx_brand
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
+
+$(OBJS_DIR)/%.o:	$(UTSBASE)/common/brand/lx/io/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
+$(LINTS_DIR)/%.ln:	$(UTSBASE)/common/brand/lx/io/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
--- /dev/null	Wed Oct  5 00:32:42 2016
+++ new/usr/src/uts/intel/lx_proc/Makefile	Wed Oct  5 00:32:42 2016
@@ -0,0 +1,117 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# uts/intel/lx_proc/Makefile
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# Copyright 2015 Joyent, Inc.
+#
+#	This makefile drives the production of the lxproc file system
+#	kernel module.
+#
+#	i86 architecture dependent
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Path to where brand common sources live
+#
+LX_CMN		= $(SRC)/common/brand/lx
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= lx_proc
+OBJECTS		= $(LX_PROC_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(LX_PROC_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(USR_FS_DIR)/$(MODULE)
+
+INC_PATH	+= -I$(UTSBASE)/common/brand/lx -I$(LX_CMN)
+INC_PATH	+= -I$(UTSBASE)/common/fs/zfs
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+#
+#	Overrides.
+#
+CFLAGS		+= $(CCVERBOSE)
+
+#
+#	Depends on procfs and lx_brand
+#
+LDFLAGS		+= -dy -Nfs/procfs -Nbrand/lx_brand -Ndrv/inotify -Ndrv/ip
+LDFLAGS		+= -Nfs/sockfs
+
+#
+# For now, disable these lint checks; maintainers should endeavor
+# to investigate and remove these for maximum lint coverage.
+# Please do not carry these forward to new Makefiles.
+#
+LINTTAGS	+= -erroff=E_PTRDIFF_OVERFLOW
+LINTTAGS	+= -erroff=E_ASSIGN_NARROW_CONV
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
+
+#
+#	Include brand-specific rules
+#
+
+include $(UTSBASE)/intel/lx_proc/Makefile.rules
--- /dev/null	Wed Oct  5 00:32:42 2016
+++ new/usr/src/uts/intel/lx_proc/Makefile.rules	Wed Oct  5 00:32:42 2016
@@ -0,0 +1,38 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#ident	"%Z%%M%	%I%	%E% SMI"
+
+#
+#	Section 1a: C object build rules
+#
+$(OBJS_DIR)/%.o:		$(UTSBASE)/common/brand/lx/procfs/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
+#
+#	Section 1b: Lint `object' build rules.
+#
+$(LINTS_DIR)/%.ln:		$(UTSBASE)/common/brand/lx/procfs/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
--- /dev/null	Wed Oct  5 00:32:42 2016
+++ new/usr/src/uts/intel/lx_ptm/Makefile	Wed Oct  5 00:32:42 2016
@@ -0,0 +1,91 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# uts/intel/lx_ptm/Makefile
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+#	This makefile drives the production of the lx_ptm driver
+#
+#	intel architecture dependent
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= lx_ptm
+OBJECTS		= $(LX_PTM_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(LX_PTM_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(USR_DRV_DIR)/$(MODULE)
+CONF_SRCDIR	= $(UTSBASE)/common/brand/lx/io
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY) $(SRC_CONFILE)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+CPPFLAGS	+= -I$(UTSBASE)/common/brand/lx
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
+
+$(OBJS_DIR)/%.o:	$(UTSBASE)/common/brand/lx/io/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
+$(LINTS_DIR)/%.ln:	$(UTSBASE)/common/brand/lx/io/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
--- /dev/null	Wed Oct  5 00:32:42 2016
+++ new/usr/src/uts/intel/lx_sysfs/Makefile	Wed Oct  5 00:32:42 2016
@@ -0,0 +1,66 @@
+#
+# 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 Joyent, Inc.
+#
+
+UTSBASE	= ../..
+
+LX_CMN		= $(SRC)/common/brand/lx
+
+MODULE		= lx_sysfs
+OBJECTS		= $(LX_SYS_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(LX_SYS_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(USR_FS_DIR)/$(MODULE)
+
+INC_PATH	+= -I$(UTSBASE)/common/brand/lx -I$(LX_CMN)
+
+include $(UTSBASE)/intel/Makefile.intel
+
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+CFLAGS		+= $(CCVERBOSE)
+
+LDFLAGS		+= -dy -Nbrand/lx_brand -Ndrv/ip
+
+#
+# For now, disable these lint checks; maintainers should endeavor
+# to investigate and remove these for maximum lint coverage.
+# Please do not carry these forward to new Makefiles.
+#
+# XXX JJ
+# LINTTAGS	+= -erroff=E_PTRDIFF_OVERFLOW
+# LINTTAGS	+= -erroff=E_ASSIGN_NARROW_CONV
+
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+include $(UTSBASE)/intel/Makefile.targ
+
+include $(UTSBASE)/intel/lx_sysfs/Makefile.rules
--- /dev/null	Wed Oct  5 00:32:42 2016
+++ new/usr/src/uts/intel/lx_sysfs/Makefile.rules	Wed Oct  5 00:32:42 2016
@@ -0,0 +1,21 @@
+#
+# 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 2015 Joyent, Inc.  All rights reserved.
+#
+
+$(OBJS_DIR)/%.o:		$(UTSBASE)/common/brand/lx/sysfs/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
+$(LINTS_DIR)/%.ln:		$(UTSBASE)/common/brand/lx/sysfs/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
--- /dev/null	Wed Oct  5 00:32:42 2016
+++ new/usr/src/uts/intel/lx_systrace/Makefile	Wed Oct  5 00:32:42 2016
@@ -0,0 +1,80 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+UTSBASE	= ../..
+
+MODULE		= lx_systrace
+OBJECTS		= $(LX_SYSTRACE_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(LX_SYSTRACE_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(USR_DRV_DIR)/$(MODULE)
+ROOTLINK	= $(USR_DTRACE_DIR)/$(MODULE)
+CONF_SRCDIR	= $(UTSBASE)/common/brand/lx/dtrace
+
+include $(UTSBASE)/intel/Makefile.intel
+
+ALL_TARGET	= $(BINARY) $(SRC_CONFILE)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE) $(ROOTLINK) $(ROOT_CONFFILE)
+
+CPPFLAGS += -I$(UTSBASE)/common/brand/lx
+
+LDFLAGS         += -dy -Ndrv/dtrace -Nbrand/lx_brand
+
+#
+# For now, disable these lint checks; maintainers should endeavor
+# to investigate and remove these for maximum lint coverage.
+# Please do not carry these forward to new Makefiles.
+#
+LINTTAGS	+= -erroff=E_STATIC_UNUSED
+
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+$(ROOTLINK):	$(USR_DTRACE_DIR) $(ROOTMODULE)
+	-$(RM) $@; ln $(ROOTMODULE) $@
+
+include $(UTSBASE)/intel/Makefile.targ
+
+$(OBJS_DIR)/%.o:	$(UTSBASE)/common/brand/lx/dtrace/%.c
+	$(COMPILE.c) -o $@ $<
+	$(CTFCONVERT_O)
+
+$(LINTS_DIR)/%.ln:	$(UTSBASE)/common/brand/lx/dtrace/%.c
+	@($(LHEAD) $(LINT.c) $< $(LTAIL))
--- /dev/null	Wed Oct  5 00:32:42 2016
+++ new/usr/src/uts/intel/lxautofs/Makefile	Wed Oct  5 00:32:42 2016
@@ -0,0 +1,114 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+# Copyright 2016 Joyent, Inc.
+#
+
+#
+#	This makefile drives the production of the lxautofs file system
+#	kernel module.
+#
+#	i86 architecture dependent
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+#	Note that the name of the actual filesystem is lxautofs and
+#	not lx_autofs.  This is becase filesystem names are stupidly
+#	limited to 8 characters.
+#
+MODULE		= lxautofs
+OBJECTS		= $(LX_AUTOFS_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(LX_AUTOFS_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(USR_DRV_DIR)/$(MODULE)
+ROOTLINK	= $(USR_FS_DIR)/$(MODULE)
+CONF_SRCDIR	= $(UTSBASE)/common/brand/lx/autofs
+
+INC_PATH	+= -I$(UTSBASE)/common/brand/lx
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/intel/Makefile.intel
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE) $(ROOTLINK) $(ROOT_CONFFILE)
+
+#
+#	Overrides.
+#
+CFLAGS		+= $(CCVERBOSE)
+LDFLAGS		+= -dy -Nfs/nfs
+
+#
+# For now, disable these lint checks; maintainers should endeavor
+# to investigate and remove these for maximum lint coverage.
+# Please do not carry these forward to new Makefiles.
+#
+LINTTAGS	+= -erroff=E_ASSIGN_NARROW_CONV
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+$(ROOTLINK):    $(ROOT_FS_DIR) $(ROOTMODULE)
+	-$(RM) $@; ln $(ROOTMODULE) $@
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/intel/Makefile.targ
+
+#
+#	Include brand-specific rules
+#
+
+include $(UTSBASE)/intel/lxautofs/Makefile.rules
--- old/usr/src/uts/intel/mac/Makefile	Wed Oct  5 00:32:42 2016
+++ new/usr/src/uts/intel/mac/Makefile	Wed Oct  5 00:32:42 2016
@@ -56,7 +56,6 @@
 #
 CFLAGS		+= $(CCVERBOSE)
 LDFLAGS		+= -dy
-INC_PATH	+= -I$(UTSBASE)/common/io/bpf
 
 LINTTAGS	+= -erroff=E_PTRDIFF_OVERFLOW
 LINTTAGS	+= -erroff=E_BAD_PTR_CAST_ALIGN
--- old/usr/src/uts/intel/mac_ether/Makefile	Wed Oct  5 00:32:43 2016
+++ new/usr/src/uts/intel/mac_ether/Makefile	Wed Oct  5 00:32:43 2016
@@ -56,7 +56,6 @@
 #
 CFLAGS		+= $(CCVERBOSE)
 LDFLAGS		+= -dy -N misc/mac
-INC_PATH	+= -I$(UTSBASE)/common/io/bpf
 
 #
 #	Default build targets.
--- old/usr/src/uts/intel/mac_ib/Makefile	Wed Oct  5 00:32:43 2016
+++ new/usr/src/uts/intel/mac_ib/Makefile	Wed Oct  5 00:32:43 2016
@@ -56,7 +56,6 @@
 #
 CFLAGS		+= $(CCVERBOSE)
 LDFLAGS		+= -dy -N misc/mac
-INC_PATH	+= -I$(UTSBASE)/common/io/bpf
 
 #
 #	Default build targets.
--- old/usr/src/uts/intel/mac_wifi/Makefile	Wed Oct  5 00:32:43 2016
+++ new/usr/src/uts/intel/mac_wifi/Makefile	Wed Oct  5 00:32:43 2016
@@ -58,7 +58,6 @@
 #
 CFLAGS		+= $(CCVERBOSE)
 LDFLAGS		+= -dy -Nmisc/mac
-INC_PATH	+= -I$(UTSBASE)/common/io/bpf
 
 #
 #	Default build targets.
--- old/usr/src/uts/intel/sockpfp/Makefile	Wed Oct  5 00:32:43 2016
+++ new/usr/src/uts/intel/sockpfp/Makefile	Wed Oct  5 00:32:43 2016
@@ -58,7 +58,7 @@
 #
 CFLAGS += $(CCVERBOSE)
 
-LDFLAGS += -dy -Nfs/sockfs -Nmisc/dls -Nmisc/mac -Ndrv/bpf -Ndrv/ip
+LDFLAGS += -dy -Nfs/sockfs -Nmisc/dls -Nmisc/mac -Ndrv/ip
 INC_PATH += -I$(UTSBASE)/common/inet/sockmods -I$(UTSBASE)/common/io/bpf
 
 #
--- old/usr/src/uts/intel/spdsock/Makefile	Wed Oct  5 00:32:43 2016
+++ new/usr/src/uts/intel/spdsock/Makefile	Wed Oct  5 00:32:43 2016
@@ -60,8 +60,6 @@
 #
 LDFLAGS += -dy -Ndrv/ip
 
-INC_PATH += -I$(UTSBASE)/common/io/bpf
-
 #
 # For now, disable these lint checks; maintainers should endeavor
 # to investigate and remove these for maximum lint coverage.
--- old/usr/src/uts/intel/sys/machbrand.h	Wed Oct  5 00:32:43 2016
+++ new/usr/src/uts/intel/sys/machbrand.h	Wed Oct  5 00:32:43 2016
@@ -20,6 +20,7 @@
  */
 /*
  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 
 #ifndef _SYS_MACHBRAND_H
@@ -32,20 +33,25 @@
 #ifndef	_ASM
 
 #include <sys/model.h>
+#include <sys/thread.h>
 
 struct brand_mach_ops {
 	void	(*b_sysenter)(void);
+	void	(*b_int80)(void);
 	void	(*b_int91)(void);
 	void	(*b_syscall)(void);
 	void	(*b_syscall32)(void);
+	greg_t	(*b_fixsegreg)(greg_t, model_t);
+	uintptr_t (*b_fsbase)(klwp_t *, uintptr_t);
 };
 
 #endif	/* _ASM */
 
 #define	BRAND_CB_SYSENTER	0
-#define	BRAND_CB_INT91		1
-#define	BRAND_CB_SYSCALL	2
-#define	BRAND_CB_SYSCALL32	3
+#define	BRAND_CB_INT80		1
+#define	BRAND_CB_INT91		2
+#define	BRAND_CB_SYSCALL	3
+#define	BRAND_CB_SYSCALL32	4
 
 #ifdef	__cplusplus
 }
--- old/usr/src/uts/intel/sys/machlock.h	Wed Oct  5 00:32:43 2016
+++ new/usr/src/uts/intel/sys/machlock.h	Wed Oct  5 00:32:43 2016
@@ -21,13 +21,12 @@
 /*
  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright 2016 Joyent, Inc.
  */
 
 #ifndef _SYS_MACHLOCK_H
 #define	_SYS_MACHLOCK_H
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 #ifndef _ASM
 #include <sys/types.h>
 #include <sys/time.h>
@@ -76,7 +75,7 @@
 /*
  * Externs for CLOCK_LOCK and clock resolution
  */
-extern volatile int hres_lock;
+extern volatile uint32_t hres_lock;
 extern hrtime_t hrtime_base;
 extern int clock_res;
 
--- old/usr/src/uts/intel/sys/segments.h	Wed Oct  5 00:32:43 2016
+++ new/usr/src/uts/intel/sys/segments.h	Wed Oct  5 00:32:43 2016
@@ -2,7 +2,7 @@
  * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 /*
- * Copyright 2011 Joyent, Inc. All rights reserved.
+ * Copyright 2016 Joyent, Inc.
  */
 
 #ifndef	_SYS_SEGMENTS_H
@@ -522,6 +522,7 @@
 #define	GDT_B16DATA	4	/* bios call 16 bit data descriptor */
 #define	GDT_B64CODE	5	/* dboot 64 bit code descriptor */
 #define	GDT_BGSTMP	7	/* kmdb descriptor only used early in boot */
+#define	GDT_CPUID	16	/* store numeric id of current CPU */
 
 #if defined(__amd64)
 
@@ -530,8 +531,8 @@
 #define	GDT_U32CODE	8	/* 32-bit process on 64-bit kernel %cs */
 #define	GDT_UDATA	9	/* user data seg %ds (32 and 64 bit) */
 #define	GDT_UCODE	10	/* native user code  seg %cs */
-#define	GDT_LDT		12	/* LDT for current process */
-#define	GDT_KTSS	14	/* kernel tss */
+#define	GDT_LDT		12	/* (12-13) LDT for current process */
+#define	GDT_KTSS	14	/* (14-15) kernel tss */
 #define	GDT_FS		GDT_NULL /* kernel %fs segment selector */
 #define	GDT_GS		GDT_NULL /* kernel %gs segment selector */
 #define	GDT_LWPFS	55	/* lwp private %fs segment selector (32-bit) */
@@ -683,6 +684,8 @@
 extern void achktrap(), mcetrap();
 extern void xmtrap();
 extern void fasttrap();
+extern void sys_int80();
+extern void brand_sys_int80();
 extern void dtrace_ret();
 
 #if !defined(__amd64)
--- old/usr/src/uts/intel/sys/ucontext.h	Wed Oct  5 00:32:43 2016
+++ new/usr/src/uts/intel/sys/ucontext.h	Wed Oct  5 00:32:44 2016
@@ -21,7 +21,7 @@
 
 /*
  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
- *
+ * Copyright 2015 Joyent, Inc.
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
@@ -84,9 +84,16 @@
 	sigset_t   	uc_sigmask;
 	stack_t 	uc_stack;
 	mcontext_t 	uc_mcontext;
-	long		uc_filler[5];	/* see ABI spec for Intel386 */
+	/*
+	 * The Intel386 ABI specification includes a 5-element array of longs
+	 * called "uc_filler", padding the size of the struct to 512 bytes.  To
+	 * allow zone brands to communicate extra data right the way through
+	 * the signal handling process, from sigacthandler to setcontext, we
+	 * steal the first three of these longs as a brand-private member.
+	 */
+	void		*uc_brand_data[3];
+	long		uc_filler[2];
 };
-
 #if defined(_SYSCALL32)
 
 /* Kernel view of user ILP32 ucontext structure */
@@ -97,7 +104,8 @@
 	sigset_t	uc_sigmask;
 	stack32_t	uc_stack;
 	mcontext32_t	uc_mcontext;
-	int32_t		uc_filler[5];
+	caddr32_t	uc_brand_data[3];
+	int32_t		uc_filler[2];
 } ucontext32_t;
 
 #if defined(_KERNEL)
--- old/usr/src/uts/intel/sys/x86_archext.h	Wed Oct  5 00:32:44 2016
+++ new/usr/src/uts/intel/sys/x86_archext.h	Wed Oct  5 00:32:44 2016
@@ -28,7 +28,7 @@
  * All rights reserved.
  */
 /*
- * Copyright 2015 Joyent, Inc.
+ * Copyright 2016 Joyent, Inc.
  * Copyright 2012 Jens Elkner <jel+illumos@cs.uni-magdeburg.de>
  * Copyright 2012 Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
  * Copyright 2014 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
--- /dev/null	Wed Oct  5 00:32:44 2016
+++ new/usr/src/uts/intel/zfd/Makefile	Wed Oct  5 00:32:44 2016
@@ -0,0 +1,48 @@
+#
+# 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 2014 Joyent, Inc.  All rights reserved.
+#
+# uts/intel/zfd/Makefile
+
+UTSBASE	= ../..
+
+MODULE		= zfd
+OBJECTS		= $(ZFD_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(ZFD_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(USR_DRV_DIR)/$(MODULE)
+
+include $(UTSBASE)/intel/Makefile.intel
+
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+include $(UTSBASE)/intel/Makefile.targ
--- old/usr/src/uts/sparc/Makefile.sparc	Wed Oct  5 00:32:44 2016
+++ new/usr/src/uts/sparc/Makefile.sparc	Wed Oct  5 00:32:44 2016
@@ -219,7 +219,7 @@
 DRV_KMODS	+= pseudo ptc ptm pts ptsl ramdisk random rsm rts sad
 DRV_KMODS	+= simnet softmac sppp sppptun sy sysevent sysmsg 
 DRV_KMODS	+= spdsock
-DRV_KMODS	+= tcp tcp6 timerfd tl tnf ttymux udp udp6 wc winlock zcons
+DRV_KMODS	+= tcp tcp6 timerfd tl tnf ttymux udp udp6 wc winlock zcons zfs
 DRV_KMODS	+= ippctl
 DRV_KMODS	+= dld
 DRV_KMODS	+= ipd
@@ -241,6 +241,7 @@
 DRV_KMODS	+= dca
 DRV_KMODS	+= eventfd
 DRV_KMODS	+= signalfd
+DRV_KMODS	+= inotify
 
 #
 #       Hardware Drivers in common space
--- old/usr/src/uts/sparc/bpf/Makefile	Wed Oct  5 00:32:44 2016
+++ new/usr/src/uts/sparc/bpf/Makefile	Wed Oct  5 00:32:44 2016
@@ -60,7 +60,7 @@
 #
 #
 CFLAGS		+= $(CCVERBOSE)
-LDFLAGS		+= -dy -Nmisc/mac -Nmisc/dls -Ndrv/ipnet -Nmisc/neti
+LDFLAGS		+= -dy -Nmisc/mac -Nmisc/dls -Ndrv/ipnet -Nmisc/neti -Ndrv/ip
 INC_PATH	+= -I$(UTSBASE)/common/io/bpf
 
 #
--- old/usr/src/uts/sparc/dld/Makefile	Wed Oct  5 00:32:44 2016
+++ new/usr/src/uts/sparc/dld/Makefile	Wed Oct  5 00:32:44 2016
@@ -57,7 +57,6 @@
 $(RELEASE_BUILD)CFLAGS		+= -xinline=auto -xcrossfile
 $(RELEASE_BUILD)COPTIMIZE	= -xO5
 LDFLAGS				+= -dy -N misc/dls -N misc/mac
-INC_PATH			+= -I$(UTSBASE)/common/io/bpf
 
 #
 # For now, disable these lint checks; maintainers should endeavor
--- old/usr/src/uts/sparc/dls/Makefile	Wed Oct  5 00:32:44 2016
+++ new/usr/src/uts/sparc/dls/Makefile	Wed Oct  5 00:32:44 2016
@@ -55,7 +55,6 @@
 $(RELEASE_BUILD)CFLAGS		+= -xinline=auto -xcrossfile
 $(RELEASE_BUILD)COPTIMIZE	= -xO5
 LDFLAGS				+= -dy -N misc/mac
-INC_PATH			+= -I$(UTSBASE)/common/io/bpf
 
 #
 # For now, disable these lint checks; maintainers should endeavor
--- old/usr/src/uts/sparc/icmp/Makefile	Wed Oct  5 00:32:44 2016
+++ new/usr/src/uts/sparc/icmp/Makefile	Wed Oct  5 00:32:44 2016
@@ -22,6 +22,7 @@
 # uts/sparc/icmp/Makefile
 # Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
+# Copyright 2016 Joyent, Inc.
 #
 #	This makefile drives the production of the icmp IP driver
 #
@@ -62,6 +63,8 @@
 LINT_TARGET	= $(MODULE).lint
 INSTALL_TARGET	= $(BINARY) $(ROOTMODULE) $(ROOTLINK) $(ROOT_CONFFILE)
 
+INC_PATH	+= -I$(UTSBASE)/common/io/bpf
+
 #
 # lint pass one enforcement
 #
--- /dev/null	Wed Oct  5 00:32:44 2016
+++ new/usr/src/uts/sparc/inotify/Makefile	Wed Oct  5 00:32:44 2016
@@ -0,0 +1,70 @@
+#
+# 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 (c) 2014 Joyent, Inc.  All rights reserved.
+#
+
+#
+#	Path to the base of the uts directory tree (usually /usr/src/uts).
+#
+UTSBASE	= ../..
+
+#
+#	Define the module and object file sets.
+#
+MODULE		= inotify
+OBJECTS		= $(INOTIFY_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(INOTIFY_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(USR_DRV_DIR)/$(MODULE)
+CONF_SRCDIR	= $(UTSBASE)/common/io
+
+#
+#	Include common rules.
+#
+include $(UTSBASE)/sparc/Makefile.sparc
+
+LINTTAGS	+= -erroff=E_STRUCT_DERIVED_FROM_FLEX_MBR
+CERRWARN	+= -_gcc=-Wno-parentheses
+LDFLAGS		+= -dy -Nfs/specfs
+
+#
+#	Define targets
+#
+ALL_TARGET	= $(BINARY) $(SRC_CONFILE)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE)
+
+#
+#	Default build targets.
+#
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+#
+#	Include common targets.
+#
+include $(UTSBASE)/sparc/Makefile.targ
--- old/usr/src/uts/sparc/iptun/Makefile	Wed Oct  5 00:32:44 2016
+++ new/usr/src/uts/sparc/iptun/Makefile	Wed Oct  5 00:32:44 2016
@@ -54,7 +54,6 @@
 #
 CFLAGS 		+= $(CCVERBOSE)
 LDFLAGS 	+= -dy -Ndrv/dld -Nmisc/dls -Nmisc/mac -Ndrv/ip
-INC_PATH	+= -I$(UTSBASE)/common/io/bpf
 
 LINTTAGS	+= -erroff=E_BAD_PTR_CAST_ALIGN
 LINTTAGS	+= -erroff=E_PTRDIFF_OVERFLOW
--- old/usr/src/uts/sparc/mac/Makefile	Wed Oct  5 00:32:44 2016
+++ new/usr/src/uts/sparc/mac/Makefile	Wed Oct  5 00:32:44 2016
@@ -60,7 +60,6 @@
 $(RELEASE_BUILD)CFLAGS		+= -xinline=auto -xcrossfile
 $(RELEASE_BUILD)COPTIMIZE	= -xO5
 LDFLAGS				+= -dy
-INC_PATH			+= -I$(UTSBASE)/common/io/bpf
 
 LINTTAGS			+= -erroff=E_PTRDIFF_OVERFLOW
 LINTTAGS			+= -erroff=E_BAD_PTR_CAST_ALIGN
--- old/usr/src/uts/sparc/mac_ether/Makefile	Wed Oct  5 00:32:45 2016
+++ new/usr/src/uts/sparc/mac_ether/Makefile	Wed Oct  5 00:32:45 2016
@@ -56,7 +56,6 @@
 #
 CFLAGS		+= $(CCVERBOSE)
 LDFLAGS		+= -dy -N misc/mac
-INC_PATH	+= -I$(UTSBASE)/common/io/bpf
 
 #
 #	Default build targets.
--- old/usr/src/uts/sparc/mac_ib/Makefile	Wed Oct  5 00:32:45 2016
+++ new/usr/src/uts/sparc/mac_ib/Makefile	Wed Oct  5 00:32:45 2016
@@ -56,7 +56,6 @@
 #
 CFLAGS		+= $(CCVERBOSE)
 LDFLAGS		+= -dy -N misc/mac
-INC_PATH	+= -I$(UTSBASE)/common/io/bpf
 
 #
 #	Default build targets.
--- old/usr/src/uts/sparc/mac_wifi/Makefile	Wed Oct  5 00:32:45 2016
+++ new/usr/src/uts/sparc/mac_wifi/Makefile	Wed Oct  5 00:32:45 2016
@@ -58,7 +58,6 @@
 #
 CFLAGS		+= $(CCVERBOSE)
 LDFLAGS		+= -dy -Nmisc/mac
-INC_PATH	+= -I$(UTSBASE)/common/io/bpf
 
 #
 #	Default build targets.
--- old/usr/src/uts/sparc/sockpfp/Makefile	Wed Oct  5 00:32:45 2016
+++ new/usr/src/uts/sparc/sockpfp/Makefile	Wed Oct  5 00:32:45 2016
@@ -58,7 +58,7 @@
 #
 CFLAGS += $(CCVERBOSE)
 
-LDFLAGS += -dy -Nfs/sockfs -Nmisc/dls -Nmisc/mac -Ndrv/bpf -Ndrv/ip
+LDFLAGS += -dy -Nfs/sockfs -Nmisc/dls -Nmisc/mac -Ndrv/ip
 INC_PATH += -I$(UTSBASE)/common/inet/sockmods -I$(UTSBASE)/common/io/bpf
 
 #
--- old/usr/src/uts/sparc/spdsock/Makefile	Wed Oct  5 00:32:45 2016
+++ new/usr/src/uts/sparc/spdsock/Makefile	Wed Oct  5 00:32:45 2016
@@ -61,11 +61,6 @@
 LDFLAGS += -dy -Ndrv/ip
 
 #
-# Overrides
-#
-INC_PATH += -I$(UTSBASE)/common/io/bpf
-
-#
 # lint pass one enforcement
 #
 CFLAGS += $(CCVERBOSE)
--- old/usr/src/uts/sparc/syscall/getcontext.c	Wed Oct  5 00:32:45 2016
+++ new/usr/src/uts/sparc/syscall/getcontext.c	Wed Oct  5 00:32:45 2016
@@ -20,6 +20,9 @@
  */
 
 /*
+ * Copyright 2015 Joyent, Inc.
+ */
+/*
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
@@ -110,10 +113,15 @@
 		ucp->uc_flags &= ~UC_FPU;
 	ucp->uc_mcontext.gwins = (gwindows_t *)NULL;
 
-	/*
-	 * Save signal mask.
-	 */
-	sigktou(mask, &ucp->uc_sigmask);
+	if (mask != NULL) {
+		/*
+		 * Save signal mask.
+		 */
+		sigktou(mask, &ucp->uc_sigmask);
+	} else {
+		ucp->uc_flags &= ~UC_SIGMASK;
+		bzero(&ucp->uc_sigmask, sizeof (ucp->uc_sigmask));
+	}
 }
 
 
@@ -412,11 +420,16 @@
 		ucp->uc_flags &= ~UC_FPU;
 	ucp->uc_mcontext.gwins = (caddr32_t)NULL;
 
-	/*
-	 * Save signal mask (the 32- and 64-bit sigset_t structures are
-	 * identical).
-	 */
-	sigktou(mask, (sigset_t *)&ucp->uc_sigmask);
+	if (mask != NULL) {
+		/*
+		 * Save signal mask (the 32- and 64-bit sigset_t structures are
+		 * identical).
+		 */
+		sigktou(mask, (sigset_t *)&ucp->uc_sigmask);
+	} else {
+		ucp->uc_flags &= ~UC_SIGMASK;
+		bzero(&ucp->uc_sigmask, sizeof (ucp->uc_sigmask));
+	}
 }
 
 int
--- /dev/null	Wed Oct  5 00:32:45 2016
+++ new/usr/src/uts/sparc/zfd/Makefile	Wed Oct  5 00:32:45 2016
@@ -0,0 +1,50 @@
+#
+# 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 2014 Joyent, Inc.  All rights reserved.
+#
+# uts/intel/zfd/Makefile
+
+UTSBASE	= ../..
+
+MODULE		= zfd
+OBJECTS		= $(ZFD_OBJS:%=$(OBJS_DIR)/%)
+LINTS		= $(ZFD_OBJS:%.o=$(LINTS_DIR)/%.ln)
+ROOTMODULE	= $(USR_DRV_DIR)/$(MODULE)
+
+include $(UTSBASE)/sparc/Makefile.sparc
+
+ALL_TARGET	= $(BINARY)
+LINT_TARGET	= $(MODULE).lint
+INSTALL_TARGET	= $(BINARY) $(ROOTMODULE)
+
+CFLAGS		+= $(CCVERBOSE)
+
+.KEEP_STATE:
+
+def:		$(DEF_DEPS)
+
+all:		$(ALL_DEPS)
+
+clean:		$(CLEAN_DEPS)
+
+clobber:	$(CLOBBER_DEPS)
+
+lint:		$(LINT_DEPS)
+
+modlintlib:	$(MODLINTLIB_DEPS)
+
+clean.lint:	$(CLEAN_LINT_DEPS)
+
+install:	$(INSTALL_DEPS)
+
+include $(UTSBASE)/sparc/Makefile.targ
--- old/usr/src/uts/sun4/brand/common/brand_solaris.s	Wed Oct  5 00:32:45 2016
+++ new/usr/src/uts/sun4/brand/common/brand_solaris.s	Wed Oct  5 00:32:45 2016
@@ -236,7 +236,7 @@
 #endif /* sun4v */
 	ldn	[%g2 + CPU_THREAD], %g3;	/* get thread ptr */
 	ldn	[%g3 + T_PROCP], %g4;		/* get proc ptr */
-	ldn	[%g4 + P_BRAND_DATA], %g5;	/* get brand data ptr */
+	ldn	[%g4 + __P_BRAND_DATA], %g5;	/* get brand data ptr */
 	ldn	[%g5 + SPD_HANDLER], %g5;	/* get userland brnd hdlr ptr */
 	brz	%g5, _exit;			/* has it been set? */
 	nop;
--- old/usr/src/uts/sun4/ml/offsets.in	Wed Oct  5 00:32:46 2016
+++ new/usr/src/uts/sun4/ml/offsets.in	Wed Oct  5 00:32:46 2016
@@ -109,7 +109,7 @@
 	p_utraps
 	p_agenttp
 	p_brand
-	p_brand_data
+	__p_brand_data
 
 \#define	P_UTRAP4	(UT_ILLTRAP_INSTRUCTION * CPTRSIZE)
 \#define	P_UTRAP7	(UT_FP_DISABLED * CPTRSIZE)
