--- old/./manifest	Mon Nov 26 00:31:44 2012
+++ new/./manifest	Mon Nov 26 00:31:44 2012
@@ -5327,6 +5327,7 @@
 d usr/lib/fm/topo/plugins 0755 root bin
 f usr/lib/fm/topo/plugins/disk.so 0555 root bin
 f usr/lib/fm/topo/plugins/fac_prov_ipmi.so 0555 root bin
+f usr/lib/fm/topo/plugins/fac_prov_mptsas.so 0555 root bin
 f usr/lib/fm/topo/plugins/ipmi.so 0555 root bin
 f usr/lib/fm/topo/plugins/ses.so 0555 root bin
 f usr/lib/fm/topo/plugins/xfp.so 0555 root bin
@@ -10090,6 +10091,7 @@
 f usr/platform/i86pc/lib/fm/topo/maps/i86pc-hc-topology.xml 0444 root sys
 f usr/platform/i86pc/lib/fm/topo/maps/i86pc-legacy-hc-topology.xml 0444 root sys
 f usr/platform/i86pc/lib/fm/topo/maps/psu-hc-topology.xml 0444 root sys
+f usr/platform/i86pc/lib/fm/topo/maps/Joyent-Compute-Platform-1101-disk-hc-topology.xml 0444 root sys
 d usr/platform/i86pc/lib/fm/topo/plugins 0755 root sys
 f usr/platform/i86pc/lib/fm/topo/plugins/chip.so 0555 root sys
 f usr/platform/i86pc/lib/fm/topo/plugins/hostbridge.so 0555 root sys
--- old/usr/src/cmd/mdb/common/modules/mpt_sas/mpt_sas.c	Mon Nov 26 00:31:45 2012
+++ new/usr/src/cmd/mdb/common/modules/mpt_sas/mpt_sas.c	Mon Nov 26 00:31:45 2012
@@ -286,8 +286,10 @@
 		mdb_printf("devhdl %x, sasaddress %"PRIx64", phymask %x,"
 		    "devinfo %x\n", ptgt->m_devhdl, ptgt->m_sas_wwn,
 		    ptgt->m_phymask, ptgt->m_deviceinfo);
-		mdb_printf("throttle %x, dr_flag %x, m_t_ncmds %x\n",
-		    ptgt->m_t_throttle, ptgt->m_dr_flag, ptgt->m_t_ncmds);
+		mdb_printf("throttle %x, dr_flag %x, m_t_ncmds %x, "
+		    "enclosure %x, slot_num %x\n", ptgt->m_t_throttle,
+		    ptgt->m_dr_flag, ptgt->m_t_ncmds, ptgt->m_enclosure,
+		    ptgt->m_slot_num);
 
 		mdb_free(ptgt, sizeof (mptsas_target_t));
 		ptgt = (mptsas_target_t *)hash_traverse(
--- old/usr/src/lib/fm/topo/libtopo/common/topo_hc.h	Mon Nov 26 00:31:45 2012
+++ new/usr/src/lib/fm/topo/libtopo/common/topo_hc.h	Mon Nov 26 00:31:45 2012
@@ -115,6 +115,10 @@
 
 #define	TOPO_PGROUP_BINDING	"binding"
 #define	TOPO_BINDING_OCCUPANT	"occupant-path"
+#define	TOPO_BINDING_DRIVER	"driver"
+#define	TOPO_BINDING_DEVCTL	"devctl"
+#define	TOPO_BINDING_ENCLOSURE	"enclosure"
+#define	TOPO_BINDING_SLOT	"slot"
 
 #define	TOPO_PGROUP_STORAGE	"storage"
 #define	TOPO_STORAGE_INITIATOR_PORT	"initiator-port"
--- old/usr/src/lib/fm/topo/libtopo/common/topo_xml.c	Mon Nov 26 00:31:45 2012
+++ new/usr/src/lib/fm/topo/libtopo/common/topo_xml.c	Mon Nov 26 00:31:45 2012
@@ -1376,16 +1376,10 @@
 
 		if ((fprov = xmlGetProp(cn, (xmlChar *)Provider)) == NULL)
 			goto fenumdone;
-
-		if (xmlStrcmp(fprov, (xmlChar *)"fac_prov_ipmi") != 0) {
-			topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
-			    "Invalid provider specified: %s\n", fprov);
-			goto fenumdone;
-		}
-
 		/*
-		 * Invoke enum entry point in fac provider which will cause the
-		 * facility enumeration node method to be registered.
+		 * Invoke enum entry point in facility provider which will
+		 * cause the facility enumeration node method to be
+		 * registered.
 		 */
 		if (fac_enum_run(mp, ptn, (const char *)fprov) != 0) {
 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
@@ -1440,12 +1434,6 @@
 		    xmlStrcmp(ftype, (xmlChar *)Indicator) != 0)
 			goto facdone;
 
-		if (xmlStrcmp(provider, (xmlChar *)"fac_prov_ipmi") != 0) {
-			topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "fac_process: "
-			    "Invalid provider attr value: %s\n", provider);
-			goto facdone;
-		}
-
 		if ((ntn = topo_node_facbind(mp, ptn, (char *)fname,
 		    (char *)ftype)) == NULL)
 			goto facdone;
@@ -1463,8 +1451,9 @@
 			}
 		}
 		/*
-		 * Invoke enum entry point in fac_prov_ipmi module, which will
-		 * cause the provider methods to be registered on this node
+		 * Invoke enum entry point in the facility provider module,
+		 * which will cause the provider methods to be registered on
+		 * this node
 		 */
 		if (fac_enum_run(mp, ntn, (const char *)provider) != 0) {
 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "fac_process: "
--- /dev/null	Mon Nov 26 00:31:45 2012
+++ new/usr/src/lib/fm/topo/maps/Joyent,Joyent-Compute-Platform-1101/Joyent-Compute-Platform-1101-disk-hc-topology.xmlgenksh	Mon Nov 26 00:31:45 2012
@@ -0,0 +1,98 @@
+#!/bin/ksh
+#
+# 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) 2012, Joyent, Inc. All rights reserved.
+#
+
+function do_node
+{
+  cat <<EOF
+    <node instance='${1}'>
+      <propgroup name='protocol' version='1' name-stability='Private'
+        data-stability='Private'>
+        <propval name='label' type='string' value='${2}' />
+      </propgroup>
+      <propgroup name='binding' version='1' name-stability='Private'
+        data-stability='Private'>
+        <propval name='driver' type='string' value='mpt_sas' />
+        <propval name='devctl' type='string' value='${3}' />
+        <propval name='enclosure' type='uint32' value='${4}' />
+        <propval name='slot' type='uint32' value='${5}' />
+      </propgroup>
+    </node>
+EOF
+}
+
+
+cat <<EOF
+<topology name='disk' scheme='hc'>
+  <range name='bay' min='0' max='15'>
+    <facility name='fail' type='indicator' provider='fac_prov_mptsas' >
+      <propgroup name='facility' version='1' name-stability='Private'
+        data-stability='Private' >
+        <propval name='type' type='uint32' value='0' />
+        <propmethod name='mptsas_led_mode' version='0' propname='mode'
+          proptype='uint32' mutable='1'>
+        </propmethod>
+      </propgroup>
+    </facility>
+    <facility name='ident' type='indicator' provider='fac_prov_mptsas' >
+      <propgroup name='facility' version='1' name-stability='Private'
+        data-stability='Private' >
+        <propval name='type' type='uint32' value='1' />
+        <propmethod name='mptsas_led_mode' version='0' propname='mode'
+          proptype='uint32' mutable='1'>
+        </propmethod>
+      </propgroup>
+    </facility>
+    <facility name='ok2rm' type='indicator' provider='fac_prov_mptsas' >
+      <propgroup name='facility' version='1' name-stability='Private'
+        data-stability='Private' >
+        <propval name='type' type='uint32' value='2' />
+        <propmethod name='mptsas_led_mode' version='0' propname='mode'
+          proptype='uint32' mutable='1'>
+        </propmethod>
+      </propgroup>
+    </facility>
+EOF
+
+enclosure=1
+bay=0
+disk=0
+slot=0
+devctl='/devices/pci@0,0/pci8086,3c02@1/pci15d9,691@0:devctl'
+while (( slot <= 7 )); do
+  do_node $bay "Front Disk $disk" "$devctl" $enclosure $slot
+  (( bay = bay + 1 ))
+  (( disk = disk + 1 ))
+  (( slot = slot + 1 ))
+done
+
+slot=0
+devctl='/devices/pci@0,0/pci8086,3c06@2,2/pci15d9,691@0:devctl'
+while (( slot <= 7 )); do
+  do_node $bay "Front Disk $disk" "$devctl" $enclosure $slot
+  (( bay = bay + 1 ))
+  (( disk = disk + 1 ))
+  (( slot = slot + 1 ))
+done
+
+cat <<EOF
+    <dependents grouping='children'>
+      <range name='disk' min='0' max='0'>
+        <enum-method name='disk' version='1' />
+      </range>
+    </dependents>
+  </range>
+</topology>
+EOF
--- /dev/null	Mon Nov 26 00:31:45 2012
+++ new/usr/src/lib/fm/topo/maps/Joyent,Joyent-Compute-Platform-1101/Makefile	Mon Nov 26 00:31:45 2012
@@ -0,0 +1,41 @@
+#
+# 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 (c) 2012, Joyent, Inc. All rights reserved.
+#
+
+# NOTE: The name of the xml file we are building is 'platform'
+# specific, but its build is structured as 'arch' specific since
+# 'uname -i' on all x86 platforms returns i86pc.
+
+ARCH = i86pc
+CLASS = arch
+DTDFILE = topology.dtd.1
+TOPOFILE = Joyent-Compute-Platform-1101-disk-hc-topology.xml
+SRCDIR = ../Joyent,Joyent-Compute-Platform-1101
+
+PLATFORM = Joyent-Compute-Platform-1101
+TOPOBASE = ../i86pc/i86pc-hc-topology.xml
+
+include ../Makefile.map
--- old/usr/src/lib/fm/topo/maps/Makefile	Mon Nov 26 00:31:45 2012
+++ new/usr/src/lib/fm/topo/maps/Makefile	Mon Nov 26 00:31:45 2012
@@ -48,7 +48,8 @@
 		SUNW,Netra-X4200-M2 \
 		SUNW,Sun-Fire-X4500 \
 		SUNW,Sun-Fire-X4540 \
-		SUNW,Sun-Fire-X4600-M2
+		SUNW,Sun-Fire-X4600-M2 \
+		Joyent,Joyent-Compute-Platform-1101
 
 SUBDIRS = $($(MACH)_SUBDIRS)
 
--- old/usr/src/lib/fm/topo/maps/Makefile.map	Mon Nov 26 00:31:45 2012
+++ new/usr/src/lib/fm/topo/maps/Makefile.map	Mon Nov 26 00:31:45 2012
@@ -22,12 +22,12 @@
 #
 # Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
+# Copyright (c) 2012, Joyent, Inc. All rights reserved.
 #
-# ident	"%Z%%M%	%I%	%E% SMI"
 
 .KEEP_STATE:
 .SUFFIXES:
-.SUFFIXES: .xml .xmlgen $(SUFFIXES)
+.SUFFIXES: .xml .xmlgen .xmlgenksh $(SUFFIXES)
 
 MODCLASS = maps
 
@@ -54,6 +54,11 @@
 	$(RM) $@ 
 	$(CAT) ../common/xmlgen-header.xml > $@
 	$(PERL) $< >> $@
+
+.xmlgenksh.xml:
+	$(RM) $@
+	$(CAT) ../common/xmlgen-header-new.xml > $@
+	$(KSH93) $< >> $@
 	
 %.xml: ../common/%.xml
 	$(RM) $@
--- /dev/null	Mon Nov 26 00:31:45 2012
+++ new/usr/src/lib/fm/topo/maps/common/xmlgen-header-new.xml	Mon Nov 26 00:31:45 2012
@@ -0,0 +1,2 @@
+<?xml version="1.0"?>
+<!DOCTYPE topology SYSTEM "/usr/share/lib/xml/dtd/topology.dtd.1">
--- old/usr/src/lib/fm/topo/maps/i86pc/i86pc-legacy-hc-topology.xml	Mon Nov 26 00:31:45 2012
+++ new/usr/src/lib/fm/topo/maps/i86pc/i86pc-legacy-hc-topology.xml	Mon Nov 26 00:31:45 2012
@@ -2,6 +2,7 @@
 <!DOCTYPE topology SYSTEM "/usr/share/lib/xml/dtd/topology.dtd.1">
 <!--
 Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
+Copyright (c) 2012, Joyent, Inc. All rights reserved.
 
  CDDL HEADER START
 
@@ -150,6 +151,18 @@
           <propmap name='Sun-Fire-X4600-M2-disk' />
        </range>
     </set>
+
+    <set type='product' setlist='Joyent-Compute-Platform-1101'>
+       <range name='psu' min='0' max='100'>
+          <enum-method name='ipmi' version='1' />
+       </range>
+       <range name='fan' min='0' max='100'>
+          <enum-method name='ipmi' version='1' />
+       </range>
+       <range name='bay' min='0' max='15'>
+          <propmap name='Joyent-Compute-Platform-1101-disk' />
+       </range>
+    </set>
 
     <set type='product' setlist='default'>
        <range name='psu' min='0' max='100'>
--- old/usr/src/lib/fm/topo/modules/common/Makefile	Mon Nov 26 00:31:45 2012
+++ new/usr/src/lib/fm/topo/modules/common/Makefile	Mon Nov 26 00:31:45 2012
@@ -23,11 +23,11 @@
 # Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
 
 SUBDIRS =		\
 	disk		\
 	fac_prov_ipmi	\
+	fac_prov_mptsas	\
 	ipmi		\
 	ses		\
 	xfp
--- old/usr/src/lib/fm/topo/modules/common/disk/Makefile	Mon Nov 26 00:31:46 2012
+++ new/usr/src/lib/fm/topo/modules/common/disk/Makefile	Mon Nov 26 00:31:46 2012
@@ -22,14 +22,15 @@
 # Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 # Use is subject to license terms.
 #
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
 
 MODULE = disk
 CLASS = common
 
-MODULESRCS = disk.c disk_common.c
+MODULESRCS = disk.c disk_common.c disk_mptsas.c
 
 include ../../Makefile.plugin
 
+CPPFLAGS += -I$(SRC)/uts/common
+
 LDLIBS += -ldevinfo -ldevid -lcfgadm -ldiskstatus
 
--- old/usr/src/lib/fm/topo/modules/common/disk/disk.c	Mon Nov 26 00:31:46 2012
+++ new/usr/src/lib/fm/topo/modules/common/disk/disk.c	Mon Nov 26 00:31:46 2012
@@ -33,6 +33,7 @@
 #include <fm/libdiskstatus.h>
 #include <sys/fm/protocol.h>
 #include "disk.h"
+#include "disk_drivers.h"
 
 static int disk_enum(topo_mod_t *, tnode_t *, const char *,
 	topo_instance_t, topo_instance_t, void *, void *);
@@ -43,6 +44,30 @@
 static const topo_modinfo_t disk_info =
 	{DISK, FM_FMRI_SCHEME_HC, DISK_VERSION, &disk_ops};
 
+static int
+disk_declare_driver(topo_mod_t *mod, tnode_t *baynode, topo_list_t *dlistp,
+    char *driver)
+{
+	int err;
+
+	if (strcmp("mpt_sas", driver) == 0) {
+		char *sas_address = NULL;
+		tnode_t *child = NULL;
+
+		if (disk_mptsas_find_disk(mod, baynode, &sas_address) != 0)
+			return (err);
+
+		err = disk_declare_addr(mod, baynode, dlistp,
+		    sas_address, &child);
+		topo_mod_strfree(mod, sas_address);
+
+		return (err);
+	}
+
+	topo_mod_dprintf(mod, "unknown disk driver '%s'\n", driver);
+	return (-1);
+}
+
 /*ARGSUSED*/
 static int
 disk_enum(topo_mod_t *mod, tnode_t *baynode,
@@ -49,7 +74,7 @@
     const char *name, topo_instance_t min, topo_instance_t max,
     void *arg, void *notused)
 {
-	char		*device;
+	char		*device, *driver;
 	int		err;
 	nvlist_t	*fmri;
 	topo_list_t	*dlistp = topo_mod_getspecific(mod);
@@ -75,6 +100,22 @@
 	nvlist_free(fmri);
 
 	/*
+	 * For internal storage, first check to see if we need to
+	 * request more detail from an HBA driver.
+	 */
+	if (topo_prop_get_string(baynode, TOPO_PGROUP_BINDING,
+	    TOPO_BINDING_DRIVER, &driver, &err) == 0) {
+		err = disk_declare_driver(mod, baynode, dlistp, driver);
+
+		topo_mod_strfree(mod, driver);
+		return (err);
+	} else if (err != ETOPO_PROP_NOENT) {
+		topo_mod_dprintf(mod, "disk_enum: "
+		    "binding error %s\n", topo_strerror(err));
+		return (-1);
+	}
+
+	/*
 	 * For internal storage, get the path to the occupant from the
 	 * binding group of the bay node
 	 */
--- old/usr/src/lib/fm/topo/modules/common/disk/disk.h	Mon Nov 26 00:31:46 2012
+++ new/usr/src/lib/fm/topo/modules/common/disk/disk.h	Mon Nov 26 00:31:46 2012
@@ -54,6 +54,7 @@
  */
 #define	TOPO_PGROUP_BINDING		"binding"
 #define	TOPO_BINDING_OCCUPANT		"occupant-path"
+#define	TOPO_BINDING_DRIVER		"driver"
 
 /*
  * device node information.
--- /dev/null	Mon Nov 26 00:31:46 2012
+++ new/usr/src/lib/fm/topo/modules/common/disk/disk_mptsas.c	Mon Nov 26 00:31:46 2012
@@ -0,0 +1,117 @@
+/*
+ * 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) 2012, Joyent, Inc. All rights reserved.
+ */
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stropts.h>
+#include <string.h>
+
+#include <fm/topo_mod.h>
+#include <fm/topo_list.h>
+
+#include <sys/scsi/adapters/mpt_sas/mpi/mpi2_type.h>
+#include <sys/scsi/adapters/mpt_sas/mpi/mpi2.h>
+#include <sys/scsi/adapters/mpt_sas/mpi/mpi2_init.h>
+#include <sys/scsi/adapters/mpt_sas/mptsas_ioctl.h>
+
+static int
+get_sas_address(topo_mod_t *mod, char *devctl, uint32_t enclosure,
+    uint32_t slot, char **sas_address)
+{
+	int err = -1;
+	int fd, i;
+	mptsas_get_disk_info_t gdi;
+	mptsas_disk_info_t *di = NULL;
+	size_t disz;
+
+	bzero(&gdi, sizeof (gdi));
+
+	fd = open(devctl, O_RDWR);
+	if (fd == -1) {
+		topo_mod_dprintf(mod, "could not open '%s' for ioctl: %s\n",
+		    devctl, strerror(errno));
+		return (-1);
+	}
+
+	if (ioctl(fd, MPTIOCTL_GET_DISK_INFO, &gdi) == -1) {
+		topo_mod_dprintf(mod, "ioctl 1 on '%s' failed: %s\n", devctl,
+		    strerror(errno));
+		goto out;
+	}
+
+	gdi.DiskInfoArraySize = disz = sizeof (mptsas_disk_info_t) *
+	    gdi.DiskCount;
+	gdi.PtrDiskInfoArray = di = topo_mod_alloc(mod, disz);
+	if (di == NULL) {
+		topo_mod_dprintf(mod, "memory allocation failed\n");
+		goto out;
+	}
+
+	if (ioctl(fd, MPTIOCTL_GET_DISK_INFO, &gdi) == -1) {
+		topo_mod_dprintf(mod, "ioctl 2 on '%s' failed: %s\n", devctl,
+		    strerror(errno));
+		goto out;
+	}
+
+	for (i = 0; i < gdi.DiskCount; i++) {
+		if (di[i].Enclosure == enclosure && di[i].Slot == slot) {
+			char sas[17]; /* 16 hex digits and NUL */
+			(void) snprintf(sas, 17, "%llx", di[i].SasAddress);
+			topo_mod_dprintf(mod, "found mpt_sas disk (%d/%d) "
+			    "with adddress %s\n", enclosure, slot, sas);
+			*sas_address = topo_mod_strdup(mod, sas);
+			err = 0;
+			break;
+		}
+	}
+
+out:
+	if (di != NULL)
+		topo_mod_free(mod, di, disz);
+	(void) close(fd);
+	return (err);
+}
+
+int
+disk_mptsas_find_disk(topo_mod_t *mod, tnode_t *baynode, char **sas_address)
+{
+	char *devctl = NULL;
+	uint32_t enclosure, slot;
+	int err;
+
+	/*
+	 * Get the required properties from the node.  These come from
+	 * the static XML mapping.
+	 */
+	if (topo_prop_get_string(baynode, TOPO_PGROUP_BINDING,
+	    TOPO_BINDING_DEVCTL, &devctl, &err) != 0 ||
+	    topo_prop_get_uint32(baynode, TOPO_PGROUP_BINDING,
+	    TOPO_BINDING_ENCLOSURE, &enclosure, &err) != 0 ||
+	    topo_prop_get_uint32(baynode, TOPO_PGROUP_BINDING,
+	    TOPO_BINDING_SLOT, &slot, &err) != 0) {
+		if (devctl != NULL)
+			topo_mod_strfree(mod, devctl);
+		topo_mod_dprintf(mod, "bay node was missing mpt_sas binding "
+		    "properties\n");
+		return (-1);
+	}
+
+	err = get_sas_address(mod, devctl, enclosure, slot, sas_address);
+	topo_mod_strfree(mod, devctl);
+	return (err);
+}
--- /dev/null	Mon Nov 26 00:31:46 2012
+++ new/usr/src/lib/fm/topo/modules/common/fac_prov_mptsas/Makefile	Mon Nov 26 00:31:46 2012
@@ -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 2008 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+# Copyright (c) 2012, Joyent, Inc. All rights reserved.
+#
+
+MODULE = fac_prov_mptsas
+CLASS = common
+
+MODULESRCS = fac_prov_mptsas.c
+
+include ../../Makefile.plugin
+
+CPPFLAGS += -I$(SRC)/uts/common
--- /dev/null	Mon Nov 26 00:31:46 2012
+++ new/usr/src/lib/fm/topo/modules/common/fac_prov_mptsas/fac_prov_mptsas.c	Mon Nov 26 00:31:46 2012
@@ -0,0 +1,233 @@
+/*
+ * 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) 2012, Joyent, Inc. All rights reserved.
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <strings.h>
+#include <limits.h>
+#include <alloca.h>
+#include <errno.h>
+#include <libnvpair.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/fm/protocol.h>
+#include <fm/libtopo.h>
+#include <fm/topo_mod.h>
+
+#include "sys/scsi/adapters/mpt_sas/mptsas_ioctl.h"
+
+#define	TOPO_METH_MPTSAS_LED_MODE_VERSION	0
+
+static int fac_prov_mptsas_enum(topo_mod_t *, tnode_t *, const char *,
+    topo_instance_t, topo_instance_t, void *, void *);
+
+/*
+ * mpt_sas facility provider methods
+ */
+static int mptsas_led_mode(topo_mod_t *, tnode_t *, topo_version_t,
+    nvlist_t *, nvlist_t **);
+
+const topo_modops_t mptsas_ops = { fac_prov_mptsas_enum, NULL };
+
+const topo_modinfo_t mptsas_info =
+	{ "mpt_sas facility provider", FM_FMRI_SCHEME_HC, TOPO_VERSION,
+	&mptsas_ops };
+
+static const topo_method_t mptsas_fac_methods[] = {
+	{ "mptsas_led_mode", TOPO_PROP_METH_DESC,
+	    TOPO_METH_MPTSAS_LED_MODE_VERSION,
+	    TOPO_STABILITY_INTERNAL, mptsas_led_mode },
+	{ NULL }
+};
+
+/*ARGSUSED*/
+int
+_topo_init(topo_mod_t *mod, topo_version_t version)
+{
+	if (getenv("TOPOFACMPTSASDEBUG") != NULL)
+		topo_mod_setdebug(mod);
+
+	return (topo_mod_register(mod, &mptsas_info, TOPO_VERSION));
+}
+
+void
+_topo_fini(topo_mod_t *mod)
+{
+	topo_mod_unregister(mod);
+}
+
+static int
+do_led_control(topo_mod_t *mod, char *devctl, uint16_t enclosure,
+    uint16_t slot, uint8_t led, uint32_t *ledmode, boolean_t set)
+{
+	int fd;
+	mptsas_led_control_t lc;
+
+	lc.Command = set ? MPTSAS_LEDCTL_FLAG_SET : MPTSAS_LEDCTL_FLAG_GET;
+	lc.Enclosure = enclosure;
+	lc.Slot = slot;
+	lc.Led = led;
+	lc.LedStatus = *ledmode;
+
+	fd = open(devctl, O_RDWR);
+	if ((fd = open(devctl, O_RDWR)) == -1) {
+		topo_mod_dprintf(mod, "devctl open failed: %s",
+		    strerror(errno));
+		return (-1);
+	}
+
+	if (ioctl(fd, MPTIOCTL_LED_CONTROL, &lc) == -1) {
+		topo_mod_dprintf(mod, "led control ioctl failed: %s",
+		    strerror(errno));
+		(void) close(fd);
+		return (-1);
+	}
+
+	*ledmode = lc.LedStatus ? TOPO_LED_STATE_ON : TOPO_LED_STATE_OFF;
+
+	(void) close(fd);
+	return (0);
+}
+
+static int
+mptsas_led_mode(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
+    nvlist_t *in, nvlist_t **out)
+{
+	int err, ret = 0;
+	tnode_t *pnode = topo_node_parent(node);
+	uint32_t type, ledmode = 0;
+	nvlist_t *pargs, *nvl;
+	char *driver = NULL, *devctl = NULL;
+	uint32_t enclosure, slot;
+	uint8_t mptsas_led;
+	boolean_t set;
+
+	if (vers > TOPO_METH_MPTSAS_LED_MODE_VERSION)
+		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
+
+	if (topo_prop_get_string(pnode, TOPO_PGROUP_BINDING,
+	    TOPO_BINDING_DRIVER, &driver, &err) != 0 ||
+	    strcmp("mpt_sas", driver) != 0) {
+		topo_mod_dprintf(mod, "%s: Facility driver was not mpt_sas",
+		    __func__);
+		ret = topo_mod_seterrno(mod, EMOD_NVL_INVAL);
+		goto out;
+	}
+	if (topo_prop_get_uint32(node, TOPO_PGROUP_FACILITY, TOPO_FACILITY_TYPE,
+	    &type, &err) != 0) {
+		topo_mod_dprintf(mod, "%s: Failed to lookup %s property "
+		    "(%s)", __func__, TOPO_FACILITY_TYPE, topo_strerror(err));
+		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
+	}
+	switch (type) {
+	case (TOPO_LED_TYPE_SERVICE):
+		mptsas_led = MPTSAS_LEDCTL_LED_FAIL;
+		break;
+	case (TOPO_LED_TYPE_LOCATE):
+		mptsas_led = MPTSAS_LEDCTL_LED_IDENT;
+		break;
+	case (TOPO_LED_TYPE_OK2RM):
+		mptsas_led = MPTSAS_LEDCTL_LED_OK2RM;
+		break;
+	default:
+		topo_mod_dprintf(mod, "%s: Invalid LED type: 0x%x\n", __func__,
+		    type);
+		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
+	}
+	if (topo_prop_get_string(pnode, TOPO_PGROUP_BINDING,
+	    TOPO_BINDING_DEVCTL, &devctl, &err) != 0 ||
+	    topo_prop_get_uint32(pnode, TOPO_PGROUP_BINDING,
+	    TOPO_BINDING_ENCLOSURE, &enclosure, &err) != 0 ||
+	    topo_prop_get_uint32(pnode, TOPO_PGROUP_BINDING,
+	    TOPO_BINDING_SLOT, &slot, &err) != 0) {
+		topo_mod_dprintf(mod, "%s: Facility was missing mpt_sas binding"
+		    " properties\n", __func__);
+		ret = topo_mod_seterrno(mod, EMOD_NVL_INVAL);
+		goto out;
+	}
+
+	if ((nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &pargs) == 0) &&
+	    nvlist_exists(pargs, TOPO_PROP_VAL_VAL)) {
+		/*
+		 * Set the LED mode
+		 */
+		set = B_TRUE;
+		if ((ret = nvlist_lookup_uint32(pargs, TOPO_PROP_VAL_VAL,
+		    &ledmode)) != 0) {
+			topo_mod_dprintf(mod, "%s: Failed to lookup %s nvpair "
+			    "(%s)\n", __func__, TOPO_PROP_VAL_VAL,
+			    strerror(ret));
+			ret = topo_mod_seterrno(mod, EMOD_NVL_INVAL);
+			goto out;
+		}
+		topo_mod_dprintf(mod, "%s: Setting LED mode to %s\n", __func__,
+		    ledmode ? "ON" : "OFF");
+	} else {
+		/*
+		 * Get the LED mode
+		 */
+		set = B_FALSE;
+		topo_mod_dprintf(mod, "%s: Getting LED mode\n", __func__);
+	}
+
+	if (do_led_control(mod, devctl, enclosure, slot, mptsas_led, &ledmode,
+	    set) != 0) {
+		topo_mod_dprintf(mod, "%s: do_led_control failed", __func__);
+		ret = topo_mod_seterrno(mod, EMOD_UNKNOWN);
+		goto out;
+	}
+
+	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
+	    nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, TOPO_LED_MODE) != 0 ||
+	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_UINT32) != 0 ||
+	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, ledmode) != 0) {
+		topo_mod_dprintf(mod, "%s: Failed to allocate 'out' nvlist\n",
+		    __func__);
+		nvlist_free(nvl);
+		ret = topo_mod_seterrno(mod, EMOD_NOMEM);
+		goto out;
+	}
+	*out = nvl;
+
+out:
+	if (driver != NULL)
+		topo_mod_strfree(mod, driver);
+	if (devctl != NULL)
+		topo_mod_strfree(mod, devctl);
+	return (ret);
+}
+
+/*ARGSUSED*/
+static int
+fac_prov_mptsas_enum(topo_mod_t *mod, tnode_t *rnode, const char *name,
+    topo_instance_t min, topo_instance_t max, void *arg, void *unused)
+{
+	if (topo_node_flags(rnode) == TOPO_NODE_FACILITY) {
+		if (topo_method_register(mod, rnode, mptsas_fac_methods) != 0) {
+			topo_mod_dprintf(mod, "%s: topo_method_register() "
+			    "failed: %s", __func__, topo_mod_errmsg(mod));
+			return (-1);
+		}
+		return (0);
+	}
+
+	topo_mod_dprintf(mod, "%s: unexpected node flags %x", __func__,
+	    topo_node_flags(rnode));
+	return (-1);
+}
--- old/usr/src/uts/common/fs/zfs/sys/vdev_disk.h	Mon Nov 26 00:31:46 2012
+++ new/usr/src/uts/common/fs/zfs/sys/vdev_disk.h	Mon Nov 26 00:31:46 2012
@@ -21,7 +21,8 @@
 /*
  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
- * Copyright 2011 Joyent, Inc. All rights reserved.
+ * Copyright (c) 2012 Joyent, Inc. All rights reserved.
+ * Copyright 2012 Nexenta Systems, Inc.  All rights reserved.
  */
 
 #ifndef _SYS_VDEV_DISK_H
@@ -44,6 +45,8 @@
 	ddi_devid_t	vd_devid;
 	char		*vd_minor;
 	ldi_handle_t	vd_lh;
+	list_t		vd_ldi_cbs;
+	boolean_t	vd_ldi_offline;
 } vdev_disk_t;
 #endif
 
--- old/usr/src/uts/common/fs/zfs/vdev_disk.c	Mon Nov 26 00:31:46 2012
+++ new/usr/src/uts/common/fs/zfs/vdev_disk.c	Mon Nov 26 00:31:46 2012
@@ -22,6 +22,7 @@
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2012 by Delphix. All rights reserved.
  * Copyright (c) 2012, Joyent, Inc. All rights reserved.
+ * Copyright 2012 Nexenta Systems, Inc.  All rights reserved.
  */
 
 #include <sys/zfs_context.h>
@@ -42,12 +43,149 @@
 
 extern ldi_ident_t zfs_li;
 
+static void vdev_disk_close(vdev_t *);
+
 typedef struct vdev_disk_buf {
 	buf_t	vdb_buf;
 	zio_t	*vdb_io;
 } vdev_disk_buf_t;
 
+typedef struct vdev_disk_ldi_cb {
+	list_node_t		lcb_next;
+	ldi_callback_id_t	lcb_id;
+} vdev_disk_ldi_cb_t;
+
+static void vdev_disk_alloc(vdev_t *vd)
+{
+	vdev_disk_t *dvd;
+
+	dvd = vd->vdev_tsd = kmem_zalloc(sizeof (vdev_disk_t), KM_SLEEP);
+	/*
+	 * Create the LDI event callback list.
+	 */
+	list_create(&dvd->vd_ldi_cbs, sizeof (vdev_disk_ldi_cb_t),
+	    offsetof(vdev_disk_ldi_cb_t, lcb_next));
+}
+
+static void vdev_disk_free(vdev_t *vd)
+{
+	vdev_disk_t *dvd = vd->vdev_tsd;
+	vdev_disk_ldi_cb_t *lcb;
+
+	/*
+	 * We have already closed the LDI handle. Clean up the LDI event
+	 * callbacks and free vd->vdev_tsd.
+	 */
+	while ((lcb = list_head(&dvd->vd_ldi_cbs)) != NULL) {
+		list_remove(&dvd->vd_ldi_cbs, lcb);
+		(void) ldi_ev_remove_callbacks(lcb->lcb_id);
+		kmem_free(lcb, sizeof (vdev_disk_ldi_cb_t));
+	}
+	list_destroy(&dvd->vd_ldi_cbs);
+	kmem_free(dvd, sizeof (vdev_disk_t));
+	vd->vdev_tsd = NULL;
+}
+
+/* ARGSUSED */
+static int
+vdev_disk_off_notify(ldi_handle_t lh, ldi_ev_cookie_t ecookie, void *arg,
+    void *ev_data)
+{
+	vdev_t *vd = (vdev_t *)arg;
+	vdev_disk_t *dvd = vd->vdev_tsd;
+
+	/*
+	 * Ignore events other than offline.
+	 */
+	if (strcmp(ldi_ev_get_type(ecookie), LDI_EV_OFFLINE) != 0)
+		return (LDI_EV_SUCCESS);
+
+	/*
+	 * All LDI handles must be closed for the state change to succeed, so
+	 * call on vdev_disk_close() to do this.
+	 *
+	 * We inform vdev_disk_close that it is being called from offline
+	 * notify context so it will defer cleanup of LDI event callbacks and
+	 * freeing of vd->vdev_tsd to the offline finalize or a reopen.
+	 */
+	dvd->vd_ldi_offline = B_TRUE;
+	vdev_disk_close(vd);
+
+	/*
+	 * Now that the device is closed, request that the spa_async_thread
+	 * mark the device as REMOVED and notify FMA of the removal.
+	 */
+	zfs_post_remove(vd->vdev_spa, vd);
+	vd->vdev_remove_wanted = B_TRUE;
+	spa_async_request(vd->vdev_spa, SPA_ASYNC_REMOVE);
+
+	return (LDI_EV_SUCCESS);
+}
+
+/* ARGSUSED */
 static void
+vdev_disk_off_finalize(ldi_handle_t lh, ldi_ev_cookie_t ecookie,
+    int ldi_result, void *arg, void *ev_data)
+{
+	vdev_t *vd = (vdev_t *)arg;
+	vdev_disk_t *dvd = vd->vdev_tsd;
+	vdev_disk_ldi_cb_t *lcb;
+
+	/*
+	 * Ignore events other than offline.
+	 */
+	if (strcmp(ldi_ev_get_type(ecookie), LDI_EV_OFFLINE) != 0)
+		return;
+
+	/*
+	 * We have already closed the LDI handle in notify.
+	 * Clean up the LDI event callbacks and free vd->vdev_tsd.
+	 */
+	vdev_disk_free(vd);
+
+	/*
+	 * Request that the vdev be reopened if the offline state change was
+	 * unsuccessful.
+	 */
+	if (ldi_result != LDI_EV_SUCCESS) {
+		vd->vdev_probe_wanted = B_TRUE;
+		spa_async_request(vd->vdev_spa, SPA_ASYNC_PROBE);
+	}
+}
+
+static ldi_ev_callback_t vdev_disk_off_callb = {
+	.cb_vers = LDI_EV_CB_VERS,
+	.cb_notify = vdev_disk_off_notify,
+	.cb_finalize = vdev_disk_off_finalize
+};
+
+/* ARGSUSED */
+static void
+vdev_disk_dgrd_finalize(ldi_handle_t lh, ldi_ev_cookie_t ecookie,
+    int ldi_result, void *arg, void *ev_data)
+{
+	vdev_t *vd = (vdev_t *)arg;
+
+	/*
+	 * Ignore events other than degrade.
+	 */
+	if (strcmp(ldi_ev_get_type(ecookie), LDI_EV_DEGRADE) != 0)
+		return;
+
+	/*
+	 * Degrade events always succeed. Mark the vdev as degraded.
+	 * This status is purely informative for the user.
+	 */
+	(void) vdev_degrade(vd->vdev_spa, vd->vdev_guid, 0);
+}
+
+static ldi_ev_callback_t vdev_disk_dgrd_callb = {
+	.cb_vers = LDI_EV_CB_VERS,
+	.cb_notify = NULL,
+	.cb_finalize = vdev_disk_dgrd_finalize
+};
+
+static void
 vdev_disk_hold(vdev_t *vd)
 {
 	ddi_devid_t devid;
@@ -141,8 +279,10 @@
     uint64_t *ashift)
 {
 	spa_t *spa = vd->vdev_spa;
-	vdev_disk_t *dvd;
+	vdev_disk_t *dvd = vd->vdev_tsd;
 	struct dk_minfo_ext dkmext;
+	ldi_ev_cookie_t ecookie;
+	vdev_disk_ldi_cb_t *lcb;
 	int error;
 	dev_t dev;
 	int otyp;
@@ -159,13 +299,25 @@
 	 * Reopen the device if it's not currently open. Otherwise,
 	 * just update the physical size of the device.
 	 */
-	if (vd->vdev_tsd != NULL) {
-		ASSERT(vd->vdev_reopening);
-		dvd = vd->vdev_tsd;
-		goto skip_open;
+	if (dvd != NULL) {
+		if (dvd->vd_ldi_offline && dvd->vd_lh == NULL) {
+			/*
+			 * If we are opening a device in its offline notify
+			 * context, the LDI handle was just closed. Clean
+			 * up the LDI event callbacks and free vd->vdev_tsd.
+			 */
+			vdev_disk_free(vd);
+		} else {
+			VERIFY(vd->vdev_reopening);
+			goto skip_open;
+		}
 	}
 
-	dvd = vd->vdev_tsd = kmem_zalloc(sizeof (vdev_disk_t), KM_SLEEP);
+	/*
+	 * Create vd->vdev_tsd.
+	 */
+	vdev_disk_alloc(vd);
+	dvd = vd->vdev_tsd;
 
 	/*
 	 * When opening a disk device, we want to preserve the user's original
@@ -199,23 +351,28 @@
 		if (vd->vdev_wholedisk == -1ULL) {
 			size_t len = strlen(vd->vdev_path) + 3;
 			char *buf = kmem_alloc(len, KM_SLEEP);
-			ldi_handle_t lh;
 
 			(void) snprintf(buf, len, "%ss0", vd->vdev_path);
 
-			if (ldi_open_by_name(buf, spa_mode(spa), kcred,
-			    &lh, zfs_li) == 0) {
+			error = ldi_open_by_name(buf, spa_mode(spa), kcred,
+			    &dvd->vd_lh, zfs_li);
+			if (error == 0) {
 				spa_strfree(vd->vdev_path);
 				vd->vdev_path = buf;
 				vd->vdev_wholedisk = 1ULL;
-				(void) ldi_close(lh, spa_mode(spa), kcred);
 			} else {
 				kmem_free(buf, len);
 			}
 		}
 
-		error = ldi_open_by_name(vd->vdev_path, spa_mode(spa), kcred,
-		    &dvd->vd_lh, zfs_li);
+		/*
+		 * If we have not yet opened the device, try to open it by the
+		 * specified path.
+		 */
+		if (error != 0) {
+			error = ldi_open_by_name(vd->vdev_path, spa_mode(spa),
+			    kcred, &dvd->vd_lh, zfs_li);
+		}
 
 		/*
 		 * Compare the devid to the stored value.
@@ -299,6 +456,27 @@
 		kmem_free(physpath, MAXPATHLEN);
 	}
 
+	/*
+	 * Register callbacks for the LDI offline event.
+	 */
+	if (ldi_ev_get_cookie(dvd->vd_lh, LDI_EV_OFFLINE, &ecookie) ==
+	    LDI_EV_SUCCESS) {
+		lcb = kmem_zalloc(sizeof (vdev_disk_ldi_cb_t), KM_SLEEP);
+		list_insert_tail(&dvd->vd_ldi_cbs, lcb);
+		(void) ldi_ev_register_callbacks(dvd->vd_lh, ecookie,
+		    &vdev_disk_off_callb, (void *) vd, &lcb->lcb_id);
+	}
+
+	/*
+	 * Register callbacks for the LDI degrade event.
+	 */
+	if (ldi_ev_get_cookie(dvd->vd_lh, LDI_EV_DEGRADE, &ecookie) ==
+	    LDI_EV_SUCCESS) {
+		lcb = kmem_zalloc(sizeof (vdev_disk_ldi_cb_t), KM_SLEEP);
+		list_insert_tail(&dvd->vd_ldi_cbs, lcb);
+		(void) ldi_ev_register_callbacks(dvd->vd_lh, ecookie,
+		    &vdev_disk_dgrd_callb, (void *) vd, &lcb->lcb_id);
+	}
 skip_open:
 	/*
 	 * Determine the actual size of the device.
@@ -350,22 +528,36 @@
 vdev_disk_close(vdev_t *vd)
 {
 	vdev_disk_t *dvd = vd->vdev_tsd;
+	vdev_disk_ldi_cb_t *lcb;
 
 	if (vd->vdev_reopening || dvd == NULL)
 		return;
 
-	if (dvd->vd_minor != NULL)
+	if (dvd->vd_minor != NULL) {
 		ddi_devid_str_free(dvd->vd_minor);
+		dvd->vd_minor = NULL;
+	}
 
-	if (dvd->vd_devid != NULL)
+	if (dvd->vd_devid != NULL) {
 		ddi_devid_free(dvd->vd_devid);
+		dvd->vd_devid = NULL;
+	}
 
-	if (dvd->vd_lh != NULL)
+	if (dvd->vd_lh != NULL) {
 		(void) ldi_close(dvd->vd_lh, spa_mode(vd->vdev_spa), kcred);
+		dvd->vd_lh = NULL;
+	}
 
 	vd->vdev_delayed_close = B_FALSE;
-	kmem_free(dvd, sizeof (vdev_disk_t));
-	vd->vdev_tsd = NULL;
+	/*
+	 * If we closed the LDI handle due to an offline notify from LDI,
+	 * don't free vd->vdev_tsd or unregister the callbacks here;
+	 * the offline finalize callback or a reopen will take care of it.
+	 */
+	if (dvd->vd_ldi_offline)
+		return;
+
+	vdev_disk_free(vd);
 }
 
 int
@@ -378,7 +570,7 @@
 	 * If the vdev is closed, it's likely in the REMOVED or FAULTED state.
 	 * Nothing to be done here but return failure.
 	 */
-	if (dvd == NULL)
+	if (dvd == NULL || (dvd->vd_ldi_offline && dvd->vd_lh == NULL))
 		return (EIO);
 
 	ASSERT(vd->vdev_ops == &vdev_disk_ops);
@@ -465,6 +657,15 @@
 	buf_t *bp;
 	int error;
 
+	/*
+	 * If the vdev is closed, it's likely in the REMOVED or FAULTED state.
+	 * Nothing to be done here but return failure.
+	 */
+	if (dvd == NULL || (dvd->vd_ldi_offline && dvd->vd_lh == NULL)) {
+		zio->io_error = ENXIO;
+		return (ZIO_PIPELINE_CONTINUE);
+	}
+
 	if (zio->io_type == ZIO_TYPE_IOCTL) {
 		/* XXPOLICY */
 		if (!vdev_readable(vd)) {
--- old/usr/src/uts/common/io/scsi/adapters/mpt_sas/mptsas.c	Mon Nov 26 00:31:46 2012
+++ new/usr/src/uts/common/io/scsi/adapters/mpt_sas/mptsas.c	Mon Nov 26 00:31:46 2012
@@ -22,6 +22,7 @@
 /*
  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
  */
 
 /*
@@ -69,6 +70,7 @@
 #include <sys/file.h>
 #include <sys/cpuvar.h>
 #include <sys/policy.h>
+#include <sys/model.h>
 #include <sys/sysevent.h>
 #include <sys/sysevent/eventdefs.h>
 #include <sys/sysevent/dr.h>
@@ -369,8 +371,7 @@
     mptsas_phymask_t *phymask);
 static mptsas_target_t *mptsas_addr_to_ptgt(mptsas_t *mpt, char *addr,
     mptsas_phymask_t phymask);
-static int mptsas_set_led_status(mptsas_t *mpt, mptsas_target_t *ptgt,
-    uint32_t slotstatus);
+static int mptsas_flush_led_status(mptsas_t *mpt, mptsas_target_t *ptgt);
 
 
 /*
@@ -1089,6 +1090,10 @@
 		goto fail;
 	}
 
+	/* Mark us as a primary ioctl node for an instance. */
+	(void) ddi_prop_update_int(DDI_DEV_T_NONE, dip, "primary-ioctl-node",
+	    instance);
+
 	/* Indicate that we are 'sizeof (scsi_*(9S))' clean. */
 	scsi_size_clean(dip);
 
@@ -6451,7 +6456,8 @@
 		}
 
 		mutex_enter(&mpt->m_mutex);
-		if (mptsas_set_led_status(mpt, ptgt, 0) != DDI_SUCCESS) {
+		ptgt->m_led_status = 0;
+		if (mptsas_flush_led_status(mpt, ptgt) != DDI_SUCCESS) {
 			NDBG14(("mptsas: clear LED for tgt %x failed",
 			    ptgt->m_slot_num));
 		}
@@ -11951,6 +11957,156 @@
 }
 
 static int
+led_control(mptsas_t *mpt, intptr_t data, int mode)
+{
+	int ret = 0;
+	mptsas_led_control_t lc;
+	mptsas_target_t *ptgt;
+
+	if (ddi_copyin((void *)data, &lc, sizeof (lc), mode) != 0) {
+		return (EFAULT);
+	}
+
+	if ((lc.Command != MPTSAS_LEDCTL_FLAG_SET &&
+	    lc.Command != MPTSAS_LEDCTL_FLAG_GET) ||
+	    lc.Led < MPTSAS_LEDCTL_LED_IDENT ||
+	    lc.Led > MPTSAS_LEDCTL_LED_OK2RM) {
+		return (EINVAL);
+	}
+
+	/* Locate the target we're interrogating... */
+	mutex_enter(&mpt->m_mutex);
+	ptgt = (mptsas_target_t *)mptsas_hash_traverse(&mpt->m_active->m_tgttbl,
+	    MPTSAS_HASH_FIRST);
+	while (ptgt != NULL) {
+		if (ptgt->m_enclosure == lc.Enclosure &&
+		    ptgt->m_slot_num == lc.Slot) {
+			break;
+		}
+		ptgt = (mptsas_target_t *)mptsas_hash_traverse(
+		    &mpt->m_active->m_tgttbl, MPTSAS_HASH_NEXT);
+	}
+	if (ptgt == NULL) {
+		/* We could not find a target for that enclosure/slot. */
+		mutex_exit(&mpt->m_mutex);
+		return (ENOENT);
+	}
+
+	if (lc.Command == MPTSAS_LEDCTL_FLAG_SET) {
+		/* Update our internal LED state. */
+		ptgt->m_led_status &= ~(1 << (lc.Led - 1));
+		ptgt->m_led_status |= (!!lc.LedStatus) << (lc.Led - 1);
+
+		/* Flush it to the controller. */
+		ret = mptsas_flush_led_status(mpt, ptgt);
+		mutex_exit(&mpt->m_mutex);
+		return (ret);
+	}
+
+	/* Return our internal LED state. */
+	lc.LedStatus = !!(ptgt->m_led_status & (1 << (lc.Led - 1)));
+	mutex_exit(&mpt->m_mutex);
+
+	if (ddi_copyout(&lc, (void *)data, sizeof (lc), mode) != 0) {
+		return (EFAULT);
+	}
+
+	return (0);
+}
+
+static int
+get_disk_info(mptsas_t *mpt, intptr_t data, int mode)
+{
+	int i;
+	int count = 0;
+	int ret = 0;
+	mptsas_target_t *ptgt;
+	mptsas_disk_info_t *di;
+	STRUCT_DECL(mptsas_get_disk_info, gdi);
+
+	STRUCT_INIT(gdi, get_udatamodel());
+
+	if (ddi_copyin((void *)data, STRUCT_BUF(gdi), STRUCT_SIZE(gdi),
+	    mode) != 0) {
+		return (EFAULT);
+	}
+
+restart:
+	/* Find out how many targets there are. */
+	mutex_enter(&mpt->m_mutex);
+	ptgt = (mptsas_target_t *)mptsas_hash_traverse(&mpt->m_active->m_tgttbl,
+	    MPTSAS_HASH_FIRST);
+	while (ptgt != NULL) {
+		count++;
+		ptgt = (mptsas_target_t *)mptsas_hash_traverse(
+		    &mpt->m_active->m_tgttbl, MPTSAS_HASH_NEXT);
+	}
+	mutex_exit(&mpt->m_mutex);
+
+	/*
+	 * If we haven't been asked to copy out information on each target,
+	 * then just return the count.
+	 */
+	STRUCT_FSET(gdi, DiskCount, count);
+	if (STRUCT_FGETP(gdi, PtrDiskInfoArray) == NULL)
+		goto copy_out;
+
+	/*
+	 * If we haven't been given a large enough buffer to copy out into,
+	 * let the caller know.
+	 */
+	if (STRUCT_FGET(gdi, DiskInfoArraySize) <
+	    count * sizeof (mptsas_disk_info_t)) {
+		ret = ENOSPC;
+		goto copy_out;
+	}
+
+	di = kmem_zalloc(count * sizeof (mptsas_disk_info_t), KM_SLEEP);
+
+	mutex_enter(&mpt->m_mutex);
+	i = 0;
+	ptgt = (mptsas_target_t *)mptsas_hash_traverse(&mpt->m_active->m_tgttbl,
+	    MPTSAS_HASH_FIRST);
+	while (ptgt != NULL) {
+		if (i >= count) {
+			/*
+			 * The number of targets changed while we weren't
+			 * looking.  Go again.
+			 */
+			mutex_exit(&mpt->m_mutex);
+			kmem_free(di, count * sizeof (mptsas_disk_info_t));
+			goto restart;
+		}
+		di[i].Instance = mpt->m_instance;
+		di[i].Enclosure = ptgt->m_enclosure;
+		di[i].Slot = ptgt->m_slot_num;
+		di[i].SasAddress = ptgt->m_sas_wwn;
+
+		ptgt = (mptsas_target_t *)mptsas_hash_traverse(
+		    &mpt->m_active->m_tgttbl, MPTSAS_HASH_NEXT);
+		i++;
+	}
+	mutex_exit(&mpt->m_mutex);
+	STRUCT_FSET(gdi, DiskCount, i);
+
+	/* Copy out the disk information to the caller. */
+	if (ddi_copyout((void *)di, STRUCT_FGETP(gdi, PtrDiskInfoArray),
+	    i * sizeof (mptsas_disk_info_t), mode) != 0) {
+		ret = EFAULT;
+	}
+
+	kmem_free(di, count * sizeof (mptsas_disk_info_t));
+
+copy_out:
+	if (ddi_copyout(STRUCT_BUF(gdi), (void *)data, STRUCT_SIZE(gdi),
+	    mode) != 0) {
+		ret = EFAULT;
+	}
+
+	return (ret);
+}
+
+static int
 mptsas_ioctl(dev_t dev, int cmd, intptr_t data, int mode, cred_t *credp,
     int *rval)
 {
@@ -12037,31 +12193,14 @@
 			} else if (cmd == DEVCTL_DEVICE_OFFLINE) {
 				ptgt->m_tgt_unconfigured = 1;
 			}
-			slotstatus = 0;
-#ifdef MPTSAS_GET_LED
-			/*
-			 * The get led status can't get a valid/reasonable
-			 * state, so ignore the get led status, and write the
-			 * required value directly
-			 */
-			if (mptsas_get_led_status(mpt, ptgt, &slotstatus) !=
-			    DDI_SUCCESS) {
-				NDBG14(("mptsas_ioctl: get LED for tgt %s "
-				    "failed %x", addr, slotstatus));
-				slotstatus = 0;
-			}
-			NDBG14(("mptsas_ioctl: LED status %x for %s",
-			    slotstatus, addr));
-#endif
 			if (cmd == DEVCTL_DEVICE_OFFLINE) {
-				slotstatus |=
-				    MPI2_SEP_REQ_SLOTSTATUS_REQUEST_REMOVE;
+				ptgt->m_led_status |=
+				    (1 << (MPTSAS_LEDCTL_LED_OK2RM - 1));
 			} else {
-				slotstatus &=
-				    ~MPI2_SEP_REQ_SLOTSTATUS_REQUEST_REMOVE;
+				ptgt->m_led_status &=
+				    ~(1 << (MPTSAS_LEDCTL_LED_OK2RM - 1));
 			}
-			if (mptsas_set_led_status(mpt, ptgt, slotstatus) !=
-			    DDI_SUCCESS) {
+			if (mptsas_flush_led_status(mpt, ptgt) != DDI_SUCCESS) {
 				NDBG14(("mptsas_ioctl: set LED for tgt %s "
 				    "failed %x", addr, slotstatus));
 			}
@@ -12071,6 +12210,12 @@
 		goto out;
 	}
 	switch (cmd) {
+		case MPTIOCTL_GET_DISK_INFO:
+			status = get_disk_info(mpt, data, mode);
+			break;
+		case MPTIOCTL_LED_CONTROL:
+			status = led_control(mpt, data, mode);
+			break;
 		case MPTIOCTL_UPDATE_FLASH:
 			if (ddi_copyin((void *)data, &flashdata,
 				sizeof (struct mptsas_update_flash), mode)) {
@@ -14531,8 +14676,9 @@
 				    (ptgt->m_tgt_unconfigured == 0)) {
 					rval = mdi_pi_online(*pip, 0);
 					mutex_enter(&mpt->m_mutex);
-					(void) mptsas_set_led_status(mpt, ptgt,
-					    0);
+					ptgt->m_led_status = 0;
+					(void) mptsas_flush_led_status(mpt,
+					    ptgt);
 					mutex_exit(&mpt->m_mutex);
 				} else {
 					rval = DDI_SUCCESS;
@@ -14789,8 +14935,8 @@
 		mdi_rtn = mdi_pi_online(*pip, 0);
 		if (mdi_rtn == MDI_SUCCESS) {
 			mutex_enter(&mpt->m_mutex);
-			if (mptsas_set_led_status(mpt, ptgt, 0) !=
-			    DDI_SUCCESS) {
+			ptgt->m_led_status = 0;
+			if (mptsas_flush_led_status(mpt, ptgt) != DDI_SUCCESS) {
 				NDBG14(("mptsas: clear LED for slot %x "
 				    "failed", ptgt->m_slot_num));
 			}
@@ -15151,8 +15297,8 @@
 		}
 		if (ndi_rtn == NDI_SUCCESS) {
 			mutex_enter(&mpt->m_mutex);
-			if (mptsas_set_led_status(mpt, ptgt, 0) !=
-			    DDI_SUCCESS) {
+			ptgt->m_led_status = 0;
+			if (mptsas_flush_led_status(mpt, ptgt) != DDI_SUCCESS) {
 				NDBG14(("mptsas: clear LED for tgt %x "
 				    "failed", ptgt->m_slot_num));
 			}
@@ -16053,23 +16199,26 @@
 	return (ptgt);
 }
 
-#ifdef MPTSAS_GET_LED
 static int
-mptsas_get_led_status(mptsas_t *mpt, mptsas_target_t *ptgt,
-    uint32_t *slotstatus)
+mptsas_flush_led_status(mptsas_t *mpt, mptsas_target_t *ptgt)
 {
-	return (mptsas_send_sep(mpt, ptgt, slotstatus,
-	    MPI2_SEP_REQ_ACTION_READ_STATUS));
-}
-#endif
-static int
-mptsas_set_led_status(mptsas_t *mpt, mptsas_target_t *ptgt, uint32_t slotstatus)
-{
+	uint32_t slotstatus = 0;
+
+	/* Build an MPI2 Slot Status based on our view of the world */
+	if (ptgt->m_led_status & (1 << (MPTSAS_LEDCTL_LED_IDENT - 1)))
+		slotstatus |= MPI2_SEP_REQ_SLOTSTATUS_IDENTIFY_REQUEST;
+	if (ptgt->m_led_status & (1 << (MPTSAS_LEDCTL_LED_FAIL - 1)))
+		slotstatus |= MPI2_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT;
+	if (ptgt->m_led_status & (1 << (MPTSAS_LEDCTL_LED_OK2RM - 1)))
+		slotstatus |= MPI2_SEP_REQ_SLOTSTATUS_REQUEST_REMOVE;
+
+	/* Write it to the controller */
 	NDBG14(("mptsas_ioctl: set LED status %x for slot %x",
 	    slotstatus, ptgt->m_slot_num));
 	return (mptsas_send_sep(mpt, ptgt, &slotstatus,
 	    MPI2_SEP_REQ_ACTION_WRITE_STATUS));
 }
+
 /*
  *  send sep request, use enclosure/slot addressing
  */
--- old/usr/src/uts/common/os/devcfg.c	Mon Nov 26 00:31:48 2012
+++ new/usr/src/uts/common/os/devcfg.c	Mon Nov 26 00:31:48 2012
@@ -5890,6 +5890,13 @@
 		}
 	}
 
+	/*
+	 * dv_mknod places a hold on the dev_info_t for each devfs node
+	 * created.  If we're to succeed in detaching this device, we must
+	 * first release all outstanding references held by devfs.
+	 */
+	(void) devfs_clean(pdip, NULL, DV_CLEAN_FORCE);
+
 	if (i_ddi_detachchild(dip, flags) != DDI_SUCCESS) {
 		if (flags & NDI_DEVI_OFFLINE) {
 			RIO_DEBUG((CE_NOTE, "devi_detach_node: offline failed."
--- old/usr/src/uts/common/os/driver_lyr.c	Mon Nov 26 00:31:49 2012
+++ new/usr/src/uts/common/os/driver_lyr.c	Mon Nov 26 00:31:49 2012
@@ -144,6 +144,9 @@
 	{ NULL}			/* must terminate list */
 };
 
+static ldi_ev_callback_impl_t *walker_next = NULL;
+static ldi_ev_callback_impl_t *walker_prev = NULL;
+
 void
 ldi_init(void)
 {
@@ -3329,8 +3332,10 @@
 
 	ret = LDI_EV_NONE;
 	ldi_ev_lock();
+	VERIFY(walker_next == NULL);
 	listp = &ldi_ev_callback_list.le_head;
-	for (lecp = list_head(listp); lecp; lecp = list_next(listp, lecp)) {
+	for (lecp = list_head(listp); lecp; lecp = walker_next) {
+		walker_next = list_next(listp, lecp);
 
 		/* Check if matching device */
 		if (!ldi_ev_device_match(lecp, dip, dev, spec_type))
@@ -3386,7 +3391,9 @@
 	 * Undo notifies already sent
 	 */
 	lecp = list_prev(listp, lecp);
-	for (; lecp; lecp = list_prev(listp, lecp)) {
+	VERIFY(walker_prev == NULL);
+	for (; lecp; lecp = walker_prev) {
+		walker_prev = list_prev(listp, lecp);
 
 		/*
 		 * Check if matching device
@@ -3437,6 +3444,8 @@
 	}
 
 out:
+	walker_next = NULL;
+	walker_prev = NULL;
 	ldi_ev_unlock();
 
 	if (ret == LDI_EV_NONE) {
@@ -3552,8 +3561,10 @@
 	    " event=%s", (void *)dip, ldi_result, event));
 
 	ldi_ev_lock();
+	VERIFY(walker_next == NULL);
 	listp = &ldi_ev_callback_list.le_head;
-	for (lecp = list_head(listp); lecp; lecp = list_next(listp, lecp)) {
+	for (lecp = list_head(listp); lecp; lecp = walker_next) {
+		walker_next = list_next(listp, lecp);
 
 		if (lecp->lec_finalize == NULL) {
 			LDI_EVDBG((CE_NOTE, "ldi_invoke_finalize(): No "
@@ -3604,6 +3615,7 @@
 			lecp->lec_finalize = NULL;
 		}
 	}
+	walker_next = NULL;
 	ldi_ev_unlock();
 
 	if (found)
@@ -3685,6 +3697,13 @@
 		next = list_next(listp, lecp);
 		if (lecp->lec_id == id) {
 			ASSERT(found == NULL);
+
+			/* If there is a walk in progress, move it along... */
+			if (walker_next == lecp)
+				walker_next = next;
+			if (walker_prev == lecp)
+				walker_prev = list_prev(listp, walker_prev);
+
 			list_remove(listp, lecp);
 			found = lecp;
 		}
--- old/usr/src/uts/common/sys/scsi/adapters/mpt_sas/mptsas_ioctl.h	Mon Nov 26 00:31:49 2012
+++ new/usr/src/uts/common/sys/scsi/adapters/mpt_sas/mptsas_ioctl.h	Mon Nov 26 00:31:49 2012
@@ -22,6 +22,7 @@
 /*
  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
  */
 
 /*
@@ -71,6 +72,8 @@
 #define	MPTIOCTL_GET_PCI_INFO		(MPTIOCTL | 8)
 #define	MPTIOCTL_DIAG_ACTION		(MPTIOCTL | 9)
 #define	MPTIOCTL_REG_ACCESS		(MPTIOCTL | 10)
+#define	MPTIOCTL_GET_DISK_INFO		(MPTIOCTL | 11)
+#define	MPTIOCTL_LED_CONTROL		(MPTIOCTL | 12)
 
 /*
  *  The following are our ioctl() return status values.  If everything went
@@ -296,6 +299,55 @@
 	uint32_t	RegData;
 } mptsas_reg_access_t;
 
+/*
+ * Disk Toplogy Information
+ */
+typedef struct mptsas_disk_info_t
+{
+	uint64_t	SasAddress;
+	uint16_t	Instance;
+	uint16_t	Enclosure;
+	uint16_t	Slot;
+} mptsas_disk_info_t;
+
+typedef struct mptsas_get_disk_info
+{
+	uint16_t		DiskCount;
+	mptsas_disk_info_t	*PtrDiskInfoArray;
+	uint64_t		DiskInfoArraySize;
+} mptsas_get_disk_info_t;
+
+#ifdef _KERNEL
+
+typedef struct mptsas_get_disk_info32
+{
+	uint16_t		DiskCount;
+	caddr32_t		PtrDiskInfoArray;
+	uint64_t		DiskInfoArraySize;
+} mptsas_get_disk_info32_t;
+
+#endif /* _KERNEL */
+
+/*
+ * LED Control
+ */
+
+typedef struct mptsas_led_control
+{
+	uint8_t		Command;
+	uint16_t	Enclosure;
+	uint16_t	Slot;
+	uint8_t		Led;
+	uint8_t		LedStatus;
+} mptsas_led_control_t;
+
+#define	MPTSAS_LEDCTL_FLAG_SET		1
+#define	MPTSAS_LEDCTL_FLAG_GET		2
+
+#define	MPTSAS_LEDCTL_LED_IDENT		1
+#define	MPTSAS_LEDCTL_LED_FAIL		2
+#define	MPTSAS_LEDCTL_LED_OK2RM		3
+
 #ifdef  __cplusplus
 }
 #endif
--- old/usr/src/uts/common/sys/scsi/adapters/mpt_sas/mptsas_var.h	Mon Nov 26 00:31:49 2012
+++ new/usr/src/uts/common/sys/scsi/adapters/mpt_sas/mptsas_var.h	Mon Nov 26 00:31:49 2012
@@ -22,6 +22,7 @@
 /*
  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
+ * Copyright (c) 2012, Joyent, Inc. All rights reserved.
  */
 
 /*
@@ -208,6 +209,7 @@
 		uint16_t		m_enclosure;
 		uint16_t		m_slot_num;
 		uint32_t		m_tgt_unconfigured;
+		uint8_t			m_led_status;
 
 		/*
 		 * For the common case, the elements in this structure are
