From owner-svn-src-stable-8@FreeBSD.ORG Fri Feb 18 16:29:39 2011 Return-Path: Delivered-To: svn-src-stable-8@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 348AF106566B; Fri, 18 Feb 2011 16:29:39 +0000 (UTC) (envelope-from ken@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 1ED108FC18; Fri, 18 Feb 2011 16:29:39 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id p1IGTdFk064420; Fri, 18 Feb 2011 16:29:39 GMT (envelope-from ken@svn.freebsd.org) Received: (from ken@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id p1IGTc8C064411; Fri, 18 Feb 2011 16:29:38 GMT (envelope-from ken@svn.freebsd.org) Message-Id: <201102181629.p1IGTc8C064411@svn.freebsd.org> From: "Kenneth D. Merry" Date: Fri, 18 Feb 2011 16:29:38 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-8@freebsd.org X-SVN-Group: stable-8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r218810 - in stable/8/sys: amd64/conf conf dev/bwn dev/mps dev/siba dev/sis mips/mips modules modules/mps X-BeenThere: svn-src-stable-8@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: SVN commit messages for only the 8-stable src tree List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 18 Feb 2011 16:29:39 -0000 Author: ken Date: Fri Feb 18 16:29:38 2011 New Revision: 218810 URL: http://svn.freebsd.org/changeset/base/218810 Log: MFC: 212420, 212616, 212772, 212802, 213535, 213702, 213704, 213707, 213708, 213743, 213839, 213840, 213882, 213898, 216088, 216227, 216363, 216368: Merge the mps(4) driver into stable/8. This is currently only included in GENERIC on amd64, since that is the only architecture it has been tested on. Added: stable/8/sys/dev/mps/ - copied from r212420, head/sys/dev/mps/ stable/8/sys/modules/mps/ - copied from r212420, head/sys/modules/mps/ Modified: stable/8/sys/amd64/conf/GENERIC stable/8/sys/conf/files stable/8/sys/dev/bwn/if_bwn.c stable/8/sys/dev/mps/mps.c stable/8/sys/dev/mps/mps_ioctl.h stable/8/sys/dev/mps/mps_pci.c stable/8/sys/dev/mps/mps_sas.c stable/8/sys/dev/mps/mps_user.c stable/8/sys/dev/mps/mpsvar.h stable/8/sys/dev/siba/siba_bwn.c stable/8/sys/dev/sis/if_sisreg.h stable/8/sys/mips/mips/mp_machdep.c stable/8/sys/modules/Makefile stable/8/sys/modules/mps/Makefile Directory Properties: stable/8/sys/ (props changed) stable/8/sys/amd64/include/xen/ (props changed) stable/8/sys/cddl/contrib/opensolaris/ (props changed) stable/8/sys/contrib/dev/acpica/ (props changed) stable/8/sys/contrib/pf/ (props changed) Modified: stable/8/sys/amd64/conf/GENERIC ============================================================================== --- stable/8/sys/amd64/conf/GENERIC Fri Feb 18 16:21:09 2011 (r218809) +++ stable/8/sys/amd64/conf/GENERIC Fri Feb 18 16:29:38 2011 (r218810) @@ -114,6 +114,7 @@ device hptiop # Highpoint RocketRaid 3 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 Modified: stable/8/sys/conf/files ============================================================================== --- stable/8/sys/conf/files Fri Feb 18 16:21:09 2011 (r218809) +++ stable/8/sys/conf/files Fri Feb 18 16:29:38 2011 (r218810) @@ -1303,6 +1303,11 @@ dev/mmc/mmcbr_if.m standard 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 Modified: stable/8/sys/dev/bwn/if_bwn.c ============================================================================== --- stable/8/sys/dev/bwn/if_bwn.c Fri Feb 18 16:21:09 2011 (r218809) +++ stable/8/sys/dev/bwn/if_bwn.c Fri Feb 18 16:29:38 2011 (r218810) @@ -2882,7 +2882,7 @@ bwn_set_channel(struct ieee80211com *ic) 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 @@ bwn_switch_band(struct bwn_softc *sc, st 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); Modified: stable/8/sys/dev/mps/mps.c ============================================================================== --- head/sys/dev/mps/mps.c Fri Sep 10 15:03:56 2010 (r212420) +++ stable/8/sys/dev/mps/mps.c Fri Feb 18 16:29:38 2011 (r218810) @@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include @@ -380,7 +381,7 @@ mps_request_sync(struct mps_softc *sc, v return (0); } -static void +void mps_enqueue_request(struct mps_softc *sc, struct mps_command *cm) { @@ -607,9 +608,16 @@ mps_alloc_queues(struct mps_softc *sc) static int mps_alloc_replies(struct mps_softc *sc) { - int rsize; + int rsize, num_replies; + + /* + * 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 * sc->num_replies * 4; + 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 @@ mps_init_queues(struct mps_softc *sc) 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 @@ mps_attach(struct mps_softc *sc) 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 @@ mps_attach(struct mps_softc *sc) 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 @@ mps_attach(struct mps_softc *sc) 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 @@ mps_attach(struct mps_softc *sc) * 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 @@ mps_attach(struct mps_softc *sc) /* 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 @@ mps_intr_locked(void *data) 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 @@ mps_intr_locked(void *data) 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 @@ mps_intr_locked(void *data) 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 @@ mps_dispatch_event(struct mps_softc *sc, 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,33 +1418,88 @@ mps_deregister_events(struct mps_softc * 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 @@ -1399,70 +1507,164 @@ mps_data_cb(void *arg, bus_dma_segment_t * 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) - */ + */ - for (i = 0; i < nsegs; i++) { + /* + * 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"); - /* 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); + + /* 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 - sge = (MPI2_SGE_SIMPLE64 *)&chain->chain->MpiSimple; - sglspace = 128; - } + 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); + } - 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); + 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); +} - /* Case 1, Fill in one SGE element and break */ - if (segsleft == 1) - break; +/* + * 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; + + /* + * 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); - sglspace -= MPS_SGE64_SIZE; - segsleft--; + 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; - /* 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); - - /* Advance to the next element to be filled in. */ - sge++; - } - - /* 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); + 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); + } + + /* + * 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); @@ -1470,13 +1672,27 @@ mps_data_cb(void *arg, bus_dma_segment_t 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 @@ mps_map_command(struct mps_softc *sc, st MPI2_SGE_FLAGS_SHIFT; sge->Address = 0; } - mps_enqueue_request(sc, cm); + mps_enqueue_request(sc, cm); } return (error); @@ -1549,9 +1765,9 @@ mps_read_config_page(struct mps_softc *s 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; Modified: stable/8/sys/dev/mps/mps_ioctl.h ============================================================================== --- head/sys/dev/mps/mps_ioctl.h Fri Sep 10 15:03:56 2010 (r212420) +++ stable/8/sys/dev/mps/mps_ioctl.h Fri Feb 18 16:29:38 2011 (r218810) @@ -103,44 +103,4 @@ struct mps_usr_command { #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_ */ Modified: stable/8/sys/dev/mps/mps_pci.c ============================================================================== --- head/sys/dev/mps/mps_pci.c Fri Sep 10 15:03:56 2010 (r212420) +++ stable/8/sys/dev/mps/mps_pci.c Fri Feb 18 16:29:38 2011 (r218810) @@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include Modified: stable/8/sys/dev/mps/mps_sas.c ============================================================================== --- head/sys/dev/mps/mps_sas.c Fri Sep 10 15:03:56 2010 (r212420) +++ stable/8/sys/dev/mps/mps_sas.c Fri Feb 18 16:29:38 2011 (r218810) @@ -41,6 +41,8 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include +#include #include #include @@ -55,6 +57,9 @@ __FBSDID("$FreeBSD$"); #include #include #include +#if __FreeBSD_version >= 900026 +#include +#endif #include #include @@ -69,9 +74,11 @@ struct mpssas_target { 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 @@ struct mpssas_devprobe { 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_probe_device_complete 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 @@ mpssas_probe_device_complete(struct mps_ 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 @@ mpssas_prepare_remove(struct mpssas_soft 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 @@ mpssas_remove_device(struct mps_softc *s 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 @@ mps_attach_sas(struct mps_softc *sc) { struct mpssas_softc *sassc; int error = 0; + int num_sim_reqs; mps_dprint(sc, MPS_TRACE, "%s\n", __func__); @@ -603,15 +667,30 @@ mps_attach_sas(struct mps_softc *sc) 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 @@ mpssas_action(struct cam_sim *sim, union 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 @@ mpssas_scsiio_timeout(void *data) 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 @@ mpssas_scsiio_timeout(void *data) 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 @@ mpssas_abort_complete(struct mps_softc * 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 @@ mpssas_recovery(struct mps_softc *sc, st { struct mps_command *cm; MPI2_SCSI_TASK_MANAGE_REQUEST *req, *orig_req; - int error; cm = mps_alloc_command(sc); if (cm == NULL) { @@ -1013,25 +1115,204 @@ mpssas_recovery(struct mps_softc *sc, st 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__); - } -#if 0 - error = mpssas_reset(sc, targ, &resetcm); - if ((error != 0) && (error != EBUSY)) { - mps_printf(sc, "Error resetting device!\n"); - mps_unlock(sc); - return; + 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); } +} + +static void +mpssas_tm_complete(struct mps_softc *sc, struct mps_command *cm, int error) +{ + MPI2_SCSI_TASK_MANAGE_REPLY *resp; + + resp = (MPI2_SCSI_TASK_MANAGE_REPLY *)cm->cm_reply; - targ->flags |= MPSSAS_TARGET_INRESET; + resp->ResponseCode = error; - cm->cm_complete = mpssas_resettimeout_complete; - cm->cm_complete_data = cm; - mps_map_command(sassc->sc, cm); -#endif + /* + * Call the callback for this command, it will be + * removed from the list and freed via the callback. + */ + cm->cm_complete(sc, cm); +} *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***