From owner-freebsd-stable@FreeBSD.ORG Wed Feb 16 17:37:34 2011 Return-Path: Delivered-To: freebsd-stable@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 39AE01065670 for ; Wed, 16 Feb 2011 17:37:34 +0000 (UTC) (envelope-from ken@kdm.org) Received: from nargothrond.kdm.org (nargothrond.kdm.org [70.56.43.81]) by mx1.freebsd.org (Postfix) with ESMTP id 207318FC0A for ; Wed, 16 Feb 2011 17:37:32 +0000 (UTC) Received: from nargothrond.kdm.org (localhost [127.0.0.1]) by nargothrond.kdm.org (8.14.2/8.14.2) with ESMTP id p1GHQhBB038284; Wed, 16 Feb 2011 10:26:43 -0700 (MST) (envelope-from ken@nargothrond.kdm.org) Received: (from ken@localhost) by nargothrond.kdm.org (8.14.2/8.14.2/Submit) id p1GHQhRl038283; Wed, 16 Feb 2011 10:26:43 -0700 (MST) (envelope-from ken) Date: Wed, 16 Feb 2011 10:26:43 -0700 From: "Kenneth D. Merry" To: Dmitry Morozovsky Message-ID: <20110216172643.GA37858@nargothrond.kdm.org> References: <4D5BF78E.7010306@digsys.bg> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="6c2NcOVqGQ03X4Wi" Content-Disposition: inline In-Reply-To: User-Agent: Mutt/1.4.2i Cc: freebsd-stable@freebsd.org, Daniel Kalchev Subject: Re: LSI SAS 2008 (mfi) on SuperMicro X8SI6-F X-BeenThere: freebsd-stable@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Production branch of FreeBSD source code List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 16 Feb 2011 17:37:34 -0000 --6c2NcOVqGQ03X4Wi Content-Type: text/plain; charset=us-ascii Content-Disposition: inline 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@FreeBSD.ORG --6c2NcOVqGQ03X4Wi Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="mps_stable.20110216.2.txt" 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 #include #include +#include #include #include @@ -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 #include #include +#include +#include #include #include @@ -55,6 +57,9 @@ #include #include #include +#if __FreeBSD_version >= 900026 +#include +#endif #include #include @@ -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 #include #include +#include #include #include 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 __FBSDID("$FreeBSD$"); +#include "opt_compat.h" + #include #include #include @@ -47,6 +49,8 @@ #include #include #include +#include +#include #include #include @@ -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 --6c2NcOVqGQ03X4Wi--