diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2010-11-12 17:20:18 +0000 |
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2010-11-12 17:20:18 +0000 |
| commit | 5fbbc222c809dc1da75590d2ff48592da7a34233 (patch) | |
| tree | f1e172e71559ebd572f16647203a27e631184ee6 /sys | |
| parent | 1170f3d12ebd03d02f8bb4f075319f461e38d7d9 (diff) | |
| parent | 542aef4874baf2030a1e5fd995419e25398252d4 (diff) | |
Notes
Diffstat (limited to 'sys')
47 files changed, 2113 insertions, 558 deletions
diff --git a/sys/arm/conf/CAMBRIA b/sys/arm/conf/CAMBRIA index 174a7c785a8f..ed16ed83fab1 100644 --- a/sys/arm/conf/CAMBRIA +++ b/sys/arm/conf/CAMBRIA @@ -90,6 +90,10 @@ device ad7418 # AD7418 on I2C bus device cambria_fled # Font Panel LED on I2C bus device cambria_led # 8-LED latch +device gpio +device gpioled +device cambria_gpio # GPIO pins on J11 + device ata device atadisk # ATA disk drives device avila_ata # Gateworks CF/IDE support diff --git a/sys/arm/conf/CAMBRIA.hints b/sys/arm/conf/CAMBRIA.hints index 6a1f889349d2..f727c367ff99 100644 --- a/sys/arm/conf/CAMBRIA.hints +++ b/sys/arm/conf/CAMBRIA.hints @@ -54,6 +54,10 @@ hint.fled.0.addr=0x5a # Octal LED Latch hint.led_cambria.0.at="ixp0" +# GPIO pins +hint.gpio_cambria.0.at="iicbus0" +hint.gpio_cambria.0.addr=0x56 + # Analog Devices AD7418 temperature sensor hint.ad7418.0.at="iicbus0" hint.ad7418.0.addr=0x50 diff --git a/sys/arm/xscale/ixp425/cambria_gpio.c b/sys/arm/xscale/ixp425/cambria_gpio.c new file mode 100644 index 000000000000..89b07d852b7a --- /dev/null +++ b/sys/arm/xscale/ixp425/cambria_gpio.c @@ -0,0 +1,471 @@ +/*- + * Copyright (c) 2010, Andrew Thompson <thompsa@FreeBSD.org> + * 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 unmodified, 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. + */ + +/* + * GPIO driver for Gateworks Cambria + * + * Note: + * The Cambria PLD does not set the i2c ack bit after each write, if we used the + * regular iicbus interface it would abort the xfer after the address byte + * times out and not write our latch. To get around this we grab the iicbus and + * then do our own bit banging. This is a comprimise to changing all the iicbb + * device methods to allow a flag to be passed down and is similir to how Linux + * does it. + * + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> + +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/rman.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/gpio.h> + +#include <arm/xscale/ixp425/ixp425reg.h> +#include <arm/xscale/ixp425/ixp425var.h> +#include <arm/xscale/ixp425/ixdp425reg.h> + +#include <dev/iicbus/iiconf.h> +#include <dev/iicbus/iicbus.h> + +#include "iicbb_if.h" +#include "gpio_if.h" + +#define IIC_M_WR 0 /* write operation */ +#define PLD_ADDR 0xac /* slave address */ + +#define I2C_DELAY 10 + +#define GPIO_CONF_CLR(sc, reg, mask) \ + GPIO_CONF_WRITE_4(sc, reg, GPIO_CONF_READ_4(sc, reg) &~ (mask)) +#define GPIO_CONF_SET(sc, reg, mask) \ + GPIO_CONF_WRITE_4(sc, reg, GPIO_CONF_READ_4(sc, reg) | (mask)) + +#define GPIO_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define GPIO_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define GPIO_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) + +#define GPIO_PINS 5 +struct cambria_gpio_softc { + device_t sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_gpio_ioh; + struct mtx sc_mtx; + struct gpio_pin sc_pins[GPIO_PINS]; + uint8_t sc_latch; +}; + +struct cambria_gpio_pin { + const char *name; + int pin; + int flags; +}; + +extern struct ixp425_softc *ixp425_softc; + +static struct cambria_gpio_pin cambria_gpio_pins[GPIO_PINS] = { + { "GPIO0", 0, GPIO_PIN_OUTPUT }, + { "GPIO1", 1, GPIO_PIN_OUTPUT }, + { "GPIO2", 2, GPIO_PIN_OUTPUT }, + { "GPIO3", 3, GPIO_PIN_OUTPUT }, + { "GPIO4", 4, GPIO_PIN_OUTPUT }, +}; + +/* + * Helpers + */ +static int cambria_gpio_read(struct cambria_gpio_softc *, uint32_t, unsigned int *); +static int cambria_gpio_write(struct cambria_gpio_softc *); + +/* + * Driver stuff + */ +static int cambria_gpio_probe(device_t dev); +static int cambria_gpio_attach(device_t dev); +static int cambria_gpio_detach(device_t dev); + +/* + * GPIO interface + */ +static int cambria_gpio_pin_max(device_t dev, int *maxpin); +static int cambria_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps); +static int cambria_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t + *flags); +static int cambria_gpio_pin_getname(device_t dev, uint32_t pin, char *name); +static int cambria_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags); +static int cambria_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value); +static int cambria_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val); +static int cambria_gpio_pin_toggle(device_t dev, uint32_t pin); + +static int +i2c_getsda(struct cambria_gpio_softc *sc) +{ + uint32_t reg; + + mtx_lock(&Giant); + GPIO_CONF_SET(sc, IXP425_GPIO_GPOER, GPIO_I2C_SDA_BIT); + + reg = GPIO_CONF_READ_4(sc, IXP425_GPIO_GPINR); + mtx_unlock(&Giant); + return (reg & GPIO_I2C_SDA_BIT); +} + +static void +i2c_setsda(struct cambria_gpio_softc *sc, int val) +{ + + mtx_lock(&Giant); + GPIO_CONF_CLR(sc, IXP425_GPIO_GPOUTR, GPIO_I2C_SDA_BIT); + if (val) + GPIO_CONF_SET(sc, IXP425_GPIO_GPOER, GPIO_I2C_SDA_BIT); + else + GPIO_CONF_CLR(sc, IXP425_GPIO_GPOER, GPIO_I2C_SDA_BIT); + mtx_unlock(&Giant); + DELAY(I2C_DELAY); +} + +static void +i2c_setscl(struct cambria_gpio_softc *sc, int val) +{ + + mtx_lock(&Giant); + GPIO_CONF_CLR(sc, IXP425_GPIO_GPOUTR, GPIO_I2C_SCL_BIT); + if (val) + GPIO_CONF_SET(sc, IXP425_GPIO_GPOER, GPIO_I2C_SCL_BIT); + else + GPIO_CONF_CLR(sc, IXP425_GPIO_GPOER, GPIO_I2C_SCL_BIT); + mtx_unlock(&Giant); + DELAY(I2C_DELAY); +} + +static void +i2c_sendstart(struct cambria_gpio_softc *sc) +{ + i2c_setsda(sc, 1); + i2c_setscl(sc, 1); + i2c_setsda(sc, 0); + i2c_setscl(sc, 0); +} + +static void +i2c_sendstop(struct cambria_gpio_softc *sc) +{ + i2c_setscl(sc, 1); + i2c_setsda(sc, 1); + i2c_setscl(sc, 0); + i2c_setsda(sc, 0); +} + +static void +i2c_sendbyte(struct cambria_gpio_softc *sc, u_char data) +{ + int i; + + for (i=7; i>=0; i--) { + i2c_setsda(sc, data & (1<<i)); + i2c_setscl(sc, 1); + i2c_setscl(sc, 0); + } + i2c_setscl(sc, 1); + i2c_getsda(sc); + i2c_setscl(sc, 0); +} + +static u_char +i2c_readbyte(struct cambria_gpio_softc *sc) +{ + int i; + unsigned char data=0; + + for (i=7; i>=0; i--) + { + i2c_setscl(sc, 1); + if (i2c_getsda(sc)) + data |= (1<<i); + i2c_setscl(sc, 0); + } + return data; +} + +static int +cambria_gpio_read(struct cambria_gpio_softc *sc, uint32_t pin, unsigned int *val) +{ + device_t dev = sc->sc_dev; + int error; + + error = iicbus_request_bus(device_get_parent(dev), dev, + IIC_DONTWAIT); + if (error) + return (error); + + i2c_sendstart(sc); + i2c_sendbyte(sc, PLD_ADDR | LSB); + *val = (i2c_readbyte(sc) & (1 << pin)) != 0; + i2c_sendstop(sc); + + iicbus_release_bus(device_get_parent(dev), dev); + + return (0); +} + +static int +cambria_gpio_write(struct cambria_gpio_softc *sc) +{ + device_t dev = sc->sc_dev; + int error; + + error = iicbus_request_bus(device_get_parent(dev), dev, + IIC_DONTWAIT); + if (error) + return (error); + + i2c_sendstart(sc); + i2c_sendbyte(sc, PLD_ADDR & ~LSB); + i2c_sendbyte(sc, sc->sc_latch); + i2c_sendstop(sc); + + iicbus_release_bus(device_get_parent(dev), dev); + + return (0); +} + +static int +cambria_gpio_pin_max(device_t dev, int *maxpin) +{ + + *maxpin = GPIO_PINS - 1; + return (0); +} + +static int +cambria_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) +{ + struct cambria_gpio_softc *sc = device_get_softc(dev); + + if (pin >= GPIO_PINS) + return (EINVAL); + + *caps = sc->sc_pins[pin].gp_caps; + return (0); +} + +static int +cambria_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) +{ + struct cambria_gpio_softc *sc = device_get_softc(dev); + + if (pin >= GPIO_PINS) + return (EINVAL); + + *flags = sc->sc_pins[pin].gp_flags; + return (0); +} + +static int +cambria_gpio_pin_getname(device_t dev, uint32_t pin, char *name) +{ + struct cambria_gpio_softc *sc = device_get_softc(dev); + + if (pin >= GPIO_PINS) + return (EINVAL); + + memcpy(name, sc->sc_pins[pin].gp_name, GPIOMAXNAME); + return (0); +} + +static int +cambria_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) +{ + struct cambria_gpio_softc *sc = device_get_softc(dev); + int error; + + if (pin >= GPIO_PINS) + return (EINVAL); + + /* Filter out unwanted flags */ + if ((flags &= sc->sc_pins[pin].gp_caps) != flags) + return (EINVAL); + + /* Can't mix input/output together */ + if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) == + (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) + return (EINVAL); + + GPIO_LOCK(sc); + sc->sc_pins[pin].gp_flags = flags; + + sc->sc_latch |= (1 << pin); + error = cambria_gpio_write(sc); + GPIO_UNLOCK(sc); + + return (error); +} + +static int +cambria_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) +{ + struct cambria_gpio_softc *sc = device_get_softc(dev); + int error; + + if (pin >= GPIO_PINS || sc->sc_pins[pin].gp_flags != GPIO_PIN_OUTPUT) + return (EINVAL); + + GPIO_LOCK(sc); + if (value) + sc->sc_latch |= (1 << pin); + else + sc->sc_latch &= ~(1 << pin); + error = cambria_gpio_write(sc); + GPIO_UNLOCK(sc); + + return (error); +} + +static int +cambria_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) +{ + struct cambria_gpio_softc *sc = device_get_softc(dev); + int error = 0; + + if (pin >= GPIO_PINS) + return (EINVAL); + + GPIO_LOCK(sc); + if (sc->sc_pins[pin].gp_flags == GPIO_PIN_OUTPUT) + *val = (sc->sc_latch & (1 << pin)) ? 1 : 0; + else + error = cambria_gpio_read(sc, pin, val); + GPIO_UNLOCK(sc); + + return (error); +} + +static int +cambria_gpio_pin_toggle(device_t dev, uint32_t pin) +{ + struct cambria_gpio_softc *sc = device_get_softc(dev); + int error; + + if (pin >= GPIO_PINS || sc->sc_pins[pin].gp_flags != GPIO_PIN_OUTPUT) + return (EINVAL); + + GPIO_LOCK(sc); + sc->sc_latch ^= (1 << pin); + error = cambria_gpio_write(sc); + GPIO_UNLOCK(sc); + + return (error); +} + +static int +cambria_gpio_probe(device_t dev) +{ + + device_set_desc(dev, "Gateworks Cambria GPIO driver"); + return (0); +} + +static int +cambria_gpio_attach(device_t dev) +{ + struct cambria_gpio_softc *sc = device_get_softc(dev); + int pin; + + sc->sc_dev = dev; + sc->sc_iot = ixp425_softc->sc_iot; + sc->sc_gpio_ioh = ixp425_softc->sc_gpio_ioh; + + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, + MTX_DEF); + + for (pin = 0; pin < GPIO_PINS; pin++) { + struct cambria_gpio_pin *p = &cambria_gpio_pins[pin]; + + strncpy(sc->sc_pins[pin].gp_name, p->name, GPIOMAXNAME); + sc->sc_pins[pin].gp_pin = pin; + sc->sc_pins[pin].gp_caps = GPIO_PIN_INPUT|GPIO_PIN_OUTPUT; + sc->sc_pins[pin].gp_flags = 0; + cambria_gpio_pin_setflags(dev, pin, p->flags); + } + + device_add_child(dev, "gpioc", device_get_unit(dev)); + device_add_child(dev, "gpiobus", device_get_unit(dev)); + return (bus_generic_attach(dev)); +} + +static int +cambria_gpio_detach(device_t dev) +{ + struct cambria_gpio_softc *sc = device_get_softc(dev); + + KASSERT(mtx_initialized(&sc->sc_mtx), ("gpio mutex not initialized")); + + bus_generic_detach(dev); + + mtx_destroy(&sc->sc_mtx); + + return(0); +} + +static device_method_t cambria_gpio_methods[] = { + DEVMETHOD(device_probe, cambria_gpio_probe), + DEVMETHOD(device_attach, cambria_gpio_attach), + DEVMETHOD(device_detach, cambria_gpio_detach), + + /* GPIO protocol */ + DEVMETHOD(gpio_pin_max, cambria_gpio_pin_max), + DEVMETHOD(gpio_pin_getname, cambria_gpio_pin_getname), + DEVMETHOD(gpio_pin_getflags, cambria_gpio_pin_getflags), + DEVMETHOD(gpio_pin_getcaps, cambria_gpio_pin_getcaps), + DEVMETHOD(gpio_pin_setflags, cambria_gpio_pin_setflags), + DEVMETHOD(gpio_pin_get, cambria_gpio_pin_get), + DEVMETHOD(gpio_pin_set, cambria_gpio_pin_set), + DEVMETHOD(gpio_pin_toggle, cambria_gpio_pin_toggle), + {0, 0}, +}; + +static driver_t cambria_gpio_driver = { + "gpio_cambria", + cambria_gpio_methods, + sizeof(struct cambria_gpio_softc), +}; +static devclass_t cambria_gpio_devclass; +extern devclass_t gpiobus_devclass, gpioc_devclass; +extern driver_t gpiobus_driver, gpioc_driver; + +DRIVER_MODULE(gpio_cambria, iicbus, cambria_gpio_driver, cambria_gpio_devclass, 0, 0); +DRIVER_MODULE(gpiobus, gpio_cambria, gpiobus_driver, gpiobus_devclass, 0, 0); +DRIVER_MODULE(gpioc, gpio_cambria, gpioc_driver, gpioc_devclass, 0, 0); +MODULE_VERSION(gpio_cambria, 1); +MODULE_DEPEND(gpio_cambria, iicbus, 1, 1, 1); diff --git a/sys/arm/xscale/ixp425/files.avila b/sys/arm/xscale/ixp425/files.avila index 38b93296256b..5008e0910a12 100644 --- a/sys/arm/xscale/ixp425/files.avila +++ b/sys/arm/xscale/ixp425/files.avila @@ -6,4 +6,5 @@ arm/xscale/ixp425/avila_gpio.c optional avila_gpio arm/xscale/ixp425/cambria_exp_space.c standard arm/xscale/ixp425/cambria_fled.c optional cambria_fled arm/xscale/ixp425/cambria_led.c optional cambria_led +arm/xscale/ixp425/cambria_gpio.c optional cambria_gpio arm/xscale/ixp425/ixdp425_pci.c optional pci diff --git a/sys/conf/files b/sys/conf/files index ce2eb82ed4ba..c859ec82b3a5 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -2598,6 +2598,8 @@ netinet/ip_mroute.c optional mrouting inet | mrouting inet6 netinet/ip_options.c optional inet netinet/ip_output.c optional inet netinet/raw_ip.c optional inet +netinet/cc/cc.c optional inet +netinet/cc/cc_newreno.c optional inet netinet/sctp_asconf.c optional inet sctp netinet/sctp_auth.c optional inet sctp netinet/sctp_bsd_addr.c optional inet sctp diff --git a/sys/conf/files.powerpc b/sys/conf/files.powerpc index 021c51d413f9..d46b39fcf936 100644 --- a/sys/conf/files.powerpc +++ b/sys/conf/files.powerpc @@ -85,7 +85,6 @@ powerpc/aim/mmu_oea.c optional aim powerpc powerpc/aim/mmu_oea64.c optional aim powerpc/aim/mp_cpudep.c optional aim smp powerpc/aim/nexus.c optional aim -powerpc/aim/ofw_machdep.c optional aim powerpc/aim/ofwmagic.S optional aim powerpc/aim/slb.c optional aim powerpc64 powerpc/aim/swtch32.S optional aim powerpc @@ -131,6 +130,7 @@ powerpc/mpc85xx/nexus.c optional mpc85xx powerpc/mpc85xx/openpic_fdt.c optional fdt powerpc/mpc85xx/pci_fdt.c optional pci mpc85xx powerpc/ofw/ofw_cpu.c optional aim +powerpc/ofw/ofw_machdep.c optional aim powerpc/ofw/ofw_pcibus.c optional pci aim powerpc/ofw/ofw_pcib_pci.c optional pci aim powerpc/ofw/ofw_real.c optional aim diff --git a/sys/dev/acpica/acpi_cpu.c b/sys/dev/acpica/acpi_cpu.c index 52934af690ad..eb34753e4f85 100644 --- a/sys/dev/acpica/acpi_cpu.c +++ b/sys/dev/acpica/acpi_cpu.c @@ -668,9 +668,19 @@ acpi_cpu_cx_cst(struct acpi_cpu_softc *sc) count = MAX_CX_STATES; } - /* Set up all valid states. */ + sc->cpu_non_c3 = 0; sc->cpu_cx_count = 0; cx_ptr = sc->cpu_cx_states; + + /* + * C1 has been required since just after ACPI 1.0. + * Reserve the first slot for it. + */ + cx_ptr->type = ACPI_STATE_C0; + cx_ptr++; + sc->cpu_cx_count++; + + /* Set up all valid states. */ for (i = 0; i < count; i++) { pkg = &top->Package.Elements[i + 1]; if (!ACPI_PKG_VALID(pkg, 4) || @@ -685,9 +695,14 @@ acpi_cpu_cx_cst(struct acpi_cpu_softc *sc) /* Validate the state to see if we should use it. */ switch (cx_ptr->type) { case ACPI_STATE_C1: - sc->cpu_non_c3 = i; - cx_ptr++; - sc->cpu_cx_count++; + if (sc->cpu_cx_states[0].type == ACPI_STATE_C0) { + /* This is the first C1 state. Use the reserved slot. */ + sc->cpu_cx_states[0] = *cx_ptr; + } else { + sc->cpu_non_c3 = i; + cx_ptr++; + sc->cpu_cx_count++; + } continue; case ACPI_STATE_C2: sc->cpu_non_c3 = i; @@ -726,6 +741,13 @@ acpi_cpu_cx_cst(struct acpi_cpu_softc *sc) } AcpiOsFree(buf.Pointer); + /* If C1 state was not found, we need one now. */ + cx_ptr = sc->cpu_cx_states; + if (cx_ptr->type == ACPI_STATE_C0) { + cx_ptr->type = ACPI_STATE_C1; + cx_ptr->trans_lat = 0; + } + return (0); } diff --git a/sys/dev/gpio/gpiobus.c b/sys/dev/gpio/gpiobus.c index 3a044fd75f4c..d9736949596d 100644 --- a/sys/dev/gpio/gpiobus.c +++ b/sys/dev/gpio/gpiobus.c @@ -481,7 +481,7 @@ static device_method_t gpiobus_methods[] = { { 0, 0 } }; -static driver_t gpiobus_driver = { +driver_t gpiobus_driver = { "gpiobus", gpiobus_methods, sizeof(struct gpiobus_softc) diff --git a/sys/dev/gpio/gpioc.c b/sys/dev/gpio/gpioc.c index e92326e196a2..6a4c0c91938d 100644 --- a/sys/dev/gpio/gpioc.c +++ b/sys/dev/gpio/gpioc.c @@ -188,7 +188,7 @@ static device_method_t gpioc_methods[] = { { 0, 0 } }; -static driver_t gpioc_driver = { +driver_t gpioc_driver = { "gpioc", gpioc_methods, sizeof(struct gpioc_softc) diff --git a/sys/kern/kern_proc.c b/sys/kern/kern_proc.c index ad4e8d9c3477..9cfbc2011ca8 100644 --- a/sys/kern/kern_proc.c +++ b/sys/kern/kern_proc.c @@ -784,10 +784,10 @@ fill_kinfo_proc_only(struct proc *p, struct kinfo_proc *kp) calcru(p, &kp->ki_rusage.ru_utime, &kp->ki_rusage.ru_stime); PROC_SUNLOCK(p); calccru(p, &kp->ki_childutime, &kp->ki_childstime); - - /* Some callers want child-times in a single value */ + /* Some callers want child times in a single value. */ kp->ki_childtime = kp->ki_childstime; timevaladd(&kp->ki_childtime, &kp->ki_childutime); + tp = NULL; if (p->p_pgrp) { kp->ki_pgid = p->p_pgrp->pg_id; diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c index d6c9854fa1fb..e3e8e288a179 100644 --- a/sys/kern/uipc_socket.c +++ b/sys/kern/uipc_socket.c @@ -2386,6 +2386,7 @@ sosetopt(struct socket *so, struct sockopt *sopt) struct linger l; struct timeval tv; u_long val; + uint32_t val32; #ifdef MAC struct mac extmac; #endif @@ -2461,6 +2462,15 @@ sosetopt(struct socket *so, struct sockopt *sopt) so->so_fibnum = 0; } break; + + case SO_USER_COOKIE: + error = sooptcopyin(sopt, &val32, sizeof val32, + sizeof val32); + if (error) + goto bad; + so->so_user_cookie = val32; + break; + case SO_SNDBUF: case SO_RCVBUF: case SO_SNDLOWAT: diff --git a/sys/netinet/cc.h b/sys/netinet/cc.h new file mode 100644 index 000000000000..6f24f117bf54 --- /dev/null +++ b/sys/netinet/cc.h @@ -0,0 +1,161 @@ +/*- + * Copyright (c) 2007-2008 + * Swinburne University of Technology, Melbourne, Australia. + * Copyright (c) 2009-2010 Lawrence Stewart <lstewart@freebsd.org> + * Copyright (c) 2010 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed at the Centre for Advanced Internet + * Architectures, Swinburne University, by Lawrence Stewart and James Healy, + * made possible in part by a grant from the Cisco University Research Program + * Fund at Community Foundation Silicon Valley. + * + * Portions of this software were developed at the Centre for Advanced + * Internet Architectures, Swinburne University of Technology, Melbourne, + * Australia by David Hayes 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. + * + * $FreeBSD$ + */ + +/* + * This software was first released in 2007 by James Healy and Lawrence Stewart + * whilst working on the NewTCP research project at Swinburne University's + * Centre for Advanced Internet Architectures, Melbourne, Australia, which was + * made possible in part by a grant from the Cisco University Research Program + * Fund at Community Foundation Silicon Valley. More details are available at: + * http://caia.swin.edu.au/urp/newtcp/ + */ + +#ifndef _NETINET_CC_H_ +#define _NETINET_CC_H_ + +/* XXX: TCP_CA_NAME_MAX define lives in tcp.h for compat reasons. */ +#include <netinet/tcp.h> + +/* Global CC vars. */ +extern STAILQ_HEAD(cc_head, cc_algo) cc_list; +extern const int tcprexmtthresh; +extern struct cc_algo newreno_cc_algo; + +/* Define the new net.inet.tcp.cc sysctl tree. */ +SYSCTL_DECL(_net_inet_tcp_cc); + +/* CC housekeeping functions. */ +void cc_init(void); +int cc_register_algo(struct cc_algo *add_cc); +int cc_deregister_algo(struct cc_algo *remove_cc); + +/* + * Wrapper around transport structs that contain same-named congestion + * control variables. Allows algos to be shared amongst multiple CC aware + * transprots. + */ +struct cc_var { + void *cc_data; /* Per-connection private CC algorithm data. */ + int bytes_this_ack; /* # bytes acked by the current ACK. */ + tcp_seq curack; /* Most recent ACK. */ + uint32_t flags; /* Flags for cc_var (see below) */ + int type; /* Indicates which ptr is valid in ccvc. */ + union ccv_container { + struct tcpcb *tcp; + struct sctp_nets *sctp; + } ccvc; +}; + +/* cc_var flags. */ +#define CCF_ABC_SENTAWND 0x0001 /* ABC counted cwnd worth of bytes? */ +#define CCF_CWND_LIMITED 0x0002 /* Are we currently cwnd limited? */ + +/* ACK types passed to the ack_received() hook. */ +#define CC_ACK 0x0001 /* Regular in sequence ACK. */ +#define CC_DUPACK 0x0002 /* Duplicate ACK. */ +#define CC_PARTIALACK 0x0004 /* Not yet. */ +#define CC_SACK 0x0008 /* Not yet. */ + +/* + * Congestion signal types passed to the cong_signal() hook. The highest order 8 + * bits (0x01000000 - 0x80000000) are reserved for CC algos to declare their own + * congestion signal types. + */ +#define CC_ECN 0x000001/* ECN marked packet received. */ +#define CC_RTO 0x000002/* RTO fired. */ +#define CC_RTO_ERR 0x000004/* RTO fired in error. */ +#define CC_NDUPACK 0x000008/* Threshold of dupack's reached. */ + +/* + * Structure to hold data and function pointers that together represent a + * congestion control algorithm. + */ +struct cc_algo { + char name[TCP_CA_NAME_MAX]; + + /* Init global module state on kldload. */ + int (*mod_init)(void); + + /* Cleanup global module state on kldunload. */ + int (*mod_destroy)(void); + + /* Init CC state for a new control block. */ + int (*cb_init)(struct cc_var *ccv); + + /* Cleanup CC state for a terminating control block. */ + void (*cb_destroy)(struct cc_var *ccv); + + /* Init variables for a newly established connection. */ + void (*conn_init)(struct cc_var *ccv); + + /* Called on receipt of an ack. */ + void (*ack_received)(struct cc_var *ccv, uint16_t type); + + /* Called on detection of a congestion signal. */ + void (*cong_signal)(struct cc_var *ccv, uint32_t type); + + /* Called after exiting congestion recovery. */ + void (*post_recovery)(struct cc_var *ccv); + + /* Called when data transfer resumes after an idle period. */ + void (*after_idle)(struct cc_var *ccv); + + STAILQ_ENTRY (cc_algo) entries; +}; + +/* Macro to obtain the CC algo's struct ptr. */ +#define CC_ALGO(tp) ((tp)->cc_algo) + +/* Macro to obtain the CC algo's data ptr. */ +#define CC_DATA(tp) ((tp)->ccv->cc_data) + +/* Macro to obtain the system default CC algo's struct ptr. */ +#define CC_DEFAULT() STAILQ_FIRST(&cc_list) + +extern struct rwlock cc_list_lock; +#define CC_LIST_LOCK_INIT() rw_init(&cc_list_lock, "cc_list") +#define CC_LIST_LOCK_DESTROY() rw_destroy(&cc_list_lock) +#define CC_LIST_RLOCK() rw_rlock(&cc_list_lock) +#define CC_LIST_RUNLOCK() rw_runlock(&cc_list_lock) +#define CC_LIST_WLOCK() rw_wlock(&cc_list_lock) +#define CC_LIST_WUNLOCK() rw_wunlock(&cc_list_lock) +#define CC_LIST_WLOCK_ASSERT() rw_assert(&cc_list_lock, RA_WLOCKED) + +#endif /* _NETINET_CC_H_ */ diff --git a/sys/netinet/cc/cc.c b/sys/netinet/cc/cc.c new file mode 100644 index 000000000000..4643ca40105e --- /dev/null +++ b/sys/netinet/cc/cc.c @@ -0,0 +1,340 @@ +/*- + * Copyright (c) 2007-2008 + * Swinburne University of Technology, Melbourne, Australia. + * Copyright (c) 2009-2010 Lawrence Stewart <lstewart@freebsd.org> + * Copyright (c) 2010 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed at the Centre for Advanced Internet + * Architectures, Swinburne University, by Lawrence Stewart and James Healy, + * made possible in part by a grant from the Cisco University Research Program + * Fund at Community Foundation Silicon Valley. + * + * Portions of this software were developed at the Centre for Advanced + * Internet Architectures, Swinburne University of Technology, Melbourne, + * Australia by David Hayes 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. + */ + +/* + * This software was first released in 2007 by James Healy and Lawrence Stewart + * whilst working on the NewTCP research project at Swinburne University's + * Centre for Advanced Internet Architectures, Melbourne, Australia, which was + * made possible in part by a grant from the Cisco University Research Program + * Fund at Community Foundation Silicon Valley. More details are available at: + * http://caia.swin.edu.au/urp/newtcp/ + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/libkern.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/mutex.h> +#include <sys/queue.h> +#include <sys/rwlock.h> +#include <sys/sbuf.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/sysctl.h> + +#include <net/if.h> +#include <net/if_var.h> + +#include <netinet/cc.h> +#include <netinet/in.h> +#include <netinet/in_pcb.h> +#include <netinet/tcp_var.h> + +#include <netinet/cc/cc_module.h> + +/* + * List of available cc algorithms on the current system. First element + * is used as the system default CC algorithm. + */ +struct cc_head cc_list = STAILQ_HEAD_INITIALIZER(cc_list); + +/* Protects the cc_list TAILQ. */ +struct rwlock cc_list_lock; + +/* + * Set the default CC algorithm to new_default. The default is identified + * by being the first element in the cc_list TAILQ. + */ +static void +cc_set_default(struct cc_algo *new_default) +{ + CC_LIST_WLOCK_ASSERT(); + + /* + * Make the requested system default CC algorithm the first element in + * the list if it isn't already. + */ + if (new_default != CC_DEFAULT()) { + STAILQ_REMOVE(&cc_list, new_default, cc_algo, entries); + STAILQ_INSERT_HEAD(&cc_list, new_default, entries); + } +} + +/* + * Sysctl handler to show and change the default CC algorithm. + */ +static int +cc_default_algo(SYSCTL_HANDLER_ARGS) +{ + struct cc_algo *funcs; + int err, found; + + err = found = 0; + + if (req->newptr == NULL) { + char default_cc[TCP_CA_NAME_MAX]; + + /* Just print the current default. */ + CC_LIST_RLOCK(); + strlcpy(default_cc, CC_DEFAULT()->name, TCP_CA_NAME_MAX); + CC_LIST_RUNLOCK(); + err = sysctl_handle_string(oidp, default_cc, 1, req); + } else { + /* Find algo with specified name and set it to default. */ + CC_LIST_WLOCK(); + STAILQ_FOREACH(funcs, &cc_list, entries) { + if (strncmp((char *)req->newptr, funcs->name, + TCP_CA_NAME_MAX) == 0) { + found = 1; + cc_set_default(funcs); + } + } + CC_LIST_WUNLOCK(); + + if (!found) + err = ESRCH; + } + + return (err); +} + +/* + * Sysctl handler to display the list of available CC algorithms. + */ +static int +cc_list_available(SYSCTL_HANDLER_ARGS) +{ + struct cc_algo *algo; + struct sbuf *s; + int err, first; + + err = 0; + first = 1; + s = sbuf_new(NULL, NULL, TCP_CA_NAME_MAX, SBUF_AUTOEXTEND); + + if (s == NULL) + return (ENOMEM); + + CC_LIST_RLOCK(); + STAILQ_FOREACH(algo, &cc_list, entries) { + err = sbuf_printf(s, first ? "%s" : ", %s", algo->name); + if (err) + break; + first = 0; + } + CC_LIST_RUNLOCK(); + + if (!err) { + sbuf_finish(s); + err = sysctl_handle_string(oidp, sbuf_data(s), 1, req); + } + + sbuf_delete(s); + return (err); +} + +/* + * Initialise CC subsystem on system boot. + */ +void +cc_init() +{ + CC_LIST_LOCK_INIT(); + STAILQ_INIT(&cc_list); +} + +/* + * Returns non-zero on success, 0 on failure. + */ +int +cc_deregister_algo(struct cc_algo *remove_cc) +{ + struct cc_algo *funcs, *tmpfuncs; + struct tcpcb *tp; + struct inpcb *inp; + int err; + + err = ENOENT; + + /* Never allow newreno to be deregistered. */ + if (&newreno_cc_algo == remove_cc) + return (EPERM); + + /* Remove algo from cc_list so that new connections can't use it. */ + CC_LIST_WLOCK(); + STAILQ_FOREACH_SAFE(funcs, &cc_list, entries, tmpfuncs) { + if (funcs == remove_cc) { + /* + * If we're removing the current system default, + * reset the default to newreno. + */ + if (strncmp(CC_DEFAULT()->name, remove_cc->name, + TCP_CA_NAME_MAX) == 0) + cc_set_default(&newreno_cc_algo); + + STAILQ_REMOVE(&cc_list, funcs, cc_algo, entries); + err = 0; + break; + } + } + CC_LIST_WUNLOCK(); + + if (!err) { + /* + * Check all active control blocks and change any that are + * using this algorithm back to newreno. If the algorithm that + * was in use requires cleanup code to be run, call it. + * + * New connections already part way through being initialised + * with the CC algo we're removing will not race with this code + * because the INP_INFO_WLOCK is held during initialisation. + * We therefore don't enter the loop below until the connection + * list has stabilised. + */ + INP_INFO_RLOCK(&V_tcbinfo); + LIST_FOREACH(inp, &V_tcb, inp_list) { + INP_WLOCK(inp); + /* Important to skip tcptw structs. */ + if (!(inp->inp_flags & INP_TIMEWAIT) && + (tp = intotcpcb(inp)) != NULL) { + /* + * By holding INP_WLOCK here, we are + * assured that the connection is not + * currently executing inside the CC + * module's functions i.e. it is safe to + * make the switch back to newreno. + */ + if (CC_ALGO(tp) == remove_cc) { + tmpfuncs = CC_ALGO(tp); + /* Newreno does not require any init. */ + CC_ALGO(tp) = &newreno_cc_algo; + if (tmpfuncs->cb_destroy != NULL) + tmpfuncs->cb_destroy(tp->ccv); + } + } + INP_WUNLOCK(inp); + } + INP_INFO_RUNLOCK(&V_tcbinfo); + } + + return (err); +} + +/* + * Returns 0 on success, non-zero on failure. + */ +int +cc_register_algo(struct cc_algo *add_cc) +{ + struct cc_algo *funcs; + int err; + + err = 0; + + /* + * Iterate over list of registered CC algorithms and make sure + * we're not trying to add a duplicate. + */ + CC_LIST_WLOCK(); + STAILQ_FOREACH(funcs, &cc_list, entries) { + if (funcs == add_cc || strncmp(funcs->name, add_cc->name, + TCP_CA_NAME_MAX) == 0) + err = EEXIST; + } + + if (!err) + STAILQ_INSERT_TAIL(&cc_list, add_cc, entries); + + CC_LIST_WUNLOCK(); + + return (err); +} + +/* + * Handles kld related events. Returns 0 on success, non-zero on failure. + */ +int +cc_modevent(module_t mod, int event_type, void *data) +{ + struct cc_algo *algo; + int err; + + err = 0; + algo = (struct cc_algo *)data; + + switch(event_type) { + case MOD_LOAD: + if (algo->mod_init != NULL) + err = algo->mod_init(); + if (!err) + err = cc_register_algo(algo); + break; + + case MOD_QUIESCE: + case MOD_SHUTDOWN: + case MOD_UNLOAD: + err = cc_deregister_algo(algo); + if (!err && algo->mod_destroy != NULL) + algo->mod_destroy(); + if (err == ENOENT) + err = 0; + break; + + default: + err = EINVAL; + break; + } + + return (err); +} + +/* Declare sysctl tree and populate it. */ +SYSCTL_NODE(_net_inet_tcp, OID_AUTO, cc, CTLFLAG_RW, NULL, + "congestion control related settings"); + +SYSCTL_PROC(_net_inet_tcp_cc, OID_AUTO, algorithm, CTLTYPE_STRING|CTLFLAG_RW, + NULL, 0, cc_default_algo, "A", "default congestion control algorithm"); + +SYSCTL_PROC(_net_inet_tcp_cc, OID_AUTO, available, CTLTYPE_STRING|CTLFLAG_RD, + NULL, 0, cc_list_available, "A", + "list available congestion control algorithms"); diff --git a/sys/netinet/cc/cc_module.h b/sys/netinet/cc/cc_module.h new file mode 100644 index 000000000000..f3fe7525c5b9 --- /dev/null +++ b/sys/netinet/cc/cc_module.h @@ -0,0 +1,70 @@ +/*- + * Copyright (c) 2009-2010 Lawrence Stewart <lstewart@freebsd.org> + * All rights reserved. + * + * This software was developed by Lawrence Stewart while studying at the Centre + * for Advanced Internet Architectures, Swinburne University, made possible in + * part by a grant from the Cisco University Research Program Fund at Community + * Foundation Silicon Valley. + * + * 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. + * + * $FreeBSD$ + */ + +/* + * This software was first released in 2009 by Lawrence Stewart as part of the + * NewTCP research project at Swinburne University's Centre for Advanced + * Internet Architectures, Melbourne, Australia, which was made possible in part + * by a grant from the Cisco University Research Program Fund at Community + * Foundation Silicon Valley. More details are available at: + * http://caia.swin.edu.au/urp/newtcp/ + */ + +#ifndef _NETINET_CC_MODULE_H_ +#define _NETINET_CC_MODULE_H_ + +/* + * Allows a CC algorithm to manipulate a commonly named CC variable regardless + * of the transport protocol and associated C struct. + * XXXLAS: Out of action until the work to support SCTP is done. + * +#define CCV(ccv, what) \ +(*( \ + (ccv)->type == IPPROTO_TCP ? &(ccv)->ccvc.tcp->what : \ + &(ccv)->ccvc.sctp->what \ +)) + */ +#define CCV(ccv, what) (ccv)->ccvc.tcp->what + +#define DECLARE_CC_MODULE(ccname, ccalgo) \ + static moduledata_t cc_##ccname = { \ + .name = #ccname, \ + .evhand = cc_modevent, \ + .priv = ccalgo \ + }; \ + DECLARE_MODULE(ccname, cc_##ccname, \ + SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY) + +int cc_modevent(module_t mod, int type, void *data); + +#endif /* _NETINET_CC_MODULE_H_ */ diff --git a/sys/netinet/cc/cc_newreno.c b/sys/netinet/cc/cc_newreno.c new file mode 100644 index 000000000000..e3835100ddb6 --- /dev/null +++ b/sys/netinet/cc/cc_newreno.c @@ -0,0 +1,231 @@ +/*- + * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1994, 1995 + * The Regents of the University of California. + * Copyright (c) 2007-2008,2010 + * Swinburne University of Technology, Melbourne, Australia. + * Copyright (c) 2009-2010 Lawrence Stewart <lstewart@freebsd.org> + * Copyright (c) 2010 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed at the Centre for Advanced Internet + * Architectures, Swinburne University, by Lawrence Stewart, James Healy and + * David Hayes, made possible in part by a grant from the Cisco University + * Research Program Fund at Community Foundation Silicon Valley. + * + * Portions of this software were developed at the Centre for Advanced + * Internet Architectures, Swinburne University of Technology, Melbourne, + * Australia by David Hayes 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. + */ + +/* + * This software was first released in 2007 by James Healy and Lawrence Stewart + * whilst working on the NewTCP research project at Swinburne University's + * Centre for Advanced Internet Architectures, Melbourne, Australia, which was + * made possible in part by a grant from the Cisco University Research Program + * Fund at Community Foundation Silicon Valley. More details are available at: + * http://caia.swin.edu.au/urp/newtcp/ + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/sysctl.h> + +#include <net/if.h> +#include <net/if_var.h> + +#include <netinet/cc.h> +#include <netinet/in.h> +#include <netinet/in_pcb.h> +#include <netinet/tcp_seq.h> +#include <netinet/tcp_var.h> + +#include <netinet/cc/cc_module.h> + +void newreno_ack_received(struct cc_var *ccv, uint16_t type); +void newreno_cong_signal(struct cc_var *ccv, uint32_t type); +void newreno_post_recovery(struct cc_var *ccv); +void newreno_after_idle(struct cc_var *ccv); + +struct cc_algo newreno_cc_algo = { + .name = "newreno", + .ack_received = newreno_ack_received, + .cong_signal = newreno_cong_signal, + .post_recovery = newreno_post_recovery, + .after_idle = newreno_after_idle +}; + +/* + * Increase cwnd on receipt of a successful ACK: + * if cwnd <= ssthresh, increases by 1 MSS per ACK + * if cwnd > ssthresh, increase by ~1 MSS per RTT + */ +void +newreno_ack_received(struct cc_var *ccv, uint16_t type) +{ + if (type == CC_ACK && !IN_RECOVERY(CCV(ccv, t_flags)) && + (ccv->flags & CCF_CWND_LIMITED)) { + u_int cw = CCV(ccv, snd_cwnd); + u_int incr = CCV(ccv, t_maxseg); + + /* + * Regular in-order ACK, open the congestion window. + * Method depends on which congestion control state we're + * in (slow start or cong avoid) and if ABC (RFC 3465) is + * enabled. + * + * slow start: cwnd <= ssthresh + * cong avoid: cwnd > ssthresh + * + * slow start and ABC (RFC 3465): + * Grow cwnd exponentially by the amount of data + * ACKed capping the max increment per ACK to + * (abc_l_var * maxseg) bytes. + * + * slow start without ABC (RFC 5681): + * Grow cwnd exponentially by maxseg per ACK. + * + * cong avoid and ABC (RFC 3465): + * Grow cwnd linearly by maxseg per RTT for each + * cwnd worth of ACKed data. + * + * cong avoid without ABC (RFC 5681): + * Grow cwnd linearly by approximately maxseg per RTT using + * maxseg^2 / cwnd per ACK as the increment. + * If cwnd > maxseg^2, fix the cwnd increment at 1 byte to + * avoid capping cwnd. + */ + if (cw > CCV(ccv, snd_ssthresh)) { + if (V_tcp_do_rfc3465) { + if (ccv->flags & CCF_ABC_SENTAWND) + ccv->flags &= ~CCF_ABC_SENTAWND; + else + incr = 0; + } else + incr = max((incr * incr / cw), 1); + } else if (V_tcp_do_rfc3465) { + /* + * In slow-start with ABC enabled and no RTO in sight? + * (Must not use abc_l_var > 1 if slow starting after + * an RTO. On RTO, snd_nxt = snd_una, so the + * snd_nxt == snd_max check is sufficient to + * handle this). + * + * XXXLAS: Find a way to signal SS after RTO that + * doesn't rely on tcpcb vars. + */ + if (CCV(ccv, snd_nxt) == CCV(ccv, snd_max)) + incr = min(ccv->bytes_this_ack, + V_tcp_abc_l_var * CCV(ccv, t_maxseg)); + else + incr = min(ccv->bytes_this_ack, CCV(ccv, t_maxseg)); + } + /* ABC is on by default, so incr equals 0 frequently. */ + if (incr > 0) + CCV(ccv, snd_cwnd) = min(cw + incr, + TCP_MAXWIN << CCV(ccv, snd_scale)); + } +} + +/* + * manage congestion signals + */ +void +newreno_cong_signal(struct cc_var *ccv, uint32_t type) +{ + u_int win; + + win = max(CCV(ccv, snd_cwnd) / 2 / CCV(ccv, t_maxseg), 2) * + CCV(ccv, t_maxseg); + + switch (type) { + case CC_NDUPACK: + if (!IN_FASTRECOVERY(CCV(ccv, t_flags))) { + if (!IN_CONGRECOVERY(CCV(ccv, t_flags))) + CCV(ccv, snd_ssthresh) = win; + ENTER_RECOVERY(CCV(ccv, t_flags)); + } + break; + case CC_ECN: + if (!IN_CONGRECOVERY(CCV(ccv, t_flags))) { + CCV(ccv, snd_ssthresh) = win; + CCV(ccv, snd_cwnd) = win; + ENTER_CONGRECOVERY(CCV(ccv, t_flags)); + } + break; + } +} + +/* + * decrease the cwnd in response to packet loss or a transmit timeout. + * th can be null, in which case cwnd will be set according to reno instead + * of new reno. + */ +void +newreno_post_recovery(struct cc_var *ccv) +{ + if (IN_FASTRECOVERY(CCV(ccv, t_flags))) { + /* + * Fast recovery will conclude after returning from this + * function. Window inflation should have left us with + * approximately snd_ssthresh outstanding data. But in case we + * would be inclined to send a burst, better to do it via the + * slow start mechanism. + * + * XXXLAS: Find a way to do this without needing curack + */ + if (SEQ_GT(ccv->curack + CCV(ccv, snd_ssthresh), + CCV(ccv, snd_max))) + CCV(ccv, snd_cwnd) = CCV(ccv, snd_max) - + ccv->curack + CCV(ccv, t_maxseg); + else + CCV(ccv, snd_cwnd) = CCV(ccv, snd_ssthresh); + } +} + +/* + * if a connection has been idle for a while and more data is ready to be sent, + * reset cwnd + */ +void +newreno_after_idle(struct cc_var *ccv) +{ + /* + * We have been idle for "a while" and no acks are expected to clock out + * any data we send -- slow start to get ack "clock" running again. + */ + if (V_tcp_do_rfc3390) + CCV(ccv, snd_cwnd) = min(4 * CCV(ccv, t_maxseg), + max(2 * CCV(ccv, t_maxseg), 4380)); + else + CCV(ccv, snd_cwnd) = CCV(ccv, t_maxseg) * 2; +} + + +DECLARE_CC_MODULE(newreno, &newreno_cc_algo); diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h index cf5d8d03a90e..fdcc5fd4c1df 100644 --- a/sys/netinet/ip_fw.h +++ b/sys/netinet/ip_fw.h @@ -192,10 +192,13 @@ enum ipfw_opcodes { /* arguments (4 byte each) */ O_SETFIB, /* arg1=FIB number */ O_FIB, /* arg1=FIB desired fib number */ + + O_SOCKARG, /* socket argument */ O_LAST_OPCODE /* not an opcode! */ }; + /* * The extension header are filtered only for presence using a bit * vector with a flag for each header. diff --git a/sys/netinet/ipfw/ip_fw2.c b/sys/netinet/ipfw/ip_fw2.c index c291089c6f85..43b2d1114295 100644 --- a/sys/netinet/ipfw/ip_fw2.c +++ b/sys/netinet/ipfw/ip_fw2.c @@ -1801,6 +1801,39 @@ do { \ match = 1; break; + case O_SOCKARG: { + struct inpcb *inp = args->inp; + struct inpcbinfo *pi; + + if (is_ipv6) /* XXX can we remove this ? */ + break; + + if (proto == IPPROTO_TCP) + pi = &V_tcbinfo; + else if (proto == IPPROTO_UDP) + pi = &V_udbinfo; + else + break; + + /* For incomming packet, lookup up the + inpcb using the src/dest ip/port tuple */ + if (inp == NULL) { + INP_INFO_RLOCK(pi); + inp = in_pcblookup_hash(pi, + src_ip, htons(src_port), + dst_ip, htons(dst_port), + 0, NULL); + INP_INFO_RUNLOCK(pi); + } + + if (inp && inp->inp_socket) { + tablearg = inp->inp_socket->so_user_cookie; + if (tablearg) + match = 1; + } + break; + } + case O_TAGGED: { struct m_tag *mtag; uint32_t tag = (cmd->arg1 == IP_FW_TABLEARG) ? diff --git a/sys/netinet/ipfw/ip_fw_sockopt.c b/sys/netinet/ipfw/ip_fw_sockopt.c index c50572873a16..0c903eedf5c7 100644 --- a/sys/netinet/ipfw/ip_fw_sockopt.c +++ b/sys/netinet/ipfw/ip_fw_sockopt.c @@ -572,6 +572,7 @@ check_ipfw_struct(struct ip_fw *rule, int size) case O_IPTOS: case O_IPPRECEDENCE: case O_IPVER: + case O_SOCKARG: case O_TCPWIN: case O_TCPFLAGS: case O_TCPOPTS: diff --git a/sys/netinet/libalias/alias_sctp.c b/sys/netinet/libalias/alias_sctp.c index 22cf5cd65457..3bd5331d1764 100644 --- a/sys/netinet/libalias/alias_sctp.c +++ b/sys/netinet/libalias/alias_sctp.c @@ -1,9 +1,7 @@ -/** - * @file alias_sctp.c - * Copyright (c) 2008, Centre for Advanced Internet Architectures - * Swinburne University of Technology, Melbourne, Australia - * (CRICOS number 00111D). - * +/*- + * Copyright (c) 2008 + * Swinburne University of Technology, Melbourne, Australia. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -12,11 +10,7 @@ * 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 names of the authors, the "Centre for Advanced Internet Architectures" - * and "Swinburne University of Technology" may not be used to endorse - * or promote products derived from this software without specific - * prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 @@ -28,7 +22,9 @@ * 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. - * + */ + +/* * Alias_sctp forms part of the libalias kernel module to handle * Network Address Translation (NAT) for the SCTP protocol. * diff --git a/sys/netinet/libalias/alias_sctp.h b/sys/netinet/libalias/alias_sctp.h index b4608ee4bbe3..80ed96568d4d 100644 --- a/sys/netinet/libalias/alias_sctp.h +++ b/sys/netinet/libalias/alias_sctp.h @@ -1,9 +1,7 @@ -/** - * @file alias_sctp.h - * Copyright (c) 2008, Centre for Advanced Internet Architectures - * Swinburne University of Technology, Melbourne, Australia - * (CRICOS number 00111D). - * +/*- + * Copyright (c) 2008 + * Swinburne University of Technology, Melbourne, Australia. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -12,11 +10,7 @@ * 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 names of the authors, the "Centre for Advanced Internet Architectures" - * and "Swinburne University of Technology" may not be used to endorse - * or promote products derived from this software without specific - * prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 @@ -28,7 +22,9 @@ * 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. - * + */ + +/* * Alias_sctp forms part of the libalias kernel module to handle * Network Address Translation (NAT) for the SCTP protocol. * diff --git a/sys/netinet/siftr.c b/sys/netinet/siftr.c index 176d0d0a30fe..5f2a6927b2fa 100644 --- a/sys/netinet/siftr.c +++ b/sys/netinet/siftr.c @@ -1,7 +1,6 @@ /*- - * Copyright (c) 2007-2009, Centre for Advanced Internet Architectures - * Swinburne University of Technology, Melbourne, Australia - * (CRICOS number 00111D). + * Copyright (c) 2007-2009 + * Swinburne University of Technology, Melbourne, Australia. * Copyright (c) 2009-2010, The FreeBSD Foundation * All rights reserved. * diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c index 22a2ea4a89c7..8fb9a52dc2a8 100644 --- a/sys/netinet/tcp_input.c +++ b/sys/netinet/tcp_input.c @@ -1,6 +1,20 @@ /*- * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1994, 1995 * The Regents of the University of California. All rights reserved. + * Copyright (c) 2007-2008,2010 + * Swinburne University of Technology, Melbourne, Australia. + * Copyright (c) 2009-2010 Lawrence Stewart <lstewart@freebsd.org> + * Copyright (c) 2010 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed at the Centre for Advanced Internet + * Architectures, Swinburne University, by Lawrence Stewart, James Healy and + * David Hayes, made possible in part by a grant from the Cisco University + * Research Program Fund at Community Foundation Silicon Valley. + * + * Portions of this software were developed at the Centre for Advanced + * Internet Architectures, Swinburne University of Technology, Melbourne, + * Australia by David Hayes 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 @@ -61,6 +75,7 @@ __FBSDID("$FreeBSD$"); #define TCPSTATES /* for logging */ +#include <netinet/cc.h> #include <netinet/in.h> #include <netinet/in_pcb.h> #include <netinet/in_systm.h> @@ -75,7 +90,6 @@ __FBSDID("$FreeBSD$"); #include <netinet6/in6_pcb.h> #include <netinet6/ip6_var.h> #include <netinet6/nd6.h> -#include <netinet/tcp.h> #include <netinet/tcp_fsm.h> #include <netinet/tcp_seq.h> #include <netinet/tcp_timer.h> @@ -96,7 +110,7 @@ __FBSDID("$FreeBSD$"); #include <security/mac/mac_framework.h> -static const int tcprexmtthresh = 3; +const int tcprexmtthresh = 3; VNET_DEFINE(struct tcpstat, tcpstat); SYSCTL_VNET_STRUCT(_net_inet_tcp, TCPCTL_STATS, stats, CTLFLAG_RW, @@ -132,19 +146,16 @@ SYSCTL_VNET_INT(_net_inet_tcp, OID_AUTO, rfc3042, CTLFLAG_RW, "Enable RFC 3042 (Limited Transmit)"); VNET_DEFINE(int, tcp_do_rfc3390) = 1; -#define V_tcp_do_rfc3390 VNET(tcp_do_rfc3390) SYSCTL_VNET_INT(_net_inet_tcp, OID_AUTO, rfc3390, CTLFLAG_RW, &VNET_NAME(tcp_do_rfc3390), 0, "Enable RFC 3390 (Increasing TCP's Initial Congestion Window)"); VNET_DEFINE(int, tcp_do_rfc3465) = 1; -#define V_tcp_do_rfc3465 VNET(tcp_do_rfc3465) SYSCTL_VNET_INT(_net_inet_tcp, OID_AUTO, rfc3465, CTLFLAG_RW, &VNET_NAME(tcp_do_rfc3465), 0, "Enable RFC 3465 (Appropriate Byte Counting)"); VNET_DEFINE(int, tcp_abc_l_var) = 2; -#define V_tcp_abc_l_var VNET(tcp_abc_l_var) SYSCTL_VNET_INT(_net_inet_tcp, OID_AUTO, abc_l_var, CTLFLAG_RW, &VNET_NAME(tcp_abc_l_var), 2, "Cap the max cwnd increment during slow-start to this number of segments"); @@ -203,8 +214,10 @@ static void tcp_pulloutofband(struct socket *, struct tcphdr *, struct mbuf *, int); static void tcp_xmit_timer(struct tcpcb *, int); static void tcp_newreno_partial_ack(struct tcpcb *, struct tcphdr *); -static void inline - tcp_congestion_exp(struct tcpcb *); +static void inline cc_ack_received(struct tcpcb *tp, struct tcphdr *th, + uint16_t type); +static void inline cc_conn_init(struct tcpcb *tp); +static void inline cc_post_recovery(struct tcpcb *tp, struct tcphdr *th); /* * Kernel module interface for updating tcpstat. The argument is an index @@ -220,20 +233,188 @@ kmod_tcpstat_inc(int statnum) (*((u_long *)&V_tcpstat + statnum))++; } +/* + * CC wrapper hook functions + */ static void inline -tcp_congestion_exp(struct tcpcb *tp) +cc_ack_received(struct tcpcb *tp, struct tcphdr *th, uint16_t type) { - u_int win; - - win = min(tp->snd_wnd, tp->snd_cwnd) / - 2 / tp->t_maxseg; - if (win < 2) - win = 2; - tp->snd_ssthresh = win * tp->t_maxseg; - ENTER_FASTRECOVERY(tp); - tp->snd_recover = tp->snd_max; - if (tp->t_flags & TF_ECN_PERMIT) - tp->t_flags |= TF_ECN_SND_CWR; + INP_WLOCK_ASSERT(tp->t_inpcb); + + tp->ccv->bytes_this_ack = BYTES_THIS_ACK(tp, th); + if (tp->snd_cwnd == min(tp->snd_cwnd, tp->snd_wnd)) + tp->ccv->flags |= CCF_CWND_LIMITED; + else + tp->ccv->flags &= ~CCF_CWND_LIMITED; + + if (type == CC_ACK) { + if (tp->snd_cwnd > tp->snd_ssthresh) { + tp->t_bytes_acked += min(tp->ccv->bytes_this_ack, + V_tcp_abc_l_var * tp->t_maxseg); + if (tp->t_bytes_acked >= tp->snd_cwnd) { + tp->t_bytes_acked -= tp->snd_cwnd; + tp->ccv->flags |= CCF_ABC_SENTAWND; + } + } else { + tp->ccv->flags &= ~CCF_ABC_SENTAWND; + tp->t_bytes_acked = 0; + } + } + + if (CC_ALGO(tp)->ack_received != NULL) { + /* XXXLAS: Find a way to live without this */ + tp->ccv->curack = th->th_ack; + CC_ALGO(tp)->ack_received(tp->ccv, type); + } +} + +static void inline +cc_conn_init(struct tcpcb *tp) +{ + struct hc_metrics_lite metrics; + struct inpcb *inp = tp->t_inpcb; + int rtt; +#ifdef INET6 + int isipv6 = ((inp->inp_vflag & INP_IPV6) != 0) ? 1 : 0; +#endif + + INP_WLOCK_ASSERT(tp->t_inpcb); + + tcp_hc_get(&inp->inp_inc, &metrics); + + if (tp->t_srtt == 0 && (rtt = metrics.rmx_rtt)) { + tp->t_srtt = rtt; + tp->t_rttbest = tp->t_srtt + TCP_RTT_SCALE; + TCPSTAT_INC(tcps_usedrtt); + if (metrics.rmx_rttvar) { + tp->t_rttvar = metrics.rmx_rttvar; + TCPSTAT_INC(tcps_usedrttvar); + } else { + /* default variation is +- 1 rtt */ + tp->t_rttvar = + tp->t_srtt * TCP_RTTVAR_SCALE / TCP_RTT_SCALE; + } + TCPT_RANGESET(tp->t_rxtcur, + ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1, + tp->t_rttmin, TCPTV_REXMTMAX); + } + if (metrics.rmx_ssthresh) { + /* + * There's some sort of gateway or interface + * buffer limit on the path. Use this to set + * the slow start threshhold, but set the + * threshold to no less than 2*mss. + */ + tp->snd_ssthresh = max(2 * tp->t_maxseg, metrics.rmx_ssthresh); + TCPSTAT_INC(tcps_usedssthresh); + } + + /* + * Set the slow-start flight size depending on whether this + * is a local network or not. + * + * Extend this so we cache the cwnd too and retrieve it here. + * Make cwnd even bigger than RFC3390 suggests but only if we + * have previous experience with the remote host. Be careful + * not make cwnd bigger than remote receive window or our own + * send socket buffer. Maybe put some additional upper bound + * on the retrieved cwnd. Should do incremental updates to + * hostcache when cwnd collapses so next connection doesn't + * overloads the path again. + * + * XXXAO: Initializing the CWND from the hostcache is broken + * and in its current form not RFC conformant. It is disabled + * until fixed or removed entirely. + * + * RFC3390 says only do this if SYN or SYN/ACK didn't got lost. + * We currently check only in syncache_socket for that. + */ +/* #define TCP_METRICS_CWND */ +#ifdef TCP_METRICS_CWND + if (metrics.rmx_cwnd) + tp->snd_cwnd = max(tp->t_maxseg, min(metrics.rmx_cwnd / 2, + min(tp->snd_wnd, so->so_snd.sb_hiwat))); + else +#endif + if (V_tcp_do_rfc3390) + tp->snd_cwnd = min(4 * tp->t_maxseg, + max(2 * tp->t_maxseg, 4380)); +#ifdef INET6 + else if ((isipv6 && in6_localaddr(&inp->in6p_faddr)) || + (!isipv6 && in_localaddr(inp->inp_faddr))) +#else + else if (in_localaddr(inp->inp_faddr)) +#endif + tp->snd_cwnd = tp->t_maxseg * V_ss_fltsz_local; + else + tp->snd_cwnd = tp->t_maxseg * V_ss_fltsz; + + if (CC_ALGO(tp)->conn_init != NULL) + CC_ALGO(tp)->conn_init(tp->ccv); +} + +void inline +cc_cong_signal(struct tcpcb *tp, struct tcphdr *th, uint32_t type) +{ + INP_WLOCK_ASSERT(tp->t_inpcb); + + switch(type) { + case CC_NDUPACK: + if (!IN_FASTRECOVERY(tp->t_flags)) { + tp->snd_recover = tp->snd_max; + if (tp->t_flags & TF_ECN_PERMIT) + tp->t_flags |= TF_ECN_SND_CWR; + } + break; + case CC_ECN: + if (!IN_CONGRECOVERY(tp->t_flags)) { + TCPSTAT_INC(tcps_ecn_rcwnd); + tp->snd_recover = tp->snd_max; + if (tp->t_flags & TF_ECN_PERMIT) + tp->t_flags |= TF_ECN_SND_CWR; + } + break; + case CC_RTO: + tp->t_dupacks = 0; + tp->t_bytes_acked = 0; + EXIT_RECOVERY(tp->t_flags); + tp->snd_cwnd = tp->t_maxseg; + break; + case CC_RTO_ERR: + TCPSTAT_INC(tcps_sndrexmitbad); + /* RTO was unnecessary, so reset everything. */ + tp->snd_cwnd = tp->snd_cwnd_prev; + tp->snd_ssthresh = tp->snd_ssthresh_prev; + tp->snd_recover = tp->snd_recover_prev; + if (tp->t_flags & TF_WASFRECOVERY) + ENTER_FASTRECOVERY(tp->t_flags); + if (tp->t_flags & TF_WASCRECOVERY) + ENTER_CONGRECOVERY(tp->t_flags); + tp->snd_nxt = tp->snd_max; + tp->t_badrxtwin = 0; + break; + } + + if (CC_ALGO(tp)->cong_signal != NULL) { + if (th != NULL) + tp->ccv->curack = th->th_ack; + CC_ALGO(tp)->cong_signal(tp->ccv, type); + } +} + +static void inline +cc_post_recovery(struct tcpcb *tp, struct tcphdr *th) +{ + INP_WLOCK_ASSERT(tp->t_inpcb); + + /* XXXLAS: KASSERT that we're in recovery? */ + + if (CC_ALGO(tp)->post_recovery != NULL) { + tp->ccv->curack = th->th_ack; + CC_ALGO(tp)->post_recovery(tp->ccv); + } + /* XXXLAS: EXIT_RECOVERY ? */ + tp->t_bytes_acked = 0; } /* Neighbor Discovery, Neighbor Unreachability Detection Upper layer hint. */ @@ -1157,14 +1338,9 @@ tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so, TCPSTAT_INC(tcps_ecn_ect1); break; } - /* - * Congestion experienced. - * Ignore if we are already trying to recover. - */ - if ((thflags & TH_ECE) && - SEQ_LEQ(th->th_ack, tp->snd_recover)) { - TCPSTAT_INC(tcps_ecn_rcwnd); - tcp_congestion_exp(tp); + /* Congestion experienced. */ + if (thflags & TH_ECE) { + cc_cong_signal(tp, th, CC_ECN); } } @@ -1259,15 +1435,9 @@ tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so, if (tlen == 0) { if (SEQ_GT(th->th_ack, tp->snd_una) && SEQ_LEQ(th->th_ack, tp->snd_max) && - tp->snd_cwnd >= tp->snd_wnd && - ((!V_tcp_do_newreno && - !(tp->t_flags & TF_SACK_PERMIT) && - tp->t_dupacks < tcprexmtthresh) || - ((V_tcp_do_newreno || - (tp->t_flags & TF_SACK_PERMIT)) && - !IN_FASTRECOVERY(tp) && - (to.to_flags & TOF_SACK) == 0 && - TAILQ_EMPTY(&tp->snd_holes)))) { + !IN_RECOVERY(tp->t_flags) && + (to.to_flags & TOF_SACK) == 0 && + TAILQ_EMPTY(&tp->snd_holes)) { /* * This is a pure ack for outstanding data. */ @@ -1287,15 +1457,7 @@ tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so, */ if (tp->t_rxtshift == 1 && (int)(ticks - tp->t_badrxtwin) < 0) { - TCPSTAT_INC(tcps_sndrexmitbad); - tp->snd_cwnd = tp->snd_cwnd_prev; - tp->snd_ssthresh = - tp->snd_ssthresh_prev; - tp->snd_recover = tp->snd_recover_prev; - if (tp->t_flags & TF_WASFRECOVERY) - ENTER_FASTRECOVERY(tp); - tp->snd_nxt = tp->snd_max; - tp->t_badrxtwin = 0; + cc_cong_signal(tp, th, CC_RTO_ERR); } /* @@ -1321,13 +1483,22 @@ tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so, tcp_xmit_timer(tp, ticks - tp->t_rtttime); } - acked = th->th_ack - tp->snd_una; + acked = BYTES_THIS_ACK(tp, th); TCPSTAT_INC(tcps_rcvackpack); TCPSTAT_ADD(tcps_rcvackbyte, acked); sbdrop(&so->so_snd, acked); if (SEQ_GT(tp->snd_una, tp->snd_recover) && SEQ_LEQ(th->th_ack, tp->snd_recover)) tp->snd_recover = th->th_ack - 1; + + /* + * Let the congestion control algorithm update + * congestion control related information. This + * typically means increasing the congestion + * window. + */ + cc_ack_received(tp, th, CC_ACK); + tp->snd_una = th->th_ack; /* * Pull snd_wl2 up to prevent seq wrap relative @@ -1587,6 +1758,7 @@ tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so, thflags &= ~TH_SYN; } else { tp->t_state = TCPS_ESTABLISHED; + cc_conn_init(tp); tcp_timer_activate(tp, TT_KEEP, tcp_keepidle); } } else { @@ -1990,6 +2162,7 @@ tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so, tp->t_flags &= ~TF_NEEDFIN; } else { tp->t_state = TCPS_ESTABLISHED; + cc_conn_init(tp); tcp_timer_activate(tp, TT_KEEP, tcp_keepidle); } /* @@ -2058,11 +2231,10 @@ tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so, th->th_ack != tp->snd_una) tp->t_dupacks = 0; else if (++tp->t_dupacks > tcprexmtthresh || - ((V_tcp_do_newreno || - (tp->t_flags & TF_SACK_PERMIT)) && - IN_FASTRECOVERY(tp))) { + IN_FASTRECOVERY(tp->t_flags)) { + cc_ack_received(tp, th, CC_DUPACK); if ((tp->t_flags & TF_SACK_PERMIT) && - IN_FASTRECOVERY(tp)) { + IN_FASTRECOVERY(tp->t_flags)) { int awnd; /* @@ -2093,19 +2265,20 @@ tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so, * recovery. */ if (tp->t_flags & TF_SACK_PERMIT) { - if (IN_FASTRECOVERY(tp)) { + if (IN_FASTRECOVERY(tp->t_flags)) { tp->t_dupacks = 0; break; } - } else if (V_tcp_do_newreno || - V_tcp_do_ecn) { + } else { if (SEQ_LEQ(th->th_ack, tp->snd_recover)) { tp->t_dupacks = 0; break; } } - tcp_congestion_exp(tp); + /* Congestion signal before ack. */ + cc_cong_signal(tp, th, CC_NDUPACK); + cc_ack_received(tp, th, CC_DUPACK); tcp_timer_activate(tp, TT_REXMT, 0); tp->t_rtttime = 0; if (tp->t_flags & TF_SACK_PERMIT) { @@ -2129,6 +2302,7 @@ tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so, tp->snd_nxt = onxt; goto drop; } else if (V_tcp_do_rfc3042) { + cc_ack_received(tp, th, CC_DUPACK); u_long oldcwnd = tp->snd_cwnd; tcp_seq oldsndmax = tp->snd_max; u_int sent; @@ -2170,37 +2344,14 @@ tcp_do_segment(struct mbuf *m, struct tcphdr *th, struct socket *so, * If the congestion window was inflated to account * for the other side's cached packets, retract it. */ - if (V_tcp_do_newreno || (tp->t_flags & TF_SACK_PERMIT)) { - if (IN_FASTRECOVERY(tp)) { - if (SEQ_LT(th->th_ack, tp->snd_recover)) { - if (tp->t_flags & TF_SACK_PERMIT) - tcp_sack_partialack(tp, th); - else - tcp_newreno_partial_ack(tp, th); - } else { - /* - * Out of fast recovery. - * Window inflation should have left us - * with approximately snd_ssthresh - * outstanding data. - * But in case we would be inclined to - * send a burst, better to do it via - * the slow start mechanism. - */ - if (SEQ_GT(th->th_ack + - tp->snd_ssthresh, - tp->snd_max)) - tp->snd_cwnd = tp->snd_max - - th->th_ack + - tp->t_maxseg; - else - tp->snd_cwnd = tp->snd_ssthresh; - } - } - } else { - if (tp->t_dupacks >= tcprexmtthresh && - tp->snd_cwnd > tp->snd_ssthresh) - tp->snd_cwnd = tp->snd_ssthresh; + if (IN_FASTRECOVERY(tp->t_flags)) { + if (SEQ_LT(th->th_ack, tp->snd_recover)) { + if (tp->t_flags & TF_SACK_PERMIT) + tcp_sack_partialack(tp, th); + else + tcp_newreno_partial_ack(tp, th); + } else + cc_post_recovery(tp, th); } tp->t_dupacks = 0; /* @@ -2231,7 +2382,7 @@ process_ACK: ("tcp_input: process_ACK ti_locked %d", ti_locked)); INP_WLOCK_ASSERT(tp->t_inpcb); - acked = th->th_ack - tp->snd_una; + acked = BYTES_THIS_ACK(tp, th); TCPSTAT_INC(tcps_rcvackpack); TCPSTAT_ADD(tcps_rcvackbyte, acked); @@ -2242,16 +2393,8 @@ process_ACK: * original cwnd and ssthresh, and proceed to transmit where * we left off. */ - if (tp->t_rxtshift == 1 && (int)(ticks - tp->t_badrxtwin) < 0) { - TCPSTAT_INC(tcps_sndrexmitbad); - tp->snd_cwnd = tp->snd_cwnd_prev; - tp->snd_ssthresh = tp->snd_ssthresh_prev; - tp->snd_recover = tp->snd_recover_prev; - if (tp->t_flags & TF_WASFRECOVERY) - ENTER_FASTRECOVERY(tp); - tp->snd_nxt = tp->snd_max; - tp->t_badrxtwin = 0; /* XXX probably not required */ - } + if (tp->t_rxtshift == 1 && (int)(ticks - tp->t_badrxtwin) < 0) + cc_cong_signal(tp, th, CC_RTO_ERR); /* * If we have a timestamp reply, update smoothed @@ -2298,61 +2441,12 @@ process_ACK: goto step6; /* - * When new data is acked, open the congestion window. - * Method depends on which congestion control state we're - * in (slow start or cong avoid) and if ABC (RFC 3465) is - * enabled. - * - * slow start: cwnd <= ssthresh - * cong avoid: cwnd > ssthresh - * - * slow start and ABC (RFC 3465): - * Grow cwnd exponentially by the amount of data - * ACKed capping the max increment per ACK to - * (abc_l_var * maxseg) bytes. - * - * slow start without ABC (RFC 2581): - * Grow cwnd exponentially by maxseg per ACK. - * - * cong avoid and ABC (RFC 3465): - * Grow cwnd linearly by maxseg per RTT for each - * cwnd worth of ACKed data. - * - * cong avoid without ABC (RFC 2581): - * Grow cwnd linearly by approximately maxseg per RTT using - * maxseg^2 / cwnd per ACK as the increment. - * If cwnd > maxseg^2, fix the cwnd increment at 1 byte to - * avoid capping cwnd. + * Let the congestion control algorithm update congestion + * control related information. This typically means increasing + * the congestion window. */ - if ((!V_tcp_do_newreno && !(tp->t_flags & TF_SACK_PERMIT)) || - !IN_FASTRECOVERY(tp)) { - u_int cw = tp->snd_cwnd; - u_int incr = tp->t_maxseg; - /* In congestion avoidance? */ - if (cw > tp->snd_ssthresh) { - if (V_tcp_do_rfc3465) { - tp->t_bytes_acked += acked; - if (tp->t_bytes_acked >= tp->snd_cwnd) - tp->t_bytes_acked -= cw; - else - incr = 0; - } - else - incr = max((incr * incr / cw), 1); - /* - * In slow-start with ABC enabled and no RTO in sight? - * (Must not use abc_l_var > 1 if slow starting after an - * RTO. On RTO, snd_nxt = snd_una, so the snd_nxt == - * snd_max check is sufficient to handle this). - */ - } else if (V_tcp_do_rfc3465 && - tp->snd_nxt == tp->snd_max) - incr = min(acked, - V_tcp_abc_l_var * tp->t_maxseg); - /* ABC is on by default, so (incr == 0) frequently. */ - if (incr > 0) - tp->snd_cwnd = min(cw+incr, TCP_MAXWIN<<tp->snd_scale); - } + cc_ack_received(tp, th, CC_ACK); + SOCKBUF_LOCK(&so->so_snd); if (acked > so->so_snd.sb_cc) { tp->snd_wnd -= so->so_snd.sb_cc; @@ -2366,16 +2460,14 @@ process_ACK: /* NB: sowwakeup_locked() does an implicit unlock. */ sowwakeup_locked(so); /* Detect una wraparound. */ - if ((V_tcp_do_newreno || (tp->t_flags & TF_SACK_PERMIT)) && - !IN_FASTRECOVERY(tp) && + if (!IN_RECOVERY(tp->t_flags) && SEQ_GT(tp->snd_una, tp->snd_recover) && SEQ_LEQ(th->th_ack, tp->snd_recover)) tp->snd_recover = th->th_ack - 1; - if ((V_tcp_do_newreno || (tp->t_flags & TF_SACK_PERMIT)) && - IN_FASTRECOVERY(tp) && + /* XXXLAS: Can this be moved up into cc_post_recovery? */ + if (IN_RECOVERY(tp->t_flags) && SEQ_GEQ(th->th_ack, tp->snd_recover)) { - EXIT_FASTRECOVERY(tp); - tp->t_bytes_acked = 0; + EXIT_RECOVERY(tp->t_flags); } tp->snd_una = th->th_ack; if (tp->t_flags & TF_SACK_PERMIT) { @@ -3240,24 +3332,19 @@ tcp_mss_update(struct tcpcb *tp, int offer, void tcp_mss(struct tcpcb *tp, int offer) { - int rtt, mss; + int mss; u_long bufsize; struct inpcb *inp; struct socket *so; struct hc_metrics_lite metrics; int mtuflags = 0; -#ifdef INET6 - int isipv6; -#endif + KASSERT(tp != NULL, ("%s: tp == NULL", __func__)); tcp_mss_update(tp, offer, &metrics, &mtuflags); mss = tp->t_maxseg; inp = tp->t_inpcb; -#ifdef INET6 - isipv6 = ((inp->inp_vflag & INP_IPV6) != 0) ? 1 : 0; -#endif /* * If there's a pipesize, change the socket buffer to that size, @@ -3297,71 +3384,6 @@ tcp_mss(struct tcpcb *tp, int offer) (void)sbreserve_locked(&so->so_rcv, bufsize, so, NULL); } SOCKBUF_UNLOCK(&so->so_rcv); - /* - * While we're here, check the others too. - */ - if (tp->t_srtt == 0 && (rtt = metrics.rmx_rtt)) { - tp->t_srtt = rtt; - tp->t_rttbest = tp->t_srtt + TCP_RTT_SCALE; - TCPSTAT_INC(tcps_usedrtt); - if (metrics.rmx_rttvar) { - tp->t_rttvar = metrics.rmx_rttvar; - TCPSTAT_INC(tcps_usedrttvar); - } else { - /* default variation is +- 1 rtt */ - tp->t_rttvar = - tp->t_srtt * TCP_RTTVAR_SCALE / TCP_RTT_SCALE; - } - TCPT_RANGESET(tp->t_rxtcur, - ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1, - tp->t_rttmin, TCPTV_REXMTMAX); - } - if (metrics.rmx_ssthresh) { - /* - * There's some sort of gateway or interface - * buffer limit on the path. Use this to set - * the slow start threshhold, but set the - * threshold to no less than 2*mss. - */ - tp->snd_ssthresh = max(2 * mss, metrics.rmx_ssthresh); - TCPSTAT_INC(tcps_usedssthresh); - } - - /* - * Set the slow-start flight size depending on whether this - * is a local network or not. - * - * Extend this so we cache the cwnd too and retrieve it here. - * Make cwnd even bigger than RFC3390 suggests but only if we - * have previous experience with the remote host. Be careful - * not make cwnd bigger than remote receive window or our own - * send socket buffer. Maybe put some additional upper bound - * on the retrieved cwnd. Should do incremental updates to - * hostcache when cwnd collapses so next connection doesn't - * overloads the path again. - * - * RFC3390 says only do this if SYN or SYN/ACK didn't got lost. - * We currently check only in syncache_socket for that. - */ -#define TCP_METRICS_CWND -#ifdef TCP_METRICS_CWND - if (metrics.rmx_cwnd) - tp->snd_cwnd = max(mss, - min(metrics.rmx_cwnd / 2, - min(tp->snd_wnd, so->so_snd.sb_hiwat))); - else -#endif - if (V_tcp_do_rfc3390) - tp->snd_cwnd = min(4 * mss, max(2 * mss, 4380)); -#ifdef INET6 - else if ((isipv6 && in6_localaddr(&inp->in6p_faddr)) || - (!isipv6 && in_localaddr(inp->inp_faddr))) -#else - else if (in_localaddr(inp->inp_faddr)) -#endif - tp->snd_cwnd = mss * V_ss_fltsz_local; - else - tp->snd_cwnd = mss * V_ss_fltsz; /* Check the interface for TSO capabilities. */ if (mtuflags & CSUM_TSO) @@ -3425,7 +3447,7 @@ tcp_newreno_partial_ack(struct tcpcb *tp, struct tcphdr *th) * Set snd_cwnd to one segment beyond acknowledged offset. * (tp->snd_una has not yet been updated when this function is called.) */ - tp->snd_cwnd = tp->t_maxseg + (th->th_ack - tp->snd_una); + tp->snd_cwnd = tp->t_maxseg + BYTES_THIS_ACK(tp, th); tp->t_flags |= TF_ACKNOW; (void) tcp_output(tp); tp->snd_cwnd = ocwnd; @@ -3435,8 +3457,8 @@ tcp_newreno_partial_ack(struct tcpcb *tp, struct tcphdr *th) * Partial window deflation. Relies on fact that tp->snd_una * not updated yet. */ - if (tp->snd_cwnd > th->th_ack - tp->snd_una) - tp->snd_cwnd -= th->th_ack - tp->snd_una; + if (tp->snd_cwnd > BYTES_THIS_ACK(tp, th)) + tp->snd_cwnd -= BYTES_THIS_ACK(tp, th); else tp->snd_cwnd = 0; tp->snd_cwnd += tp->t_maxseg; diff --git a/sys/netinet/tcp_output.c b/sys/netinet/tcp_output.c index b5bc3d985520..7db0adb42ded 100644 --- a/sys/netinet/tcp_output.c +++ b/sys/netinet/tcp_output.c @@ -53,6 +53,7 @@ __FBSDID("$FreeBSD$"); #include <net/route.h> #include <net/vnet.h> +#include <netinet/cc.h> #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> @@ -64,7 +65,6 @@ __FBSDID("$FreeBSD$"); #include <netinet/ip6.h> #include <netinet6/ip6_var.h> #endif -#include <netinet/tcp.h> #define TCPOUTFLAGS #include <netinet/tcp_fsm.h> #include <netinet/tcp_seq.h> @@ -102,11 +102,6 @@ SYSCTL_VNET_INT(_net_inet_tcp, OID_AUTO, local_slowstart_flightsize, CTLFLAG_RW, &VNET_NAME(ss_fltsz_local), 1, "Slow start flight size for local networks"); -VNET_DEFINE(int, tcp_do_newreno) = 1; -SYSCTL_VNET_INT(_net_inet_tcp, OID_AUTO, newreno, CTLFLAG_RW, - &VNET_NAME(tcp_do_newreno), 0, - "Enable NewReno Algorithms"); - VNET_DEFINE(int, tcp_do_tso) = 1; #define V_tcp_do_tso VNET(tcp_do_tso) SYSCTL_VNET_INT(_net_inet_tcp, OID_AUTO, tso, CTLFLAG_RW, @@ -131,6 +126,19 @@ SYSCTL_VNET_INT(_net_inet_tcp, OID_AUTO, sendbuf_max, CTLFLAG_RW, &VNET_NAME(tcp_autosndbuf_max), 0, "Max size of automatic send buffer"); +static void inline cc_after_idle(struct tcpcb *tp); + +/* + * CC wrapper hook functions + */ +static void inline +cc_after_idle(struct tcpcb *tp) +{ + INP_WLOCK_ASSERT(tp->t_inpcb); + + if (CC_ALGO(tp)->after_idle != NULL) + CC_ALGO(tp)->after_idle(tp->ccv); +} /* * Tcp output routine: figure out what should be sent and send it. @@ -241,7 +249,7 @@ again: sack_bytes_rxmt = 0; len = 0; p = NULL; - if ((tp->t_flags & TF_SACK_PERMIT) && IN_FASTRECOVERY(tp) && + if ((tp->t_flags & TF_SACK_PERMIT) && IN_FASTRECOVERY(tp->t_flags) && (p = tcp_sack_output(tp, &sack_bytes_rxmt))) { long cwin; @@ -1315,7 +1323,7 @@ out: * on the transmitter effectively destroys the TCP window, forcing * it to four packets (1.5Kx4 = 6K window). */ - if (sendalot && (!V_tcp_do_newreno || --maxburst)) + if (sendalot && --maxburst) goto again; #endif if (sendalot) diff --git a/sys/netinet/tcp_sack.c b/sys/netinet/tcp_sack.c index 737c2b2af099..47d44ec16b91 100644 --- a/sys/netinet/tcp_sack.c +++ b/sys/netinet/tcp_sack.c @@ -576,7 +576,7 @@ tcp_sack_partialack(struct tcpcb *tp, struct tcphdr *th) tcp_timer_activate(tp, TT_REXMT, 0); tp->t_rtttime = 0; /* Send one or 2 segments based on how much new data was acked. */ - if (((th->th_ack - tp->snd_una) / tp->t_maxseg) > 2) + if ((BYTES_THIS_ACK(tp, th) / tp->t_maxseg) > 2) num_segs = 2; tp->snd_cwnd = (tp->sackhint.sack_bytes_rexmit + (tp->snd_nxt - tp->sack_newdata) + num_segs * tp->t_maxseg); diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c index dc4395dc6137..8596e234f807 100644 --- a/sys/netinet/tcp_subr.c +++ b/sys/netinet/tcp_subr.c @@ -62,6 +62,7 @@ __FBSDID("$FreeBSD$"); #include <net/if.h> #include <net/vnet.h> +#include <netinet/cc.h> #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> @@ -80,7 +81,6 @@ __FBSDID("$FreeBSD$"); #include <netinet6/nd6.h> #endif #include <netinet/ip_icmp.h> -#include <netinet/tcp.h> #include <netinet/tcp_fsm.h> #include <netinet/tcp_seq.h> #include <netinet/tcp_timer.h> @@ -238,6 +238,7 @@ static char * tcp_log_addr(struct in_conninfo *inc, struct tcphdr *th, struct tcpcb_mem { struct tcpcb tcb; struct tcp_timer tt; + struct cc_var ccv; }; static VNET_DEFINE(uma_zone_t, tcpcb_zone); @@ -277,6 +278,8 @@ tcp_init(void) { int hashsize; + cc_init(); + hashsize = TCBHASHSIZE; TUNABLE_INT_FETCH("net.inet.tcp.tcbhashsize", &hashsize); if (!powerof2(hashsize)) { @@ -640,6 +643,26 @@ tcp_newtcpcb(struct inpcb *inp) if (tm == NULL) return (NULL); tp = &tm->tcb; + + /* Initialise cc_var struct for this tcpcb. */ + tp->ccv = &tm->ccv; + tp->ccv->type = IPPROTO_TCP; + tp->ccv->ccvc.tcp = tp; + + /* + * Use the current system default CC algorithm. + */ + CC_LIST_RLOCK(); + KASSERT(!STAILQ_EMPTY(&cc_list), ("cc_list is empty!")); + CC_ALGO(tp) = CC_DEFAULT(); + CC_LIST_RUNLOCK(); + + if (CC_ALGO(tp)->cb_init != NULL) + if (CC_ALGO(tp)->cb_init(tp->ccv) > 0) { + uma_zfree(V_tcpcb_zone, tm); + return (NULL); + } + #ifdef VIMAGE tp->t_vnet = inp->inp_vnet; #endif @@ -805,6 +828,12 @@ tcp_discardcb(struct tcpcb *tp) tcp_offload_detach(tp); tcp_free_sackholes(tp); + + /* Allow the CC algorithm to clean up after itself. */ + if (CC_ALGO(tp)->cb_destroy != NULL) + CC_ALGO(tp)->cb_destroy(tp->ccv); + + CC_ALGO(tp) = NULL; inp->inp_ppcb = NULL; tp->t_inpcb = NULL; uma_zfree(V_tcpcb_zone, tp); @@ -1572,7 +1601,7 @@ tcp_mtudisc(struct inpcb *inp, int errno) tcp_free_sackholes(tp); tp->snd_recover = tp->snd_max; if (tp->t_flags & TF_SACK_PERMIT) - EXIT_FASTRECOVERY(tp); + EXIT_FASTRECOVERY(tp->t_flags); tcp_output_send(tp); return (inp); } diff --git a/sys/netinet/tcp_timer.c b/sys/netinet/tcp_timer.c index 65c6a3ec41ab..2748e64601df 100644 --- a/sys/netinet/tcp_timer.c +++ b/sys/netinet/tcp_timer.c @@ -51,6 +51,7 @@ __FBSDID("$FreeBSD$"); #include <net/route.h> #include <net/vnet.h> +#include <netinet/cc.h> #include <netinet/in.h> #include <netinet/in_pcb.h> #include <netinet/in_systm.h> @@ -58,7 +59,6 @@ __FBSDID("$FreeBSD$"); #include <netinet6/in6_pcb.h> #endif #include <netinet/ip_var.h> -#include <netinet/tcp.h> #include <netinet/tcp_fsm.h> #include <netinet/tcp_timer.h> #include <netinet/tcp_var.h> @@ -515,10 +515,14 @@ tcp_timer_rexmt(void * xtp) tp->snd_cwnd_prev = tp->snd_cwnd; tp->snd_ssthresh_prev = tp->snd_ssthresh; tp->snd_recover_prev = tp->snd_recover; - if (IN_FASTRECOVERY(tp)) - tp->t_flags |= TF_WASFRECOVERY; + if (IN_FASTRECOVERY(tp->t_flags)) + tp->t_flags |= TF_WASFRECOVERY; else - tp->t_flags &= ~TF_WASFRECOVERY; + tp->t_flags &= ~TF_WASFRECOVERY; + if (IN_CONGRECOVERY(tp->t_flags)) + tp->t_flags |= TF_WASCRECOVERY; + else + tp->t_flags &= ~TF_WASCRECOVERY; tp->t_badrxtwin = ticks + (tp->t_srtt >> (TCP_RTT_SHIFT + 1)); } TCPSTAT_INC(tcps_rexmttimeo); @@ -562,40 +566,9 @@ tcp_timer_rexmt(void * xtp) * If timing a segment in this window, stop the timer. */ tp->t_rtttime = 0; - /* - * Close the congestion window down to one segment - * (we'll open it by one segment for each ack we get). - * Since we probably have a window's worth of unacked - * data accumulated, this "slow start" keeps us from - * dumping all that data as back-to-back packets (which - * might overwhelm an intermediate gateway). - * - * There are two phases to the opening: Initially we - * open by one mss on each ack. This makes the window - * size increase exponentially with time. If the - * window is larger than the path can handle, this - * exponential growth results in dropped packet(s) - * almost immediately. To get more time between - * drops but still "push" the network to take advantage - * of improving conditions, we switch from exponential - * to linear window opening at some threshhold size. - * For a threshhold, we use half the current window - * size, truncated to a multiple of the mss. - * - * (the minimum cwnd that will give us exponential - * growth is 2 mss. We don't allow the threshhold - * to go below this.) - */ - { - u_int win = min(tp->snd_wnd, tp->snd_cwnd) / 2 / tp->t_maxseg; - if (win < 2) - win = 2; - tp->snd_cwnd = tp->t_maxseg; - tp->snd_ssthresh = win * tp->t_maxseg; - tp->t_dupacks = 0; - } - EXIT_FASTRECOVERY(tp); - tp->t_bytes_acked = 0; + + cc_cong_signal(tp, 0, CC_RTO); + (void) tcp_output(tp); out: diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c index f35890bee192..a28ddef232a7 100644 --- a/sys/netinet/tcp_usrreq.c +++ b/sys/netinet/tcp_usrreq.c @@ -62,6 +62,7 @@ __FBSDID("$FreeBSD$"); #include <net/route.h> #include <net/vnet.h> +#include <netinet/cc.h> #include <netinet/in.h> #include <netinet/in_systm.h> #ifdef INET6 @@ -77,7 +78,6 @@ __FBSDID("$FreeBSD$"); #include <netinet6/ip6_var.h> #include <netinet6/scope6_var.h> #endif -#include <netinet/tcp.h> #include <netinet/tcp_fsm.h> #include <netinet/tcp_seq.h> #include <netinet/tcp_timer.h> @@ -1242,6 +1242,8 @@ tcp_ctloutput(struct socket *so, struct sockopt *sopt) struct inpcb *inp; struct tcpcb *tp; struct tcp_info ti; + char buf[TCP_CA_NAME_MAX]; + struct cc_algo *algo; error = 0; inp = sotoinpcb(so); @@ -1351,6 +1353,54 @@ tcp_ctloutput(struct socket *so, struct sockopt *sopt) error = EINVAL; break; + case TCP_CONGESTION: + INP_WUNLOCK(inp); + bzero(buf, sizeof(buf)); + error = sooptcopyin(sopt, &buf, sizeof(buf), 1); + if (error) + break; + INP_WLOCK_RECHECK(inp); + /* + * Return EINVAL if we can't find the requested cc algo. + */ + error = EINVAL; + CC_LIST_RLOCK(); + STAILQ_FOREACH(algo, &cc_list, entries) { + if (strncmp(buf, algo->name, TCP_CA_NAME_MAX) + == 0) { + /* We've found the requested algo. */ + error = 0; + /* + * We hold a write lock over the tcb + * so it's safe to do these things + * without ordering concerns. + */ + if (CC_ALGO(tp)->cb_destroy != NULL) + CC_ALGO(tp)->cb_destroy(tp->ccv); + CC_ALGO(tp) = algo; + /* + * If something goes pear shaped + * initialising the new algo, + * fall back to newreno (which + * does not require initialisation). + */ + if (algo->cb_init != NULL) + if (algo->cb_init(tp->ccv) > 0) { + CC_ALGO(tp) = &newreno_cc_algo; + /* + * The only reason init + * should fail is + * because of malloc. + */ + error = ENOMEM; + } + break; /* Break the STAILQ_FOREACH. */ + } + } + CC_LIST_RUNLOCK(); + INP_WUNLOCK(inp); + break; + default: INP_WUNLOCK(inp); error = ENOPROTOOPT; @@ -1394,6 +1444,12 @@ tcp_ctloutput(struct socket *so, struct sockopt *sopt) INP_WUNLOCK(inp); error = sooptcopyout(sopt, &ti, sizeof ti); break; + case TCP_CONGESTION: + bzero(buf, sizeof(buf)); + strlcpy(buf, CC_ALGO(tp)->name, TCP_CA_NAME_MAX); + INP_WUNLOCK(inp); + error = sooptcopyout(sopt, buf, TCP_CA_NAME_MAX); + break; default: INP_WUNLOCK(inp); error = ENOPROTOOPT; @@ -1707,6 +1763,10 @@ db_print_tflags(u_int t_flags) db_printf("%sTF_FASTRECOVERY", comma ? ", " : ""); comma = 1; } + if (t_flags & TF_CONGRECOVERY) { + db_printf("%sTF_CONGRECOVERY", comma ? ", " : ""); + comma = 1; + } if (t_flags & TF_WASFRECOVERY) { db_printf("%sTF_WASFRECOVERY", comma ? ", " : ""); comma = 1; diff --git a/sys/netinet/tcp_var.h b/sys/netinet/tcp_var.h index 0b286812f1f1..442c73621b85 100644 --- a/sys/netinet/tcp_var.h +++ b/sys/netinet/tcp_var.h @@ -195,9 +195,11 @@ struct tcpcb { struct toe_usrreqs *t_tu; /* offload operations vector */ void *t_toe; /* TOE pcb pointer */ int t_bytes_acked; /* # bytes acked during current RTT */ + struct cc_algo *cc_algo; /* congestion control algorithm */ + struct cc_var *ccv; int t_ispare; /* explicit pad for 64bit alignment */ - void *t_pspare2[6]; /* 2 CC / 4 TBD */ + void *t_pspare2[4]; /* 4 TBD */ uint64_t _pad[12]; /* 7 UTO, 5 TBD (1-2 CC/RTT?) */ }; @@ -230,10 +232,22 @@ struct tcpcb { #define TF_ECN_PERMIT 0x4000000 /* connection ECN-ready */ #define TF_ECN_SND_CWR 0x8000000 /* ECN CWR in queue */ #define TF_ECN_SND_ECE 0x10000000 /* ECN ECE in queue */ +#define TF_CONGRECOVERY 0x20000000 /* congestion recovery mode */ +#define TF_WASCRECOVERY 0x40000000 /* was in congestion recovery */ -#define IN_FASTRECOVERY(tp) (tp->t_flags & TF_FASTRECOVERY) -#define ENTER_FASTRECOVERY(tp) tp->t_flags |= TF_FASTRECOVERY -#define EXIT_FASTRECOVERY(tp) tp->t_flags &= ~TF_FASTRECOVERY +#define IN_FASTRECOVERY(t_flags) (t_flags & TF_FASTRECOVERY) +#define ENTER_FASTRECOVERY(t_flags) t_flags |= TF_FASTRECOVERY +#define EXIT_FASTRECOVERY(t_flags) t_flags &= ~TF_FASTRECOVERY + +#define IN_CONGRECOVERY(t_flags) (t_flags & TF_CONGRECOVERY) +#define ENTER_CONGRECOVERY(t_flags) t_flags |= TF_CONGRECOVERY +#define EXIT_CONGRECOVERY(t_flags) t_flags &= ~TF_CONGRECOVERY + +#define IN_RECOVERY(t_flags) (t_flags & (TF_CONGRECOVERY | TF_FASTRECOVERY)) +#define ENTER_RECOVERY(t_flags) t_flags |= (TF_CONGRECOVERY | TF_FASTRECOVERY) +#define EXIT_RECOVERY(t_flags) t_flags &= ~(TF_CONGRECOVERY | TF_FASTRECOVERY) + +#define BYTES_THIS_ACK(tp, th) (th->th_ack - tp->snd_una) /* * Flags for the t_oobflags field. @@ -562,10 +576,11 @@ VNET_DECLARE(int, tcp_mssdflt); /* XXX */ VNET_DECLARE(int, tcp_minmss); VNET_DECLARE(int, tcp_delack_enabled); VNET_DECLARE(int, tcp_do_rfc3390); -VNET_DECLARE(int, tcp_do_newreno); VNET_DECLARE(int, path_mtu_discovery); VNET_DECLARE(int, ss_fltsz); VNET_DECLARE(int, ss_fltsz_local); +VNET_DECLARE(int, tcp_do_rfc3465); +VNET_DECLARE(int, tcp_abc_l_var); #define V_tcb VNET(tcb) #define V_tcbinfo VNET(tcbinfo) #define V_tcpstat VNET(tcpstat) @@ -573,10 +588,11 @@ VNET_DECLARE(int, ss_fltsz_local); #define V_tcp_minmss VNET(tcp_minmss) #define V_tcp_delack_enabled VNET(tcp_delack_enabled) #define V_tcp_do_rfc3390 VNET(tcp_do_rfc3390) -#define V_tcp_do_newreno VNET(tcp_do_newreno) #define V_path_mtu_discovery VNET(path_mtu_discovery) #define V_ss_fltsz VNET(ss_fltsz) #define V_ss_fltsz_local VNET(ss_fltsz_local) +#define V_tcp_do_rfc3465 VNET(tcp_do_rfc3465) +#define V_tcp_abc_l_var VNET(tcp_abc_l_var) VNET_DECLARE(int, tcp_do_sack); /* SACK enabled/disabled */ VNET_DECLARE(int, tcp_sc_rst_sock_fail); /* RST on sock alloc failure */ @@ -678,6 +694,8 @@ void tcp_free_sackholes(struct tcpcb *tp); int tcp_newreno(struct tcpcb *, struct tcphdr *); u_long tcp_seq_subtract(u_long, u_long ); +void cc_cong_signal(struct tcpcb *tp, struct tcphdr *th, uint32_t type); + #endif /* _KERNEL */ #endif /* _NETINET_TCP_VAR_H_ */ diff --git a/sys/powerpc/aim/machdep.c b/sys/powerpc/aim/machdep.c index f52f7a4f5b97..87aa482b9d4b 100644 --- a/sys/powerpc/aim/machdep.c +++ b/sys/powerpc/aim/machdep.c @@ -159,8 +159,6 @@ int setfault(faultbuf); /* defined in locore.S */ long Maxmem = 0; long realmem = 0; -struct pmap ofw_pmap; - #ifndef __powerpc64__ struct bat battable[16]; #endif @@ -639,64 +637,6 @@ cpu_halt(void) OF_exit(); } -void -cpu_idle(int busy) -{ - register_t msr; - uint16_t vers; - - msr = mfmsr(); - vers = mfpvr() >> 16; - -#ifdef INVARIANTS - if ((msr & PSL_EE) != PSL_EE) { - struct thread *td = curthread; - printf("td msr %#lx\n", (u_long)td->td_md.md_saved_msr); - panic("ints disabled in idleproc!"); - } -#endif - CTR2(KTR_SPARE2, "cpu_idle(%d) at %d", - busy, curcpu); - if (powerpc_pow_enabled) { - if (!busy) { - critical_enter(); - cpu_idleclock(); - } - switch (vers) { - case IBM970: - case IBM970FX: - case IBM970MP: - case MPC7447A: - case MPC7448: - case MPC7450: - case MPC7455: - case MPC7457: - __asm __volatile("\ - dssall; sync; mtmsr %0; isync" - :: "r"(msr | PSL_POW)); - break; - default: - powerpc_sync(); - mtmsr(msr | PSL_POW); - isync(); - break; - } - if (!busy) { - cpu_activeclock(); - critical_exit(); - } - } - CTR2(KTR_SPARE2, "cpu_idle(%d) at %d done", - busy, curcpu); -} - -int -cpu_idle_wakeup(int cpu) -{ - - return (0); -} - int ptrace_set_pc(struct thread *td, unsigned long addr) { diff --git a/sys/powerpc/aim/mmu_oea.c b/sys/powerpc/aim/mmu_oea.c index 5ba029c3f5cf..7bd07e113725 100644 --- a/sys/powerpc/aim/mmu_oea.c +++ b/sys/powerpc/aim/mmu_oea.c @@ -197,8 +197,6 @@ static u_int phys_avail_count; static int regions_sz, pregions_sz; static struct ofw_map *translations; -extern struct pmap ofw_pmap; - /* * Lock for the pteg and pvo tables. */ @@ -669,10 +667,7 @@ moea_cpu_bootstrap(mmu_t mmup, int ap) isync(); for (i = 0; i < 16; i++) - mtsrin(i << ADDR_SR_SHFT, EMPTY_SEGMENT); - - __asm __volatile("mtsr %0,%1" :: "n"(KERNEL_SR), "r"(KERNEL_SEGMENT)); - __asm __volatile("mtsr %0,%1" :: "n"(KERNEL2_SR), "r"(KERNEL2_SEGMENT)); + mtsrin(i << ADDR_SR_SHFT, kernel_pmap->pm_sr[i]); powerpc_sync(); sdr = (u_int)moea_pteg_table | (moea_pteg_mask >> 10); @@ -859,11 +854,16 @@ moea_bootstrap(mmu_t mmup, vm_offset_t kernelstart, vm_offset_t kernelend) moea_vsid_bitmap[0] |= 1; /* - * Set up the Open Firmware pmap and add it's mappings. + * Initialize the kernel pmap (which is statically allocated). + */ + PMAP_LOCK_INIT(kernel_pmap); + for (i = 0; i < 16; i++) + kernel_pmap->pm_sr[i] = EMPTY_SEGMENT + i; + kernel_pmap->pm_active = ~0; + + /* + * Set up the Open Firmware mappings */ - moea_pinit(mmup, &ofw_pmap); - ofw_pmap.pm_sr[KERNEL_SR] = KERNEL_SEGMENT; - ofw_pmap.pm_sr[KERNEL2_SR] = KERNEL2_SEGMENT; if ((chosen = OF_finddevice("/chosen")) == -1) panic("moea_bootstrap: can't find /chosen"); OF_getprop(chosen, "mmu", &mmui, 4); @@ -900,16 +900,8 @@ moea_bootstrap(mmu_t mmup, vm_offset_t kernelstart, vm_offset_t kernelend) /* Enter the pages */ for (off = 0; off < translations[i].om_len; off += PAGE_SIZE) { - struct vm_page m; - - m.phys_addr = translations[i].om_pa + off; - m.md.mdpg_cache_attrs = VM_MEMATTR_DEFAULT; - m.oflags = VPO_BUSY; - PMAP_LOCK(&ofw_pmap); - moea_enter_locked(&ofw_pmap, - translations[i].om_va + off, &m, - VM_PROT_ALL, 1); - PMAP_UNLOCK(&ofw_pmap); + moea_kenter(mmup, translations[i].om_va + off, + translations[i].om_pa + off); ofw_mappings++; } } @@ -921,17 +913,6 @@ moea_bootstrap(mmu_t mmup, vm_offset_t kernelstart, vm_offset_t kernelend) ; Maxmem = powerpc_btop(phys_avail[i + 1]); - /* - * Initialize the kernel pmap (which is statically allocated). - */ - PMAP_LOCK_INIT(kernel_pmap); - for (i = 0; i < 16; i++) { - kernel_pmap->pm_sr[i] = EMPTY_SEGMENT; - } - kernel_pmap->pm_sr[KERNEL_SR] = KERNEL_SEGMENT; - kernel_pmap->pm_sr[KERNEL2_SR] = KERNEL2_SEGMENT; - kernel_pmap->pm_active = ~0; - moea_cpu_bootstrap(mmup,0); pmap_bootstrapped++; diff --git a/sys/powerpc/aim/mmu_oea64.c b/sys/powerpc/aim/mmu_oea64.c index 3e336c9e6ab6..506676fa2264 100644 --- a/sys/powerpc/aim/mmu_oea64.c +++ b/sys/powerpc/aim/mmu_oea64.c @@ -275,8 +275,6 @@ static struct mem_region *pregions; static u_int phys_avail_count; static int regions_sz, pregions_sz; -extern struct pmap ofw_pmap; - extern void bs_remap_earlyboot(void); @@ -1119,27 +1117,16 @@ moea64_bootstrap(mmu_t mmup, vm_offset_t kernelstart, vm_offset_t kernelend) chosen = OF_finddevice("/chosen"); if (chosen != -1 && OF_getprop(chosen, "mmu", &mmui, 4) != -1) { - #ifndef __powerpc64__ - moea64_pinit(mmup, &ofw_pmap); - - for (i = 0; i < 16; i++) - ofw_pmap.pm_sr[i] = kernel_pmap->pm_sr[i]; - #endif - - if ((mmu = OF_instance_to_package(mmui)) == -1) - panic("moea64_bootstrap: can't get mmu package"); - if ((sz = OF_getproplen(mmu, "translations")) == -1) - panic("moea64_bootstrap: can't get ofw translation count"); + mmu = OF_instance_to_package(mmui); + if (mmu == -1 || (sz = OF_getproplen(mmu, "translations")) == -1) + sz = 0; if (sz > 6144 /* tmpstksz - 2 KB headroom */) panic("moea64_bootstrap: too many ofw translations"); - moea64_add_ofw_mappings(mmup, mmu, sz); + if (sz > 0) + moea64_add_ofw_mappings(mmup, mmu, sz); } -#ifdef SMP - TLBSYNC(); -#endif - /* * Calculate the last available physical address. */ @@ -2385,6 +2372,9 @@ moea64_bootstrap_alloc(vm_size_t size, u_int align) if (s < phys_avail[i] || e > phys_avail[i + 1]) continue; + if (s + size > platform_real_maxaddr()) + continue; + if (s == phys_avail[i]) { phys_avail[i] += size; } else if (e == phys_avail[i + 1]) { diff --git a/sys/powerpc/aim/mp_cpudep.c b/sys/powerpc/aim/mp_cpudep.c index b0423659f431..312164e04174 100644 --- a/sys/powerpc/aim/mp_cpudep.c +++ b/sys/powerpc/aim/mp_cpudep.c @@ -228,6 +228,21 @@ cpudep_save_config(void *dummy) powerpc_sync(); break; +#ifdef __powerpc64__ + case IBMCELLBE: + if (mfmsr() & PSL_HV) { + bsp_state[0] = mfspr(SPR_HID0); + bsp_state[1] = mfspr(SPR_HID1); + bsp_state[2] = mfspr(SPR_HID4); + bsp_state[3] = mfspr(SPR_HID6); + + bsp_state[4] = mfspr(SPR_CELL_TSCR); + } + + bsp_state[5] = mfspr(SPR_CELL_TSRL); + + break; +#endif case MPC7450: case MPC7455: case MPC7457: @@ -288,6 +303,21 @@ cpudep_ap_setup() powerpc_sync(); break; +#ifdef __powerpc64__ + case IBMCELLBE: + if (mfmsr() & PSL_HV) { + mtspr(SPR_HID0, bsp_state[0]); + mtspr(SPR_HID1, bsp_state[1]); + mtspr(SPR_HID4, bsp_state[2]); + mtspr(SPR_HID6, bsp_state[3]); + + mtspr(SPR_CELL_TSCR, bsp_state[4]); + } + + mtspr(SPR_CELL_TSRL, bsp_state[5]); + + break; +#endif case MPC7450: case MPC7455: case MPC7457: diff --git a/sys/powerpc/aim/slb.c b/sys/powerpc/aim/slb.c index 9d8828ac2daa..d5fb12e779f6 100644 --- a/sys/powerpc/aim/slb.c +++ b/sys/powerpc/aim/slb.c @@ -36,9 +36,14 @@ #include <vm/vm.h> #include <vm/pmap.h> #include <vm/uma.h> +#include <vm/vm.h> #include <vm/vm_map.h> +#include <vm/vm_page.h> +#include <vm/vm_pageout.h> +#include <vm/vm_phys.h> #include <machine/md_var.h> +#include <machine/platform.h> #include <machine/pmap.h> #include <machine/vmparam.h> @@ -474,6 +479,51 @@ slb_insert_user(pmap_t pm, struct slb *slb) pm->pm_slb[i] = slb; } +static void * +slb_uma_real_alloc(uma_zone_t zone, int bytes, u_int8_t *flags, int wait) +{ + static vm_offset_t realmax = 0; + void *va; + vm_page_t m; + int pflags; + + if (realmax == 0) + realmax = platform_real_maxaddr(); + + *flags = UMA_SLAB_PRIV; + if ((wait & (M_NOWAIT|M_USE_RESERVE)) == M_NOWAIT) + pflags = VM_ALLOC_INTERRUPT | VM_ALLOC_WIRED; + else + pflags = VM_ALLOC_SYSTEM | VM_ALLOC_WIRED; + if (wait & M_ZERO) + pflags |= VM_ALLOC_ZERO; + + for (;;) { + m = vm_phys_alloc_contig(1, 0, realmax, PAGE_SIZE, + PAGE_SIZE); + if (m == NULL) { + if (wait & M_NOWAIT) + return (NULL); + VM_WAIT; + } else + break; + } + + va = (void *) VM_PAGE_TO_PHYS(m); + + if (!hw_direct_map) + pmap_kenter((vm_offset_t)va, VM_PAGE_TO_PHYS(m)); + + if ((wait & M_ZERO) && (m->flags & PG_ZERO) == 0) + bzero(va, PAGE_SIZE); + + /* vm_phys_alloc_contig does not track wiring */ + atomic_add_int(&cnt.v_wire_count, 1); + m->wire_count = 1; + + return (va); +} + static void slb_zone_init(void *dummy) { @@ -482,6 +532,11 @@ slb_zone_init(void *dummy) NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_VM); slb_cache_zone = uma_zcreate("SLB cache", 64*sizeof(struct slb *), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_VM); + + if (platform_real_maxaddr() != VM_MAX_ADDRESS) { + uma_zone_set_allocf(slb_cache_zone, slb_uma_real_alloc); + uma_zone_set_allocf(slbt_zone, slb_uma_real_alloc); + } } struct slb ** diff --git a/sys/powerpc/booke/machdep.c b/sys/powerpc/booke/machdep.c index b8a77a59b063..921dfbf81f9b 100644 --- a/sys/powerpc/booke/machdep.c +++ b/sys/powerpc/booke/machdep.c @@ -468,50 +468,6 @@ cpu_flush_dcache(void *ptr, size_t len) /* TBD */ } -/* - * cpu_idle - * - * Set Wait state enable. - */ -void -cpu_idle (int busy) -{ - register_t msr; - - msr = mfmsr(); - -#ifdef INVARIANTS - if ((msr & PSL_EE) != PSL_EE) { - struct thread *td = curthread; - printf("td msr %x\n", td->td_md.md_saved_msr); - panic("ints disabled in idleproc!"); - } -#endif - - CTR2(KTR_SPARE2, "cpu_idle(%d) at %d", - busy, curcpu); - if (!busy) { - critical_enter(); - cpu_idleclock(); - } - /* Freescale E500 core RM section 6.4.1. */ - msr = msr | PSL_WE; - __asm __volatile("msync; mtmsr %0; isync" :: "r" (msr)); - if (!busy) { - cpu_activeclock(); - critical_exit(); - } - CTR2(KTR_SPARE2, "cpu_idle(%d) at %d done", - busy, curcpu); -} - -int -cpu_idle_wakeup(int cpu) -{ - - return (0); -} - void spinlock_enter(void) { @@ -641,12 +597,3 @@ bzero(void *buf, size_t len) } } -/* - * XXX what is the better/proper place for this routine? - */ -int -mem_valid(vm_offset_t addr, int len) -{ - - return (1); -} diff --git a/sys/powerpc/include/cpufunc.h b/sys/powerpc/include/cpufunc.h index 775ef19f2eb1..914935f5e775 100644 --- a/sys/powerpc/include/cpufunc.h +++ b/sys/powerpc/include/cpufunc.h @@ -106,6 +106,17 @@ mfsrin(vm_offset_t va) } #endif +static __inline register_t +mfctrl(void) +{ + register_t value; + + __asm __volatile ("mfspr %0,136" : "=r"(value)); + + return (value); +} + + static __inline void mtdec(register_t value) { diff --git a/sys/powerpc/include/platform.h b/sys/powerpc/include/platform.h index cfa7a0d0890c..48ea0e603250 100644 --- a/sys/powerpc/include/platform.h +++ b/sys/powerpc/include/platform.h @@ -44,6 +44,7 @@ struct mem_region { }; void mem_regions(struct mem_region **, int *, struct mem_region **, int *); +vm_offset_t platform_real_maxaddr(void); u_long platform_timebase_freq(struct cpuref *); diff --git a/sys/powerpc/include/spr.h b/sys/powerpc/include/spr.h index 317f4d117629..4027461259ed 100644 --- a/sys/powerpc/include/spr.h +++ b/sys/powerpc/include/spr.h @@ -420,6 +420,10 @@ #define SPR_HID1 0x3f1 /* ..8 Hardware Implementation Register 1 */ #define SPR_HID4 0x3f4 /* ..8 Hardware Implementation Register 4 */ #define SPR_HID5 0x3f6 /* ..8 Hardware Implementation Register 5 */ +#define SPR_HID6 0x3f9 /* ..8 Hardware Implementation Register 6 */ + +#define SPR_CELL_TSRL 0x380 /* ... Cell BE Thread Status Register */ +#define SPR_CELL_TSCR 0x399 /* ... Cell BE Thread Switch Register */ #if defined(AIM) #define SPR_DBSR 0x3f0 /* 4.. Debug Status Register */ diff --git a/sys/powerpc/aim/ofw_machdep.c b/sys/powerpc/ofw/ofw_machdep.c index 1ebcd0cb7d85..f0daa4f28139 100644 --- a/sys/powerpc/aim/ofw_machdep.c +++ b/sys/powerpc/ofw/ofw_machdep.c @@ -66,7 +66,6 @@ static struct mem_region OFfree[OFMEM_REGIONS + 3]; static int nOFmem; extern register_t ofmsr[5]; -extern struct pmap ofw_pmap; static int (*ofwcall)(void *); static void *fdt; int ofw_real_mode; @@ -417,59 +416,27 @@ openfirmware_core(void *args) { int result; register_t oldmsr; - #ifndef __powerpc64__ - register_t srsave[16]; - u_int i; - #endif /* * Turn off exceptions - we really don't want to end up - * anywhere unexpected with PCPU set to something strange, - * the stack pointer wrong, or the OFW mapping enabled. + * anywhere unexpected with PCPU set to something strange + * or the stack pointer wrong. */ oldmsr = intr_disable(); ofw_sprg_prepare(); - #ifndef __powerpc64__ - if (pmap_bootstrapped && !ofw_real_mode) { - /* - * Swap the kernel's address space with Open Firmware's - */ - - for (i = 0; i < 16; i++) { - srsave[i] = mfsrin(i << ADDR_SR_SHFT); - mtsrin(i << ADDR_SR_SHFT, ofw_pmap.pm_sr[i]); - } - - /* - * Clear battable[] translations - */ - if (!(cpu_features & PPC_FEATURE_64)) { - __asm __volatile("mtdbatu 2, %0\n" - "mtdbatu 3, %0" : : "r" (0)); - } - isync(); - } - #endif +#if defined(AIM) && !defined(__powerpc64__) + /* + * Clear battable[] translations + */ + if (!(cpu_features & PPC_FEATURE_64)) + __asm __volatile("mtdbatu 2, %0\n" + "mtdbatu 3, %0" : : "r" (0)); + isync(); +#endif result = ofwcall(args); - - #ifndef __powerpc64__ - if (pmap_bootstrapped && !ofw_real_mode) { - /* - * Restore the kernel's addr space. The isync() doesn;t - * work outside the loop unless mtsrin() is open-coded - * in an asm statement :( - */ - - for (i = 0; i < 16; i++) { - mtsrin(i << ADDR_SR_SHFT, srsave[i]); - isync(); - } - } - #endif - ofw_sprg_restore(); intr_restore(oldmsr); @@ -692,16 +659,3 @@ OF_decode_addr(phandle_t dev, int regno, bus_space_tag_t *tag, return (bus_space_map(*tag, addr, size, 0, handle)); } -int -mem_valid(vm_offset_t addr, int len) -{ - int i; - - for (i = 0; i < nOFmem; i++) - if ((addr >= OFmem[i].mr_start) - && (addr + len < OFmem[i].mr_start + OFmem[i].mr_size)) - return (0); - - return (EFAULT); -} - diff --git a/sys/powerpc/powerpc/cpu.c b/sys/powerpc/powerpc/cpu.c index 4bbfcac82bbe..97011562a8e2 100644 --- a/sys/powerpc/powerpc/cpu.c +++ b/sys/powerpc/powerpc/cpu.c @@ -64,6 +64,7 @@ #include <sys/conf.h> #include <sys/cpu.h> #include <sys/kernel.h> +#include <sys/proc.h> #include <sys/sysctl.h> #include <machine/bus.h> @@ -73,12 +74,15 @@ #include <machine/smp.h> #include <machine/spr.h> -int powerpc_pow_enabled; - static void cpu_6xx_setup(int cpuid, uint16_t vers); static void cpu_e500_setup(int cpuid, uint16_t vers); static void cpu_970_setup(int cpuid, uint16_t vers); +int powerpc_pow_enabled; +void (*cpu_idle_hook)(void) = NULL; +static void cpu_idle_60x(void); +static void cpu_idle_e500(void); + struct cputab { const char *name; uint16_t version; @@ -145,6 +149,9 @@ static const struct cputab models[] = { 0, cpu_e500_setup }, { "Freescale e500v2 core", FSL_E500v2, REVFMT_MAJMIN, 0, cpu_e500_setup }, + { "IBM Cell Broadband Engine", IBMCELLBE, REVFMT_MAJMIN, + PPC_FEATURE_64 | PPC_FEATURE_HAS_ALTIVEC | PPC_FEATURE_HAS_FPU, + NULL}, { "Unknown PowerPC CPU", 0, REVFMT_HEX, 0, NULL }, }; @@ -374,6 +381,9 @@ cpu_6xx_setup(int cpuid, uint16_t vers) } printf("cpu%d: HID0 %b\n", cpuid, (int)hid0, bitmask); + + if (cpu_idle_hook == NULL) + cpu_idle_hook = cpu_idle_60x; } @@ -441,6 +451,9 @@ cpu_e500_setup(int cpuid, uint16_t vers) mtspr(SPR_HID0, hid0); printf("cpu%d: HID0 %b\n", cpuid, (int)hid0, HID0_E500_BITMASK); + + if (cpu_idle_hook == NULL) + cpu_idle_hook = cpu_idle_e500; } static void @@ -478,6 +491,8 @@ cpu_970_setup(int cpuid, uint16_t vers) : "=r" (hid0_hi) : "K" (SPR_HID0)); printf("cpu%d: HID0 %b\n", cpuid, (int)(hid0_hi), HID0_970_BITMASK); #endif + + cpu_idle_hook = cpu_idle_60x; } static int @@ -490,3 +505,87 @@ cpu_feature_bit(SYSCTL_HANDLER_ARGS) return (sysctl_handle_int(oidp, &result, 0, req)); } +void +cpu_idle(int busy) +{ + +#ifdef INVARIANTS + if ((mfmsr() & PSL_EE) != PSL_EE) { + struct thread *td = curthread; + printf("td msr %#lx\n", (u_long)td->td_md.md_saved_msr); + panic("ints disabled in idleproc!"); + } +#endif + + CTR2(KTR_SPARE2, "cpu_idle(%d) at %d", + busy, curcpu); + if (cpu_idle_hook != NULL) { + if (!busy) { + critical_enter(); + cpu_idleclock(); + } + cpu_idle_hook(); + if (!busy) { + cpu_activeclock(); + critical_exit(); + } + } + CTR2(KTR_SPARE2, "cpu_idle(%d) at %d done", + busy, curcpu); +} + +int +cpu_idle_wakeup(int cpu) +{ + return (0); +} + +static void +cpu_idle_60x(void) +{ + register_t msr; + uint16_t vers; + + if (!powerpc_pow_enabled) + return; + + msr = mfmsr(); + vers = mfpvr() >> 16; + +#ifdef AIM + switch (vers) { + case IBM970: + case IBM970FX: + case IBM970MP: + case MPC7447A: + case MPC7448: + case MPC7450: + case MPC7455: + case MPC7457: + __asm __volatile("\ + dssall; sync; mtmsr %0; isync" + :: "r"(msr | PSL_POW)); + break; + default: + powerpc_sync(); + mtmsr(msr | PSL_POW); + isync(); + break; + } +#endif +} + +static void +cpu_idle_e500(void) +{ + register_t msr; + + msr = mfmsr(); + +#ifdef E500 + /* Freescale E500 core RM section 6.4.1. */ + __asm __volatile("msync; mtmsr %0; isync" :: + "r" (msr | PSL_WE)); +#endif +} + diff --git a/sys/powerpc/powerpc/mp_machdep.c b/sys/powerpc/powerpc/mp_machdep.c index 9086db5299c0..81c5481a7878 100644 --- a/sys/powerpc/powerpc/mp_machdep.c +++ b/sys/powerpc/powerpc/mp_machdep.c @@ -78,7 +78,13 @@ machdep_ap_bootstrap(void) ; /* Initialize DEC and TB, sync with the BSP values */ +#ifdef __powerpc64__ + /* Writing to the time base register is hypervisor-privileged */ + if (mfmsr() & PSL_HV) + mttb(ap_timebase); +#else mttb(ap_timebase); +#endif decr_ap_init(); /* Serialize console output and AP count increment */ @@ -98,13 +104,6 @@ machdep_ap_bootstrap(void) sched_throw(NULL); } -struct cpu_group * -cpu_topo(void) -{ - - return (smp_topo_none()); -} - void cpu_mp_setmaxid(void) { @@ -247,7 +246,13 @@ cpu_mp_unleash(void *dummy) /* Let APs continue */ atomic_store_rel_int(&ap_letgo, 1); +#ifdef __powerpc64__ + /* Writing to the time base register is hypervisor-privileged */ + if (mfmsr() & PSL_HV) + mttb(ap_timebase); +#else mttb(ap_timebase); +#endif while (ap_awake < smp_cpus) ; diff --git a/sys/powerpc/powerpc/platform.c b/sys/powerpc/powerpc/platform.c index 2b5c60792db3..02d89ee3dae2 100644 --- a/sys/powerpc/powerpc/platform.c +++ b/sys/powerpc/powerpc/platform.c @@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$"); #include <sys/ktr.h> #include <sys/mutex.h> #include <sys/systm.h> +#include <sys/smp.h> #include <sys/sysctl.h> #include <sys/types.h> @@ -47,6 +48,7 @@ __FBSDID("$FreeBSD$"); #include <vm/vm_page.h> #include <machine/cpu.h> +#include <machine/md_var.h> #include <machine/platform.h> #include <machine/platformvar.h> #include <machine/smp.h> @@ -62,11 +64,45 @@ static char plat_name[64] = ""; SYSCTL_STRING(_hw, OID_AUTO, platform, CTLFLAG_RD | CTLFLAG_TUN, plat_name, 0, "Platform currently in use"); +static struct mem_region *pregions = NULL; +static struct mem_region *aregions = NULL; +static int npregions, naregions; + void mem_regions(struct mem_region **phys, int *physsz, struct mem_region **avail, int *availsz) { - PLATFORM_MEM_REGIONS(plat_obj, phys, physsz, avail, availsz); + if (pregions == NULL) + PLATFORM_MEM_REGIONS(plat_obj, &pregions, &npregions, + &aregions, &naregions); + + *phys = pregions; + *avail = aregions; + *physsz = npregions; + *availsz = naregions; +} + +int +mem_valid(vm_offset_t addr, int len) +{ + int i; + + if (pregions == NULL) + PLATFORM_MEM_REGIONS(plat_obj, &pregions, &npregions, + &aregions, &naregions); + + for (i = 0; i < npregions; i++) + if ((addr >= pregions[i].mr_start) + && (addr + len < pregions[i].mr_start + pregions[i].mr_size)) + return (0); + + return (EFAULT); +} + +vm_offset_t +platform_real_maxaddr(void) +{ + return (PLATFORM_REAL_MAXADDR(plat_obj)); } const char * @@ -105,6 +141,14 @@ platform_smp_start_cpu(struct pcpu *cpu) return (PLATFORM_SMP_START_CPU(plat_obj, cpu)); } +#ifdef SMP +struct cpu_group * +cpu_topo(void) +{ + return (PLATFORM_SMP_TOPO(plat_obj)); +} +#endif + /* * Reset back to firmware. */ @@ -164,9 +208,10 @@ platform_probe_and_attach() } /* - * We can't free the KOBJ, since it is static. Luckily, - * this has no ill effects since it gets reset every time. + * We can't free the KOBJ, since it is static. Reset the ops + * member of this class so that we can come back later. */ + platp->ops = NULL; } if (plat_def_impl == NULL) diff --git a/sys/powerpc/powerpc/platform_if.m b/sys/powerpc/powerpc/platform_if.m index 07e49bc884cd..94383c327d6e 100644 --- a/sys/powerpc/powerpc/platform_if.m +++ b/sys/powerpc/powerpc/platform_if.m @@ -30,10 +30,12 @@ #include <sys/lock.h> #include <sys/mutex.h> #include <sys/systm.h> +#include <sys/smp.h> #include <machine/platform.h> #include <machine/platformvar.h> #include <machine/smp.h> +#include <machine/vmparam.h> /** * @defgroup PLATFORM platform - KObj methods for PowerPC platform @@ -66,6 +68,18 @@ CODE { { return (ENOENT); } + static struct cpu_group *platform_null_smp_topo(platform_t plat) + { +#ifdef SMP + return (smp_topo_none()); +#else + return (NULL); +#endif + } + static vm_offset_t platform_null_real_maxaddr(platform_t plat) + { + return (VM_MAX_ADDRESS); + } }; /** @@ -109,6 +123,15 @@ METHOD void mem_regions { }; /** + * @brief Return the maximum address accessible in real mode + * (for use with hypervisors) + */ +METHOD vm_offset_t real_maxaddr { + platform_t _plat; +} DEFAULT platform_null_real_maxaddr; + + +/** * @brief Get the CPU's timebase frequency, in ticks per second. * * @param _cpu CPU whose timebase to query @@ -162,6 +185,13 @@ METHOD int smp_start_cpu { }; /** + * @brief Return SMP topology + */ +METHOD cpu_group_t smp_topo { + platform_t _plat; +} DEFAULT platform_null_smp_topo; + +/** * @brief Reset system */ METHOD void reset { diff --git a/sys/sys/param.h b/sys/sys/param.h index a64c77b2601c..acd1f519e383 100644 --- a/sys/sys/param.h +++ b/sys/sys/param.h @@ -58,7 +58,7 @@ * in the range 5 to 9. */ #undef __FreeBSD_version -#define __FreeBSD_version 900024 /* Master, propagated to newvers */ +#define __FreeBSD_version 900025 /* Master, propagated to newvers */ #ifndef LOCORE #include <sys/types.h> diff --git a/sys/sys/signal.h b/sys/sys/signal.h index 85116430fccc..971683516e3c 100644 --- a/sys/sys/signal.h +++ b/sys/sys/signal.h @@ -284,7 +284,7 @@ typedef struct __siginfo { #define POLL_MSG 3 /* Input message available */ #define POLL_ERR 4 /* I/O Error */ #define POLL_PRI 5 /* High priority input available */ -#define POLL_HUP 4 /* Device disconnected */ +#define POLL_HUP 6 /* Device disconnected */ #endif diff --git a/sys/sys/smp.h b/sys/sys/smp.h index 619eba813f5a..6104d3e22422 100644 --- a/sys/sys/smp.h +++ b/sys/sys/smp.h @@ -16,8 +16,6 @@ #ifndef LOCORE -#ifdef SMP - /* * Topology of a NUMA or HTT system. * @@ -41,6 +39,8 @@ struct cpu_group { int8_t cg_flags; /* Traversal modifiers. */ }; +typedef struct cpu_group *cpu_group_t; + /* * Defines common resources for CPUs in the group. The highest level * resource should be used when multiple are shared. @@ -60,6 +60,7 @@ struct cpu_group { /* * Convenience routines for building topologies. */ +#ifdef SMP struct cpu_group *smp_topo(void); struct cpu_group *smp_topo_none(void); struct cpu_group *smp_topo_1level(int l1share, int l1count, int l1flags); diff --git a/sys/sys/socket.h b/sys/sys/socket.h index 9f227482f170..6a5821988c2d 100644 --- a/sys/sys/socket.h +++ b/sys/sys/socket.h @@ -137,6 +137,7 @@ typedef __uid_t uid_t; #define SO_LISTENQLEN 0x1012 /* socket's complete queue length */ #define SO_LISTENINCQLEN 0x1013 /* socket's incomplete queue length */ #define SO_SETFIB 0x1014 /* use this FIB to route */ +#define SO_USER_COOKIE 0x1015 /* user cookie (dummynet etc.) */ #endif /* diff --git a/sys/sys/socketvar.h b/sys/sys/socketvar.h index 6d6578b68abe..94c3b24e5c3e 100644 --- a/sys/sys/socketvar.h +++ b/sys/sys/socketvar.h @@ -117,7 +117,14 @@ struct socket { void *so_accept_filter_arg; /* saved filter args */ char *so_accept_filter_str; /* saved user args */ } *so_accf; + /* + * so_fibnum, so_user_cookie and friends can be used to attach + * some user-specified metadata to a socket, which then can be + * used by the kernel for various actions. + * so_user_cookie is used by ipfw/dummynet. + */ int so_fibnum; /* routing domain for this socket */ + uint32_t so_user_cookie; }; /* |
