diff options
author | cem <cem@FreeBSD.org> | 2015-11-24 18:51:17 +0000 |
---|---|---|
committer | cem <cem@FreeBSD.org> | 2015-11-24 18:51:17 +0000 |
commit | 4ab80ee6ff1454f9397ee214821a1b74c724a724 (patch) | |
tree | e1d13ebaa4c82b44b742fdbdd200a7e514a7103f /sys | |
parent | 745b5bcad49247c234953ff587296084cefbe3c8 (diff) | |
download | FreeBSD-src-4ab80ee6ff1454f9397ee214821a1b74c724a724.zip FreeBSD-src-4ab80ee6ff1454f9397ee214821a1b74c724a724.tar.gz |
ntb: Add MW tunable for MMR Xeon errata workaround
Adds a new tunable, ntb.hw.b2b_mw_idx, which specifies the offset (from the
total number of memory windows) to use for register access on hardware with
the SDOORBELL_LOCKUP errata. The default is -1, i.e., the last memory
window.
We map BARs before the b2b_mw_idx is selected, so map them all as memory
windows initially. The register memory window should not be write-combined,
so we explicitly disable WC on the selected MW later.
This introduces a layer of abstraction between consumer memory window
indices, which exclude any exclusive errata-workaround BARs, and internal
memory window indices, which include such BARs. An internal routine,
ntb_user_mw_to_idx(), converts the former to the latter. Public APIs have
been updated to use this instead of assuming the exclusive workaround BAR is
the last available MW.
Sponsored by: EMC / Isilon Storage Division
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/ntb/ntb_hw/ntb_hw.c | 86 |
1 files changed, 57 insertions, 29 deletions
diff --git a/sys/dev/ntb/ntb_hw/ntb_hw.c b/sys/dev/ntb/ntb_hw/ntb_hw.c index 220d635..c134acd 100644 --- a/sys/dev/ntb/ntb_hw/ntb_hw.c +++ b/sys/dev/ntb/ntb_hw/ntb_hw.c @@ -264,11 +264,13 @@ bus_space_write_8(bus_space_tag_t tag, bus_space_handle_t handle, static int ntb_probe(device_t device); static int ntb_attach(device_t device); static int ntb_detach(device_t device); +static unsigned ntb_user_mw_to_idx(struct ntb_softc *, unsigned uidx); static inline enum ntb_bar ntb_mw_to_bar(struct ntb_softc *, unsigned mw); static inline bool bar_is_64bit(struct ntb_softc *, enum ntb_bar); static inline void bar_get_xlat_params(struct ntb_softc *, enum ntb_bar, uint32_t *base, uint32_t *xlat, uint32_t *lmt); static int ntb_map_pci_bars(struct ntb_softc *ntb); +static int ntb_mw_set_wc_internal(struct ntb_softc *, unsigned idx, bool wc); static void print_map_success(struct ntb_softc *, struct ntb_pci_bar_info *, const char *); static int map_mmr_bar(struct ntb_softc *ntb, struct ntb_pci_bar_info *bar); @@ -332,6 +334,14 @@ static unsigned g_ntb_enable_wc = 1; SYSCTL_UINT(_hw_ntb, OID_AUTO, enable_writecombine, CTLFLAG_RDTUN, &g_ntb_enable_wc, 0, "Set to 1 to map memory windows write combining"); +static int g_ntb_mw_idx = -1; +SYSCTL_INT(_hw_ntb, OID_AUTO, b2b_mw_idx, CTLFLAG_RDTUN, &g_ntb_mw_idx, + 0, "Use this memory window to access the peer NTB registers. A " + "non-negative value starts from the first MW index; a negative value " + "starts from the last MW index. The default is -1, i.e., the last " + "available memory window. Both sides of the NTB MUST set the same " + "value here! (Applies on Xeon platforms with SDOORBELL_LOCKUP errata.)"); + static struct ntb_hw_info pci_ids[] = { /* XXX: PS/SS IDs left out until they are supported. */ { 0x0C4E8086, "BWD Atom Processor S1200 Non-Transparent Bridge B2B", @@ -579,11 +589,6 @@ ntb_detach(device_t device) mtx_destroy(&ntb->db_mask_lock); mtx_destroy(&ntb->ctx_lock); - /* - * Redetect total MWs so we unmap properly -- in case we lowered the - * maximum to work around Xeon errata. - */ - ntb_detect_max_mw(ntb); ntb_unmap_pci_bar(ntb); return (0); @@ -596,8 +601,7 @@ static inline enum ntb_bar ntb_mw_to_bar(struct ntb_softc *ntb, unsigned mw) { - KASSERT(mw < ntb->mw_count || - (mw != B2B_MW_DISABLED && mw == ntb->b2b_mw_idx), + KASSERT(mw < ntb->mw_count, ("%s: mw:%u > count:%u", __func__, mw, (unsigned)ntb->mw_count)); KASSERT(ntb->reg->mw_bar[mw] != 0, ("invalid mw")); @@ -668,11 +672,9 @@ ntb_map_pci_bars(struct ntb_softc *ntb) ntb->bar_info[NTB_B2B_BAR_1].pbarxlat_off = XEON_PBAR2XLAT_OFFSET; ntb->bar_info[NTB_B2B_BAR_2].pci_resource_id = PCIR_BAR(4); - /* XXX Are shared MW B2Bs write-combining? */ - if (HAS_FEATURE(NTB_SDOORBELL_LOCKUP) && !HAS_FEATURE(NTB_SPLIT_BAR)) - rc = map_mmr_bar(ntb, &ntb->bar_info[NTB_B2B_BAR_2]); - else - rc = map_memory_window_bar(ntb, &ntb->bar_info[NTB_B2B_BAR_2]); + rc = map_memory_window_bar(ntb, &ntb->bar_info[NTB_B2B_BAR_2]); + if (rc != 0) + goto out; ntb->bar_info[NTB_B2B_BAR_2].psz_off = XEON_PBAR4SZ_OFFSET; ntb->bar_info[NTB_B2B_BAR_2].ssz_off = XEON_SBAR4SZ_OFFSET; ntb->bar_info[NTB_B2B_BAR_2].pbarxlat_off = XEON_PBAR4XLAT_OFFSET; @@ -681,10 +683,7 @@ ntb_map_pci_bars(struct ntb_softc *ntb) goto out; ntb->bar_info[NTB_B2B_BAR_3].pci_resource_id = PCIR_BAR(5); - if (HAS_FEATURE(NTB_SDOORBELL_LOCKUP)) - rc = map_mmr_bar(ntb, &ntb->bar_info[NTB_B2B_BAR_3]); - else - rc = map_memory_window_bar(ntb, &ntb->bar_info[NTB_B2B_BAR_3]); + rc = map_memory_window_bar(ntb, &ntb->bar_info[NTB_B2B_BAR_3]); ntb->bar_info[NTB_B2B_BAR_3].psz_off = XEON_PBAR5SZ_OFFSET; ntb->bar_info[NTB_B2B_BAR_3].ssz_off = XEON_SBAR5SZ_OFFSET; ntb->bar_info[NTB_B2B_BAR_3].pbarxlat_off = XEON_PBAR5XLAT_OFFSET; @@ -1264,14 +1263,18 @@ ntb_xeon_init_dev(struct ntb_softc *ntb) /* * There is a Xeon hardware errata related to writes to SDOORBELL or * B2BDOORBELL in conjunction with inbound access to NTB MMIO space, - * which may hang the system. To workaround this use the second memory + * which may hang the system. To workaround this, use a memory * window to access the interrupt and scratch pad registers on the * remote system. */ - if (HAS_FEATURE(NTB_SDOORBELL_LOCKUP)) - /* Use the last MW for mapping remote spad */ - ntb->b2b_mw_idx = ntb->mw_count - 1; - else if (HAS_FEATURE(NTB_B2BDOORBELL_BIT14)) + if (HAS_FEATURE(NTB_SDOORBELL_LOCKUP)) { + ntb->b2b_mw_idx = (ntb->mw_count + g_ntb_mw_idx) % + ntb->mw_count; + ntb_printf(2, "Setting up b2b mw idx %d means %u\n", + g_ntb_mw_idx, ntb->b2b_mw_idx); + rc = ntb_mw_set_wc_internal(ntb, ntb->b2b_mw_idx, false); + KASSERT(rc == 0, ("shouldn't fail")); + } else if (HAS_FEATURE(NTB_B2BDOORBELL_BIT14)) /* * HW Errata on bit 14 of b2bdoorbell register. Writes will not be * mirrored to the remote system. Shrink the number of bits by one, @@ -1481,7 +1484,6 @@ xeon_setup_b2b_mw(struct ntb_softc *ntb, const struct ntb_b2b_addr *addr, ntb->b2b_off = bar_size >> 1; else if (bar_size >= XEON_B2B_MIN_SIZE) { ntb->b2b_off = 0; - ntb->mw_count--; } else { device_printf(ntb->device, "B2B bar size is too small!\n"); @@ -1999,7 +2001,7 @@ ntb_sysctl_init(struct ntb_softc *ntb) "A", "Link status"); SYSCTL_ADD_U8(ctx, tree_par, OID_AUTO, "mw_count", CTLFLAG_RD, - &ntb->mw_count, 0, "MW count (excl. non-shared B2B register BAR)"); + &ntb->mw_count, 0, "MW count"); SYSCTL_ADD_U8(ctx, tree_par, OID_AUTO, "spad_count", CTLFLAG_RD, &ntb->spad_count, 0, "Scratchpad count"); SYSCTL_ADD_U8(ctx, tree_par, OID_AUTO, "db_count", CTLFLAG_RD, @@ -2341,6 +2343,16 @@ sysctl_handle_register(SYSCTL_HANDLER_ARGS) return (EINVAL); } +static unsigned +ntb_user_mw_to_idx(struct ntb_softc *ntb, unsigned uidx) +{ + + if (ntb->b2b_mw_idx != B2B_MW_DISABLED && ntb->b2b_off == 0 && + uidx >= ntb->b2b_mw_idx) + return (uidx + 1); + return (uidx); +} + /* * Public API to the rest of the OS */ @@ -2361,10 +2373,18 @@ ntb_get_max_spads(struct ntb_softc *ntb) return (ntb->spad_count); } +/* + * ntb_mw_count() - Get the number of memory windows available for KPI + * consumers. + * + * (Excludes any MW wholly reserved for register access.) + */ uint8_t ntb_mw_count(struct ntb_softc *ntb) { + if (ntb->b2b_mw_idx != B2B_MW_DISABLED && ntb->b2b_off == 0) + return (ntb->mw_count - 1); return (ntb->mw_count); } @@ -2495,6 +2515,7 @@ ntb_mw_get_range(struct ntb_softc *ntb, unsigned mw_idx, vm_paddr_t *base, if (mw_idx >= ntb_mw_count(ntb)) return (EINVAL); + mw_idx = ntb_user_mw_to_idx(ntb, mw_idx); bar_num = ntb_mw_to_bar(ntb, mw_idx); bar = &ntb->bar_info[bar_num]; @@ -2553,6 +2574,7 @@ ntb_mw_set_trans(struct ntb_softc *ntb, unsigned idx, bus_addr_t addr, if (idx >= ntb_mw_count(ntb)) return (EINVAL); + idx = ntb_user_mw_to_idx(ntb, idx); bar_num = ntb_mw_to_bar(ntb, idx); bar = &ntb->bar_info[bar_num]; @@ -2658,6 +2680,7 @@ ntb_mw_get_wc(struct ntb_softc *ntb, unsigned idx, bool *wc) if (idx >= ntb_mw_count(ntb)) return (EINVAL); + idx = ntb_user_mw_to_idx(ntb, idx); bar = &ntb->bar_info[ntb_mw_to_bar(ntb, idx)]; *wc = bar->mapped_wc; @@ -2676,13 +2699,21 @@ ntb_mw_get_wc(struct ntb_softc *ntb, unsigned idx, bool *wc) int ntb_mw_set_wc(struct ntb_softc *ntb, unsigned idx, bool wc) { - struct ntb_pci_bar_info *bar; - vm_memattr_t attr; - int rc; if (idx >= ntb_mw_count(ntb)) return (EINVAL); + idx = ntb_user_mw_to_idx(ntb, idx); + return (ntb_mw_set_wc_internal(ntb, idx, wc)); +} + +static int +ntb_mw_set_wc_internal(struct ntb_softc *ntb, unsigned idx, bool wc) +{ + struct ntb_pci_bar_info *bar; + vm_memattr_t attr; + int rc; + bar = &ntb->bar_info[ntb_mw_to_bar(ntb, idx)]; if (bar->mapped_wc == wc) return (0); @@ -2741,9 +2772,6 @@ ntb_get_peer_db_addr(struct ntb_softc *ntb, vm_size_t *sz_out) bar = &ntb->bar_info[NTB_CONFIG_BAR]; regoff = ntb->peer_reg->db_bell; } else { - KASSERT((HAS_FEATURE(NTB_SPLIT_BAR) && ntb->mw_count == 2) || - (!HAS_FEATURE(NTB_SPLIT_BAR) && ntb->mw_count == 1), - ("mw_count invalid after setup")); KASSERT(ntb->b2b_mw_idx != B2B_MW_DISABLED, ("invalid b2b idx")); |