diff --git a/sys/arm/broadcom/bcm2835/bcm2835_rng.c b/sys/arm/broadcom/bcm2835/bcm2835_rng.c index 5067120c80fa..bc13bdba2238 100644 --- a/sys/arm/broadcom/bcm2835/bcm2835_rng.c +++ b/sys/arm/broadcom/bcm2835/bcm2835_rng.c @@ -1,556 +1,553 @@ /* * Copyright (c) 2015, 2016, Stephen J. Kiernan * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include - -__FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static device_attach_t bcm2835_rng_attach; static device_detach_t bcm2835_rng_detach; static device_probe_t bcm2835_rng_probe; #define RNG_CTRL 0x00 /* RNG Control Register */ #define RNG_COMBLK1_OSC 0x003f0000 /* Combiner Blk 1 Oscillator */ #define RNG_COMBLK1_OSC_SHIFT 16 #define RNG_COMBLK2_OSC 0x0fc00000 /* Combiner Blk 2 Oscillator */ #define RNG_COMBLK2_OSC_SHIFT 22 #define RNG_JCLK_BYP_DIV_CNT 0x0000ff00 /* Jitter clk bypass divider count */ #define RNG_JCLK_BYP_DIV_CNT_SHIFT 8 #define RNG_JCLK_BYP_SRC 0x00000020 /* Jitter clk bypass source */ #define RNG_JCLK_BYP_SEL 0x00000010 /* Jitter clk bypass select */ #define RNG_RBG2X 0x00000002 /* RBG 2X SPEED */ #define RNG_RBGEN_BIT 0x00000001 /* Enable RNG bit */ #define BCM2835_RNG_STATUS 0x04 /* BCM2835 RNG status register */ #define BCM2838_RNG_STATUS 0x18 /* BCM2838 RNG status register */ #define BCM2838_RNG_COUNT 0x24 /* How many values available */ #define BCM2838_COUNT_VAL_MASK 0x000000ff #define BCM2835_RND_VAL_SHIFT 24 /* Shift for valid words */ #define BCM2835_RND_VAL_MASK 0x000000ff /* Number valid words mask */ #define BCM2835_RND_VAL_WARM_CNT 0x40000 /* RNG Warm Up count */ #define BCM2835_RND_WARM_CNT 0xfffff /* RNG Warm Up Count mask */ #define BCM2835_RNG_DATA 0x08 /* RNG Data Register */ #define BCM2838_RNG_DATA 0x20 #define RNG_FF_THRES 0x0c #define RNG_FF_THRES_MASK 0x0000001f #define BCM2835_RNG_INT_MASK 0x10 #define BCM2835_RNG_INT_OFF_BIT 0x00000001 #define RNG_FF_DEFAULT 0x10 /* FIFO threshold default */ #define RNG_FIFO_WORDS (RNG_FF_DEFAULT / sizeof(uint32_t)) #define RNG_NUM_OSCILLATORS 6 #define RNG_STALL_COUNT_DEFAULT 10 #define RNG_CALLOUT_TICKS (hz * 4) struct bcm_rng_conf { bus_size_t control_reg; bus_size_t status_reg; bus_size_t count_reg; bus_size_t data_reg; bus_size_t intr_mask_reg; uint32_t intr_disable_bit; uint32_t count_value_shift; uint32_t count_value_mask; uint32_t warmup_count; bool allow_2x_mode; bool can_diagnose; /* XXX diag regs */ }; static const struct bcm_rng_conf bcm2835_rng_conf = { .control_reg = RNG_CTRL, .status_reg = BCM2835_RNG_STATUS, .count_reg = BCM2835_RNG_STATUS, /* Same register */ .data_reg = BCM2835_RNG_DATA, .intr_mask_reg = BCM2835_RNG_INT_MASK, .intr_disable_bit = BCM2835_RNG_INT_OFF_BIT, .count_value_shift = BCM2835_RND_VAL_SHIFT, .count_value_mask = BCM2835_RND_VAL_MASK, .warmup_count = BCM2835_RND_VAL_WARM_CNT, .allow_2x_mode = true, .can_diagnose = true }; static const struct bcm_rng_conf bcm2838_rng_conf = { .control_reg = RNG_CTRL, .status_reg = BCM2838_RNG_STATUS, .count_reg = BCM2838_RNG_COUNT, .data_reg = BCM2838_RNG_DATA, .intr_mask_reg = 0, .intr_disable_bit = 0, .count_value_shift = 0, .count_value_mask = BCM2838_COUNT_VAL_MASK, .warmup_count = 0, .allow_2x_mode = false, .can_diagnose = false }; struct bcm2835_rng_softc { device_t sc_dev; struct resource * sc_mem_res; struct resource * sc_irq_res; void * sc_intr_hdl; struct bcm_rng_conf const* conf; uint32_t sc_buf[RNG_FIFO_WORDS]; struct callout sc_rngto; int sc_stall_count; int sc_rbg2x; long sc_underrun; }; static struct ofw_compat_data compat_data[] = { {"broadcom,bcm2835-rng", (uintptr_t)&bcm2835_rng_conf}, {"brcm,bcm2835-rng", (uintptr_t)&bcm2835_rng_conf}, {"brcm,bcm2711-rng200", (uintptr_t)&bcm2838_rng_conf}, {"brcm,bcm2838-rng", (uintptr_t)&bcm2838_rng_conf}, {"brcm,bcm2838-rng200", (uintptr_t)&bcm2838_rng_conf}, {"brcm,bcm7211-rng", (uintptr_t)&bcm2838_rng_conf}, {"brcm,bcm7278-rng", (uintptr_t)&bcm2838_rng_conf}, {"brcm,iproc-rng200", (uintptr_t)&bcm2838_rng_conf}, {NULL, 0} }; static __inline void bcm2835_rng_stat_inc_underrun(struct bcm2835_rng_softc *sc) { atomic_add_long(&sc->sc_underrun, 1); } static __inline uint32_t bcm2835_rng_read4(struct bcm2835_rng_softc *sc, bus_size_t off) { return bus_read_4(sc->sc_mem_res, off); } static __inline void bcm2835_rng_read_multi4(struct bcm2835_rng_softc *sc, bus_size_t off, uint32_t *datap, bus_size_t count) { bus_read_multi_4(sc->sc_mem_res, off, datap, count); } static __inline void bcm2835_rng_write4(struct bcm2835_rng_softc *sc, bus_size_t off, uint32_t val) { bus_write_4(sc->sc_mem_res, off, val); } static void bcm2835_rng_dump_registers(struct bcm2835_rng_softc *sc, struct sbuf *sbp) { uint32_t comblk2_osc, comblk1_osc, jclk_byp_div, val; int i; if (!sc->conf->can_diagnose) /* Not implemented. */ return; /* Display RNG control register contents */ val = bcm2835_rng_read4(sc, sc->conf->control_reg); sbuf_printf(sbp, "RNG_CTRL (%08x)\n", val); comblk2_osc = (val & RNG_COMBLK2_OSC) >> RNG_COMBLK2_OSC_SHIFT; sbuf_printf(sbp, " RNG_COMBLK2_OSC (%02x)\n", comblk2_osc); for (i = 0; i < RNG_NUM_OSCILLATORS; i++) if ((comblk2_osc & (1 << i)) == 0) sbuf_printf(sbp, " Oscillator %d enabled\n", i + 1); comblk1_osc = (val & RNG_COMBLK1_OSC) >> RNG_COMBLK1_OSC_SHIFT; sbuf_printf(sbp, " RNG_COMBLK1_OSC (%02x)\n", comblk1_osc); for (i = 0; i < RNG_NUM_OSCILLATORS; i++) if ((comblk1_osc & (1 << i)) == 0) sbuf_printf(sbp, " Oscillator %d enabled\n", i + 1); jclk_byp_div = (val & RNG_JCLK_BYP_DIV_CNT) >> RNG_JCLK_BYP_DIV_CNT_SHIFT; sbuf_printf(sbp, " RNG_JCLK_BYP_DIV_CNT (%02x)\n APB clock frequency / %d\n", jclk_byp_div, 2 * (jclk_byp_div + 1)); sbuf_printf(sbp, " RNG_JCLK_BYP_SRC:\n %s\n", (val & RNG_JCLK_BYP_SRC) ? "Use divided down APB clock" : "Use RNG clock (APB clock)"); sbuf_printf(sbp, " RNG_JCLK_BYP_SEL:\n %s\n", (val & RNG_JCLK_BYP_SEL) ? "Bypass internal jitter clock" : "Use internal jitter clock"); if ((val & RNG_RBG2X) != 0) sbuf_cat(sbp, " RNG_RBG2X: RNG 2X SPEED enabled\n"); if ((val & RNG_RBGEN_BIT) != 0) sbuf_cat(sbp, " RNG_RBGEN_BIT: RBG enabled\n"); /* Display RNG status register contents */ val = bcm2835_rng_read4(sc, sc->conf->status_reg); sbuf_printf(sbp, "RNG_CTRL (%08x)\n", val); sbuf_printf(sbp, " RND_VAL: %02x\n", (val >> sc->conf->count_value_shift) & sc->conf->count_value_mask); sbuf_printf(sbp, " RND_WARM_CNT: %05x\n", val & sc->conf->warmup_count); /* Display FIFO threshold register contents */ val = bcm2835_rng_read4(sc, RNG_FF_THRES); sbuf_printf(sbp, "RNG_FF_THRES: %05x\n", val & RNG_FF_THRES_MASK); /* Display interrupt mask register contents */ val = bcm2835_rng_read4(sc, sc->conf->intr_mask_reg); sbuf_printf(sbp, "RNG_INT_MASK: interrupt %s\n", ((val & sc->conf->intr_disable_bit) != 0) ? "disabled" : "enabled"); } static void bcm2835_rng_disable_intr(struct bcm2835_rng_softc *sc) { uint32_t mask; /* Set the interrupt off bit in the interrupt mask register */ mask = bcm2835_rng_read4(sc, sc->conf->intr_mask_reg); mask |= sc->conf->intr_disable_bit; bcm2835_rng_write4(sc, sc->conf->intr_mask_reg, mask); } static void bcm2835_rng_start(struct bcm2835_rng_softc *sc) { uint32_t ctrl; /* Disable the interrupt */ if (sc->conf->intr_mask_reg) bcm2835_rng_disable_intr(sc); /* Set the warmup count */ if (sc->conf->warmup_count > 0) bcm2835_rng_write4(sc, sc->conf->status_reg, sc->conf->warmup_count); /* Enable the RNG */ ctrl = bcm2835_rng_read4(sc, sc->conf->control_reg); ctrl |= RNG_RBGEN_BIT; if (sc->sc_rbg2x && sc->conf->allow_2x_mode) ctrl |= RNG_RBG2X; bcm2835_rng_write4(sc, sc->conf->control_reg, ctrl); } static void bcm2835_rng_stop(struct bcm2835_rng_softc *sc) { uint32_t ctrl; /* Disable the RNG */ ctrl = bcm2835_rng_read4(sc, sc->conf->control_reg); ctrl &= ~RNG_RBGEN_BIT; bcm2835_rng_write4(sc, sc->conf->control_reg, ctrl); } static void bcm2835_rng_enqueue_harvest(struct bcm2835_rng_softc *sc, uint32_t nread) { char *sc_buf_chunk; uint32_t chunk_size; uint32_t cnt; chunk_size = sizeof(((struct harvest_event *)0)->he_entropy); cnt = nread * sizeof(uint32_t); sc_buf_chunk = (void*)sc->sc_buf; while (cnt > 0) { uint32_t size; size = MIN(cnt, chunk_size); random_harvest_queue(sc_buf_chunk, size, RANDOM_PURE_BROADCOM); sc_buf_chunk += size; cnt -= size; } } static void bcm2835_rng_harvest(void *arg) { uint32_t *dest; uint32_t hwcount; u_int cnt, nread, num_avail, num_words; int seen_underrun, num_stalls; struct bcm2835_rng_softc *sc = arg; dest = sc->sc_buf; nread = num_words = 0; seen_underrun = num_stalls = 0; for (cnt = sizeof(sc->sc_buf) / sizeof(uint32_t); cnt > 0; cnt -= num_words) { /* Read count register to find out how many words available */ hwcount = bcm2835_rng_read4(sc, sc->conf->count_reg); num_avail = (hwcount >> sc->conf->count_value_shift) & sc->conf->count_value_mask; /* If we have none... */ if (num_avail == 0) { bcm2835_rng_stat_inc_underrun(sc); if (++seen_underrun >= sc->sc_stall_count) { if (num_stalls++ > 0) { device_printf(sc->sc_dev, "RNG stalled, disabling device\n"); bcm2835_rng_stop(sc); break; } else { device_printf(sc->sc_dev, "Too many underruns, resetting\n"); bcm2835_rng_stop(sc); bcm2835_rng_start(sc); seen_underrun = 0; } } /* Try again */ continue; } CTR2(KTR_DEV, "%s: %d words available in RNG FIFO", device_get_nameunit(sc->sc_dev), num_avail); /* Pull MIN(num_avail, cnt) words from the FIFO */ num_words = (num_avail > cnt) ? cnt : num_avail; bcm2835_rng_read_multi4(sc, sc->conf->data_reg, dest, num_words); dest += num_words; nread += num_words; } bcm2835_rng_enqueue_harvest(sc, nread); callout_reset(&sc->sc_rngto, RNG_CALLOUT_TICKS, bcm2835_rng_harvest, sc); } static int sysctl_bcm2835_rng_2xspeed(SYSCTL_HANDLER_ARGS) { struct bcm2835_rng_softc *sc = arg1; int error, rbg2x; rbg2x = sc->sc_rbg2x; error = sysctl_handle_int(oidp, &rbg2x, 0, req); if (error) return (error); if (req->newptr == NULL) return (error); if (rbg2x == sc->sc_rbg2x) return (0); /* Reset the RNG */ bcm2835_rng_stop(sc); sc->sc_rbg2x = rbg2x; bcm2835_rng_start(sc); return (0); } #ifdef BCM2835_RNG_DEBUG_REGISTERS static int sysctl_bcm2835_rng_dump(SYSCTL_HANDLER_ARGS) { struct sbuf sb; struct bcm2835_rng_softc *sc = arg1; int error; error = sysctl_wire_old_buffer(req, 0); if (error != 0) return (error); sbuf_new_for_sysctl(&sb, NULL, 128, req); bcm2835_rng_dump_registers(sc, &sb); error = sbuf_finish(&sb); sbuf_delete(&sb); return (error); } #endif static int bcm2835_rng_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Broadcom BCM2835/BCM2838 RNG"); return (BUS_PROBE_DEFAULT); } static int bcm2835_rng_attach(device_t dev) { struct bcm2835_rng_softc *sc; struct sysctl_ctx_list *sysctl_ctx; struct sysctl_oid *sysctl_tree; int error, rid; error = 0; sc = device_get_softc(dev); sc->sc_dev = dev; sc->conf = (void const*)ofw_bus_search_compatible(dev, compat_data)->ocd_data; KASSERT(sc->conf != NULL, ("bcm2835_rng_attach: sc->conf == NULL")); sc->sc_stall_count = RNG_STALL_COUNT_DEFAULT; /* Initialize callout */ callout_init(&sc->sc_rngto, CALLOUT_MPSAFE); TUNABLE_INT_FETCH("bcmrng.stall_count", &sc->sc_stall_count); if (sc->conf->allow_2x_mode) TUNABLE_INT_FETCH("bcmrng.2xspeed", &sc->sc_rbg2x); /* Allocate memory resources */ rid = 0; sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->sc_mem_res == NULL) { bcm2835_rng_detach(dev); return (ENXIO); } /* Start the RNG */ bcm2835_rng_start(sc); /* Dump the registers if booting verbose */ if (bootverbose) { struct sbuf sb; (void) sbuf_new(&sb, NULL, 256, SBUF_AUTOEXTEND | SBUF_INCLUDENUL); bcm2835_rng_dump_registers(sc, &sb); sbuf_trim(&sb); error = sbuf_finish(&sb); if (error == 0) device_printf(dev, "%s", sbuf_data(&sb)); sbuf_delete(&sb); } sysctl_ctx = device_get_sysctl_ctx(dev); sysctl_tree = device_get_sysctl_tree(dev); SYSCTL_ADD_LONG(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO, "underrun", CTLFLAG_RD, &sc->sc_underrun, "Number of FIFO underruns"); if (sc->conf->allow_2x_mode) SYSCTL_ADD_PROC(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO, "2xspeed", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, sysctl_bcm2835_rng_2xspeed, "I", "Enable RBG 2X SPEED"); SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO, "stall_count", CTLFLAG_RW, &sc->sc_stall_count, RNG_STALL_COUNT_DEFAULT, "Number of underruns to assume RNG stall"); #ifdef BCM2835_RNG_DEBUG_REGISTERS SYSCTL_ADD_PROC(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), OID_AUTO, "dumpregs", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, 0, sysctl_bcm2835_rng_dump, "S", "Dump RNG registers"); #endif /* * Schedule the initial harvesting one second from now, which should give the * hardware RNG plenty of time to generate the first random bytes. */ callout_reset(&sc->sc_rngto, hz, bcm2835_rng_harvest, sc); return (0); } static int bcm2835_rng_detach(device_t dev) { struct bcm2835_rng_softc *sc; sc = device_get_softc(dev); /* Stop the RNG */ bcm2835_rng_stop(sc); /* Drain the callout it */ callout_drain(&sc->sc_rngto); /* Release memory resource */ if (sc->sc_mem_res != NULL) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); return (0); } static device_method_t bcm2835_rng_methods[] = { /* Device interface */ DEVMETHOD(device_probe, bcm2835_rng_probe), DEVMETHOD(device_attach, bcm2835_rng_attach), DEVMETHOD(device_detach, bcm2835_rng_detach), DEVMETHOD_END }; static driver_t bcm2835_rng_driver = { "bcmrng", bcm2835_rng_methods, sizeof(struct bcm2835_rng_softc) }; DRIVER_MODULE(bcm2835_rng, simplebus, bcm2835_rng_driver, 0, 0); DRIVER_MODULE(bcm2835_rng, ofwbus, bcm2835_rng_driver, 0, 0); MODULE_VERSION(bcm2835_rng, 1); MODULE_DEPEND(bcm2835_rng, randomdev, 1, 1, 1); diff --git a/sys/arm/freescale/imx/imx6_ahci.c b/sys/arm/freescale/imx/imx6_ahci.c index 9a789049b984..ad46452903a4 100644 --- a/sys/arm/freescale/imx/imx6_ahci.c +++ b/sys/arm/freescale/imx/imx6_ahci.c @@ -1,363 +1,360 @@ /*- * Copyright (c) 2017 Rogiel Sulzbach * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include - -__FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #define SATA_TIMER1MS 0x000000e0 #define SATA_P0PHYCR 0x00000178 #define SATA_P0PHYCR_CR_READ (1 << 19) #define SATA_P0PHYCR_CR_WRITE (1 << 18) #define SATA_P0PHYCR_CR_CAP_DATA (1 << 17) #define SATA_P0PHYCR_CR_CAP_ADDR (1 << 16) #define SATA_P0PHYCR_CR_DATA_IN(v) ((v) & 0xffff) #define SATA_P0PHYSR 0x0000017c #define SATA_P0PHYSR_CR_ACK (1 << 18) #define SATA_P0PHYSR_CR_DATA_OUT(v) ((v) & 0xffff) /* phy registers */ #define SATA_PHY_CLOCK_RESET 0x7f3f #define SATA_PHY_CLOCK_RESET_RST (1 << 0) #define SATA_PHY_LANE0_OUT_STAT 0x2003 #define SATA_PHY_LANE0_OUT_STAT_RX_PLL_STATE (1 << 1) static struct ofw_compat_data compat_data[] = { {"fsl,imx6q-ahci", true}, {NULL, false} }; static int imx6_ahci_phy_ctrl(struct ahci_controller* sc, uint32_t bitmask, bool on) { uint32_t v; int timeout; bool state; v = ATA_INL(sc->r_mem, SATA_P0PHYCR); if (on) { v |= bitmask; } else { v &= ~bitmask; } ATA_OUTL(sc->r_mem, SATA_P0PHYCR, v); for (timeout = 5000; timeout > 0; --timeout) { v = ATA_INL(sc->r_mem, SATA_P0PHYSR); state = (v & SATA_P0PHYSR_CR_ACK) == SATA_P0PHYSR_CR_ACK; if(state == on) { break; } DELAY(100); } if (timeout > 0) { return (0); } return (ETIMEDOUT); } static int imx6_ahci_phy_addr(struct ahci_controller* sc, uint32_t addr) { int error; DELAY(100); ATA_OUTL(sc->r_mem, SATA_P0PHYCR, addr); error = imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_CAP_ADDR, true); if (error != 0) { device_printf(sc->dev, "%s: timeout on SATA_P0PHYCR_CR_CAP_ADDR=1\n", __FUNCTION__); return (error); } error = imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_CAP_ADDR, false); if (error != 0) { device_printf(sc->dev, "%s: timeout on SATA_P0PHYCR_CR_CAP_ADDR=0\n", __FUNCTION__); return (error); } return (0); } static int imx6_ahci_phy_write(struct ahci_controller* sc, uint32_t addr, uint16_t data) { int error; error = imx6_ahci_phy_addr(sc, addr); if (error != 0) { device_printf(sc->dev, "%s: error on imx6_ahci_phy_addr\n", __FUNCTION__); return (error); } ATA_OUTL(sc->r_mem, SATA_P0PHYCR, data); error = imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_CAP_DATA, true); if (error != 0) { device_printf(sc->dev, "%s: error on SATA_P0PHYCR_CR_CAP_DATA=1\n", __FUNCTION__); return (error); } if (imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_CAP_DATA, false) != 0) { device_printf(sc->dev, "%s: error on SATA_P0PHYCR_CR_CAP_DATA=0\n", __FUNCTION__); return (error); } if ((addr == SATA_PHY_CLOCK_RESET) && data) { /* we can't check ACK after RESET */ ATA_OUTL(sc->r_mem, SATA_P0PHYCR, SATA_P0PHYCR_CR_DATA_IN(data) | SATA_P0PHYCR_CR_WRITE); return (0); } error = imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_WRITE, true); if (error != 0) { device_printf(sc->dev, "%s: error on SATA_P0PHYCR_CR_WRITE=1\n", __FUNCTION__); return (error); } error = imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_WRITE, false); if (error != 0) { device_printf(sc->dev, "%s: error on SATA_P0PHYCR_CR_WRITE=0\n", __FUNCTION__); return (error); } return (0); } static int imx6_ahci_phy_read(struct ahci_controller* sc, uint32_t addr, uint16_t* val) { int error; uint32_t v; error = imx6_ahci_phy_addr(sc, addr); if (error != 0) { device_printf(sc->dev, "%s: error on imx6_ahci_phy_addr\n", __FUNCTION__); return (error); } error = imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_READ, true); if (error != 0) { device_printf(sc->dev, "%s: error on SATA_P0PHYCR_CR_READ=1\n", __FUNCTION__); return (error); } v = ATA_INL(sc->r_mem, SATA_P0PHYSR); error = imx6_ahci_phy_ctrl(sc, SATA_P0PHYCR_CR_READ, false); if (error != 0) { device_printf(sc->dev, "%s: error on SATA_P0PHYCR_CR_READ=0\n", __FUNCTION__); return (error); } *val = SATA_P0PHYSR_CR_DATA_OUT(v); return (0); } static int imx6_ahci_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) { return (ENXIO); } if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) { return (ENXIO); } device_set_desc(dev, "i.MX6 Integrated AHCI controller"); return (BUS_PROBE_DEFAULT); } static int imx6_ahci_attach(device_t dev) { struct ahci_controller* ctlr; uint16_t pllstat; uint32_t v; int error, timeout; ctlr = device_get_softc(dev); /* Power up the controller and phy. */ error = imx6_ccm_sata_enable(); if (error != 0) { device_printf(dev, "error enabling controller and phy\n"); return (error); } ctlr->vendorid = 0; ctlr->deviceid = 0; ctlr->subvendorid = 0; ctlr->subdeviceid = 0; ctlr->numirqs = 1; ctlr->r_rid = 0; if ((ctlr->r_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &ctlr->r_rid, RF_ACTIVE)) == NULL) { return (ENXIO); } v = imx_iomux_gpr_get(IOMUX_GPR13); /* Clear out existing values; these numbers are bitmasks. */ v &= ~(IOMUX_GPR13_SATA_PHY_8(7) | IOMUX_GPR13_SATA_PHY_7(0x1f) | IOMUX_GPR13_SATA_PHY_6(7) | IOMUX_GPR13_SATA_SPEED(1) | IOMUX_GPR13_SATA_PHY_5(1) | IOMUX_GPR13_SATA_PHY_4(7) | IOMUX_GPR13_SATA_PHY_3(0xf) | IOMUX_GPR13_SATA_PHY_2(0x1f) | IOMUX_GPR13_SATA_PHY_1(1) | IOMUX_GPR13_SATA_PHY_0(1)); /* setting */ v |= IOMUX_GPR13_SATA_PHY_8(5) | /* Rx 3.0db */ IOMUX_GPR13_SATA_PHY_7(0x12) | /* Rx SATA2m */ IOMUX_GPR13_SATA_PHY_6(3) | /* Rx DPLL mode */ IOMUX_GPR13_SATA_SPEED(1) | /* 3.0GHz */ IOMUX_GPR13_SATA_PHY_5(0) | /* SpreadSpectram */ IOMUX_GPR13_SATA_PHY_4(4) | /* Tx Attenuation 9/16 */ IOMUX_GPR13_SATA_PHY_3(0) | /* Tx Boost 0db */ IOMUX_GPR13_SATA_PHY_2(0x11) | /* Tx Level 1.104V */ IOMUX_GPR13_SATA_PHY_1(1); /* PLL clock enable */ imx_iomux_gpr_set(IOMUX_GPR13, v); /* phy reset */ error = imx6_ahci_phy_write(ctlr, SATA_PHY_CLOCK_RESET, SATA_PHY_CLOCK_RESET_RST); if (error != 0) { device_printf(dev, "cannot reset PHY\n"); goto fail; } for (timeout = 50; timeout > 0; --timeout) { DELAY(100); error = imx6_ahci_phy_read(ctlr, SATA_PHY_LANE0_OUT_STAT, &pllstat); if (error != 0) { device_printf(dev, "cannot read LANE0 status\n"); goto fail; } if (pllstat & SATA_PHY_LANE0_OUT_STAT_RX_PLL_STATE) { break; } } if (timeout <= 0) { device_printf(dev, "time out reading LANE0 status\n"); error = ETIMEDOUT; goto fail; } /* Support Staggered Spin-up */ v = ATA_INL(ctlr->r_mem, AHCI_CAP); ATA_OUTL(ctlr->r_mem, AHCI_CAP, v | AHCI_CAP_SSS); /* Ports Implemented. must set 1 */ v = ATA_INL(ctlr->r_mem, AHCI_PI); ATA_OUTL(ctlr->r_mem, AHCI_PI, v | (1 << 0)); /* set 1ms-timer = AHB clock / 1000 */ ATA_OUTL(ctlr->r_mem, SATA_TIMER1MS, imx_ccm_ahb_hz() / 1000); /* * Note: ahci_attach will release ctlr->r_mem on errors automatically */ return (ahci_attach(dev)); fail: bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem); return (error); } static int imx6_ahci_detach(device_t dev) { return (ahci_detach(dev)); } static device_method_t imx6_ahci_ata_methods[] = { /* device probe, attach and detach methods */ DEVMETHOD(device_probe, imx6_ahci_probe), DEVMETHOD(device_attach, imx6_ahci_attach), DEVMETHOD(device_detach, imx6_ahci_detach), /* ahci bus methods */ DEVMETHOD(bus_print_child, ahci_print_child), DEVMETHOD(bus_alloc_resource, ahci_alloc_resource), DEVMETHOD(bus_release_resource, ahci_release_resource), DEVMETHOD(bus_setup_intr, ahci_setup_intr), DEVMETHOD(bus_teardown_intr, ahci_teardown_intr), DEVMETHOD(bus_child_location, ahci_child_location), DEVMETHOD_END }; static driver_t ahci_ata_driver = { "ahci", imx6_ahci_ata_methods, sizeof(struct ahci_controller) }; DRIVER_MODULE(imx6_ahci, simplebus, ahci_ata_driver, 0, 0); MODULE_DEPEND(imx6_ahci, ahci, 1, 1, 1); SIMPLEBUS_PNP_INFO(compat_data) diff --git a/sys/dev/gpio/gpiopower.c b/sys/dev/gpio/gpiopower.c index 28ca9ad36fb6..5c369396813f 100644 --- a/sys/dev/gpio/gpiopower.c +++ b/sys/dev/gpio/gpiopower.c @@ -1,134 +1,131 @@ /*- * Copyright (c) 2016 Justin Hibbits * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include - -__FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include struct gpiopower_softc { gpio_pin_t sc_pin; int sc_rbmask; }; static void gpiopower_assert(device_t dev, int howto); static int gpiopower_probe(device_t dev) { if (ofw_bus_is_compatible(dev, "gpio-poweroff")) { device_set_desc(dev, "GPIO poweroff control"); return (0); } else if (ofw_bus_is_compatible(dev, "gpio-restart")) { device_set_desc(dev, "GPIO restart control"); return (0); } return (ENXIO); } static int gpiopower_attach(device_t dev) { struct gpiopower_softc *sc; phandle_t node; sc = device_get_softc(dev); if ((node = ofw_bus_get_node(dev)) == -1) return (ENXIO); if (ofw_gpiobus_parse_gpios(dev, "gpios", &sc->sc_pin) <= 0) { device_printf(dev, "failed to map GPIO pin\n"); return (ENXIO); } if (ofw_bus_is_compatible(dev, "gpio-poweroff")) sc->sc_rbmask = RB_HALT | RB_POWEROFF; else sc->sc_rbmask = 0; EVENTHANDLER_REGISTER(shutdown_final, gpiopower_assert, dev, SHUTDOWN_PRI_LAST); gpio_pin_setflags(sc->sc_pin, GPIO_PIN_OUTPUT); return (0); } static void gpiopower_assert(device_t dev, int howto) { struct gpiopower_softc *sc; int do_assert; sc = device_get_softc(dev); do_assert = sc->sc_rbmask ? (sc->sc_rbmask & howto) : ((howto & RB_HALT) == 0); if (!do_assert) return; if (howto & RB_POWEROFF) device_printf(dev, "powering system off\n"); else if ((howto & RB_HALT) == 0) device_printf(dev, "resetting system\n"); else return; gpio_pin_set_active(sc->sc_pin, true); /* Wait a second for it to trip */ DELAY(10000000); } static device_method_t gpiopower_methods[] = { /* Device interface */ DEVMETHOD(device_probe, gpiopower_probe), DEVMETHOD(device_attach, gpiopower_attach), DEVMETHOD_END }; static driver_t gpiopower_driver = { "gpiopower", gpiopower_methods, sizeof(struct gpiopower_softc) }; DRIVER_MODULE(gpiopower, simplebus, gpiopower_driver, 0, 0); MODULE_DEPEND(gpiopower, gpiobus, 1, 1, 1); diff --git a/sys/dev/videomode/vesagtf.c b/sys/dev/videomode/vesagtf.c index c02c421c14ac..077cf7d1a0ea 100644 --- a/sys/dev/videomode/vesagtf.c +++ b/sys/dev/videomode/vesagtf.c @@ -1,699 +1,696 @@ /* $NetBSD: vesagtf.c,v 1.2 2013/09/15 15:56:07 martin Exp $ */ /*- * Copyright (c) 2006 Itronix Inc. * All rights reserved. * * Written by Garrett D'Amore for Itronix Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of Itronix Inc. may not be used to endorse * or promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * This was derived from a userland GTF program supplied by NVIDIA. * NVIDIA's original boilerplate follows. * * Note that I have heavily modified the program for use in the EDID * kernel code for NetBSD, including removing the use of floating * point operations and making significant adjustments to minimize * error propagation while operating with integer only math. * * This has required the use of 64-bit integers in a few places, but * the upshot is that for a calculation of 1920x1200x85 (as an * example), the error deviates by only ~.004% relative to the * floating point version. This error is *well* within VESA * tolerances. */ /* * Copyright (c) 2001, Andy Ritger aritger@nvidia.com * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * o Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * o Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * o Neither the name of NVIDIA nor the names of its contributors * may be used to endorse or promote products derived from this * software without specific prior written permission. * * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * * * This program is based on the Generalized Timing Formula(GTF TM) * Standard Version: 1.0, Revision: 1.0 * * The GTF Document contains the following Copyright information: * * Copyright (c) 1994, 1995, 1996 - Video Electronics Standards * Association. Duplication of this document within VESA member * companies for review purposes is permitted. All other rights * reserved. * * While every precaution has been taken in the preparation * of this standard, the Video Electronics Standards Association and * its contributors assume no responsibility for errors or omissions, * and make no warranties, expressed or implied, of functionality * of suitability for any purpose. The sample code contained within * this standard may be used without restriction. * * * * The GTF EXCEL(TM) SPREADSHEET, a sample (and the definitive) * implementation of the GTF Timing Standard, is available at: * * ftp://ftp.vesa.org/pub/GTF/GTF_V1R1.xls * * * * This program takes a desired resolution and vertical refresh rate, * and computes mode timings according to the GTF Timing Standard. * These mode timings can then be formatted as an XFree86 modeline * or a mode description for use by fbset(8). * * * * NOTES: * * The GTF allows for computation of "margins" (the visible border * surrounding the addressable video); on most non-overscan type * systems, the margin period is zero. I've implemented the margin * computations but not enabled it because 1) I don't really have * any experience with this, and 2) neither XFree86 modelines nor * fbset fb.modes provide an obvious way for margin timings to be * included in their mode descriptions (needs more investigation). * * The GTF provides for computation of interlaced mode timings; * I've implemented the computations but not enabled them, yet. * I should probably enable and test this at some point. * * * * TODO: * * o Add support for interlaced modes. * * o Implement the other portions of the GTF: compute mode timings * given either the desired pixel clock or the desired horizontal * frequency. * * o It would be nice if this were more general purpose to do things * outside the scope of the GTF: like generate double scan mode * timings, for example. * * o Printing digits to the right of the decimal point when the * digits are 0 annoys me. * * o Error checking. * */ #ifdef _KERNEL -#include - -__FBSDID("$FreeBSD$"); #include #include #include #include #include #else #include #include #include #include "videomode.h" #include "vesagtf.h" void print_xf86_mode(struct videomode *m); #endif #define CELL_GRAN 8 /* assumed character cell granularity */ /* C' and M' are part of the Blanking Duty Cycle computation */ /* * #define C_PRIME (((C - J) * K/256.0) + J) * #define M_PRIME (K/256.0 * M) */ /* * C' and M' multiplied by 256 to give integer math. Make sure to * scale results using these back down, appropriately. */ #define C_PRIME256(p) (((p->C - p->J) * p->K) + (p->J * 256)) #define M_PRIME256(p) (p->K * p->M) #define DIVIDE(x,y) (((x) + ((y) / 2)) / (y)) /* * print_value() - print the result of the named computation; this is * useful when comparing against the GTF EXCEL spreadsheet. */ #ifdef GTFDEBUG static void print_value(int n, const char *name, unsigned val) { printf("%2d: %-27s: %u\n", n, name, val); } #else #define print_value(n, name, val) #endif /* * vert_refresh() - as defined by the GTF Timing Standard, compute the * Stage 1 Parameters using the vertical refresh frequency. In other * words: input a desired resolution and desired refresh rate, and * output the GTF mode timings. * * XXX All the code is in place to compute interlaced modes, but I don't * feel like testing it right now. * * XXX margin computations are implemented but not tested (nor used by * XFree86 of fbset mode descriptions, from what I can tell). */ void vesagtf_mode_params(unsigned h_pixels, unsigned v_lines, unsigned freq, struct vesagtf_params *params, int flags, struct videomode *vmp) { unsigned v_field_rqd; unsigned top_margin; unsigned bottom_margin; unsigned interlace; uint64_t h_period_est; unsigned vsync_plus_bp; unsigned v_back_porch __unused; unsigned total_v_lines; uint64_t v_field_est; uint64_t h_period; unsigned v_field_rate; unsigned v_frame_rate __unused; unsigned left_margin; unsigned right_margin; unsigned total_active_pixels; uint64_t ideal_duty_cycle; unsigned h_blank; unsigned total_pixels; unsigned pixel_freq; unsigned h_sync; unsigned h_front_porch; unsigned v_odd_front_porch_lines; #ifdef GTFDEBUG unsigned h_freq; #endif /* 1. In order to give correct results, the number of horizontal * pixels requested is first processed to ensure that it is divisible * by the character size, by rounding it to the nearest character * cell boundary: * * [H PIXELS RND] = ((ROUND([H PIXELS]/[CELL GRAN RND],0))*[CELLGRAN RND]) */ h_pixels = DIVIDE(h_pixels, CELL_GRAN) * CELL_GRAN; print_value(1, "[H PIXELS RND]", h_pixels); /* 2. If interlace is requested, the number of vertical lines assumed * by the calculation must be halved, as the computation calculates * the number of vertical lines per field. In either case, the * number of lines is rounded to the nearest integer. * * [V LINES RND] = IF([INT RQD?]="y", ROUND([V LINES]/2,0), * ROUND([V LINES],0)) */ v_lines = (flags & VESAGTF_FLAG_ILACE) ? DIVIDE(v_lines, 2) : v_lines; print_value(2, "[V LINES RND]", v_lines); /* 3. Find the frame rate required: * * [V FIELD RATE RQD] = IF([INT RQD?]="y", [I/P FREQ RQD]*2, * [I/P FREQ RQD]) */ v_field_rqd = (flags & VESAGTF_FLAG_ILACE) ? (freq * 2) : (freq); print_value(3, "[V FIELD RATE RQD]", v_field_rqd); /* 4. Find number of lines in Top margin: * 5. Find number of lines in Bottom margin: * * [TOP MARGIN (LINES)] = IF([MARGINS RQD?]="Y", * ROUND(([MARGIN%]/100*[V LINES RND]),0), * 0) * * Ditto for bottom margin. Note that instead of %, we use PPT, which * is parts per thousand. This helps us with integer math. */ top_margin = bottom_margin = (flags & VESAGTF_FLAG_MARGINS) ? DIVIDE(v_lines * params->margin_ppt, 1000) : 0; print_value(4, "[TOP MARGIN (LINES)]", top_margin); print_value(5, "[BOT MARGIN (LINES)]", bottom_margin); /* 6. If interlace is required, then set variable [INTERLACE]=0.5: * * [INTERLACE]=(IF([INT RQD?]="y",0.5,0)) * * To make this integer friendly, we use some special hacks in step * 7 below. Please read those comments to understand why I am using * a whole number of 1.0 instead of 0.5 here. */ interlace = (flags & VESAGTF_FLAG_ILACE) ? 1 : 0; print_value(6, "[2*INTERLACE]", interlace); /* 7. Estimate the Horizontal period * * [H PERIOD EST] = ((1/[V FIELD RATE RQD]) - [MIN VSYNC+BP]/1000000) / * ([V LINES RND] + (2*[TOP MARGIN (LINES)]) + * [MIN PORCH RND]+[INTERLACE]) * 1000000 * * To make it integer friendly, we pre-multiply the 1000000 to get to * usec. This gives us: * * [H PERIOD EST] = ((1000000/[V FIELD RATE RQD]) - [MIN VSYNC+BP]) / * ([V LINES RND] + (2 * [TOP MARGIN (LINES)]) + * [MIN PORCH RND]+[INTERLACE]) * * The other problem is that the interlace value is wrong. To get * the interlace to a whole number, we multiply both the numerator and * divisor by 2, so we can use a value of either 1 or 0 for the interlace * factor. * * This gives us: * * [H PERIOD EST] = ((2*((1000000/[V FIELD RATE RQD]) - [MIN VSYNC+BP])) / * (2*([V LINES RND] + (2*[TOP MARGIN (LINES)]) + * [MIN PORCH RND]) + [2*INTERLACE])) * * Finally we multiply by another 1000, to get value in picosec. * Why picosec? To minimize rounding errors. Gotta love integer * math and error propagation. */ h_period_est = DIVIDE(((DIVIDE(2000000000000ULL, v_field_rqd)) - (2000000 * params->min_vsbp)), ((2 * (v_lines + (2 * top_margin) + params->min_porch)) + interlace)); print_value(7, "[H PERIOD EST (ps)]", h_period_est); /* 8. Find the number of lines in V sync + back porch: * * [V SYNC+BP] = ROUND(([MIN VSYNC+BP]/[H PERIOD EST]),0) * * But recall that h_period_est is in psec. So multiply by 1000000. */ vsync_plus_bp = DIVIDE(params->min_vsbp * 1000000, h_period_est); print_value(8, "[V SYNC+BP]", vsync_plus_bp); /* 9. Find the number of lines in V back porch alone: * * [V BACK PORCH] = [V SYNC+BP] - [V SYNC RND] * * XXX is "[V SYNC RND]" a typo? should be [V SYNC RQD]? */ v_back_porch = vsync_plus_bp - params->vsync_rqd; print_value(9, "[V BACK PORCH]", v_back_porch); /* 10. Find the total number of lines in Vertical field period: * * [TOTAL V LINES] = [V LINES RND] + [TOP MARGIN (LINES)] + * [BOT MARGIN (LINES)] + [V SYNC+BP] + [INTERLACE] + * [MIN PORCH RND] */ total_v_lines = v_lines + top_margin + bottom_margin + vsync_plus_bp + interlace + params->min_porch; print_value(10, "[TOTAL V LINES]", total_v_lines); /* 11. Estimate the Vertical field frequency: * * [V FIELD RATE EST] = 1 / [H PERIOD EST] / [TOTAL V LINES] * 1000000 * * Again, we want to pre multiply by 10^9 to convert for nsec, thereby * making it usable in integer math. * * So we get: * * [V FIELD RATE EST] = 1000000000 / [H PERIOD EST] / [TOTAL V LINES] * * This is all scaled to get the result in uHz. Again, we're trying to * minimize error propagation. */ v_field_est = DIVIDE(DIVIDE(1000000000000000ULL, h_period_est), total_v_lines); print_value(11, "[V FIELD RATE EST(uHz)]", v_field_est); /* 12. Find the actual horizontal period: * * [H PERIOD] = [H PERIOD EST] / ([V FIELD RATE RQD] / [V FIELD RATE EST]) */ h_period = DIVIDE(h_period_est * v_field_est, v_field_rqd * 1000); print_value(12, "[H PERIOD(ps)]", h_period); /* 13. Find the actual Vertical field frequency: * * [V FIELD RATE] = 1 / [H PERIOD] / [TOTAL V LINES] * 1000000 * * And again, we convert to nsec ahead of time, giving us: * * [V FIELD RATE] = 1000000 / [H PERIOD] / [TOTAL V LINES] * * And another rescaling back to mHz. Gotta love it. */ v_field_rate = DIVIDE(1000000000000ULL, h_period * total_v_lines); print_value(13, "[V FIELD RATE]", v_field_rate); /* 14. Find the Vertical frame frequency: * * [V FRAME RATE] = (IF([INT RQD?]="y", [V FIELD RATE]/2, [V FIELD RATE])) * * N.B. that the result here is in mHz. */ v_frame_rate = (flags & VESAGTF_FLAG_ILACE) ? v_field_rate / 2 : v_field_rate; print_value(14, "[V FRAME RATE]", v_frame_rate); /* 15. Find number of pixels in left margin: * 16. Find number of pixels in right margin: * * [LEFT MARGIN (PIXELS)] = (IF( [MARGINS RQD?]="Y", * (ROUND( ([H PIXELS RND] * [MARGIN%] / 100 / * [CELL GRAN RND]),0)) * [CELL GRAN RND], * 0)) * * Again, we deal with margin percentages as PPT (parts per thousand). * And the calculations for left and right are the same. */ left_margin = right_margin = (flags & VESAGTF_FLAG_MARGINS) ? DIVIDE(DIVIDE(h_pixels * params->margin_ppt, 1000), CELL_GRAN) * CELL_GRAN : 0; print_value(15, "[LEFT MARGIN (PIXELS)]", left_margin); print_value(16, "[RIGHT MARGIN (PIXELS)]", right_margin); /* 17. Find total number of active pixels in image and left and right * margins: * * [TOTAL ACTIVE PIXELS] = [H PIXELS RND] + [LEFT MARGIN (PIXELS)] + * [RIGHT MARGIN (PIXELS)] */ total_active_pixels = h_pixels + left_margin + right_margin; print_value(17, "[TOTAL ACTIVE PIXELS]", total_active_pixels); /* 18. Find the ideal blanking duty cycle from the blanking duty cycle * equation: * * [IDEAL DUTY CYCLE] = [C'] - ([M']*[H PERIOD]/1000) * * However, we have modified values for [C'] as [256*C'] and * [M'] as [256*M']. Again the idea here is to get good scaling. * We use 256 as the factor to make the math fast. * * Note that this means that we have to scale it appropriately in * later calculations. * * The ending result is that our ideal_duty_cycle is 256000x larger * than the duty cycle used by VESA. But again, this reduces error * propagation. */ ideal_duty_cycle = ((C_PRIME256(params) * 1000) - (M_PRIME256(params) * h_period / 1000000)); print_value(18, "[IDEAL DUTY CYCLE]", ideal_duty_cycle); /* 19. Find the number of pixels in the blanking time to the nearest * double character cell: * * [H BLANK (PIXELS)] = (ROUND(([TOTAL ACTIVE PIXELS] * * [IDEAL DUTY CYCLE] / * (100-[IDEAL DUTY CYCLE]) / * (2*[CELL GRAN RND])), 0)) * * (2*[CELL GRAN RND]) * * Of course, we adjust to make this rounding work in integer math. */ h_blank = DIVIDE(DIVIDE(total_active_pixels * ideal_duty_cycle, (256000 * 100ULL) - ideal_duty_cycle), 2 * CELL_GRAN) * (2 * CELL_GRAN); print_value(19, "[H BLANK (PIXELS)]", h_blank); /* 20. Find total number of pixels: * * [TOTAL PIXELS] = [TOTAL ACTIVE PIXELS] + [H BLANK (PIXELS)] */ total_pixels = total_active_pixels + h_blank; print_value(20, "[TOTAL PIXELS]", total_pixels); /* 21. Find pixel clock frequency: * * [PIXEL FREQ] = [TOTAL PIXELS] / [H PERIOD] * * We calculate this in Hz rather than MHz, to get a value that * is usable with integer math. Recall that the [H PERIOD] is in * nsec. */ pixel_freq = DIVIDE(total_pixels * 1000000, DIVIDE(h_period, 1000)); print_value(21, "[PIXEL FREQ]", pixel_freq); /* 22. Find horizontal frequency: * * [H FREQ] = 1000 / [H PERIOD] * * I've ifdef'd this out, because we don't need it for any of * our calculations. * We calculate this in Hz rather than kHz, to avoid rounding * errors. Recall that the [H PERIOD] is in usec. */ #ifdef GTFDEBUG h_freq = 1000000000 / h_period; print_value(22, "[H FREQ]", h_freq); #endif /* Stage 1 computations are now complete; I should really pass the results to another function and do the Stage 2 computations, but I only need a few more values so I'll just append the computations here for now */ /* 17. Find the number of pixels in the horizontal sync period: * * [H SYNC (PIXELS)] =(ROUND(([H SYNC%] / 100 * [TOTAL PIXELS] / * [CELL GRAN RND]),0))*[CELL GRAN RND] * * Rewriting for integer math: * * [H SYNC (PIXELS)]=(ROUND((H SYNC%] * [TOTAL PIXELS] / 100 / * [CELL GRAN RND),0))*[CELL GRAN RND] */ h_sync = DIVIDE(((params->hsync_pct * total_pixels) / 100), CELL_GRAN) * CELL_GRAN; print_value(17, "[H SYNC (PIXELS)]", h_sync); /* 18. Find the number of pixels in the horizontal front porch period: * * [H FRONT PORCH (PIXELS)] = ([H BLANK (PIXELS)]/2)-[H SYNC (PIXELS)] * * Note that h_blank is always an even number of characters (i.e. * h_blank % (CELL_GRAN * 2) == 0) */ h_front_porch = (h_blank / 2) - h_sync; print_value(18, "[H FRONT PORCH (PIXELS)]", h_front_porch); /* 36. Find the number of lines in the odd front porch period: * * [V ODD FRONT PORCH(LINES)]=([MIN PORCH RND]+[INTERLACE]) * * Adjusting for the fact that the interlace is scaled: * * [V ODD FRONT PORCH(LINES)]=(([MIN PORCH RND] * 2) + [2*INTERLACE]) / 2 */ v_odd_front_porch_lines = ((2 * params->min_porch) + interlace) / 2; print_value(36, "[V ODD FRONT PORCH(LINES)]", v_odd_front_porch_lines); /* finally, pack the results in the mode struct */ vmp->hsync_start = h_pixels + h_front_porch; vmp->hsync_end = vmp->hsync_start + h_sync; vmp->htotal = total_pixels; vmp->hdisplay = h_pixels; vmp->vsync_start = v_lines + v_odd_front_porch_lines; vmp->vsync_end = vmp->vsync_start + params->vsync_rqd; vmp->vtotal = total_v_lines; vmp->vdisplay = v_lines; vmp->dot_clock = pixel_freq; } void vesagtf_mode(unsigned x, unsigned y, unsigned refresh, struct videomode *vmp) { struct vesagtf_params params; params.margin_ppt = VESAGTF_MARGIN_PPT; params.min_porch = VESAGTF_MIN_PORCH; params.vsync_rqd = VESAGTF_VSYNC_RQD; params.hsync_pct = VESAGTF_HSYNC_PCT; params.min_vsbp = VESAGTF_MIN_VSBP; params.M = VESAGTF_M; params.C = VESAGTF_C; params.K = VESAGTF_K; params.J = VESAGTF_J; vesagtf_mode_params(x, y, refresh, ¶ms, 0, vmp); } /* * The tidbit here is so that you can compile this file as a * standalone user program to generate X11 modelines using VESA GTF. * This also allows for testing of the code itself, without * necessitating a full kernel recompile. */ /* print_xf86_mode() - print the XFree86 modeline, given mode timings. */ #ifndef _KERNEL void print_xf86_mode (struct videomode *vmp) { float vf, hf; hf = 1000.0 * vmp->dot_clock / vmp->htotal; vf = 1.0 * hf / vmp->vtotal; printf("\n"); printf(" # %dx%d @ %.2f Hz (GTF) hsync: %.2f kHz; pclk: %.2f MHz\n", vmp->hdisplay, vmp->vdisplay, vf, hf, vmp->dot_clock / 1000.0); printf(" Modeline \"%dx%d_%.2f\" %.2f" " %d %d %d %d" " %d %d %d %d" " -HSync +Vsync\n\n", vmp->hdisplay, vmp->vdisplay, vf, (vmp->dot_clock / 1000.0), vmp->hdisplay, vmp->hsync_start, vmp->hsync_end, vmp->htotal, vmp->vdisplay, vmp->vsync_start, vmp->vsync_end, vmp->vtotal); } int main (int argc, char *argv[]) { struct videomode m; if (argc != 4) { printf("usage: %s x y refresh\n", argv[0]); exit(1); } vesagtf_mode(atoi(argv[1]), atoi(argv[2]), atoi(argv[3]), &m); print_xf86_mode(&m); return 0; } #endif diff --git a/sys/fs/autofs/autofs_vfsops.c b/sys/fs/autofs/autofs_vfsops.c index 07bec01517c8..473a0b2280ac 100644 --- a/sys/fs/autofs/autofs_vfsops.c +++ b/sys/fs/autofs/autofs_vfsops.c @@ -1,220 +1,218 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2014 The FreeBSD Foundation * * This software was developed by Edward Tomasz Napierala under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ -#include - __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const char *autofs_opts[] = { "from", "master_options", "master_prefix", NULL }; extern struct autofs_softc *autofs_softc; static int autofs_mount(struct mount *mp) { struct autofs_mount *amp; char *from, *fspath, *options, *prefix; int error; if (vfs_filteropt(mp->mnt_optnew, autofs_opts)) return (EINVAL); if (mp->mnt_flag & MNT_UPDATE) { autofs_flush(VFSTOAUTOFS(mp)); return (0); } if (vfs_getopt(mp->mnt_optnew, "from", (void **)&from, NULL)) return (EINVAL); if (vfs_getopt(mp->mnt_optnew, "fspath", (void **)&fspath, NULL)) return (EINVAL); if (vfs_getopt(mp->mnt_optnew, "master_options", (void **)&options, NULL)) return (EINVAL); if (vfs_getopt(mp->mnt_optnew, "master_prefix", (void **)&prefix, NULL)) return (EINVAL); amp = malloc(sizeof(*amp), M_AUTOFS, M_WAITOK | M_ZERO); mp->mnt_data = amp; amp->am_mp = mp; strlcpy(amp->am_from, from, sizeof(amp->am_from)); strlcpy(amp->am_mountpoint, fspath, sizeof(amp->am_mountpoint)); strlcpy(amp->am_options, options, sizeof(amp->am_options)); strlcpy(amp->am_prefix, prefix, sizeof(amp->am_prefix)); sx_init(&->am_lock, "autofslk"); amp->am_last_fileno = 1; vfs_getnewfsid(mp); MNT_ILOCK(mp); mp->mnt_kern_flag |= MNTK_LOOKUP_SHARED; MNT_IUNLOCK(mp); AUTOFS_XLOCK(amp); error = autofs_node_new(NULL, amp, ".", -1, &->am_root); if (error != 0) { AUTOFS_XUNLOCK(amp); free(amp, M_AUTOFS); return (error); } AUTOFS_XUNLOCK(amp); vfs_mountedfrom(mp, from); return (0); } static int autofs_unmount(struct mount *mp, int mntflags) { struct autofs_mount *amp; struct autofs_node *anp; struct autofs_request *ar; int error, flags; bool found; amp = VFSTOAUTOFS(mp); flags = 0; if (mntflags & MNT_FORCE) flags |= FORCECLOSE; error = vflush(mp, 0, flags, curthread); if (error != 0) { AUTOFS_DEBUG("vflush failed with error %d", error); return (error); } /* * All vnodes are gone, and new one will not appear - so, * no new triggerings. We can iterate over outstanding * autofs_requests and terminate them. */ for (;;) { found = false; sx_xlock(&autofs_softc->sc_lock); TAILQ_FOREACH(ar, &autofs_softc->sc_requests, ar_next) { if (ar->ar_mount != amp) continue; ar->ar_error = ENXIO; ar->ar_done = true; ar->ar_in_progress = false; found = true; } sx_xunlock(&autofs_softc->sc_lock); if (found == false) break; cv_broadcast(&autofs_softc->sc_cv); pause("autofs_umount", 1); } AUTOFS_XLOCK(amp); /* * Not terribly efficient, but at least not recursive. */ while (!RB_EMPTY(&->am_root->an_children)) { anp = RB_MIN(autofs_node_tree, &->am_root->an_children); while (!RB_EMPTY(&anp->an_children)) anp = RB_MIN(autofs_node_tree, &anp->an_children); autofs_node_delete(anp); } autofs_node_delete(amp->am_root); mp->mnt_data = NULL; AUTOFS_XUNLOCK(amp); sx_destroy(&->am_lock); free(amp, M_AUTOFS); return (0); } static int autofs_root(struct mount *mp, int flags, struct vnode **vpp) { struct autofs_mount *amp; int error; amp = VFSTOAUTOFS(mp); error = autofs_node_vn(amp->am_root, mp, flags, vpp); return (error); } static int autofs_statfs(struct mount *mp, struct statfs *sbp) { sbp->f_bsize = S_BLKSIZE; sbp->f_iosize = 0; sbp->f_blocks = 0; sbp->f_bfree = 0; sbp->f_bavail = 0; sbp->f_files = 0; sbp->f_ffree = 0; return (0); } static struct vfsops autofs_vfsops = { .vfs_fhtovp = NULL, /* XXX */ .vfs_mount = autofs_mount, .vfs_unmount = autofs_unmount, .vfs_root = autofs_root, .vfs_statfs = autofs_statfs, .vfs_init = autofs_init, .vfs_uninit = autofs_uninit, }; VFS_SET(autofs_vfsops, autofs, VFCF_SYNTHETIC | VFCF_NETWORK); MODULE_VERSION(autofs, 1); diff --git a/sys/fs/tmpfs/tmpfs_fifoops.c b/sys/fs/tmpfs/tmpfs_fifoops.c index a8a4949529e9..27f77e5d7f28 100644 --- a/sys/fs/tmpfs/tmpfs_fifoops.c +++ b/sys/fs/tmpfs/tmpfs_fifoops.c @@ -1,80 +1,78 @@ /* $NetBSD: tmpfs_fifoops.c,v 1.5 2005/12/11 12:24:29 christos Exp $ */ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2005 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Julio M. Merino Vidal, developed as part of Google's Summer of Code * 2005 program. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * tmpfs vnode interface for named pipes. */ -#include - __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include static int tmpfs_fifo_close(struct vop_close_args *v) { struct tmpfs_node *node; node = VP_TO_TMPFS_NODE(v->a_vp); tmpfs_set_accessed(VFS_TO_TMPFS(v->a_vp->v_mount), node); tmpfs_update(v->a_vp); return (fifo_specops.vop_close(v)); } /* * vnode operations vector used for fifos stored in a tmpfs file system. */ struct vop_vector tmpfs_fifoop_entries = { .vop_default = &fifo_specops, .vop_close = tmpfs_fifo_close, .vop_reclaim = tmpfs_reclaim, .vop_access = tmpfs_access, .vop_getattr = tmpfs_getattr, .vop_setattr = tmpfs_setattr, .vop_pathconf = tmpfs_pathconf, .vop_print = tmpfs_print, .vop_add_writecount = vop_stdadd_writecount_nomsync, .vop_fplookup_vexec = VOP_EAGAIN, .vop_fplookup_symlink = VOP_EAGAIN, }; VFS_VOP_VECTOR_REGISTER(tmpfs_fifoop_entries); diff --git a/sys/net/rss_config.c b/sys/net/rss_config.c index ee15ed3da2bf..266ea57b2dc9 100644 --- a/sys/net/rss_config.c +++ b/sys/net/rss_config.c @@ -1,549 +1,546 @@ /*- * Copyright (c) 2010-2011 Juniper Networks, Inc. * All rights reserved. * * This software was developed by Robert N. M. Watson under contract * to Juniper Networks, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include - -__FBSDID("$FreeBSD$"); #include "opt_inet6.h" #include #include #include #include #include #include #include #include #include #include #include #include #include /*- * Operating system parts of receiver-side scaling (RSS), which allows * network cards to direct flows to particular receive queues based on hashes * of header tuples. This implementation aligns RSS buckets with connection * groups at the TCP/IP layer, so each bucket is associated with exactly one * group. As a result, the group lookup structures (and lock) should have an * effective affinity with exactly one CPU. * * Network device drivers needing to configure RSS will query this framework * for parameters, such as the current RSS key, hashing policies, number of * bits, and indirection table mapping hashes to buckets and CPUs. They may * provide their own supplementary information, such as queue<->CPU bindings. * It is the responsibility of the network device driver to inject packets * into the stack on as close to the right CPU as possible, if playing by RSS * rules. * * TODO: * * - Synchronization for rss_key and other future-configurable parameters. * - Event handler drivers can register to pick up RSS configuration changes. * - Should we allow rss_basecpu to be configured? * - Randomize key on boot. * - IPv6 support. * - Statistics on how often there's a misalignment between hardware * placement and pcbgroup expectations. */ SYSCTL_DECL(_net_inet); SYSCTL_NODE(_net_inet, OID_AUTO, rss, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "Receive-side steering"); /* * Toeplitz is the only required hash function in the RSS spec, so use it by * default. */ static u_int rss_hashalgo = RSS_HASH_TOEPLITZ; SYSCTL_INT(_net_inet_rss, OID_AUTO, hashalgo, CTLFLAG_RDTUN, &rss_hashalgo, 0, "RSS hash algorithm"); /* * Size of the indirection table; at most 128 entries per the RSS spec. We * size it to at least 2 times the number of CPUs by default to allow useful * rebalancing. If not set explicitly with a loader tunable, we tune based * on the number of CPUs present. * * XXXRW: buckets might be better to use for the tunable than bits. */ static u_int rss_bits; SYSCTL_INT(_net_inet_rss, OID_AUTO, bits, CTLFLAG_RDTUN, &rss_bits, 0, "RSS bits"); static u_int rss_mask; SYSCTL_INT(_net_inet_rss, OID_AUTO, mask, CTLFLAG_RD, &rss_mask, 0, "RSS mask"); static const u_int rss_maxbits = RSS_MAXBITS; SYSCTL_INT(_net_inet_rss, OID_AUTO, maxbits, CTLFLAG_RD, __DECONST(int *, &rss_maxbits), 0, "RSS maximum bits"); /* * RSS's own count of the number of CPUs it could be using for processing. * Bounded to 64 by RSS constants. */ static u_int rss_ncpus; SYSCTL_INT(_net_inet_rss, OID_AUTO, ncpus, CTLFLAG_RD, &rss_ncpus, 0, "Number of CPUs available to RSS"); #define RSS_MAXCPUS (1 << (RSS_MAXBITS - 1)) static const u_int rss_maxcpus = RSS_MAXCPUS; SYSCTL_INT(_net_inet_rss, OID_AUTO, maxcpus, CTLFLAG_RD, __DECONST(int *, &rss_maxcpus), 0, "RSS maximum CPUs that can be used"); /* * Variable exists just for reporting rss_bits in a user-friendly way. */ static u_int rss_buckets; SYSCTL_INT(_net_inet_rss, OID_AUTO, buckets, CTLFLAG_RD, &rss_buckets, 0, "RSS buckets"); /* * Base CPU number; devices will add this to all CPU numbers returned by the * RSS indirection table. Currently unmodifable in FreeBSD. */ static const u_int rss_basecpu; SYSCTL_INT(_net_inet_rss, OID_AUTO, basecpu, CTLFLAG_RD, __DECONST(int *, &rss_basecpu), 0, "RSS base CPU"); /* * Print verbose debugging messages. * 0 - disable * non-zero - enable */ int rss_debug = 0; SYSCTL_INT(_net_inet_rss, OID_AUTO, debug, CTLFLAG_RWTUN, &rss_debug, 0, "RSS debug level"); /* * RSS secret key, intended to prevent attacks on load-balancing. Its * effectiveness may be limited by algorithm choice and available entropy * during the boot. * * XXXRW: And that we don't randomize it yet! * * This is the default Microsoft RSS specification key which is also * the Chelsio T5 firmware default key. */ static uint8_t rss_key[RSS_KEYSIZE] = { 0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2, 0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0, 0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4, 0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c, 0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa, }; /* * RSS hash->CPU table, which maps hashed packet headers to particular CPUs. * Drivers may supplement this table with a separate CPU<->queue table when * programming devices. */ struct rss_table_entry { uint8_t rte_cpu; /* CPU affinity of bucket. */ }; static struct rss_table_entry rss_table[RSS_TABLE_MAXLEN]; static void rss_init(__unused void *arg) { u_int i; u_int cpuid; /* * Validate tunables, coerce to sensible values. */ switch (rss_hashalgo) { case RSS_HASH_TOEPLITZ: case RSS_HASH_NAIVE: break; default: RSS_DEBUG("invalid RSS hashalgo %u, coercing to %u\n", rss_hashalgo, RSS_HASH_TOEPLITZ); rss_hashalgo = RSS_HASH_TOEPLITZ; } /* * Count available CPUs. * * XXXRW: Note incorrect assumptions regarding contiguity of this set * elsewhere. */ rss_ncpus = 0; for (i = 0; i <= mp_maxid; i++) { if (CPU_ABSENT(i)) continue; rss_ncpus++; } if (rss_ncpus > RSS_MAXCPUS) rss_ncpus = RSS_MAXCPUS; /* * Tune RSS table entries to be no less than 2x the number of CPUs * -- unless we're running uniprocessor, in which case there's not * much point in having buckets to rearrange for load-balancing! */ if (rss_ncpus > 1) { if (rss_bits == 0) rss_bits = fls(rss_ncpus - 1) + 1; /* * Microsoft limits RSS table entries to 128, so apply that * limit to both auto-detected CPU counts and user-configured * ones. */ if (rss_bits == 0 || rss_bits > RSS_MAXBITS) { RSS_DEBUG("RSS bits %u not valid, coercing to %u\n", rss_bits, RSS_MAXBITS); rss_bits = RSS_MAXBITS; } /* * Figure out how many buckets to use; warn if less than the * number of configured CPUs, although this is not a fatal * problem. */ rss_buckets = (1 << rss_bits); if (rss_buckets < rss_ncpus) RSS_DEBUG("WARNING: rss_buckets (%u) less than " "rss_ncpus (%u)\n", rss_buckets, rss_ncpus); rss_mask = rss_buckets - 1; } else { rss_bits = 0; rss_buckets = 1; rss_mask = 0; } /* * Set up initial CPU assignments: round-robin by default. */ cpuid = CPU_FIRST(); for (i = 0; i < rss_buckets; i++) { rss_table[i].rte_cpu = cpuid; cpuid = CPU_NEXT(cpuid); } /* * Randomize rrs_key. * * XXXRW: Not yet. If nothing else, will require an rss_isbadkey() * loop to check for "bad" RSS keys. */ } SYSINIT(rss_init, SI_SUB_SOFTINTR, SI_ORDER_SECOND, rss_init, NULL); static uint32_t rss_naive_hash(u_int keylen, const uint8_t *key, u_int datalen, const uint8_t *data) { uint32_t v; u_int i; v = 0; for (i = 0; i < keylen; i++) v += key[i]; for (i = 0; i < datalen; i++) v += data[i]; return (v); } uint32_t rss_hash(u_int datalen, const uint8_t *data) { switch (rss_hashalgo) { case RSS_HASH_TOEPLITZ: return (toeplitz_hash(sizeof(rss_key), rss_key, datalen, data)); case RSS_HASH_NAIVE: return (rss_naive_hash(sizeof(rss_key), rss_key, datalen, data)); default: panic("%s: unsupported/unknown hashalgo %d", __func__, rss_hashalgo); } } /* * Query the number of RSS bits in use. */ u_int rss_getbits(void) { return (rss_bits); } /* * Query the RSS bucket associated with an RSS hash. */ u_int rss_getbucket(u_int hash) { return (hash & rss_mask); } /* * Query the RSS layer bucket associated with the given * entry in the RSS hash space. * * The RSS indirection table is 0 .. rss_buckets-1, * covering the low 'rss_bits' of the total 128 slot * RSS indirection table. So just mask off rss_bits and * return that. * * NIC drivers can then iterate over the 128 slot RSS * indirection table and fetch which RSS bucket to * map it to. This will typically be a CPU queue */ u_int rss_get_indirection_to_bucket(u_int index) { return (index & rss_mask); } /* * Query the RSS CPU associated with an RSS bucket. */ u_int rss_getcpu(u_int bucket) { return (rss_table[bucket].rte_cpu); } /* * netisr CPU affinity lookup given just the hash and hashtype. */ u_int rss_hash2cpuid(uint32_t hash_val, uint32_t hash_type) { switch (hash_type) { case M_HASHTYPE_RSS_IPV4: case M_HASHTYPE_RSS_TCP_IPV4: case M_HASHTYPE_RSS_UDP_IPV4: case M_HASHTYPE_RSS_IPV6: case M_HASHTYPE_RSS_TCP_IPV6: case M_HASHTYPE_RSS_UDP_IPV6: return (rss_getcpu(rss_getbucket(hash_val))); default: return (NETISR_CPUID_NONE); } } /* * Query the RSS bucket associated with the given hash value and * type. */ int rss_hash2bucket(uint32_t hash_val, uint32_t hash_type, uint32_t *bucket_id) { switch (hash_type) { case M_HASHTYPE_RSS_IPV4: case M_HASHTYPE_RSS_TCP_IPV4: case M_HASHTYPE_RSS_UDP_IPV4: case M_HASHTYPE_RSS_IPV6: case M_HASHTYPE_RSS_TCP_IPV6: case M_HASHTYPE_RSS_UDP_IPV6: *bucket_id = rss_getbucket(hash_val); return (0); default: return (-1); } } /* * netisr CPU affinity lookup routine for use by protocols. */ struct mbuf * rss_m2cpuid(struct mbuf *m, uintptr_t source, u_int *cpuid) { M_ASSERTPKTHDR(m); *cpuid = rss_hash2cpuid(m->m_pkthdr.flowid, M_HASHTYPE_GET(m)); return (m); } int rss_m2bucket(struct mbuf *m, uint32_t *bucket_id) { M_ASSERTPKTHDR(m); return(rss_hash2bucket(m->m_pkthdr.flowid, M_HASHTYPE_GET(m), bucket_id)); } /* * Query the RSS hash algorithm. */ u_int rss_gethashalgo(void) { return (rss_hashalgo); } /* * Query the current RSS key; likely to be used by device drivers when * configuring hardware RSS. Caller must pass an array of size RSS_KEYSIZE. * * XXXRW: Perhaps we should do the accept-a-length-and-truncate thing? */ void rss_getkey(uint8_t *key) { bcopy(rss_key, key, sizeof(rss_key)); } /* * Query the number of buckets; this may be used by both network device * drivers, which will need to populate hardware shadows of the software * indirection table, and the network stack itself (such as when deciding how * many connection groups to allocate). */ u_int rss_getnumbuckets(void) { return (rss_buckets); } /* * Query the number of CPUs in use by RSS; may be useful to device drivers * trying to figure out how to map a larger number of CPUs into a smaller * number of receive queues. */ u_int rss_getnumcpus(void) { return (rss_ncpus); } /* * Return the supported RSS hash configuration. * * NICs should query this to determine what to configure in their redirection * matching table. */ inline u_int rss_gethashconfig(void) { /* Return 4-tuple for TCP; 2-tuple for others */ /* * UDP may fragment more often than TCP and thus we'll end up with * NICs returning 2-tuple fragments. * udp_init() and udplite_init() both currently initialise things * as 2-tuple. * So for now disable UDP 4-tuple hashing until all of the other * pieces are in place. */ return ( RSS_HASHTYPE_RSS_IPV4 | RSS_HASHTYPE_RSS_TCP_IPV4 | RSS_HASHTYPE_RSS_IPV6 | RSS_HASHTYPE_RSS_TCP_IPV6 | RSS_HASHTYPE_RSS_IPV6_EX | RSS_HASHTYPE_RSS_TCP_IPV6_EX #if 0 | RSS_HASHTYPE_RSS_UDP_IPV4 | RSS_HASHTYPE_RSS_UDP_IPV6 | RSS_HASHTYPE_RSS_UDP_IPV6_EX #endif ); } /* * XXXRW: Confirm that sysctl -a won't dump this keying material, don't want * it appearing in debugging output unnecessarily. */ static int sysctl_rss_key(SYSCTL_HANDLER_ARGS) { uint8_t temp_rss_key[RSS_KEYSIZE]; int error; error = priv_check(req->td, PRIV_NETINET_HASHKEY); if (error) return (error); bcopy(rss_key, temp_rss_key, sizeof(temp_rss_key)); error = sysctl_handle_opaque(oidp, temp_rss_key, sizeof(temp_rss_key), req); if (error) return (error); if (req->newptr != NULL) { /* XXXRW: Not yet. */ return (EINVAL); } return (0); } SYSCTL_PROC(_net_inet_rss, OID_AUTO, key, CTLTYPE_OPAQUE | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0, sysctl_rss_key, "", "RSS keying material"); static int sysctl_rss_bucket_mapping(SYSCTL_HANDLER_ARGS) { struct sbuf *sb; int error; int i; error = 0; error = sysctl_wire_old_buffer(req, 0); if (error != 0) return (error); sb = sbuf_new_for_sysctl(NULL, NULL, 512, req); if (sb == NULL) return (ENOMEM); for (i = 0; i < rss_buckets; i++) { sbuf_printf(sb, "%s%d:%d", i == 0 ? "" : " ", i, rss_getcpu(i)); } error = sbuf_finish(sb); sbuf_delete(sb); return (error); } SYSCTL_PROC(_net_inet_rss, OID_AUTO, bucket_mapping, CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0, sysctl_rss_bucket_mapping, "", "RSS bucket -> CPU mapping"); diff --git a/sys/netinet/in_rss.c b/sys/netinet/in_rss.c index 18cfaf986c4a..698fd86dc7a5 100644 --- a/sys/netinet/in_rss.c +++ b/sys/netinet/in_rss.c @@ -1,392 +1,389 @@ /*- * Copyright (c) 2010-2011 Juniper Networks, Inc. * All rights reserved. * * This software was developed by Robert N. M. Watson under contract * to Juniper Networks, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include - -__FBSDID("$FreeBSD$"); #include "opt_inet6.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for software rss hash support */ #include #include #include /* * Hash an IPv4 2-tuple. */ uint32_t rss_hash_ip4_2tuple(struct in_addr src, struct in_addr dst) { uint8_t data[sizeof(src) + sizeof(dst)]; u_int datalen; datalen = 0; bcopy(&src, &data[datalen], sizeof(src)); datalen += sizeof(src); bcopy(&dst, &data[datalen], sizeof(dst)); datalen += sizeof(dst); return (rss_hash(datalen, data)); } /* * Hash an IPv4 4-tuple. */ uint32_t rss_hash_ip4_4tuple(struct in_addr src, u_short srcport, struct in_addr dst, u_short dstport) { uint8_t data[sizeof(src) + sizeof(dst) + sizeof(srcport) + sizeof(dstport)]; u_int datalen; datalen = 0; bcopy(&src, &data[datalen], sizeof(src)); datalen += sizeof(src); bcopy(&dst, &data[datalen], sizeof(dst)); datalen += sizeof(dst); bcopy(&srcport, &data[datalen], sizeof(srcport)); datalen += sizeof(srcport); bcopy(&dstport, &data[datalen], sizeof(dstport)); datalen += sizeof(dstport); return (rss_hash(datalen, data)); } /* * Calculate an appropriate ipv4 2-tuple or 4-tuple given the given * IPv4 source/destination address, UDP or TCP source/destination ports * and the protocol type. * * The protocol code may wish to do a software hash of the given * tuple. This depends upon the currently configured RSS hash types. * * This assumes that the packet in question isn't a fragment. * * It also assumes the packet source/destination address * are in "incoming" packet order (ie, source is "far" address.) */ int rss_proto_software_hash_v4(struct in_addr s, struct in_addr d, u_short sp, u_short dp, int proto, uint32_t *hashval, uint32_t *hashtype) { uint32_t hash; /* * Next, choose the hash type depending upon the protocol * identifier. */ if ((proto == IPPROTO_TCP) && (rss_gethashconfig() & RSS_HASHTYPE_RSS_TCP_IPV4)) { hash = rss_hash_ip4_4tuple(s, sp, d, dp); *hashval = hash; *hashtype = M_HASHTYPE_RSS_TCP_IPV4; return (0); } else if ((proto == IPPROTO_UDP) && (rss_gethashconfig() & RSS_HASHTYPE_RSS_UDP_IPV4)) { hash = rss_hash_ip4_4tuple(s, sp, d, dp); *hashval = hash; *hashtype = M_HASHTYPE_RSS_UDP_IPV4; return (0); } else if (rss_gethashconfig() & RSS_HASHTYPE_RSS_IPV4) { /* RSS doesn't hash on other protocols like SCTP; so 2-tuple */ hash = rss_hash_ip4_2tuple(s, d); *hashval = hash; *hashtype = M_HASHTYPE_RSS_IPV4; return (0); } /* No configured available hashtypes! */ RSS_DEBUG("no available hashtypes!\n"); return (-1); } /* * Calculate an appropriate ipv4 2-tuple or 4-tuple given the given * IPv4 source/destination address, UDP or TCP source/destination ports * and the protocol type. * * The protocol code may wish to do a software hash of the given * tuple. This depends upon the currently configured RSS hash types. * * It assumes the packet source/destination address * are in "outgoing" packet order (ie, destination is "far" address.) */ uint32_t xps_proto_software_hash_v4(struct in_addr s, struct in_addr d, u_short sp, u_short dp, int proto, uint32_t *hashtype) { uint32_t hash; /* * Next, choose the hash type depending upon the protocol * identifier. */ if ((proto == IPPROTO_TCP) && (rss_gethashconfig() & RSS_HASHTYPE_RSS_TCP_IPV4)) { hash = rss_hash_ip4_4tuple(d, dp, s, sp); *hashtype = M_HASHTYPE_RSS_TCP_IPV4; return (hash); } else if ((proto == IPPROTO_UDP) && (rss_gethashconfig() & RSS_HASHTYPE_RSS_UDP_IPV4)) { hash = rss_hash_ip4_4tuple(d, dp, s, sp); *hashtype = M_HASHTYPE_RSS_UDP_IPV4; return (hash); } else if (rss_gethashconfig() & RSS_HASHTYPE_RSS_IPV4) { /* RSS doesn't hash on other protocols like SCTP; so 2-tuple */ hash = rss_hash_ip4_2tuple(d, s); *hashtype = M_HASHTYPE_RSS_IPV4; return (hash); } *hashtype = M_HASHTYPE_NONE; return (0); } /* * Do a software calculation of the RSS for the given mbuf. * * This is typically used by the input path to recalculate the RSS after * some form of packet processing (eg de-capsulation, IP fragment reassembly.) * * dir is the packet direction - RSS_HASH_PKT_INGRESS for incoming and * RSS_HASH_PKT_EGRESS for outgoing. * * Returns 0 if a hash was done, -1 if no hash was done, +1 if * the mbuf already had a valid RSS flowid. * * This function doesn't modify the mbuf. It's up to the caller to * assign flowid/flowtype as appropriate. */ int rss_mbuf_software_hash_v4(const struct mbuf *m, int dir, uint32_t *hashval, uint32_t *hashtype) { const struct ip *ip; const struct tcphdr *th; const struct udphdr *uh; uint32_t flowtype; uint8_t proto; int iphlen; int is_frag = 0; /* * XXX For now this only handles hashing on incoming mbufs. */ if (dir != RSS_HASH_PKT_INGRESS) { RSS_DEBUG("called on EGRESS packet!\n"); return (-1); } /* * First, validate that the mbuf we have is long enough * to have an IPv4 header in it. */ if (m->m_pkthdr.len < (sizeof(struct ip))) { RSS_DEBUG("short mbuf pkthdr\n"); return (-1); } if (m->m_len < (sizeof(struct ip))) { RSS_DEBUG("short mbuf len\n"); return (-1); } /* Ok, let's dereference that */ ip = mtod(m, struct ip *); proto = ip->ip_p; iphlen = ip->ip_hl << 2; /* * If this is a fragment then it shouldn't be four-tuple * hashed just yet. Once it's reassembled into a full * frame it should be re-hashed. */ if (ip->ip_off & htons(IP_MF | IP_OFFMASK)) is_frag = 1; /* * If the mbuf flowid/flowtype matches the packet type, * and we don't support the 4-tuple version of the given protocol, * then signal to the owner that it can trust the flowid/flowtype * details. * * This is a little picky - eg, if TCPv4 / UDPv4 hashing * is supported but we got a TCP/UDP frame only 2-tuple hashed, * then we shouldn't just "trust" the 2-tuple hash. We need * a 4-tuple hash. */ flowtype = M_HASHTYPE_GET(m); if (flowtype != M_HASHTYPE_NONE) { switch (proto) { case IPPROTO_UDP: if ((rss_gethashconfig() & RSS_HASHTYPE_RSS_UDP_IPV4) && (flowtype == M_HASHTYPE_RSS_UDP_IPV4) && (is_frag == 0)) { return (1); } /* * Only allow 2-tuple for UDP frames if we don't also * support 4-tuple for UDP. */ if ((rss_gethashconfig() & RSS_HASHTYPE_RSS_IPV4) && ((rss_gethashconfig() & RSS_HASHTYPE_RSS_UDP_IPV4) == 0) && flowtype == M_HASHTYPE_RSS_IPV4) { return (1); } break; case IPPROTO_TCP: if ((rss_gethashconfig() & RSS_HASHTYPE_RSS_TCP_IPV4) && (flowtype == M_HASHTYPE_RSS_TCP_IPV4) && (is_frag == 0)) { return (1); } /* * Only allow 2-tuple for TCP frames if we don't also * support 2-tuple for TCP. */ if ((rss_gethashconfig() & RSS_HASHTYPE_RSS_IPV4) && ((rss_gethashconfig() & RSS_HASHTYPE_RSS_TCP_IPV4) == 0) && flowtype == M_HASHTYPE_RSS_IPV4) { return (1); } break; default: if ((rss_gethashconfig() & RSS_HASHTYPE_RSS_IPV4) && flowtype == M_HASHTYPE_RSS_IPV4) { return (1); } break; } } /* * Decode enough information to make a hash decision. * * XXX TODO: does the hardware hash on 4-tuple if IP * options are present? */ if ((rss_gethashconfig() & RSS_HASHTYPE_RSS_TCP_IPV4) && (proto == IPPROTO_TCP) && (is_frag == 0)) { if (m->m_len < iphlen + sizeof(struct tcphdr)) { RSS_DEBUG("short TCP frame?\n"); return (-1); } th = (const struct tcphdr *)((c_caddr_t)ip + iphlen); return rss_proto_software_hash_v4(ip->ip_src, ip->ip_dst, th->th_sport, th->th_dport, proto, hashval, hashtype); } else if ((rss_gethashconfig() & RSS_HASHTYPE_RSS_UDP_IPV4) && (proto == IPPROTO_UDP) && (is_frag == 0)) { uh = (const struct udphdr *)((c_caddr_t)ip + iphlen); if (m->m_len < iphlen + sizeof(struct udphdr)) { RSS_DEBUG("short UDP frame?\n"); return (-1); } return rss_proto_software_hash_v4(ip->ip_src, ip->ip_dst, uh->uh_sport, uh->uh_dport, proto, hashval, hashtype); } else if (rss_gethashconfig() & RSS_HASHTYPE_RSS_IPV4) { /* Default to 2-tuple hash */ return rss_proto_software_hash_v4(ip->ip_src, ip->ip_dst, 0, /* source port */ 0, /* destination port */ 0, /* IPPROTO_IP */ hashval, hashtype); } else { RSS_DEBUG("no available hashtypes!\n"); return (-1); } } /* * Similar to rss_m2cpuid, but designed to be used by the IP NETISR * on incoming frames. * * If an existing RSS hash exists and it matches what the configured * hashing is, then use it. * * If there's an existing RSS hash but the desired hash is different, * or if there's no useful RSS hash, then calculate it via * the software path. * * XXX TODO: definitely want statistics here! */ struct mbuf * rss_soft_m2cpuid_v4(struct mbuf *m, uintptr_t source, u_int *cpuid) { uint32_t hash_val, hash_type; int ret; M_ASSERTPKTHDR(m); ret = rss_mbuf_software_hash_v4(m, RSS_HASH_PKT_INGRESS, &hash_val, &hash_type); if (ret > 0) { /* mbuf has a valid hash already; don't need to modify it */ *cpuid = rss_hash2cpuid(m->m_pkthdr.flowid, M_HASHTYPE_GET(m)); } else if (ret == 0) { /* hash was done; update */ m->m_pkthdr.flowid = hash_val; M_HASHTYPE_SET(m, hash_type); *cpuid = rss_hash2cpuid(m->m_pkthdr.flowid, M_HASHTYPE_GET(m)); } else { /* ret < 0 */ /* no hash was done */ *cpuid = NETISR_CPUID_NONE; } return (m); } diff --git a/sys/netinet6/in6_rss.c b/sys/netinet6/in6_rss.c index 41916cf41584..79c7bfa6e68c 100644 --- a/sys/netinet6/in6_rss.c +++ b/sys/netinet6/in6_rss.c @@ -1,417 +1,414 @@ /*- * Copyright (c) 2010-2011 Juniper Networks, Inc. * All rights reserved. * * This software was developed by Robert N. M. Watson under contract * to Juniper Networks, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ -#include - -__FBSDID("$FreeBSD$"); #include "opt_inet6.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for software rss hash support */ #include #include #include #include /* * Hash an IPv6 2-tuple. */ uint32_t rss_hash_ip6_2tuple(const struct in6_addr *src, const struct in6_addr *dst) { uint8_t data[sizeof(*src) + sizeof(*dst)]; u_int datalen; datalen = 0; bcopy(src, &data[datalen], sizeof(*src)); datalen += sizeof(*src); bcopy(dst, &data[datalen], sizeof(*dst)); datalen += sizeof(*dst); return (rss_hash(datalen, data)); } /* * Hash an IPv6 4-tuple. */ uint32_t rss_hash_ip6_4tuple(const struct in6_addr *src, u_short srcport, const struct in6_addr *dst, u_short dstport) { uint8_t data[sizeof(*src) + sizeof(*dst) + sizeof(srcport) + sizeof(dstport)]; u_int datalen; datalen = 0; bcopy(src, &data[datalen], sizeof(*src)); datalen += sizeof(*src); bcopy(dst, &data[datalen], sizeof(*dst)); datalen += sizeof(*dst); bcopy(&srcport, &data[datalen], sizeof(srcport)); datalen += sizeof(srcport); bcopy(&dstport, &data[datalen], sizeof(dstport)); datalen += sizeof(dstport); return (rss_hash(datalen, data)); } /* * Calculate an appropriate ipv6 2-tuple or 4-tuple given the given * IPv6 source/destination address, UDP or TCP source/destination ports * and the protocol type. * * The protocol code may wish to do a software hash of the given * tuple. This depends upon the currently configured RSS hash types. * * This assumes that the packet in question isn't a fragment. * * It also assumes the packet source/destination address * are in "incoming" packet order (ie, source is "far" address.) */ int rss_proto_software_hash_v6(const struct in6_addr *s, const struct in6_addr *d, u_short sp, u_short dp, int proto, uint32_t *hashval, uint32_t *hashtype) { uint32_t hash; /* * Next, choose the hash type depending upon the protocol * identifier. */ if ((proto == IPPROTO_TCP) && (rss_gethashconfig() & RSS_HASHTYPE_RSS_TCP_IPV6)) { hash = rss_hash_ip6_4tuple(s, sp, d, dp); *hashval = hash; *hashtype = M_HASHTYPE_RSS_TCP_IPV6; return (0); } else if ((proto == IPPROTO_UDP) && (rss_gethashconfig() & RSS_HASHTYPE_RSS_UDP_IPV6)) { hash = rss_hash_ip6_4tuple(s, sp, d, dp); *hashval = hash; *hashtype = M_HASHTYPE_RSS_UDP_IPV6; return (0); } else if (rss_gethashconfig() & RSS_HASHTYPE_RSS_IPV6) { /* RSS doesn't hash on other protocols like SCTP; so 2-tuple */ hash = rss_hash_ip6_2tuple(s, d); *hashval = hash; *hashtype = M_HASHTYPE_RSS_IPV6; return (0); } /* No configured available hashtypes! */ RSS_DEBUG("no available hashtypes!\n"); return (-1); } /* * Calculate an appropriate ipv6 2-tuple or 4-tuple given the given * IPv6 source/destination address, UDP or TCP source/destination ports * and the protocol type. * * The protocol code may wish to do a software hash of the given * tuple. This depends upon the currently configured RSS hash types. * * It assumes the packet source/destination address * are in "outgoin" packet order (ie, destination is "far" address.) */ uint32_t xps_proto_software_hash_v6(const struct in6_addr *s, const struct in6_addr *d, u_short sp, u_short dp, int proto, uint32_t *hashtype) { uint32_t hash; /* * Next, choose the hash type depending upon the protocol * identifier. */ if ((proto == IPPROTO_TCP) && (rss_gethashconfig() & RSS_HASHTYPE_RSS_TCP_IPV6)) { hash = rss_hash_ip6_4tuple(d, dp, s, sp); *hashtype = M_HASHTYPE_RSS_TCP_IPV6; return (hash); } else if ((proto == IPPROTO_UDP) && (rss_gethashconfig() & RSS_HASHTYPE_RSS_UDP_IPV6)) { hash = rss_hash_ip6_4tuple(d, dp, s, sp); *hashtype = M_HASHTYPE_RSS_UDP_IPV6; return (hash); } else if (rss_gethashconfig() & RSS_HASHTYPE_RSS_IPV6) { /* RSS doesn't hash on other protocols like SCTP; so 2-tuple */ hash = rss_hash_ip6_2tuple(d, s); *hashtype = M_HASHTYPE_RSS_IPV6; return (hash); } *hashtype = M_HASHTYPE_NONE; return (0); } /* * Do a software calculation of the RSS for the given mbuf. * * This is typically used by the input path to recalculate the RSS after * some form of packet processing (eg de-capsulation, IP fragment reassembly.) * * dir is the packet direction - RSS_HASH_PKT_INGRESS for incoming and * RSS_HASH_PKT_EGRESS for outgoing. * * Returns 0 if a hash was done, -1 if no hash was done, +1 if * the mbuf already had a valid RSS flowid. * * This function doesn't modify the mbuf. It's up to the caller to * assign flowid/flowtype as appropriate. */ int rss_mbuf_software_hash_v6(const struct mbuf *m, int dir, uint32_t *hashval, uint32_t *hashtype) { const struct ip6_hdr *ip6; const struct ip6_frag *ip6f; const struct tcphdr *th; const struct udphdr *uh; uint32_t flowtype; uint8_t proto; int off, newoff; int nxt; /* * XXX For now this only handles hashing on incoming mbufs. */ if (dir != RSS_HASH_PKT_INGRESS) { RSS_DEBUG("called on EGRESS packet!\n"); return (-1); } off = sizeof(struct ip6_hdr); /* * First, validate that the mbuf we have is long enough * to have an IPv6 header in it. */ if (m->m_pkthdr.len < off) { RSS_DEBUG("short mbuf pkthdr\n"); return (-1); } if (m->m_len < off) { RSS_DEBUG("short mbuf len\n"); return (-1); } /* Ok, let's dereference that */ ip6 = mtod(m, struct ip6_hdr *); proto = ip6->ip6_nxt; /* * Find the beginning of the TCP/UDP header. * * If this is a fragment then it shouldn't be four-tuple * hashed just yet. Once it's reassembled into a full * frame it should be re-hashed. */ while (proto != IPPROTO_FRAGMENT) { newoff = ip6_nexthdr(m, off, proto, &nxt); if (newoff < 0) break; off = newoff; proto = nxt; } /* * Ignore the fragment header if this is an "atomic" fragment * (offset and m bit set to 0) */ if (proto == IPPROTO_FRAGMENT) { if (m->m_len < off + sizeof(struct ip6_frag)) { RSS_DEBUG("short fragment frame?\n"); return (-1); } ip6f = (const struct ip6_frag *)((c_caddr_t)ip6 + off); if ((ip6f->ip6f_offlg & ~IP6F_RESERVED_MASK) == 0) { off = ip6_lasthdr(m, off, proto, &nxt); if (off < 0) { RSS_DEBUG("invalid extension header\n"); return (-1); } proto = nxt; } } /* * If the mbuf flowid/flowtype matches the packet type, * and we don't support the 4-tuple version of the given protocol, * then signal to the owner that it can trust the flowid/flowtype * details. * * This is a little picky - eg, if TCPv6 / UDPv6 hashing * is supported but we got a TCP/UDP frame only 2-tuple hashed, * then we shouldn't just "trust" the 2-tuple hash. We need * a 4-tuple hash. */ flowtype = M_HASHTYPE_GET(m); if (flowtype != M_HASHTYPE_NONE) { switch (proto) { case IPPROTO_UDP: if ((rss_gethashconfig() & RSS_HASHTYPE_RSS_UDP_IPV6) && (flowtype == M_HASHTYPE_RSS_UDP_IPV6)) { return (1); } /* * Only allow 2-tuple for UDP frames if we don't also * support 4-tuple for UDP. */ if ((rss_gethashconfig() & RSS_HASHTYPE_RSS_IPV6) && ((rss_gethashconfig() & RSS_HASHTYPE_RSS_UDP_IPV6) == 0) && flowtype == M_HASHTYPE_RSS_IPV6) { return (1); } break; case IPPROTO_TCP: if ((rss_gethashconfig() & RSS_HASHTYPE_RSS_TCP_IPV6) && (flowtype == M_HASHTYPE_RSS_TCP_IPV6)) { return (1); } /* * Only allow 2-tuple for TCP frames if we don't also * support 4-tuple for TCP. */ if ((rss_gethashconfig() & RSS_HASHTYPE_RSS_IPV6) && ((rss_gethashconfig() & RSS_HASHTYPE_RSS_TCP_IPV6) == 0) && flowtype == M_HASHTYPE_RSS_IPV6) { return (1); } break; default: if ((rss_gethashconfig() & RSS_HASHTYPE_RSS_IPV6) && flowtype == M_HASHTYPE_RSS_IPV6) { return (1); } break; } } /* * Decode enough information to make a hash decision. */ if ((rss_gethashconfig() & RSS_HASHTYPE_RSS_TCP_IPV6) && (proto == IPPROTO_TCP)) { if (m->m_len < off + sizeof(struct tcphdr)) { RSS_DEBUG("short TCP frame?\n"); return (-1); } th = (const struct tcphdr *)((c_caddr_t)ip6 + off); return rss_proto_software_hash_v6(&ip6->ip6_src, &ip6->ip6_dst, th->th_sport, th->th_dport, proto, hashval, hashtype); } else if ((rss_gethashconfig() & RSS_HASHTYPE_RSS_UDP_IPV6) && (proto == IPPROTO_UDP)) { if (m->m_len < off + sizeof(struct udphdr)) { RSS_DEBUG("short UDP frame?\n"); return (-1); } uh = (const struct udphdr *)((c_caddr_t)ip6 + off); return rss_proto_software_hash_v6(&ip6->ip6_src, &ip6->ip6_dst, uh->uh_sport, uh->uh_dport, proto, hashval, hashtype); } else if (rss_gethashconfig() & RSS_HASHTYPE_RSS_IPV6) { /* Default to 2-tuple hash */ return rss_proto_software_hash_v6(&ip6->ip6_src, &ip6->ip6_dst, 0, /* source port */ 0, /* destination port */ 0, /* IPPROTO_IP */ hashval, hashtype); } else { RSS_DEBUG("no available hashtypes!\n"); return (-1); } } /* * Similar to rss_m2cpuid, but designed to be used by the IPv6 NETISR * on incoming frames. * * If an existing RSS hash exists and it matches what the configured * hashing is, then use it. * * If there's an existing RSS hash but the desired hash is different, * or if there's no useful RSS hash, then calculate it via * the software path. * * XXX TODO: definitely want statistics here! */ struct mbuf * rss_soft_m2cpuid_v6(struct mbuf *m, uintptr_t source, u_int *cpuid) { uint32_t hash_val, hash_type; int ret; M_ASSERTPKTHDR(m); ret = rss_mbuf_software_hash_v6(m, RSS_HASH_PKT_INGRESS, &hash_val, &hash_type); if (ret > 0) { /* mbuf has a valid hash already; don't need to modify it */ *cpuid = rss_hash2cpuid(m->m_pkthdr.flowid, M_HASHTYPE_GET(m)); } else if (ret == 0) { /* hash was done; update */ m->m_pkthdr.flowid = hash_val; M_HASHTYPE_SET(m, hash_type); *cpuid = rss_hash2cpuid(m->m_pkthdr.flowid, M_HASHTYPE_GET(m)); } else { /* ret < 0 */ /* no hash was done */ *cpuid = NETISR_CPUID_NONE; } return (m); }