LSI SAS 2008 (mfi) on SuperMicro X8SI6-F

Kenneth D. Merry ken at freebsd.org
Wed Feb 16 17:37:34 UTC 2011


On Wed, Feb 16, 2011 at 19:23:26 +0300, Dmitry Morozovsky wrote:
> On Wed, 16 Feb 2011, Daniel Kalchev wrote:
> 
> DK> I have sucessfully used that motherboard with FreeBSD 9 and the mps driver.
> DK> The mfi driver found on the LSI site does not support this controller.
> 
> Ah that makes sense. I'm a bit reluctant to use -current on this particular 
> machine, so I would discuss MFCing mps driver wirh ken@

I've been planning to MFC it for a while, it has just been a time issue.

There are a few outstanding issues with the driver, but even with the
issues it would probably be better for people to be able to use it than
have nothing to use in -stable.

A few of the known issues:
 - Out of chain frames handling.  I'm working on this one now.
 - No device queue freezing on errors.  I'm working on this one.
 - No Integrated RAID handling.  This will get fixed when LSI completes
   their version of the driver.
 - Firmware upgrade code doesn't work properly.  This will get fixed when
   LSI completes their version of the driver.

For the happy path, things should work well enough I suppose.

I have attached a patch against -stable, try it out and let me know whether
it works.  If so I'll go ahead and MFC it.

Ken
-- 
Kenneth Merry
ken at FreeBSD.ORG
-------------- next part --------------

Property changes on: sys
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /head/sys:r212420,212616,212772,212802,213535,213702,213704,213707-213708,213743,213839-213840,213882,213898,216088,216227,216363,216368

Index: sys/conf/files
===================================================================
--- sys/conf/files	(revision 218743)
+++ sys/conf/files	(working copy)
@@ -1303,6 +1303,11 @@
 dev/mmc/mmcbus_if.m		standard
 dev/mmc/mmcsd.c			optional mmcsd
 dev/mn/if_mn.c			optional mn pci
+dev/mps/mps.c			optional mps
+dev/mps/mps_pci.c		optional mps pci
+dev/mps/mps_sas.c		optional mps
+dev/mps/mps_table.c		optional mps
+dev/mps/mps_user.c		optional mps
 dev/mpt/mpt.c			optional mpt
 dev/mpt/mpt_cam.c		optional mpt
 dev/mpt/mpt_debug.c		optional mpt
Index: sys/modules/mps/Makefile
===================================================================
--- sys/modules/mps/Makefile	(revision 212420)
+++ sys/modules/mps/Makefile	(working copy)
@@ -4,7 +4,8 @@
 
 KMOD=	mps
 SRCS=	mps_pci.c mps.c mps_sas.c mps_table.c mps_user.c
-SRCS+=	opt_mps.h opt_cam.h
+SRCS+=	opt_compat.h
+SRCS+=	opt_cam.h
 SRCS+=	device_if.h bus_if.h pci_if.h
 
 #CFLAGS += -DMPS_DEBUG
Index: sys/modules/Makefile
===================================================================
--- sys/modules/Makefile	(revision 218743)
+++ sys/modules/Makefile	(working copy)
@@ -186,6 +186,7 @@
 	mmc \
 	mmcsd \
 	${_mpt} \
+	mps \
 	mqueue \
 	msdosfs \
 	msdosfs_iconv \
Index: sys/mips/mips/mp_machdep.c
===================================================================
--- sys/mips/mips/mp_machdep.c	(revision 218743)
+++ sys/mips/mips/mp_machdep.c	(working copy)
@@ -165,7 +165,7 @@
 #if 0
 		case IPI_HARDCLOCK:
 			CTR1(KTR_SMP, "%s: IPI_HARDCLOCK", __func__);
-			hardclockintr();;
+			hardclockintr();
 			break;
 #endif
 		default:
Index: sys/dev/sis/if_sisreg.h
===================================================================
--- sys/dev/sis/if_sisreg.h	(revision 218743)
+++ sys/dev/sis/if_sisreg.h	(working copy)
@@ -497,7 +497,7 @@
 	int			sis_tx_prod;
 	int			sis_tx_cons;
 	int			sis_tx_cnt;
-	int			sis_rx_cons;;
+	int			sis_rx_cons;
 	bus_addr_t		sis_rx_paddr;
 	bus_addr_t		sis_tx_paddr;
 	struct callout		sis_stat_ch;
Index: sys/dev/siba/siba_bwn.c
===================================================================
--- sys/dev/siba/siba_bwn.c	(revision 218743)
+++ sys/dev/siba/siba_bwn.c	(working copy)
@@ -326,7 +326,7 @@
 siba_bwn_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
 {
 	struct siba_dev_softc *sd;
-	struct siba_softc *siba;;
+	struct siba_softc *siba;
 
 	sd = device_get_ivars(child);
 	siba = sd->sd_bus;
Index: sys/dev/mps/mps_ioctl.h
===================================================================
--- sys/dev/mps/mps_ioctl.h	(revision 212420)
+++ sys/dev/mps/mps_ioctl.h	(working copy)
@@ -103,44 +103,4 @@
 #define	MPSIO_RAID_ACTION	_IOWR('M', 205, struct mps_raid_action)
 #define	MPSIO_MPS_COMMAND	_IOWR('M', 210, struct mps_usr_command)
 
-#if defined(__amd64__)
-struct mps_cfg_page_req32 {
-	MPI2_CONFIG_PAGE_HEADER header;
-	uint32_t page_address;
-	uint32_t buf;
-	int	len;	
-	uint16_t ioc_status;
-};
-
-struct mps_ext_cfg_page_req32 {
-	MPI2_CONFIG_EXTENDED_PAGE_HEADER header;
-	uint32_t page_address;
-	uint32_t buf;
-	int	len;
-	uint16_t ioc_status;
-};
-
-struct mps_raid_action32 {
-	uint8_t action;
-	uint8_t volume_bus;
-	uint8_t volume_id;
-	uint8_t phys_disk_num;
-	uint32_t action_data_word;
-	uint32_t buf;
-	int len;
-	uint32_t volume_status;
-	uint32_t action_data[4];
-	uint16_t action_status;
-	uint16_t ioc_status;
-	uint8_t write;
-};
-
-#define	MPSIO_READ_CFG_HEADER32	_IOWR('M', 100, struct mps_cfg_page_req32)
-#define	MPSIO_READ_CFG_PAGE32	_IOWR('M', 101, struct mps_cfg_page_req32)
-#define	MPSIO_READ_EXT_CFG_HEADER32 _IOWR('M', 102, struct mps_ext_cfg_page_req32)
-#define	MPSIO_READ_EXT_CFG_PAGE32 _IOWR('M', 103, struct mps_ext_cfg_page_req32)
-#define	MPSIO_WRITE_CFG_PAGE32	_IOWR('M', 104, struct mps_cfg_page_req32)
-#define	MPSIO_RAID_ACTION32	_IOWR('M', 105, struct mps_raid_action32)
-#endif
-
 #endif /* !_MPS_IOCTL_H_ */
Index: sys/dev/mps/mps.c
===================================================================
--- sys/dev/mps/mps.c	(revision 212420)
+++ sys/dev/mps/mps.c	(working copy)
@@ -43,6 +43,7 @@
 #include <sys/malloc.h>
 #include <sys/uio.h>
 #include <sys/sysctl.h>
+#include <sys/endian.h>
 
 #include <machine/bus.h>
 #include <machine/resource.h>
@@ -380,7 +381,7 @@
 	return (0);
 }
 
-static void
+void
 mps_enqueue_request(struct mps_softc *sc, struct mps_command *cm)
 {
 
@@ -607,9 +608,16 @@
 static int
 mps_alloc_replies(struct mps_softc *sc)
 {
-	int rsize;
+	int rsize, num_replies;
 
-	rsize = sc->facts->ReplyFrameSize * sc->num_replies * 4; 
+	/*
+	 * sc->num_replies should be one less than sc->fqdepth.  We need to
+	 * allocate space for sc->fqdepth replies, but only sc->num_replies
+	 * replies can be used at once.
+	 */
+	num_replies = max(sc->fqdepth, sc->num_replies);
+
+	rsize = sc->facts->ReplyFrameSize * num_replies * 4; 
         if (bus_dma_tag_create( sc->mps_parent_dmat,    /* parent */
 				4, 0,			/* algnmnt, boundary */
 				BUS_SPACE_MAXADDR_32BIT,/* lowaddr */
@@ -782,11 +790,19 @@
 
 	memset((uint8_t *)sc->post_queue, 0xff, sc->pqdepth * 8);
 
+	/*
+	 * According to the spec, we need to use one less reply than we
+	 * have space for on the queue.  So sc->num_replies (the number we
+	 * use) should be less than sc->fqdepth (allocated size).
+	 */
 	if (sc->num_replies >= sc->fqdepth)
 		return (EINVAL);
 
-	for (i = 0; i < sc->num_replies; i++)
-		sc->free_queue[i] = sc->reply_busaddr + i * sc->facts->ReplyFrameSize * 4;
+	/*
+	 * Initialize all of the free queue entries.
+	 */
+	for (i = 0; i < sc->fqdepth; i++)
+		sc->free_queue[i] = sc->reply_busaddr + (i * sc->facts->ReplyFrameSize * 4);
 	sc->replyfreeindex = sc->num_replies;
 
 	return (0);
@@ -805,6 +821,9 @@
 	snprintf(tmpstr, sizeof(tmpstr), "hw.mps.%d.debug_level",
 	    device_get_unit(sc->mps_dev));
 	TUNABLE_INT_FETCH(tmpstr, &sc->mps_debug);
+	snprintf(tmpstr, sizeof(tmpstr), "hw.mps.%d.allow_multiple_tm_cmds",
+	    device_get_unit(sc->mps_dev));
+	TUNABLE_INT_FETCH(tmpstr, &sc->allow_multiple_tm_cmds);
 
 	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
 
@@ -831,6 +850,11 @@
 	    OID_AUTO, "debug_level", CTLFLAG_RW, &sc->mps_debug, 0,
 	    "mps debug level");
 
+	SYSCTL_ADD_INT(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree),
+	    OID_AUTO, "allow_multiple_tm_cmds", CTLFLAG_RW,
+	    &sc->allow_multiple_tm_cmds, 0,
+	    "allow multiple simultaneous task management cmds");
+
 	if ((error = mps_transition_ready(sc)) != 0)
 		return (error);
 
@@ -873,6 +897,7 @@
 	    sc->facts->MaxReplyDescriptorPostQueueDepth) - 1;
 	TAILQ_INIT(&sc->req_list);
 	TAILQ_INIT(&sc->chain_list);
+	TAILQ_INIT(&sc->tm_list);
 
 	if (((error = mps_alloc_queues(sc)) != 0) ||
 	    ((error = mps_alloc_replies(sc)) != 0) ||
@@ -898,7 +923,6 @@
 	 * replies.
 	 */
 	sc->replypostindex = 0;
-	sc->replycurindex = 0;
 	mps_regwrite(sc, MPI2_REPLY_FREE_HOST_INDEX_OFFSET, sc->replyfreeindex);
 	mps_regwrite(sc, MPI2_REPLY_POST_HOST_INDEX_OFFSET, 0);
 
@@ -915,7 +939,10 @@
 	/* Attach the subsystems so they can prepare their event masks. */
 	/* XXX Should be dynamic so that IM/IR and user modules can attach */
 	if (((error = mps_attach_log(sc)) != 0) ||
-	    ((error = mps_attach_sas(sc)) != 0)) {
+	    ((error = mps_attach_sas(sc)) != 0) ||
+	    ((error = mps_attach_user(sc)) != 0)) {
+		mps_printf(sc, "%s failed to attach all subsystems: error %d\n",
+		    __func__, error);
 		mps_free(sc);
 		return (error);
 	}
@@ -1199,7 +1226,8 @@
 		desc = &sc->post_queue[pq];
 		flags = desc->Default.ReplyFlags &
 		    MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK;
-		if (flags == MPI2_RPY_DESCRIPT_FLAGS_UNUSED)
+		if ((flags == MPI2_RPY_DESCRIPT_FLAGS_UNUSED)
+		 || (desc->Words.High == 0xffffffff))
 			break;
 
 		switch (flags) {
@@ -1212,9 +1240,36 @@
 			uint32_t baddr;
 			uint8_t *reply;
 
+			/*
+			 * Re-compose the reply address from the address
+			 * sent back from the chip.  The ReplyFrameAddress
+			 * is the lower 32 bits of the physical address of
+			 * particular reply frame.  Convert that address to
+			 * host format, and then use that to provide the
+			 * offset against the virtual address base
+			 * (sc->reply_frames).
+			 */
+			baddr = le32toh(desc->AddressReply.ReplyFrameAddress);
 			reply = sc->reply_frames +
-			    sc->replycurindex * sc->facts->ReplyFrameSize * 4;
-			baddr = desc->AddressReply.ReplyFrameAddress;
+				(baddr - ((uint32_t)sc->reply_busaddr));
+			/*
+			 * Make sure the reply we got back is in a valid
+			 * range.  If not, go ahead and panic here, since
+			 * we'll probably panic as soon as we deference the
+			 * reply pointer anyway.
+			 */
+			if ((reply < sc->reply_frames)
+			 || (reply > (sc->reply_frames +
+			     (sc->fqdepth * sc->facts->ReplyFrameSize * 4)))) {
+				printf("%s: WARNING: reply %p out of range!\n",
+				       __func__, reply);
+				printf("%s: reply_frames %p, fqdepth %d, "
+				       "frame size %d\n", __func__,
+				       sc->reply_frames, sc->fqdepth,
+				       sc->facts->ReplyFrameSize * 4);
+				printf("%s: baddr %#x,\n", __func__, baddr);
+				panic("Reply address out of range");
+			}
 			if (desc->AddressReply.SMID == 0) {
 				mps_dispatch_event(sc, baddr,
 				   (MPI2_EVENT_NOTIFICATION_REPLY *) reply);
@@ -1224,8 +1279,6 @@
 				cm->cm_reply_data =
 				    desc->AddressReply.ReplyFrameAddress;
 			}
-			if (++sc->replycurindex >= sc->fqdepth)
-				sc->replycurindex = 0;
 			break;
 		}
 		case MPI2_RPY_DESCRIPT_FLAGS_TARGETASSIST_SUCCESS:
@@ -1270,7 +1323,7 @@
     MPI2_EVENT_NOTIFICATION_REPLY *reply)
 {
 	struct mps_event_handle *eh;
-	int event, handled = 0;;
+	int event, handled = 0;
 
 	event = reply->Event;
 	TAILQ_FOREACH(eh, &sc->event_list, eh_list) {
@@ -1365,118 +1418,281 @@
 	return (mps_update_events(sc, NULL, NULL));
 }
 
-static void
-mps_data_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
+/*
+ * Add a chain element as the next SGE for the specified command.
+ * Reset cm_sge and cm_sgesize to indicate all the available space.
+ */
+static int
+mps_add_chain(struct mps_command *cm)
 {
-	MPI2_SGE_SIMPLE64 *sge;
 	MPI2_SGE_CHAIN32 *sgc;
-	struct mps_softc *sc;
-	struct mps_command *cm;
 	struct mps_chain *chain;
-	u_int i, segsleft, sglspace, dir, flags, sflags;
+	int space;
 
-	cm = (struct mps_command *)arg;
-	sc = cm->cm_sc;
+	if (cm->cm_sglsize < MPS_SGC_SIZE)
+		panic("MPS: Need SGE Error Code\n");
 
-        segsleft = nsegs;
-        sglspace = cm->cm_sglsize;
-        sge = (MPI2_SGE_SIMPLE64 *)&cm->cm_sge->MpiSimple;
+	chain = mps_alloc_chain(cm->cm_sc);
+	if (chain == NULL)
+		return (ENOBUFS);
 
+	space = (int)cm->cm_sc->facts->IOCRequestFrameSize * 4;
+
 	/*
-	 * Set up DMA direction flags.  Note no support for
-	 * bi-directional transactions.
+	 * Note: a double-linked list is used to make it easier to
+	 * walk for debugging.
 	 */
-        sflags = MPI2_SGE_FLAGS_ADDRESS_SIZE;
-        if (cm->cm_flags & MPS_CM_FLAGS_DATAOUT) {
-                sflags |= MPI2_SGE_FLAGS_DIRECTION;
-		dir = BUS_DMASYNC_PREWRITE;
-	} else
-		dir = BUS_DMASYNC_PREREAD;
+	TAILQ_INSERT_TAIL(&cm->cm_chain_list, chain, chain_link);
 
+	sgc = (MPI2_SGE_CHAIN32 *)&cm->cm_sge->MpiChain;
+	sgc->Length = space;
+	sgc->NextChainOffset = 0;
+	sgc->Flags = MPI2_SGE_FLAGS_CHAIN_ELEMENT;
+	sgc->Address = chain->chain_busaddr;
+
+	cm->cm_sge = (MPI2_SGE_IO_UNION *)&chain->chain->MpiSimple;
+	cm->cm_sglsize = space;
+	return (0);
+}
+
+/*
+ * Add one scatter-gather element (chain, simple, transaction context)
+ * to the scatter-gather list for a command.  Maintain cm_sglsize and
+ * cm_sge as the remaining size and pointer to the next SGE to fill
+ * in, respectively.
+ */
+int
+mps_push_sge(struct mps_command *cm, void *sgep, size_t len, int segsleft)
+{
+	MPI2_SGE_TRANSACTION_UNION *tc = sgep;
+	MPI2_SGE_SIMPLE64 *sge = sgep;
+	int error, type;
+
+	type = (tc->Flags & MPI2_SGE_FLAGS_ELEMENT_MASK);
+
+#ifdef INVARIANTS
+	switch (type) {
+	case MPI2_SGE_FLAGS_TRANSACTION_ELEMENT: {
+		if (len != tc->DetailsLength + 4)
+			panic("TC %p length %u or %zu?", tc,
+			    tc->DetailsLength + 4, len);
+		}
+		break;
+	case MPI2_SGE_FLAGS_CHAIN_ELEMENT:
+		/* Driver only uses 32-bit chain elements */
+		if (len != MPS_SGC_SIZE)
+			panic("CHAIN %p length %u or %zu?", sgep,
+			    MPS_SGC_SIZE, len);
+		break;
+	case MPI2_SGE_FLAGS_SIMPLE_ELEMENT:
+		/* Driver only uses 64-bit SGE simple elements */
+		sge = sgep;
+		if (len != MPS_SGE64_SIZE)
+			panic("SGE simple %p length %u or %zu?", sge,
+			    MPS_SGE64_SIZE, len);
+		if (((sge->FlagsLength >> MPI2_SGE_FLAGS_SHIFT) &
+		    MPI2_SGE_FLAGS_ADDRESS_SIZE) == 0)
+			panic("SGE simple %p flags %02x not marked 64-bit?",
+			    sge, sge->FlagsLength >> MPI2_SGE_FLAGS_SHIFT);
+
+		break;
+	default:
+		panic("Unexpected SGE %p, flags %02x", tc, tc->Flags);
+	}
+#endif
+
 	/*
 	 * case 1: 1 more segment, enough room for it
 	 * case 2: 2 more segments, enough room for both
 	 * case 3: >=2 more segments, only enough room for 1 and a chain
 	 * case 4: >=1 more segment, enough room for only a chain
 	 * case 5: >=1 more segment, no room for anything (error)
+         */
+
+	/*
+	 * There should be room for at least a chain element, or this
+	 * code is buggy.  Case (5).
 	 */
+	if (cm->cm_sglsize < MPS_SGC_SIZE)
+		panic("MPS: Need SGE Error Code\n");
 
-	for (i = 0; i < nsegs; i++) {
-
-		/* Case 5 Error.  This should never happen. */
-		if (sglspace < MPS_SGC_SIZE) {
-			panic("MPS: Need SGE Error Code\n");
+	if (segsleft >= 2 &&
+	    cm->cm_sglsize < len + MPS_SGC_SIZE + MPS_SGE64_SIZE) {
+		/*
+		 * There are 2 or more segments left to add, and only
+		 * enough room for 1 and a chain.  Case (3).
+		 *
+		 * Mark as last element in this chain if necessary.
+		 */
+		if (type == MPI2_SGE_FLAGS_SIMPLE_ELEMENT) {
+			sge->FlagsLength |=
+				(MPI2_SGE_FLAGS_LAST_ELEMENT << MPI2_SGE_FLAGS_SHIFT);
 		}
 
 		/*
-		 * Case 4, Fill in a chain element, allocate a chain,
-		 * fill in one SGE element, continue.
+		 * Add the item then a chain.  Do the chain now,
+		 * rather than on the next iteration, to simplify
+		 * understanding the code.
 		 */
-		if ((sglspace >= MPS_SGC_SIZE) && (sglspace < MPS_SGE64_SIZE)) {
-			chain = mps_alloc_chain(sc);
-			if (chain == NULL) {
-				/* Resource shortage, roll back! */
-				printf("out of chain frames\n");
-				return;
-			}
+		cm->cm_sglsize -= len;
+		bcopy(sgep, cm->cm_sge, len);
+		cm->cm_sge = (MPI2_SGE_IO_UNION *)((uintptr_t)cm->cm_sge + len);
+		return (mps_add_chain(cm));
+	}
 
-			/*
-			 * Note: a double-linked list is used to make it
-			 * easier to walk for debugging.
-			 */
-			TAILQ_INSERT_TAIL(&cm->cm_chain_list, chain,chain_link);
+	if (segsleft >= 1 && cm->cm_sglsize < len + MPS_SGC_SIZE) {
+		/*
+		 * 1 or more segment, enough room for only a chain.
+		 * Hope the previous element wasn't a Simple entry
+		 * that needed to be marked with
+		 * MPI2_SGE_FLAGS_LAST_ELEMENT.  Case (4).
+		 */
+		if ((error = mps_add_chain(cm)) != 0)
+			return (error);
+	}
 
-			sgc = (MPI2_SGE_CHAIN32 *)sge;
-			sgc->Length = 128;
-			sgc->NextChainOffset = 0;
-			sgc->Flags = MPI2_SGE_FLAGS_CHAIN_ELEMENT;
-			sgc->Address = chain->chain_busaddr;
+#ifdef INVARIANTS
+	/* Case 1: 1 more segment, enough room for it. */
+	if (segsleft == 1 && cm->cm_sglsize < len)
+		panic("1 seg left and no room? %u versus %zu",
+		    cm->cm_sglsize, len);
 
-			sge = (MPI2_SGE_SIMPLE64 *)&chain->chain->MpiSimple;
-			sglspace = 128;
-		}
+	/* Case 2: 2 more segments, enough room for both */
+	if (segsleft == 2 && cm->cm_sglsize < len + MPS_SGE64_SIZE)
+		panic("2 segs left and no room? %u versus %zu",
+		    cm->cm_sglsize, len);
+#endif
 
-		flags = MPI2_SGE_FLAGS_SIMPLE_ELEMENT;
-		sge->FlagsLength = segs[i].ds_len |
-		   ((sflags | flags) << MPI2_SGE_FLAGS_SHIFT);
-		mps_from_u64(segs[i].ds_addr, &sge->Address);
+	if (segsleft == 1 && type == MPI2_SGE_FLAGS_SIMPLE_ELEMENT) {
+		/*
+		 * Last element of the last segment of the entire
+		 * buffer.
+		 */
+		sge->FlagsLength |= ((MPI2_SGE_FLAGS_LAST_ELEMENT |
+		    MPI2_SGE_FLAGS_END_OF_BUFFER |
+		    MPI2_SGE_FLAGS_END_OF_LIST) << MPI2_SGE_FLAGS_SHIFT);
+	}
 
-		/* Case 1, Fill in one SGE element and break */
-		if (segsleft == 1)
-			break;
+	cm->cm_sglsize -= len;
+	bcopy(sgep, cm->cm_sge, len);
+	cm->cm_sge = (MPI2_SGE_IO_UNION *)((uintptr_t)cm->cm_sge + len);
+	return (0);
+}
 
-		sglspace -= MPS_SGE64_SIZE;
-		segsleft--;
+/*
+ * Add one dma segment to the scatter-gather list for a command.
+ */
+int
+mps_add_dmaseg(struct mps_command *cm, vm_paddr_t pa, size_t len, u_int flags,
+    int segsleft)
+{
+	MPI2_SGE_SIMPLE64 sge;
 
-		/* Case 3, prepare for a chain on the next loop */
-		if ((segsleft > 0) && (sglspace < MPS_SGE64_SIZE))
-			sge->FlagsLength |= 
-			    (MPI2_SGE_FLAGS_LAST_ELEMENT <<
-			    MPI2_SGE_FLAGS_SHIFT);
+	/*
+	 * This driver always uses 64-bit address elements for
+	 * simplicity.
+	 */
+	flags |= MPI2_SGE_FLAGS_SIMPLE_ELEMENT | MPI2_SGE_FLAGS_ADDRESS_SIZE;
+	sge.FlagsLength = len | (flags << MPI2_SGE_FLAGS_SHIFT);
+	mps_from_u64(pa, &sge.Address);
 
-		/* Advance to the next element to be filled in. */
-		sge++;
+	return (mps_push_sge(cm, &sge, sizeof sge, segsleft));
+}
+
+static void
+mps_data_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
+{
+	struct mps_softc *sc;
+	struct mps_command *cm;
+	u_int i, dir, sflags;
+
+	cm = (struct mps_command *)arg;
+	sc = cm->cm_sc;
+
+	/*
+	 * In this case, just print out a warning and let the chip tell the
+	 * user they did the wrong thing.
+	 */
+	if ((cm->cm_max_segs != 0) && (nsegs > cm->cm_max_segs)) {
+		mps_printf(sc, "%s: warning: busdma returned %d segments, "
+			   "more than the %d allowed\n", __func__, nsegs,
+			   cm->cm_max_segs);
 	}
 
-	/* Last element of the last segment of the entire buffer */
-	flags = MPI2_SGE_FLAGS_LAST_ELEMENT |
-	    MPI2_SGE_FLAGS_END_OF_BUFFER |
-	    MPI2_SGE_FLAGS_END_OF_LIST;
-	sge->FlagsLength |= (flags << MPI2_SGE_FLAGS_SHIFT);
+	/*
+	 * Set up DMA direction flags.  Note that we don't support
+	 * bi-directional transfers, with the exception of SMP passthrough.
+	 */
+	sflags = 0;
+	if (cm->cm_flags & MPS_CM_FLAGS_SMP_PASS) {
+		/*
+		 * We have to add a special case for SMP passthrough, there
+		 * is no easy way to generically handle it.  The first
+		 * S/G element is used for the command (therefore the
+		 * direction bit needs to be set).  The second one is used
+		 * for the reply.  We'll leave it to the caller to make
+		 * sure we only have two buffers.
+		 */
+		/*
+		 * Even though the busdma man page says it doesn't make
+		 * sense to have both direction flags, it does in this case.
+		 * We have one s/g element being accessed in each direction.
+		 */
+		dir = BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD;
 
+		/*
+		 * Set the direction flag on the first buffer in the SMP
+		 * passthrough request.  We'll clear it for the second one.
+		 */
+		sflags |= MPI2_SGE_FLAGS_DIRECTION |
+			  MPI2_SGE_FLAGS_END_OF_BUFFER;
+	} else if (cm->cm_flags & MPS_CM_FLAGS_DATAOUT) {
+		sflags |= MPI2_SGE_FLAGS_DIRECTION;
+		dir = BUS_DMASYNC_PREWRITE;
+	} else
+		dir = BUS_DMASYNC_PREREAD;
+
+	for (i = 0; i < nsegs; i++) {
+		if ((cm->cm_flags & MPS_CM_FLAGS_SMP_PASS)
+		 && (i != 0)) {
+			sflags &= ~MPI2_SGE_FLAGS_DIRECTION;
+		}
+		error = mps_add_dmaseg(cm, segs[i].ds_addr, segs[i].ds_len,
+		    sflags, nsegs - i);
+		if (error != 0) {
+			/* Resource shortage, roll back! */
+			mps_printf(sc, "out of chain frames\n");
+			return;
+		}
+	}
+
 	bus_dmamap_sync(sc->buffer_dmat, cm->cm_dmamap, dir);
 	mps_enqueue_request(sc, cm);
 
 	return;
 }
 
+static void
+mps_data_cb2(void *arg, bus_dma_segment_t *segs, int nsegs, bus_size_t mapsize,
+	     int error)
+{
+	mps_data_cb(arg, segs, nsegs, error);
+}
+
+/*
+ * Note that the only error path here is from bus_dmamap_load(), which can
+ * return EINPROGRESS if it is waiting for resources.
+ */
 int
 mps_map_command(struct mps_softc *sc, struct mps_command *cm)
 {
 	MPI2_SGE_SIMPLE32 *sge;
 	int error = 0;
 
-	if ((cm->cm_data != NULL) && (cm->cm_length != 0)) {
+	if (cm->cm_flags & MPS_CM_FLAGS_USE_UIO) {
+		error = bus_dmamap_load_uio(sc->buffer_dmat, cm->cm_dmamap,
+		    &cm->cm_uio, mps_data_cb2, cm, 0);
+	} else if ((cm->cm_data != NULL) && (cm->cm_length != 0)) {
 		error = bus_dmamap_load(sc->buffer_dmat, cm->cm_dmamap,
 		    cm->cm_data, cm->cm_length, mps_data_cb, cm, 0);
 	} else {
@@ -1490,7 +1706,7 @@
 			    MPI2_SGE_FLAGS_SHIFT;
 			sge->Address = 0;
 		}
-		mps_enqueue_request(sc, cm);	
+		mps_enqueue_request(sc, cm);
 	}
 
 	return (error);
@@ -1549,9 +1765,9 @@
 	cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_DATAIN;
 	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
 
+	cm->cm_complete_data = params;
 	if (params->callback != NULL) {
 		cm->cm_complete = mps_config_complete;
-		cm->cm_complete_data = params;
 		return (mps_map_command(sc, cm));
 	} else {
 		cm->cm_complete = NULL;
Index: sys/dev/mps/mps_sas.c
===================================================================
--- sys/dev/mps/mps_sas.c	(revision 212420)
+++ sys/dev/mps/mps_sas.c	(working copy)
@@ -41,6 +41,8 @@
 #include <sys/malloc.h>
 #include <sys/uio.h>
 #include <sys/sysctl.h>
+#include <sys/sglist.h>
+#include <sys/endian.h>
 
 #include <machine/bus.h>
 #include <machine/resource.h>
@@ -55,6 +57,9 @@
 #include <cam/cam_periph.h>
 #include <cam/scsi/scsi_all.h>
 #include <cam/scsi/scsi_message.h>
+#if __FreeBSD_version >= 900026
+#include <cam/scsi/smp_all.h>
+#endif
 
 #include <dev/mps/mpi/mpi2_type.h>
 #include <dev/mps/mpi/mpi2.h>
@@ -69,9 +74,11 @@
 	uint16_t	handle;
 	uint8_t		linkrate;
 	uint64_t	devname;
+	uint64_t	sasaddr;
 	uint32_t	devinfo;
 	uint16_t	encl_handle;
 	uint16_t	encl_slot;
+	uint16_t	parent_handle;
 	int		flags;
 #define MPSSAS_TARGET_INABORT	(1 << 0)
 #define MPSSAS_TARGET_INRESET	(1 << 1)
@@ -114,6 +121,7 @@
 
 MALLOC_DEFINE(M_MPSSAS, "MPSSAS", "MPS SAS memory");
 
+static __inline int mpssas_set_lun(uint8_t *lun, u_int ccblun);
 static struct mpssas_target * mpssas_alloc_target(struct mpssas_softc *,
     struct mpssas_target *);
 static struct mpssas_target * mpssas_find_target(struct mpssas_softc *, int,
@@ -135,14 +143,64 @@
 static void mpssas_scsiio_timeout(void *data);
 static void mpssas_abort_complete(struct mps_softc *sc, struct mps_command *cm);
 static void mpssas_recovery(struct mps_softc *, struct mps_command *);
+static int mpssas_map_tm_request(struct mps_softc *sc, struct mps_command *cm);
+static void mpssas_issue_tm_request(struct mps_softc *sc,
+				    struct mps_command *cm);
+static void mpssas_tm_complete(struct mps_softc *sc, struct mps_command *cm,
+			       int error);
+static int mpssas_complete_tm_request(struct mps_softc *sc,
+				      struct mps_command *cm, int free_cm);
 static void mpssas_action_scsiio(struct mpssas_softc *, union ccb *);
 static void mpssas_scsiio_complete(struct mps_softc *, struct mps_command *);
-static int mpssas_resetdev(struct mpssas_softc *, struct mps_command *);
+#if __FreeBSD_version >= 900026
+static void mpssas_smpio_complete(struct mps_softc *sc, struct mps_command *cm);
+static void mpssas_send_smpcmd(struct mpssas_softc *sassc, union ccb *ccb,
+			       uint64_t sasaddr);
+static void mpssas_action_smpio(struct mpssas_softc *sassc, union ccb *ccb);
+#endif /* __FreeBSD_version >= 900026 */
+static void mpssas_resetdev(struct mpssas_softc *, struct mps_command *);
 static void mpssas_action_resetdev(struct mpssas_softc *, union ccb *);
 static void mpssas_resetdev_complete(struct mps_softc *, struct mps_command *);
 static void mpssas_freeze_device(struct mpssas_softc *, struct mpssas_target *);
 static void mpssas_unfreeze_device(struct mpssas_softc *, struct mpssas_target *) __unused;
 
+/*
+ * Abstracted so that the driver can be backwards and forwards compatible
+ * with future versions of CAM that will provide this functionality.
+ */
+#define MPS_SET_LUN(lun, ccblun)	\
+	mpssas_set_lun(lun, ccblun)
+
+static __inline int
+mpssas_set_lun(uint8_t *lun, u_int ccblun)
+{
+	uint64_t *newlun;
+
+	newlun = (uint64_t *)lun;
+	*newlun = 0;
+	if (ccblun <= 0xff) {
+		/* Peripheral device address method, LUN is 0 to 255 */
+		lun[1] = ccblun;
+	} else if (ccblun <= 0x3fff) {
+		/* Flat space address method, LUN is <= 16383 */
+		scsi_ulto2b(ccblun, lun);
+		lun[0] |= 0x40;
+	} else if (ccblun <= 0xffffff) {
+		/* Extended flat space address method, LUN is <= 16777215 */
+		scsi_ulto3b(ccblun, &lun[1]);
+		/* Extended Flat space address method */
+		lun[0] = 0xc0;
+		/* Length = 1, i.e. LUN is 3 bytes long */
+		lun[0] |= 0x10;
+		/* Extended Address Method */
+		lun[0] |= 0x02;
+	} else {
+		return (EINVAL);
+	}
+
+	return (0);
+}
+
 static struct mpssas_target *
 mpssas_alloc_target(struct mpssas_softc *sassc, struct mpssas_target *probe)
 {
@@ -305,6 +363,8 @@
 		probe->target.devinfo = buf->DeviceInfo;
 		probe->target.encl_handle = buf->EnclosureHandle;
 		probe->target.encl_slot = buf->Slot;
+		probe->target.sasaddr = mps_to_u64(&buf->SASAddress);
+		probe->target.parent_handle = buf->ParentDevHandle;
 
 		if (buf->DeviceInfo & MPI2_SAS_DEVICE_INFO_DIRECT_ATTACH) {
 			params->page_address =
@@ -438,7 +498,7 @@
 	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
 	cm->cm_complete = mpssas_remove_device;
 	cm->cm_targ = targ;
-	mps_map_command(sc, cm);
+	mpssas_issue_tm_request(sc, cm);
 }
 
 static void
@@ -453,6 +513,9 @@
 
 	reply = (MPI2_SCSI_TASK_MANAGE_REPLY *)cm->cm_reply;
 	handle = cm->cm_targ->handle;
+
+	mpssas_complete_tm_request(sc, cm, /*free_cm*/ 0);
+
 	if (reply->IOCStatus != MPI2_IOCSTATUS_SUCCESS) {
 		mps_printf(sc, "Failure 0x%x reseting device 0x%04x\n", 
 		   reply->IOCStatus, handle);
@@ -594,6 +657,7 @@
 {
 	struct mpssas_softc *sassc;
 	int error = 0;
+	int num_sim_reqs;
 
 	mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
 
@@ -603,15 +667,30 @@
 	sc->sassc = sassc;
 	sassc->sc = sc;
 
-	if ((sassc->devq = cam_simq_alloc(sc->num_reqs)) == NULL) {
+	/*
+	 * Tell CAM that we can handle 5 fewer requests than we have
+	 * allocated.  If we allow the full number of requests, all I/O
+	 * will halt when we run out of resources.  Things work fine with
+	 * just 1 less request slot given to CAM than we have allocated.
+	 * We also need a couple of extra commands so that we can send down
+	 * abort, reset, etc. requests when commands time out.  Otherwise
+	 * we could wind up in a situation with sc->num_reqs requests down
+	 * on the card and no way to send an abort.
+	 *
+	 * XXX KDM need to figure out why I/O locks up if all commands are
+	 * used.
+	 */
+	num_sim_reqs = sc->num_reqs - 5;
+
+	if ((sassc->devq = cam_simq_alloc(num_sim_reqs)) == NULL) {
 		mps_dprint(sc, MPS_FAULT, "Cannot allocate SIMQ\n");
 		error = ENOMEM;
 		goto out;
 	}
 
 	sassc->sim = cam_sim_alloc(mpssas_action, mpssas_poll, "mps", sassc,
-	    device_get_unit(sc->mps_dev), &sc->mps_mtx, sc->num_reqs, sc->num_reqs,
-	    sassc->devq);
+	    device_get_unit(sc->mps_dev), &sc->mps_mtx, num_sim_reqs,
+	    num_sim_reqs, sassc->devq);
 	if (sassc->sim == NULL) {
 		mps_dprint(sc, MPS_FAULT, "Cannot allocate SIM\n");
 		error = EINVAL;
@@ -890,6 +969,11 @@
 	case XPT_SCSI_IO:
 		mpssas_action_scsiio(sassc, ccb);
 		return;
+#if __FreeBSD_version >= 900026
+	case XPT_SMP_IO:
+		mpssas_action_smpio(sassc, ccb);
+		return;
+#endif /* __FreeBSD_version >= 900026 */
 	default:
 		ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
 		break;
@@ -928,6 +1012,9 @@
 	struct mps_softc *sc;
 	struct mps_command *cm;
 	struct mpssas_target *targ;
+#if 0
+	char cdb_str[(SCSI_MAX_CDBLEN * 3) + 1];
+#endif
 
 	cm = (struct mps_command *)data;
 	sc = cm->cm_sc;
@@ -952,6 +1039,22 @@
 
 	xpt_print(ccb->ccb_h.path, "SCSI command timeout on device handle "
 		  "0x%04x SMID %d\n", targ->handle, cm->cm_desc.Default.SMID);
+	/*
+	 * XXX KDM this is useful for debugging purposes, but the existing
+	 * scsi_op_desc() implementation can't handle a NULL value for
+	 * inq_data.  So this will remain commented out until I bring in
+	 * those changes as well.
+	 */
+#if 0
+	xpt_print(ccb->ccb_h.path, "Timed out command: %s. CDB %s\n",
+		  scsi_op_desc((ccb->ccb_h.flags & CAM_CDB_POINTER) ?
+		  		ccb->csio.cdb_io.cdb_ptr[0] :
+				ccb->csio.cdb_io.cdb_bytes[0], NULL),
+		  scsi_cdb_string((ccb->ccb_h.flags & CAM_CDB_POINTER) ?
+				   ccb->csio.cdb_io.cdb_ptr :
+				   ccb->csio.cdb_io.cdb_bytes, cdb_str,
+		  		   sizeof(cdb_str)));
+#endif
 
 	/* Inform CAM about the timeout and that recovery is starting. */
 #if 0
@@ -983,7 +1086,7 @@
 	mps_printf(sc, "%s: abort request on handle %#04x SMID %d "
 		   "complete\n", __func__, req->DevHandle, req->TaskMID);
 
-	mps_free_command(sc, cm);
+	mpssas_complete_tm_request(sc, cm, /*free_cm*/ 1);
 }
 
 static void
@@ -991,7 +1094,6 @@
 {
 	struct mps_command *cm;
 	MPI2_SCSI_TASK_MANAGE_REQUEST *req, *orig_req;
-	int error;
 
 	cm = mps_alloc_command(sc);
 	if (cm == NULL) {
@@ -1013,27 +1115,206 @@
 	cm->cm_data = NULL;
 	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
 
+	mpssas_issue_tm_request(sc, cm);
+
+}
+
+/*
+ * Can return 0 or EINPROGRESS on success.  Any other value means failure.
+ */
+static int
+mpssas_map_tm_request(struct mps_softc *sc, struct mps_command *cm)
+{
+	int error;
+
+	error = 0;
+
+	cm->cm_flags |= MPS_CM_FLAGS_ACTIVE;
 	error = mps_map_command(sc, cm);
+	if ((error == 0)
+	 || (error == EINPROGRESS))
+		sc->tm_cmds_active++;
 
-	if (error != 0) {
-		mps_printf(sc, "%s: error mapping abort request!\n", __func__);
+	return (error);
+}
+
+static void
+mpssas_issue_tm_request(struct mps_softc *sc, struct mps_command *cm)
+{
+	int freeze_queue, send_command, error;
+
+	freeze_queue = 0;
+	send_command = 0;
+	error = 0;
+
+	mtx_assert(&sc->mps_mtx, MA_OWNED);
+
+	/*
+	 * If there are no other pending task management commands, go
+	 * ahead and send this one.  There is a small amount of anecdotal
+	 * evidence that sending lots of task management commands at once
+	 * may cause the controller to lock up.  Or, if the user has
+	 * configured the driver (via the allow_multiple_tm_cmds variable) to
+	 * not serialize task management commands, go ahead and send the
+	 * command if even other task management commands are pending.
+	 */
+	if (TAILQ_FIRST(&sc->tm_list) == NULL) {
+		send_command = 1;
+		freeze_queue = 1;
+	} else if (sc->allow_multiple_tm_cmds != 0)
+		send_command = 1;
+
+	TAILQ_INSERT_TAIL(&sc->tm_list, cm, cm_link);
+	if (send_command != 0) {
+		/*
+		 * Freeze the SIM queue while we issue the task management
+		 * command.  According to the Fusion-MPT 2.0 spec, task
+		 * management requests are serialized, and so the host
+		 * should not send any I/O requests while task management
+		 * requests are pending.
+		 */
+		if (freeze_queue != 0)
+			xpt_freeze_simq(sc->sassc->sim, 1);
+
+		error = mpssas_map_tm_request(sc, cm);
+
+		/*
+		 * At present, there is no error path back from
+		 * mpssas_map_tm_request() (which calls mps_map_command())
+		 * when cm->cm_data == NULL.  But since there is a return
+		 * value, we check it just in case the implementation
+		 * changes later.
+		 */
+		if ((error != 0)
+		 && (error != EINPROGRESS))
+			mpssas_tm_complete(sc, cm,
+			    MPI2_SCSITASKMGMT_RSP_TM_FAILED);
 	}
-#if 0
-	error = mpssas_reset(sc, targ, &resetcm);
-	if ((error != 0) && (error != EBUSY)) {
-		mps_printf(sc, "Error resetting device!\n");
-		mps_unlock(sc);
-		return;
-	}
+}
 
-	targ->flags |= MPSSAS_TARGET_INRESET;
+static void
+mpssas_tm_complete(struct mps_softc *sc, struct mps_command *cm, int error)
+{
+	MPI2_SCSI_TASK_MANAGE_REPLY *resp;
 
-	cm->cm_complete = mpssas_resettimeout_complete;
-	cm->cm_complete_data = cm;
-	mps_map_command(sassc->sc, cm);
-#endif
+	resp = (MPI2_SCSI_TASK_MANAGE_REPLY *)cm->cm_reply;
+
+	resp->ResponseCode = error;
+
+	/*
+	 * Call the callback for this command, it will be
+	 * removed from the list and freed via the callback.
+	 */
+	cm->cm_complete(sc, cm);
 }
 
+/*
+ * Complete a task management request.  The basic completion operation will
+ * always succeed.  Returns status for sending any further task management
+ * commands that were queued.
+ */
+static int
+mpssas_complete_tm_request(struct mps_softc *sc, struct mps_command *cm,
+			   int free_cm)
+{
+	int error;
+
+	error = 0;
+
+	mtx_assert(&sc->mps_mtx, MA_OWNED);
+
+	TAILQ_REMOVE(&sc->tm_list, cm, cm_link);
+	cm->cm_flags &= ~MPS_CM_FLAGS_ACTIVE;
+	sc->tm_cmds_active--;
+
+	if (free_cm != 0)
+		mps_free_command(sc, cm);
+
+	if (TAILQ_FIRST(&sc->tm_list) == NULL) {
+		/*
+		 * Release the SIM queue, we froze it when we sent the first
+		 * task management request.
+		 */
+		xpt_release_simq(sc->sassc->sim, 1);
+	} else if ((sc->tm_cmds_active == 0)
+		|| (sc->allow_multiple_tm_cmds != 0)) {
+		int error;
+		struct mps_command *cm2;
+
+restart_traversal:
+
+		/*
+		 * We don't bother using TAILQ_FOREACH_SAFE here, but
+		 * rather use the standard version and just restart the
+		 * list traversal if we run into the error case.
+		 * TAILQ_FOREACH_SAFE allows safe removal of the current
+		 * list element, but if you have a queue of task management
+		 * commands, all of which have mapping errors, you'll end
+		 * up with recursive calls to this routine and so you could
+		 * wind up removing more than just the current list element.
+		 */
+		TAILQ_FOREACH(cm2, &sc->tm_list, cm_link) {
+			MPI2_SCSI_TASK_MANAGE_REQUEST *req;
+
+			/* This command is active, no need to send it again */
+			if (cm2->cm_flags & MPS_CM_FLAGS_ACTIVE)
+				continue;
+
+			req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)cm2->cm_req;
+
+			mps_printf(sc, "%s: sending deferred task management "
+			    "request for handle %#04x SMID %d\n", __func__,
+			    req->DevHandle, req->TaskMID);
+
+			error = mpssas_map_tm_request(sc, cm2);
+
+			/*
+			 * Check for errors.  If we had an error, complete
+			 * this command with an error, and keep going through
+			 * the list until we are able to send at least one
+			 * command or all of them are completed with errors.
+			 *
+			 * We don't want to wind up in a situation where
+			 * we're stalled out with no way for queued task
+			 * management commands to complete.
+			 *
+			 * Note that there is not currently an error path
+			 * back from mpssas_map_tm_request() (which calls
+			 * mps_map_command()) when cm->cm_data == NULL.
+			 * But we still want to check for errors here in
+			 * case the implementation changes, or in case
+			 * there is some reason for a data payload here.
+			 */
+			if ((error != 0)
+			 && (error != EINPROGRESS)) {
+				mpssas_tm_complete(sc, cm,
+				    MPI2_SCSITASKMGMT_RSP_TM_FAILED);
+
+				/*
+				 * If we don't currently have any commands
+				 * active, go back to the beginning and see
+				 * if there are any more that can be started.
+				 * Otherwise, we're done here.
+				 */
+				if (sc->tm_cmds_active == 0)
+					goto restart_traversal;
+				else
+					break;
+			}
+
+			/*
+			 * If the user only wants one task management command
+			 * active at a time, we're done, since we've
+			 * already successfully sent a command at this point.
+			 */
+			if (sc->allow_multiple_tm_cmds == 0)
+				break;
+		}
+	}
+
+	return (error);
+}
+
 static void
 mpssas_action_scsiio(struct mpssas_softc *sassc, union ccb *ccb)
 {
@@ -1123,14 +1404,12 @@
 		break;
 	}
 
-	/* XXX Need to handle multi-level LUNs */
-	if (csio->ccb_h.target_lun > 255) {
+	if (MPS_SET_LUN(req->LUN, csio->ccb_h.target_lun) != 0) {
 		mps_free_command(sc, cm);
 		ccb->ccb_h.status = CAM_LUN_INVALID;
 		xpt_done(ccb);
 		return;
 	}
-	req->LUN[1] = csio->ccb_h.target_lun;
 
 	if (csio->ccb_h.flags & CAM_CDB_POINTER)
 		bcopy(csio->cdb_io.cdb_ptr, &req->CDB.CDB32[0], csio->cdb_len);
@@ -1138,6 +1417,9 @@
 		bcopy(csio->cdb_io.cdb_bytes, &req->CDB.CDB32[0],csio->cdb_len);
 	req->IoFlags = csio->cdb_len;
 
+	/*
+	 * XXX need to handle S/G lists and physical addresses here.
+	 */
 	cm->cm_data = csio->data_ptr;
 	cm->cm_length = csio->dxfer_len;
 	cm->cm_sge = &req->SGL;
@@ -1219,11 +1501,9 @@
 		ccb->ccb_h.status = CAM_REQ_CMP;
 		break;
 	case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN:
-		/*
-		 * XXX any way to report this?
-		 */
+		/* resid is ignored for this condition */
 		ccb->csio.resid = 0;
-		ccb->ccb_h.status = CAM_REQ_CMP;
+		ccb->ccb_h.status = CAM_DATA_RUN_ERR;
 		break;
 	case MPI2_IOCSTATUS_SCSI_INVALID_DEVHANDLE:
 	case MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE:
@@ -1304,13 +1584,335 @@
 	xpt_done(ccb);
 }
 
+#if __FreeBSD_version >= 900026
 static void
+mpssas_smpio_complete(struct mps_softc *sc, struct mps_command *cm)
+{
+	MPI2_SMP_PASSTHROUGH_REPLY *rpl;
+	MPI2_SMP_PASSTHROUGH_REQUEST *req;
+	uint64_t sasaddr;
+	union ccb *ccb;
+
+	ccb = cm->cm_complete_data;
+	rpl = (MPI2_SMP_PASSTHROUGH_REPLY *)cm->cm_reply;
+	if (rpl == NULL) {
+		mps_dprint(sc, MPS_INFO, "%s: NULL cm_reply!\n", __func__);
+		ccb->ccb_h.status = CAM_REQ_CMP_ERR;
+		goto bailout;
+	}
+
+	req = (MPI2_SMP_PASSTHROUGH_REQUEST *)cm->cm_req;
+	sasaddr = le32toh(req->SASAddress.Low);
+	sasaddr |= ((uint64_t)(le32toh(req->SASAddress.High))) << 32;
+
+	if ((rpl->IOCStatus & MPI2_IOCSTATUS_MASK) != MPI2_IOCSTATUS_SUCCESS ||
+	    rpl->SASStatus != MPI2_SASSTATUS_SUCCESS) {
+		mps_dprint(sc, MPS_INFO, "%s: IOCStatus %04x SASStatus %02x\n",
+		    __func__, rpl->IOCStatus, rpl->SASStatus);
+		ccb->ccb_h.status = CAM_REQ_CMP_ERR;
+		goto bailout;
+	}
+
+	mps_dprint(sc, MPS_INFO, "%s: SMP request to SAS address "
+		   "%#jx completed successfully\n", __func__,
+		   (uintmax_t)sasaddr);
+
+	if (ccb->smpio.smp_response[2] == SMP_FR_ACCEPTED)
+		ccb->ccb_h.status = CAM_REQ_CMP;
+	else
+		ccb->ccb_h.status = CAM_SMP_STATUS_ERROR;
+
+bailout:
+	/*
+	 * We sync in both directions because we had DMAs in the S/G list
+	 * in both directions.
+	 */
+	bus_dmamap_sync(sc->buffer_dmat, cm->cm_dmamap,
+			BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
+	bus_dmamap_unload(sc->buffer_dmat, cm->cm_dmamap);
+	mps_free_command(sc, cm);
+	xpt_done(ccb);
+}
+
+static void
+mpssas_send_smpcmd(struct mpssas_softc *sassc, union ccb *ccb, uint64_t sasaddr)
+{
+	struct mps_command *cm;
+	uint8_t *request, *response;
+	MPI2_SMP_PASSTHROUGH_REQUEST *req;
+	struct mps_softc *sc;
+	struct sglist *sg;
+	int error;
+
+	sc = sassc->sc;
+	sg = NULL;
+	error = 0;
+
+	/*
+	 * XXX We don't yet support physical addresses here.
+	 */
+	if (ccb->ccb_h.flags & (CAM_DATA_PHYS|CAM_SG_LIST_PHYS)) {
+		mps_printf(sc, "%s: physical addresses not supported\n",
+			   __func__);
+		ccb->ccb_h.status = CAM_REQ_INVALID;
+		xpt_done(ccb);
+		return;
+	}
+
+	/*
+	 * If the user wants to send an S/G list, check to make sure they
+	 * have single buffers.
+	 */
+	if (ccb->ccb_h.flags & CAM_SCATTER_VALID) {
+		/*
+		 * The chip does not support more than one buffer for the
+		 * request or response.
+		 */
+	 	if ((ccb->smpio.smp_request_sglist_cnt > 1)
+		  || (ccb->smpio.smp_response_sglist_cnt > 1)) {
+			mps_printf(sc, "%s: multiple request or response "
+				   "buffer segments not supported for SMP\n",
+				   __func__);
+			ccb->ccb_h.status = CAM_REQ_INVALID;
+			xpt_done(ccb);
+			return;
+		}
+
+		/*
+		 * The CAM_SCATTER_VALID flag was originally implemented
+		 * for the XPT_SCSI_IO CCB, which only has one data pointer.
+		 * We have two.  So, just take that flag to mean that we
+		 * might have S/G lists, and look at the S/G segment count
+		 * to figure out whether that is the case for each individual
+		 * buffer.
+		 */
+		if (ccb->smpio.smp_request_sglist_cnt != 0) {
+			bus_dma_segment_t *req_sg;
+
+			req_sg = (bus_dma_segment_t *)ccb->smpio.smp_request;
+			request = (uint8_t *)req_sg[0].ds_addr;
+		} else
+			request = ccb->smpio.smp_request;
+
+		if (ccb->smpio.smp_response_sglist_cnt != 0) {
+			bus_dma_segment_t *rsp_sg;
+
+			rsp_sg = (bus_dma_segment_t *)ccb->smpio.smp_response;
+			response = (uint8_t *)rsp_sg[0].ds_addr;
+		} else
+			response = ccb->smpio.smp_response;
+	} else {
+		request = ccb->smpio.smp_request;
+		response = ccb->smpio.smp_response;
+	}
+
+	cm = mps_alloc_command(sc);
+	if (cm == NULL) {
+		mps_printf(sc, "%s: cannot allocate command\n", __func__);
+		ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+		xpt_done(ccb);
+		return;
+	}
+
+	req = (MPI2_SMP_PASSTHROUGH_REQUEST *)cm->cm_req;
+	bzero(req, sizeof(*req));
+	req->Function = MPI2_FUNCTION_SMP_PASSTHROUGH;
+
+	/* Allow the chip to use any route to this SAS address. */
+	req->PhysicalPort = 0xff;
+
+	req->RequestDataLength = ccb->smpio.smp_request_len;
+	req->SGLFlags = 
+	    MPI2_SGLFLAGS_SYSTEM_ADDRESS_SPACE | MPI2_SGLFLAGS_SGL_TYPE_MPI;
+
+	mps_dprint(sc, MPS_INFO, "%s: sending SMP request to SAS "
+		   "address %#jx\n", __func__, (uintmax_t)sasaddr);
+
+	mpi_init_sge(cm, req, &req->SGL);
+
+	/*
+	 * Set up a uio to pass into mps_map_command().  This allows us to
+	 * do one map command, and one busdma call in there.
+	 */
+	cm->cm_uio.uio_iov = cm->cm_iovec;
+	cm->cm_uio.uio_iovcnt = 2;
+	cm->cm_uio.uio_segflg = UIO_SYSSPACE;
+
+	/*
+	 * The read/write flag isn't used by busdma, but set it just in
+	 * case.  This isn't exactly accurate, either, since we're going in
+	 * both directions.
+	 */
+	cm->cm_uio.uio_rw = UIO_WRITE;
+
+	cm->cm_iovec[0].iov_base = request;
+	cm->cm_iovec[0].iov_len = req->RequestDataLength;
+	cm->cm_iovec[1].iov_base = response;
+	cm->cm_iovec[1].iov_len = ccb->smpio.smp_response_len;
+
+	cm->cm_uio.uio_resid = cm->cm_iovec[0].iov_len +
+			       cm->cm_iovec[1].iov_len;
+
+	/*
+	 * Trigger a warning message in mps_data_cb() for the user if we
+	 * wind up exceeding two S/G segments.  The chip expects one
+	 * segment for the request and another for the response.
+	 */
+	cm->cm_max_segs = 2;
+
+	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+	cm->cm_complete = mpssas_smpio_complete;
+	cm->cm_complete_data = ccb;
+
+	/*
+	 * Tell the mapping code that we're using a uio, and that this is
+	 * an SMP passthrough request.  There is a little special-case
+	 * logic there (in mps_data_cb()) to handle the bidirectional
+	 * transfer.  
+	 */
+	cm->cm_flags |= MPS_CM_FLAGS_USE_UIO | MPS_CM_FLAGS_SMP_PASS |
+			MPS_CM_FLAGS_DATAIN | MPS_CM_FLAGS_DATAOUT;
+
+	/* The chip data format is little endian. */
+	req->SASAddress.High = htole32(sasaddr >> 32);
+	req->SASAddress.Low = htole32(sasaddr);
+
+	/*
+	 * XXX Note that we don't have a timeout/abort mechanism here.
+	 * From the manual, it looks like task management requests only
+	 * work for SCSI IO and SATA passthrough requests.  We may need to
+	 * have a mechanism to retry requests in the event of a chip reset
+	 * at least.  Hopefully the chip will insure that any errors short
+	 * of that are relayed back to the driver.
+	 */
+	error = mps_map_command(sc, cm);
+	if ((error != 0) && (error != EINPROGRESS)) {
+		mps_printf(sc, "%s: error %d returned from mps_map_command()\n",
+			   __func__, error);
+		goto bailout_error;
+	}
+
+	return;
+
+bailout_error:
+	mps_free_command(sc, cm);
+	ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+	xpt_done(ccb);
+	return;
+
+}
+
+static void
+mpssas_action_smpio(struct mpssas_softc *sassc, union ccb *ccb)
+{
+	struct mps_softc *sc;
+	struct mpssas_target *targ;
+	uint64_t sasaddr = 0;
+
+	sc = sassc->sc;
+
+	/*
+	 * Make sure the target exists.
+	 */
+	targ = &sassc->targets[ccb->ccb_h.target_id];
+	if (targ->handle == 0x0) {
+		mps_printf(sc, "%s: target %d does not exist!\n", __func__,
+			   ccb->ccb_h.target_id);
+		ccb->ccb_h.status = CAM_SEL_TIMEOUT;
+		xpt_done(ccb);
+		return;
+	}
+
+	/*
+	 * If this device has an embedded SMP target, we'll talk to it
+	 * directly.
+	 * figure out what the expander's address is.
+	 */
+	if ((targ->devinfo & MPI2_SAS_DEVICE_INFO_SMP_TARGET) != 0)
+		sasaddr = targ->sasaddr;
+
+	/*
+	 * If we don't have a SAS address for the expander yet, try
+	 * grabbing it from the page 0x83 information cached in the
+	 * transport layer for this target.  LSI expanders report the
+	 * expander SAS address as the port-associated SAS address in
+	 * Inquiry VPD page 0x83.  Maxim expanders don't report it in page
+	 * 0x83.
+	 *
+	 * XXX KDM disable this for now, but leave it commented out so that
+	 * it is obvious that this is another possible way to get the SAS
+	 * address.
+	 *
+	 * The parent handle method below is a little more reliable, and
+	 * the other benefit is that it works for devices other than SES
+	 * devices.  So you can send a SMP request to a da(4) device and it
+	 * will get routed to the expander that device is attached to.
+	 * (Assuming the da(4) device doesn't contain an SMP target...)
+	 */
+#if 0
+	if (sasaddr == 0)
+		sasaddr = xpt_path_sas_addr(ccb->ccb_h.path);
+#endif
+
+	/*
+	 * If we still don't have a SAS address for the expander, look for
+	 * the parent device of this device, which is probably the expander.
+	 */
+	if (sasaddr == 0) {
+		struct mpssas_target *parent_target;
+
+		if (targ->parent_handle == 0x0) {
+			mps_printf(sc, "%s: handle %d does not have a valid "
+				   "parent handle!\n", __func__, targ->handle);
+			ccb->ccb_h.status = CAM_REQ_INVALID;
+			goto bailout;
+		}
+		parent_target = mpssas_find_target(sassc, 0,
+						   targ->parent_handle);
+
+		if (parent_target == NULL) {
+			mps_printf(sc, "%s: handle %d does not have a valid "
+				   "parent target!\n", __func__, targ->handle);
+			ccb->ccb_h.status = CAM_REQ_INVALID;
+			goto bailout;
+		}
+
+		if ((parent_target->devinfo &
+		     MPI2_SAS_DEVICE_INFO_SMP_TARGET) == 0) {
+			mps_printf(sc, "%s: handle %d parent %d does not "
+				   "have an SMP target!\n", __func__,
+				   targ->handle, parent_target->handle);
+			ccb->ccb_h.status = CAM_REQ_INVALID;
+			goto bailout;
+
+		}
+
+		sasaddr = parent_target->sasaddr;
+	}
+
+	if (sasaddr == 0) {
+		mps_printf(sc, "%s: unable to find SAS address for handle %d\n",
+			   __func__, targ->handle);
+		ccb->ccb_h.status = CAM_REQ_INVALID;
+		goto bailout;
+	}
+	mpssas_send_smpcmd(sassc, ccb, sasaddr);
+
+	return;
+
+bailout:
+	xpt_done(ccb);
+
+}
+
+#endif /* __FreeBSD_version >= 900026 */
+
+static void
 mpssas_action_resetdev(struct mpssas_softc *sassc, union ccb *ccb)
 {
 	struct mps_softc *sc;
 	struct mps_command *cm;
 	struct mpssas_target *targ;
-	int error;
 
 	sc = sassc->sc;
 	targ = &sassc->targets[ccb->ccb_h.target_id];
@@ -1323,7 +1925,7 @@
 
 	cm = mps_alloc_command(sc);
 	if (cm == NULL) {
-		mps_printf(sc, "mpssas_action_resetdev: cannot alloc command\n");
+		mps_printf(sc, "%s: cannot alloc command\n", __func__);
 		ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
 		xpt_done(ccb);
 		return;
@@ -1333,20 +1935,14 @@
 	cm->cm_complete = mpssas_resetdev_complete;
 	cm->cm_complete_data = ccb;
 
-	error = mpssas_resetdev(sassc, cm);
-	if (error) {
-		ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
-		xpt_done(ccb);
-		return;
-	}
+	mpssas_resetdev(sassc, cm);
 }
 
-static int
+static void
 mpssas_resetdev(struct mpssas_softc *sassc, struct mps_command *cm)
 {
 	MPI2_SCSI_TASK_MANAGE_REQUEST *req;
 	struct mps_softc *sc;
-	int error;
 
 	mps_dprint(sassc->sc, MPS_TRACE, "%s\n", __func__);
 
@@ -1363,8 +1959,7 @@
 	cm->cm_data = NULL;
 	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
 
-	error = mps_map_command(sassc->sc, cm);
-	return (error);
+	mpssas_issue_tm_request(sc, cm);
 }
 
 static void
@@ -1386,7 +1981,8 @@
 	else
 		ccb->ccb_h.status = CAM_REQ_CMP_ERR;
 
-	mps_free_command(sc, cm);
+	mpssas_complete_tm_request(sc, cm, /*free_cm*/ 1);
+
 	xpt_done(ccb);
 }
 
Index: sys/dev/mps/mps_pci.c
===================================================================
--- sys/dev/mps/mps_pci.c	(revision 212420)
+++ sys/dev/mps/mps_pci.c	(working copy)
@@ -38,6 +38,7 @@
 #include <sys/conf.h>
 #include <sys/malloc.h>
 #include <sys/sysctl.h>
+#include <sys/uio.h>
 
 #include <machine/bus.h>
 #include <machine/resource.h>
Index: sys/dev/mps/mps_user.c
===================================================================
--- sys/dev/mps/mps_user.c	(revision 212420)
+++ sys/dev/mps/mps_user.c	(working copy)
@@ -33,6 +33,8 @@
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
+#include "opt_compat.h"
+
 #include <sys/types.h>
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -47,6 +49,8 @@
 #include <sys/sysctl.h>
 #include <sys/ioccom.h>
 #include <sys/endian.h>
+#include <sys/proc.h>
+#include <sys/sysent.h>
 
 #include <machine/bus.h>
 #include <machine/resource.h>
@@ -64,17 +68,41 @@
 
 static d_open_t		mps_open;
 static d_close_t	mps_close;
-static d_ioctl_t	mps_ioctl;
+static d_ioctl_t	mps_ioctl_devsw;
 
 static struct cdevsw mps_cdevsw = {
 	.d_version =	D_VERSION,
 	.d_flags =	0,
 	.d_open =	mps_open,
 	.d_close =	mps_close,
-	.d_ioctl =	mps_ioctl,
+	.d_ioctl =	mps_ioctl_devsw,
 	.d_name =	"mps",
 };
 
+typedef int (mps_user_f)(struct mps_command *, struct mps_usr_command *);
+static mps_user_f	mpi_pre_ioc_facts;
+static mps_user_f	mpi_pre_port_facts;
+static mps_user_f	mpi_pre_fw_download;
+static mps_user_f	mpi_pre_fw_upload;
+static mps_user_f	mpi_pre_sata_passthrough;
+static mps_user_f	mpi_pre_smp_passthrough;
+static mps_user_f	mpi_pre_config;
+static mps_user_f	mpi_pre_sas_io_unit_control;
+
+static int mps_user_read_cfg_header(struct mps_softc *,
+				    struct mps_cfg_page_req *);
+static int mps_user_read_cfg_page(struct mps_softc *,
+				  struct mps_cfg_page_req *, void *);
+static int mps_user_read_extcfg_header(struct mps_softc *,
+				     struct mps_ext_cfg_page_req *);
+static int mps_user_read_extcfg_page(struct mps_softc *,
+				     struct mps_ext_cfg_page_req *, void *);
+static int mps_user_write_cfg_page(struct mps_softc *,
+				   struct mps_cfg_page_req *, void *);
+static int mps_user_setup_request(struct mps_command *,
+				  struct mps_usr_command *);
+static int mps_user_command(struct mps_softc *, struct mps_usr_command *);
+
 static MALLOC_DEFINE(M_MPSUSER, "mps_user", "Buffers for mps(4) ioctls");
 
 int
@@ -294,43 +322,255 @@
 	return (0);
 }
 
+void
+mpi_init_sge(struct mps_command *cm, void *req, void *sge)
+{
+	int off, space;
+
+	space = (int)cm->cm_sc->facts->IOCRequestFrameSize * 4;
+	off = (uintptr_t)sge - (uintptr_t)req;
+
+	KASSERT(off < space, ("bad pointers %p %p, off %d, space %d",
+            req, sge, off, space));
+
+	cm->cm_sge = sge;
+	cm->cm_sglsize = space - off;
+}
+
+/*
+ * Prepare the mps_command for an IOC_FACTS request.
+ */
+static int
+mpi_pre_ioc_facts(struct mps_command *cm, struct mps_usr_command *cmd)
+{
+	MPI2_IOC_FACTS_REQUEST *req = (void *)cm->cm_req;
+	MPI2_IOC_FACTS_REPLY *rpl;
+
+	if (cmd->req_len != sizeof *req)
+		return (EINVAL);
+	if (cmd->rpl_len != sizeof *rpl)
+		return (EINVAL);
+
+	cm->cm_sge = NULL;
+	cm->cm_sglsize = 0;
+	return (0);
+}
+
+/*
+ * Prepare the mps_command for a PORT_FACTS request.
+ */
+static int
+mpi_pre_port_facts(struct mps_command *cm, struct mps_usr_command *cmd)
+{
+	MPI2_PORT_FACTS_REQUEST *req = (void *)cm->cm_req;
+	MPI2_PORT_FACTS_REPLY *rpl;
+
+	if (cmd->req_len != sizeof *req)
+		return (EINVAL);
+	if (cmd->rpl_len != sizeof *rpl)
+		return (EINVAL);
+
+	cm->cm_sge = NULL;
+	cm->cm_sglsize = 0;
+	return (0);
+}
+
+/*
+ * Prepare the mps_command for a FW_DOWNLOAD request.
+ */
+static int
+mpi_pre_fw_download(struct mps_command *cm, struct mps_usr_command *cmd)
+{
+	MPI2_FW_DOWNLOAD_REQUEST *req = (void *)cm->cm_req;
+	MPI2_FW_DOWNLOAD_REPLY *rpl;
+	MPI2_FW_DOWNLOAD_TCSGE tc;
+	int error;
+
+	/*
+	 * This code assumes there is room in the request's SGL for
+	 * the TransactionContext plus at least a SGL chain element.
+	 */
+	CTASSERT(sizeof req->SGL >= sizeof tc + MPS_SGC_SIZE);
+
+	if (cmd->req_len != sizeof *req)
+		return (EINVAL);
+	if (cmd->rpl_len != sizeof *rpl)
+		return (EINVAL);
+
+	if (cmd->len == 0)
+		return (EINVAL);
+
+	error = copyin(cmd->buf, cm->cm_data, cmd->len);
+	if (error != 0)
+		return (error);
+
+	mpi_init_sge(cm, req, &req->SGL);
+	bzero(&tc, sizeof tc);
+
+	/*
+	 * For now, the F/W image must be provided in a single request.
+	 */
+	if ((req->MsgFlags & MPI2_FW_DOWNLOAD_MSGFLGS_LAST_SEGMENT) == 0)
+		return (EINVAL);
+	if (req->TotalImageSize != cmd->len)
+		return (EINVAL);
+
+	/*
+	 * The value of the first two elements is specified in the
+	 * Fusion-MPT Message Passing Interface document.
+	 */
+	tc.ContextSize = 0;
+	tc.DetailsLength = 12;
+	tc.ImageOffset = 0;
+	tc.ImageSize = cmd->len;
+
+	cm->cm_flags |= MPS_CM_FLAGS_DATAOUT;
+
+	return (mps_push_sge(cm, &tc, sizeof tc, 0));
+}
+
+/*
+ * Prepare the mps_command for a FW_UPLOAD request.
+ */
+static int
+mpi_pre_fw_upload(struct mps_command *cm, struct mps_usr_command *cmd)
+{
+	MPI2_FW_UPLOAD_REQUEST *req = (void *)cm->cm_req;
+	MPI2_FW_UPLOAD_REPLY *rpl;
+	MPI2_FW_UPLOAD_TCSGE tc;
+
+	/*
+	 * This code assumes there is room in the request's SGL for
+	 * the TransactionContext plus at least a SGL chain element.
+	 */
+	CTASSERT(sizeof req->SGL >= sizeof tc + MPS_SGC_SIZE);
+
+	if (cmd->req_len != sizeof *req)
+		return (EINVAL);
+	if (cmd->rpl_len != sizeof *rpl)
+		return (EINVAL);
+
+	mpi_init_sge(cm, req, &req->SGL);
+	if (cmd->len == 0) {
+		/* Perhaps just asking what the size of the fw is? */
+		return (0);
+	}
+
+	bzero(&tc, sizeof tc);
+
+	/*
+	 * The value of the first two elements is specified in the
+	 * Fusion-MPT Message Passing Interface document.
+	 */
+	tc.ContextSize = 0;
+	tc.DetailsLength = 12;
+	/*
+	 * XXX Is there any reason to fetch a partial image?  I.e. to
+	 * set ImageOffset to something other than 0?
+	 */
+	tc.ImageOffset = 0;
+	tc.ImageSize = cmd->len;
+
+	return (mps_push_sge(cm, &tc, sizeof tc, 0));
+}
+
+/*
+ * Prepare the mps_command for a SATA_PASSTHROUGH request.
+ */
+static int
+mpi_pre_sata_passthrough(struct mps_command *cm, struct mps_usr_command *cmd)
+{
+	MPI2_SATA_PASSTHROUGH_REQUEST *req = (void *)cm->cm_req;
+	MPI2_SATA_PASSTHROUGH_REPLY *rpl;
+
+	if (cmd->req_len != sizeof *req)
+		return (EINVAL);
+	if (cmd->rpl_len != sizeof *rpl)
+		return (EINVAL);
+
+	mpi_init_sge(cm, req, &req->SGL);
+	return (0);
+}
+
+/*
+ * Prepare the mps_command for a SMP_PASSTHROUGH request.
+ */
+static int
+mpi_pre_smp_passthrough(struct mps_command *cm, struct mps_usr_command *cmd)
+{
+	MPI2_SMP_PASSTHROUGH_REQUEST *req = (void *)cm->cm_req;
+	MPI2_SMP_PASSTHROUGH_REPLY *rpl;
+
+	if (cmd->req_len != sizeof *req)
+		return (EINVAL);
+	if (cmd->rpl_len != sizeof *rpl)
+		return (EINVAL);
+
+	mpi_init_sge(cm, req, &req->SGL);
+	return (0);
+}
+
+/*
+ * Prepare the mps_command for a CONFIG request.
+ */
+static int
+mpi_pre_config(struct mps_command *cm, struct mps_usr_command *cmd)
+{
+	MPI2_CONFIG_REQUEST *req = (void *)cm->cm_req;
+	MPI2_CONFIG_REPLY *rpl;
+
+	if (cmd->req_len != sizeof *req)
+		return (EINVAL);
+	if (cmd->rpl_len != sizeof *rpl)
+		return (EINVAL);
+
+	mpi_init_sge(cm, req, &req->PageBufferSGE);
+	return (0);
+}
+
+/*
+ * Prepare the mps_command for a SAS_IO_UNIT_CONTROL request.
+ */
+static int
+mpi_pre_sas_io_unit_control(struct mps_command *cm,
+			     struct mps_usr_command *cmd)
+{
+
+	cm->cm_sge = NULL;
+	cm->cm_sglsize = 0;
+	return (0);
+}
+
+/*
+ * A set of functions to prepare an mps_command for the various
+ * supported requests.
+ */
 struct mps_user_func {
-	U8 Func;
-	U8 SgOff;
+	U8		Function;
+	mps_user_f	*f_pre;
 } mps_user_func_list[] = {
-	{ MPI2_FUNCTION_IOC_FACTS,	0 },
-	{ MPI2_FUNCTION_PORT_FACTS,	0 },
-	{ MPI2_FUNCTION_FW_DOWNLOAD, 	offsetof(Mpi2FWDownloadRequest,SGL)},
-	{ MPI2_FUNCTION_FW_UPLOAD,	offsetof(Mpi2FWUploadRequest_t,SGL)},
-	{ MPI2_FUNCTION_SATA_PASSTHROUGH,offsetof(Mpi2SataPassthroughRequest_t,SGL)},
-	{ MPI2_FUNCTION_SMP_PASSTHROUGH, offsetof(Mpi2SmpPassthroughRequest_t,SGL)},
-	{ MPI2_FUNCTION_CONFIG,		offsetof(Mpi2ConfigRequest_t,PageBufferSGE)},
-	{ MPI2_FUNCTION_SAS_IO_UNIT_CONTROL,	0 },
-};	
+	{ MPI2_FUNCTION_IOC_FACTS,		mpi_pre_ioc_facts },
+	{ MPI2_FUNCTION_PORT_FACTS,		mpi_pre_port_facts },
+	{ MPI2_FUNCTION_FW_DOWNLOAD, 		mpi_pre_fw_download },
+	{ MPI2_FUNCTION_FW_UPLOAD,		mpi_pre_fw_upload },
+	{ MPI2_FUNCTION_SATA_PASSTHROUGH,	mpi_pre_sata_passthrough },
+	{ MPI2_FUNCTION_SMP_PASSTHROUGH,	mpi_pre_smp_passthrough},
+	{ MPI2_FUNCTION_CONFIG,			mpi_pre_config},
+	{ MPI2_FUNCTION_SAS_IO_UNIT_CONTROL,	mpi_pre_sas_io_unit_control },
+	{ 0xFF,					NULL } /* list end */
+};
 
 static int
-mps_user_verify_request(MPI2_REQUEST_HEADER *hdr, MPI2_SGE_IO_UNION **psgl)
+mps_user_setup_request(struct mps_command *cm, struct mps_usr_command *cmd)
 {
-	int i, err = EINVAL;
+	MPI2_REQUEST_HEADER *hdr = (MPI2_REQUEST_HEADER *)cm->cm_req;	
+	struct mps_user_func *f;
 
-	for (i = 0; i < sizeof(mps_user_func_list) /
-	    sizeof(mps_user_func_list[0]); i++ ) {
-		struct mps_user_func *func = &mps_user_func_list[i];
-		
-		if (hdr->Function == func->Func) {
-			if (psgl != NULL) {
-				if (func->SgOff != 0)
-					*psgl = (PTR_MPI2_SGE_IO_UNION)
-					    ((char*)hdr + func->SgOff);
-				else
-					*psgl = NULL;
-				err = 0;
-				break;
-			}
-		}
-	}	
-
-	return err;
+	for (f = mps_user_func_list; f->f_pre != NULL; f++) {
+		if (hdr->Function == f->Function)
+			return (f->f_pre(cm, cmd));
+	}
+	return (EINVAL);
 }	
 
 static int
@@ -338,9 +578,8 @@
 {
 	MPI2_REQUEST_HEADER *hdr;	
 	MPI2_DEFAULT_REPLY *rpl;
-	MPI2_SGE_IO_UNION *sgl;	
-	void *buf;
-	struct mps_command *cm;
+	void *buf = NULL;
+	struct mps_command *cm = NULL;
 	int err = 0;
 	int sz;
 
@@ -359,16 +598,22 @@
 	mps_dprint(sc, MPS_INFO, "mps_user_command: req %p %d  rpl %p %d\n",
 		    cmd->req, cmd->req_len, cmd->rpl, cmd->rpl_len );
 
-	copyin(cmd->req, hdr, cmd->req_len);
+	if (cmd->req_len > (int)sc->facts->IOCRequestFrameSize * 4) {
+		err = EINVAL;
+		goto RetFreeUnlocked;
+	}
+	err = copyin(cmd->req, hdr, cmd->req_len);
+	if (err != 0)
+		goto RetFreeUnlocked;
 
 	mps_dprint(sc, MPS_INFO, "mps_user_command: Function %02X  "
 	    "MsgFlags %02X\n", hdr->Function, hdr->MsgFlags );
 
-	err = mps_user_verify_request(hdr, &sgl);
+	err = mps_user_setup_request(cm, cmd);
 	if (err != 0) {
 		mps_printf(sc, "mps_user_command: unsupported function 0x%X\n",
 		    hdr->Function );
-		goto RetFree;
+		goto RetFreeUnlocked;
 	}
 
 	if (cmd->len > 0) {
@@ -376,24 +621,22 @@
 		cm->cm_data = buf;
 		cm->cm_length = cmd->len;
 	} else {
-		buf = NULL;
 		cm->cm_data = NULL;
 		cm->cm_length = 0;
 	}
 
-	cm->cm_sge = sgl;
-	cm->cm_sglsize = sizeof(MPI2_SGE_IO_UNION);
 	cm->cm_flags = MPS_CM_FLAGS_SGE_SIMPLE | MPS_CM_FLAGS_WAKEUP;
 	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
 
 	mps_lock(sc);
 	err = mps_map_command(sc, cm);
 
-	if (err != 0) {
-		mps_printf(sc, "mps_user_command: request timed out\n");
+	if (err != 0 && err != EINPROGRESS) {
+		mps_printf(sc, "%s: invalid request: error %d\n",
+		    __func__, err);
 		goto Ret;
 	}
-	msleep(cm, &sc->mps_mtx, 0, "mpsuser", 0); /* 30 seconds */
+	msleep(cm, &sc->mps_mtx, 0, "mpsuser", 0);
 
 	rpl = (MPI2_DEFAULT_REPLY *)cm->cm_reply;
 	sz = rpl->MsgLength * 4;
@@ -408,41 +651,29 @@
 
 	mps_unlock(sc);
 	copyout(rpl, cmd->rpl, sz);
-	if (buf != NULL) {
+	if (buf != NULL)
 		copyout(buf, cmd->buf, cmd->len);
-		free(buf, M_MPSUSER);
-	}
-	mps_lock(sc);
-
 	mps_dprint(sc, MPS_INFO, "mps_user_command: reply size %d\n", sz );
 
-RetFree:	   
-	mps_free_command(sc, cm);
-
+RetFreeUnlocked:
+	mps_lock(sc);
+	if (cm != NULL)
+		mps_free_command(sc, cm);
 Ret:
 	mps_unlock(sc);
-	return err;
+	if (buf != NULL)
+		free(buf, M_MPSUSER);
+	return (err);
 }	
 
-#ifdef __amd64__
-#define	PTRIN(p)		((void *)(uintptr_t)(p))
-#define PTROUT(v)		((u_int32_t)(uintptr_t)(v))
-#endif
-
 static int
-mps_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag,
+mps_ioctl(struct cdev *dev, u_long cmd, void *arg, int flag,
     struct thread *td)
 {
 	struct mps_softc *sc;
 	struct mps_cfg_page_req *page_req;
 	struct mps_ext_cfg_page_req *ext_page_req;
 	void *mps_page;
-#ifdef __amd64__
-	struct mps_cfg_page_req32 *page_req32;
-	struct mps_cfg_page_req page_req_swab;
-	struct mps_ext_cfg_page_req32 *ext_page_req32;
-	struct mps_ext_cfg_page_req ext_page_req_swab;
-#endif
 	int error;
 
 	mps_page = NULL;
@@ -450,47 +681,12 @@
 	page_req = (void *)arg;
 	ext_page_req = (void *)arg;
 
-#ifdef __amd64__
-	/* Convert 32-bit structs to native ones. */
-	page_req32 = (void *)arg;
-	ext_page_req32 = (void *)arg;
 	switch (cmd) {
-	case MPSIO_READ_CFG_HEADER32:
-	case MPSIO_READ_CFG_PAGE32:
-	case MPSIO_WRITE_CFG_PAGE32:
-		page_req = &page_req_swab;
-		page_req->header = page_req32->header;
-		page_req->page_address = page_req32->page_address;
-		page_req->buf = PTRIN(page_req32->buf);
-		page_req->len = page_req32->len;
-		page_req->ioc_status = page_req32->ioc_status;
-		break;
-	case MPSIO_READ_EXT_CFG_HEADER32:
-	case MPSIO_READ_EXT_CFG_PAGE32:
-		ext_page_req = &ext_page_req_swab;
-		ext_page_req->header = ext_page_req32->header;
-		ext_page_req->page_address = ext_page_req32->page_address;
-		ext_page_req->buf = PTRIN(ext_page_req32->buf);
-		ext_page_req->len = ext_page_req32->len;
-		ext_page_req->ioc_status = ext_page_req32->ioc_status;
-		break;
-	default:
-		return (ENOIOCTL);
-	}
-#endif
-
-	switch (cmd) {
-#ifdef __amd64__
-	case MPSIO_READ_CFG_HEADER32:
-#endif
 	case MPSIO_READ_CFG_HEADER:
 		mps_lock(sc);
 		error = mps_user_read_cfg_header(sc, page_req);
 		mps_unlock(sc);
 		break;
-#ifdef __amd64__
-	case MPSIO_READ_CFG_PAGE32:
-#endif
 	case MPSIO_READ_CFG_PAGE:
 		mps_page = malloc(page_req->len, M_MPSUSER, M_WAITOK | M_ZERO);
 		error = copyin(page_req->buf, mps_page,
@@ -504,17 +700,11 @@
 			break;
 		error = copyout(mps_page, page_req->buf, page_req->len);
 		break;
-#ifdef __amd64__
-	case MPSIO_READ_EXT_CFG_HEADER32:
-#endif
 	case MPSIO_READ_EXT_CFG_HEADER:
 		mps_lock(sc);
 		error = mps_user_read_extcfg_header(sc, ext_page_req);
 		mps_unlock(sc);
 		break;
-#ifdef __amd64__
-	case MPSIO_READ_EXT_CFG_PAGE32:
-#endif
 	case MPSIO_READ_EXT_CFG_PAGE:
 		mps_page = malloc(ext_page_req->len, M_MPSUSER, M_WAITOK|M_ZERO);
 		error = copyin(ext_page_req->buf, mps_page,
@@ -528,9 +718,6 @@
 			break;
 		error = copyout(mps_page, ext_page_req->buf, ext_page_req->len);
 		break;
-#ifdef __amd64__
-	case MPSIO_WRITE_CFG_PAGE32:
-#endif
 	case MPSIO_WRITE_CFG_PAGE:
 		mps_page = malloc(page_req->len, M_MPSUSER, M_WAITOK|M_ZERO);
 		error = copyin(page_req->buf, mps_page, page_req->len);
@@ -551,33 +738,207 @@
 	if (mps_page != NULL)
 		free(mps_page, M_MPSUSER);
 
-	if (error)
-		return (error);
+	return (error);
+}
 
-#ifdef __amd64__
-	/* Convert native structs to 32-bit ones. */
-	switch (cmd) {
+#ifdef COMPAT_FREEBSD32
+
+/* Macros from compat/freebsd32/freebsd32.h */
+#define	PTRIN(v)	(void *)(uintptr_t)(v)
+#define	PTROUT(v)	(uint32_t)(uintptr_t)(v)
+
+#define	CP(src,dst,fld) do { (dst).fld = (src).fld; } while (0)
+#define	PTRIN_CP(src,dst,fld)				\
+	do { (dst).fld = PTRIN((src).fld); } while (0)
+#define	PTROUT_CP(src,dst,fld) \
+	do { (dst).fld = PTROUT((src).fld); } while (0)
+
+struct mps_cfg_page_req32 {
+	MPI2_CONFIG_PAGE_HEADER header;
+	uint32_t page_address;
+	uint32_t buf;
+	int	len;	
+	uint16_t ioc_status;
+};
+
+struct mps_ext_cfg_page_req32 {
+	MPI2_CONFIG_EXTENDED_PAGE_HEADER header;
+	uint32_t page_address;
+	uint32_t buf;
+	int	len;
+	uint16_t ioc_status;
+};
+
+struct mps_raid_action32 {
+	uint8_t action;
+	uint8_t volume_bus;
+	uint8_t volume_id;
+	uint8_t phys_disk_num;
+	uint32_t action_data_word;
+	uint32_t buf;
+	int len;
+	uint32_t volume_status;
+	uint32_t action_data[4];
+	uint16_t action_status;
+	uint16_t ioc_status;
+	uint8_t write;
+};
+
+struct mps_usr_command32 {
+	uint32_t req;
+	uint32_t req_len;
+	uint32_t rpl;
+	uint32_t rpl_len;
+	uint32_t buf;
+	int len;
+	uint32_t flags;
+};
+
+#define	MPSIO_READ_CFG_HEADER32	_IOWR('M', 200, struct mps_cfg_page_req32)
+#define	MPSIO_READ_CFG_PAGE32	_IOWR('M', 201, struct mps_cfg_page_req32)
+#define	MPSIO_READ_EXT_CFG_HEADER32 _IOWR('M', 202, struct mps_ext_cfg_page_req32)
+#define	MPSIO_READ_EXT_CFG_PAGE32 _IOWR('M', 203, struct mps_ext_cfg_page_req32)
+#define	MPSIO_WRITE_CFG_PAGE32	_IOWR('M', 204, struct mps_cfg_page_req32)
+#define	MPSIO_RAID_ACTION32	_IOWR('M', 205, struct mps_raid_action32)
+#define	MPSIO_MPS_COMMAND32	_IOWR('M', 210, struct mps_usr_command32)
+
+static int
+mps_ioctl32(struct cdev *dev, u_long cmd32, void *_arg, int flag,
+    struct thread *td)
+{
+	struct mps_cfg_page_req32 *page32 = _arg;
+	struct mps_ext_cfg_page_req32 *ext32 = _arg;
+	struct mps_raid_action32 *raid32 = _arg;
+	struct mps_usr_command32 *user32 = _arg;
+	union {
+		struct mps_cfg_page_req page;
+		struct mps_ext_cfg_page_req ext;
+		struct mps_raid_action raid;
+		struct mps_usr_command user;
+	} arg;
+	u_long cmd;
+	int error;
+
+	switch (cmd32) {
 	case MPSIO_READ_CFG_HEADER32:
 	case MPSIO_READ_CFG_PAGE32:
 	case MPSIO_WRITE_CFG_PAGE32:
-		page_req32->header = page_req->header;
-		page_req32->page_address = page_req->page_address;
-		page_req32->buf = PTROUT(page_req->buf);
-		page_req32->len = page_req->len;
-		page_req32->ioc_status = page_req->ioc_status;
+		if (cmd32 == MPSIO_READ_CFG_HEADER32)
+			cmd = MPSIO_READ_CFG_HEADER;
+		else if (cmd32 == MPSIO_READ_CFG_PAGE32)
+			cmd = MPSIO_READ_CFG_PAGE;
+		else
+			cmd = MPSIO_WRITE_CFG_PAGE;
+		CP(*page32, arg.page, header);
+		CP(*page32, arg.page, page_address);
+		PTRIN_CP(*page32, arg.page, buf);
+		CP(*page32, arg.page, len);
+		CP(*page32, arg.page, ioc_status);
 		break;
+
 	case MPSIO_READ_EXT_CFG_HEADER32:
-	case MPSIO_READ_EXT_CFG_PAGE32:		
-		ext_page_req32->header = ext_page_req->header;
-		ext_page_req32->page_address = ext_page_req->page_address;
-		ext_page_req32->buf = PTROUT(ext_page_req->buf);
-		ext_page_req32->len = ext_page_req->len;
-		ext_page_req32->ioc_status = ext_page_req->ioc_status;
+	case MPSIO_READ_EXT_CFG_PAGE32:
+		if (cmd32 == MPSIO_READ_EXT_CFG_HEADER32)
+			cmd = MPSIO_READ_EXT_CFG_HEADER;
+		else
+			cmd = MPSIO_READ_EXT_CFG_PAGE;
+		CP(*ext32, arg.ext, header);
+		CP(*ext32, arg.ext, page_address);
+		PTRIN_CP(*ext32, arg.ext, buf);
+		CP(*ext32, arg.ext, len);
+		CP(*ext32, arg.ext, ioc_status);
 		break;
+
+	case MPSIO_RAID_ACTION32:
+		cmd = MPSIO_RAID_ACTION;
+		CP(*raid32, arg.raid, action);
+		CP(*raid32, arg.raid, volume_bus);
+		CP(*raid32, arg.raid, volume_id);
+		CP(*raid32, arg.raid, phys_disk_num);
+		CP(*raid32, arg.raid, action_data_word);
+		PTRIN_CP(*raid32, arg.raid, buf);
+		CP(*raid32, arg.raid, len);
+		CP(*raid32, arg.raid, volume_status);
+		bcopy(raid32->action_data, arg.raid.action_data,
+		    sizeof arg.raid.action_data);
+		CP(*raid32, arg.raid, ioc_status);
+		CP(*raid32, arg.raid, write);
+		break;
+
+	case MPSIO_MPS_COMMAND32:
+		cmd = MPSIO_MPS_COMMAND;
+		PTRIN_CP(*user32, arg.user, req);
+		CP(*user32, arg.user, req_len);
+		PTRIN_CP(*user32, arg.user, rpl);
+		CP(*user32, arg.user, rpl_len);
+		PTRIN_CP(*user32, arg.user, buf);
+		CP(*user32, arg.user, len);
+		CP(*user32, arg.user, flags);
+		break;
 	default:
 		return (ENOIOCTL);
 	}
-#endif
 
-	return (0);
+	error = mps_ioctl(dev, cmd, &arg, flag, td);
+	if (error == 0 && (cmd32 & IOC_OUT) != 0) {
+		switch (cmd32) {
+		case MPSIO_READ_CFG_HEADER32:
+		case MPSIO_READ_CFG_PAGE32:
+		case MPSIO_WRITE_CFG_PAGE32:
+			CP(arg.page, *page32, header);
+			CP(arg.page, *page32, page_address);
+			PTROUT_CP(arg.page, *page32, buf);
+			CP(arg.page, *page32, len);
+			CP(arg.page, *page32, ioc_status);
+			break;
+
+		case MPSIO_READ_EXT_CFG_HEADER32:
+		case MPSIO_READ_EXT_CFG_PAGE32:
+			CP(arg.ext, *ext32, header);
+			CP(arg.ext, *ext32, page_address);
+			PTROUT_CP(arg.ext, *ext32, buf);
+			CP(arg.ext, *ext32, len);
+			CP(arg.ext, *ext32, ioc_status);
+			break;
+
+		case MPSIO_RAID_ACTION32:
+			CP(arg.raid, *raid32, action);
+			CP(arg.raid, *raid32, volume_bus);
+			CP(arg.raid, *raid32, volume_id);
+			CP(arg.raid, *raid32, phys_disk_num);
+			CP(arg.raid, *raid32, action_data_word);
+			PTROUT_CP(arg.raid, *raid32, buf);
+			CP(arg.raid, *raid32, len);
+			CP(arg.raid, *raid32, volume_status);
+			bcopy(arg.raid.action_data, raid32->action_data,
+			    sizeof arg.raid.action_data);
+			CP(arg.raid, *raid32, ioc_status);
+			CP(arg.raid, *raid32, write);
+			break;
+
+		case MPSIO_MPS_COMMAND32:
+			PTROUT_CP(arg.user, *user32, req);
+			CP(arg.user, *user32, req_len);
+			PTROUT_CP(arg.user, *user32, rpl);
+			CP(arg.user, *user32, rpl_len);
+			PTROUT_CP(arg.user, *user32, buf);
+			CP(arg.user, *user32, len);
+			CP(arg.user, *user32, flags);
+			break;
+		}
+	}
+
+	return (error);
 }
+#endif /* COMPAT_FREEBSD32 */
+
+static int
+mps_ioctl_devsw(struct cdev *dev, u_long com, caddr_t arg, int flag,
+    struct thread *td)
+{
+#ifdef COMPAT_FREEBSD32
+	if (SV_CURPROC_FLAG(SV_ILP32))
+		return (mps_ioctl32(dev, com, arg, flag, td));
+#endif
+	return (mps_ioctl(dev, com, arg, flag, td));
+}
Index: sys/dev/mps/mpsvar.h
===================================================================
--- sys/dev/mps/mpsvar.h	(revision 212420)
+++ sys/dev/mps/mpsvar.h	(working copy)
@@ -60,11 +60,19 @@
 	uint32_t			chain_busaddr;
 };
 
+/*
+ * This needs to be at least 2 to support SMP passthrough.
+ */
+#define	MPS_IOVEC_COUNT	2
+
 struct mps_command {
 	TAILQ_ENTRY(mps_command)	cm_link;
 	struct mps_softc		*cm_sc;
 	void				*cm_data;
 	u_int				cm_length;
+	struct uio			cm_uio;
+	struct iovec			cm_iovec[MPS_IOVEC_COUNT];
+	u_int				cm_max_segs;
 	u_int				cm_sglsize;
 	MPI2_SGE_IO_UNION		*cm_sge;
 	uint8_t				*cm_req;
@@ -81,6 +89,9 @@
 #define MPS_CM_FLAGS_DATAOUT		(1 << 3)
 #define MPS_CM_FLAGS_DATAIN		(1 << 4)
 #define MPS_CM_FLAGS_WAKEUP		(1 << 5)
+#define MPS_CM_FLAGS_ACTIVE		(1 << 6)
+#define MPS_CM_FLAGS_USE_UIO		(1 << 7)
+#define MPS_CM_FLAGS_SMP_PASS		(1 << 8)
 	u_int				cm_state;
 #define MPS_CM_STATE_FREE		0
 #define MPS_CM_STATE_BUSY		1
@@ -109,6 +120,8 @@
 #define MPS_FLAGS_BUSY		(1 << 2)
 #define MPS_FLAGS_SHUTDOWN	(1 << 3)
 	u_int				mps_debug;
+	u_int				allow_multiple_tm_cmds;
+	int				tm_cmds_active;
 	struct sysctl_ctx_list		sysctl_ctx;
 	struct sysctl_oid		*sysctl_tree;
 	struct mps_command		*commands;
@@ -119,9 +132,9 @@
 
 	TAILQ_HEAD(, mps_command)	req_list;
 	TAILQ_HEAD(, mps_chain)		chain_list;
+	TAILQ_HEAD(, mps_command)	tm_list;
 	int				replypostindex;
 	int				replyfreeindex;
-	int				replycurindex;
 
 	struct resource			*mps_regs_resource;
 	bus_space_handle_t		mps_bhandle;
@@ -234,12 +247,15 @@
 {
 	struct mps_chain *chain, *chain_temp;
 
-	if (cm->cm_reply != NULL)
+	if (cm->cm_reply != NULL) {
 		mps_free_reply(sc, cm->cm_reply_data);
+		cm->cm_reply = NULL;
+	}
 	cm->cm_flags = 0;
 	cm->cm_complete = NULL;
 	cm->cm_complete_data = NULL;
 	cm->cm_targ = 0;
+	cm->cm_max_segs = 0;
 	cm->cm_state = MPS_CM_STATE_FREE;
 	TAILQ_FOREACH_SAFE(chain, &cm->cm_chain_list, chain_link, chain_temp) {
 		TAILQ_REMOVE(&cm->cm_chain_list, chain, chain_link);
@@ -355,12 +371,16 @@
 int mps_update_events(struct mps_softc *, struct mps_event_handle *, uint8_t *);
 int mps_deregister_events(struct mps_softc *, struct mps_event_handle *);
 int mps_request_polled(struct mps_softc *sc, struct mps_command *cm);
+void mps_enqueue_request(struct mps_softc *, struct mps_command *);
+int mps_push_sge(struct mps_command *, void *, size_t, int);
+int mps_add_dmaseg(struct mps_command *, vm_paddr_t, size_t, u_int, int);
 int mps_attach_sas(struct mps_softc *sc);
 int mps_detach_sas(struct mps_softc *sc);
 int mps_map_command(struct mps_softc *sc, struct mps_command *cm);
 int mps_read_config_page(struct mps_softc *, struct mps_config_params *);
 int mps_write_config_page(struct mps_softc *, struct mps_config_params *);
 void mps_memaddr_cb(void *, bus_dma_segment_t *, int , int );
+void mpi_init_sge(struct mps_command *cm, void *req, void *sge);
 int mps_attach_user(struct mps_softc *);
 void mps_detach_user(struct mps_softc *);
 
Index: sys/dev/bwn/if_bwn.c
===================================================================
--- sys/dev/bwn/if_bwn.c	(revision 218743)
+++ sys/dev/bwn/if_bwn.c	(working copy)
@@ -2882,7 +2882,7 @@
 
 	error = bwn_switch_band(sc, ic->ic_curchan);
 	if (error)
-		goto fail;;
+		goto fail;
 	bwn_mac_suspend(mac);
 	bwn_set_txretry(mac, BWN_RETRY_SHORT, BWN_RETRY_LONG);
 	chan = ieee80211_chan2ieee(ic, ic->ic_curchan);
@@ -8260,7 +8260,7 @@
 	device_printf(sc->sc_dev, "switching to %s-GHz band\n",
 	    IEEE80211_IS_CHAN_2GHZ(chan) ? "2" : "5");
 
-	down_dev = sc->sc_curmac;;
+	down_dev = sc->sc_curmac;
 	status = down_dev->mac_status;
 	if (status >= BWN_MAC_STATUS_STARTED)
 		bwn_core_stop(down_dev);

Property changes on: sys/contrib/pf
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /head/sys/contrib/pf:r212420,212616,212772,212802,213535,213702,213704,213707-213708,213743,213839-213840,213882,213898,216088,216227,216363,216368


Property changes on: sys/contrib/dev/acpica
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /head/sys/contrib/dev/acpica:r212420,212616,212772,212802,213535,213702,213704,213707-213708,213743,213839-213840,213882,213898,216088,216227,216363,216368


Property changes on: sys/cddl/contrib/opensolaris
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /head/sys/cddl/contrib/opensolaris:r212420,212616,212772,212802,213535,213702,213704,213707-213708,213743,213839-213840,213882,213898,216088,216227,216363,216368


Property changes on: sys/amd64/include/xen
___________________________________________________________________
Modified: svn:mergeinfo
   Merged /head/sys/amd64/include/xen:r212420,212616,212772,212802,213535,213702,213704,213707-213708,213743,213839-213840,213882,213898,216088,216227,216363,216368

Index: sys/amd64/conf/GENERIC
===================================================================
--- sys/amd64/conf/GENERIC	(revision 218743)
+++ sys/amd64/conf/GENERIC	(working copy)
@@ -114,6 +114,7 @@
 device		isp		# Qlogic family
 #device		ispfw		# Firmware for QLogic HBAs- normally a module
 device		mpt		# LSI-Logic MPT-Fusion
+device		mps		# LSI-Logic MPT-Fusion 2
 #device		ncr		# NCR/Symbios Logic
 device		sym		# NCR/Symbios Logic (newer chipsets + those of `ncr')
 device		trm		# Tekram DC395U/UW/F DC315U adapters


More information about the freebsd-stable mailing list