diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2016-10-31 19:02:42 +0000 |
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2016-10-31 19:02:42 +0000 |
| commit | 02ebdc78239c4e929e42896931a4f04526e04440 (patch) | |
| tree | b2635ce18a57392f126c5599b6baaf68c4b92575 /sys/dev | |
| parent | 5763f79695f9b1ffacce55a8594cb7be08c3f31c (diff) | |
| parent | 130a08a362287342b83f8a78914db38a325145a3 (diff) | |
Notes
Diffstat (limited to 'sys/dev')
66 files changed, 4247 insertions, 1920 deletions
diff --git a/sys/dev/aacraid/aacraid_pci.c b/sys/dev/aacraid/aacraid_pci.c index 46eb53c85aba..0f67c742be0b 100644 --- a/sys/dev/aacraid/aacraid_pci.c +++ b/sys/dev/aacraid/aacraid_pci.c @@ -102,8 +102,6 @@ struct aac_ident "Adaptec RAID Controller"}, {0x9005, 0x028d, 0, 0, AAC_HWIF_SRCV, 0, "Adaptec RAID Controller"}, - {0x9005, 0x028f, 0, 0, AAC_HWIF_SRCV, 0, - "Adaptec RAID Controller"}, {0, 0, 0, 0, 0, 0, 0} }; diff --git a/sys/dev/bfe/if_bfe.c b/sys/dev/bfe/if_bfe.c index eaad8f0b5981..50cb173f8e73 100644 --- a/sys/dev/bfe/if_bfe.c +++ b/sys/dev/bfe/if_bfe.c @@ -793,6 +793,8 @@ bfe_list_newbuf(struct bfe_softc *sc, int c) int nsegs; m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); + if (m == NULL) + return (ENOBUFS); m->m_len = m->m_pkthdr.len = MCLBYTES; if (bus_dmamap_load_mbuf_sg(sc->bfe_rxmbuf_tag, sc->bfe_rx_sparemap, diff --git a/sys/dev/bxe/bxe.c b/sys/dev/bxe/bxe.c index c5daac58da56..98978da9a53f 100644 --- a/sys/dev/bxe/bxe.c +++ b/sys/dev/bxe/bxe.c @@ -5603,7 +5603,7 @@ bxe_tx_start(if_t ifp) fp = &sc->fp[0]; - if (ifp->if_drv_flags & IFF_DRV_OACTIVE) { + if (if_getdrvflags(ifp) & IFF_DRV_OACTIVE) { fp->eth_q_stats.tx_queue_full_return++; return; } @@ -5643,7 +5643,7 @@ bxe_tx_mq_start_locked(struct bxe_softc *sc, } } - if (!sc->link_vars.link_up || !(ifp->if_drv_flags & IFF_DRV_RUNNING)) { + if (!sc->link_vars.link_up || !(if_getdrvflags(ifp) & IFF_DRV_RUNNING)) { fp->eth_q_stats.tx_request_link_down_failures++; goto bxe_tx_mq_start_locked_exit; } diff --git a/sys/dev/chromebook_platform/chromebook_platform.c b/sys/dev/chromebook_platform/chromebook_platform.c new file mode 100644 index 000000000000..5cfeb9cb1e4e --- /dev/null +++ b/sys/dev/chromebook_platform/chromebook_platform.c @@ -0,0 +1,102 @@ +/*- + * Copyright (c) 2016 The FreeBSD Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/errno.h> +#include <sys/bus.h> + +#include <dev/pci/pcivar.h> +#include <dev/pci/pcireg.h> +#include <dev/iicbus/iicbus.h> +#include <dev/iicbus/iiconf.h> + +/* + * Driver that attaches I2C devices. + */ +static struct { + uint32_t pci_id; + const char *name; + uint8_t addr; +} slaves[] = { + { 0x9c628086, "isl", 0x88 }, + { 0x9c618086, "cyapa", 0xce }, +}; + +static void +chromebook_i2c_identify(driver_t *driver, device_t bus) +{ + device_t controller; + device_t child; + int i; + + /* + * A stopgap approach to preserve the status quo. + * A more intelligent approach is required to correctly + * identify a machine model and hardware available on it. + * For instance, DMI could be used. + * See http://lxr.free-electrons.com/source/drivers/platform/chrome/chromeos_laptop.c + */ + controller = device_get_parent(bus); + if (strcmp(device_get_name(controller), "ig4iic") != 0) + return; + + for (i = 0; i < nitems(slaves); i++) { + if (device_find_child(bus, slaves[i].name, -1) != NULL) + continue; + if (slaves[i].pci_id != pci_get_devid(controller)) + continue; + child = BUS_ADD_CHILD(bus, 0, slaves[i].name, -1); + if (child != NULL) + iicbus_set_addr(child, slaves[i].addr); + } +} + +static device_method_t chromebook_i2c_methods[] = { + DEVMETHOD(device_identify, chromebook_i2c_identify), + { 0, 0 } +}; + +static driver_t chromebook_i2c_driver = { + "chromebook_i2c", + chromebook_i2c_methods, + 0 /* no softc */ +}; + +static devclass_t chromebook_i2c_devclass; + +DRIVER_MODULE(chromebook_i2c, iicbus, chromebook_i2c_driver, + chromebook_i2c_devclass, 0, 0); +MODULE_VERSION(chromebook_i2c, 1); + diff --git a/sys/dev/cxgbe/common/t4_hw.c b/sys/dev/cxgbe/common/t4_hw.c index 553993a66f2b..9ba4b9566518 100644 --- a/sys/dev/cxgbe/common/t4_hw.c +++ b/sys/dev/cxgbe/common/t4_hw.c @@ -432,6 +432,21 @@ int t4_wr_mbox_meat_timeout(struct adapter *adap, int mbox, const void *cmd, CH_ERR(adap, "command %#x in mailbox %d timed out\n", *(const u8 *)cmd, mbox); + /* If DUMP_MBOX is set the mbox has already been dumped */ + if ((adap->debug_flags & DF_DUMP_MBOX) == 0) { + p = cmd; + CH_ERR(adap, "mbox: %016llx %016llx %016llx %016llx " + "%016llx %016llx %016llx %016llx\n", + (unsigned long long)be64_to_cpu(p[0]), + (unsigned long long)be64_to_cpu(p[1]), + (unsigned long long)be64_to_cpu(p[2]), + (unsigned long long)be64_to_cpu(p[3]), + (unsigned long long)be64_to_cpu(p[4]), + (unsigned long long)be64_to_cpu(p[5]), + (unsigned long long)be64_to_cpu(p[6]), + (unsigned long long)be64_to_cpu(p[7])); + } + t4_report_fw_error(adap); t4_fatal_err(adap); return ret; @@ -5855,10 +5870,13 @@ void t4_get_port_stats(struct adapter *adap, int idx, struct port_stats *p) p->tx_ppp6 = GET_STAT(TX_PORT_PPP6); p->tx_ppp7 = GET_STAT(TX_PORT_PPP7); - if (stat_ctl & F_COUNTPAUSESTATTX) { - p->tx_frames -= p->tx_pause; - p->tx_octets -= p->tx_pause * 64; - p->tx_mcast_frames -= p->tx_pause; + if (chip_id(adap) >= CHELSIO_T5) { + if (stat_ctl & F_COUNTPAUSESTATTX) { + p->tx_frames -= p->tx_pause; + p->tx_octets -= p->tx_pause * 64; + } + if (stat_ctl & F_COUNTPAUSEMCTX) + p->tx_mcast_frames -= p->tx_pause; } p->rx_pause = GET_STAT(RX_PORT_PAUSE); @@ -5889,10 +5907,13 @@ void t4_get_port_stats(struct adapter *adap, int idx, struct port_stats *p) p->rx_ppp6 = GET_STAT(RX_PORT_PPP6); p->rx_ppp7 = GET_STAT(RX_PORT_PPP7); - if (stat_ctl & F_COUNTPAUSESTATRX) { - p->rx_frames -= p->rx_pause; - p->rx_octets -= p->rx_pause * 64; - p->rx_mcast_frames -= p->rx_pause; + if (chip_id(adap) >= CHELSIO_T5) { + if (stat_ctl & F_COUNTPAUSESTATRX) { + p->rx_frames -= p->rx_pause; + p->rx_octets -= p->rx_pause * 64; + } + if (stat_ctl & F_COUNTPAUSEMCRX) + p->rx_mcast_frames -= p->rx_pause; } p->rx_ovflow0 = (bgmap & 1) ? GET_STAT_COM(RX_BG_0_MAC_DROP_FRAME) : 0; diff --git a/sys/dev/cxgbe/t4_sge.c b/sys/dev/cxgbe/t4_sge.c index 3748b2bc80f7..88c2e91bc7ae 100644 --- a/sys/dev/cxgbe/t4_sge.c +++ b/sys/dev/cxgbe/t4_sge.c @@ -2110,24 +2110,6 @@ m_advance(struct mbuf **pm, int *poffset, int len) return ((void *)p); } -static inline int -same_paddr(char *a, char *b) -{ - - if (a == b) - return (1); - else if (a != NULL && b != NULL) { - vm_offset_t x = (vm_offset_t)a; - vm_offset_t y = (vm_offset_t)b; - - if ((x & PAGE_MASK) == (y & PAGE_MASK) && - pmap_kextract(x) == pmap_kextract(y)) - return (1); - } - - return (0); -} - /* * Can deal with empty mbufs in the chain that have m_len = 0, but the chain * must have at least one mbuf that's not empty. @@ -2135,24 +2117,25 @@ same_paddr(char *a, char *b) static inline int count_mbuf_nsegs(struct mbuf *m) { - char *prev_end, *start; + vm_paddr_t lastb, next; + vm_offset_t va; int len, nsegs; MPASS(m != NULL); nsegs = 0; - prev_end = NULL; + lastb = 0; for (; m; m = m->m_next) { len = m->m_len; if (__predict_false(len == 0)) continue; - start = mtod(m, char *); - - nsegs += sglist_count(start, len); - if (same_paddr(prev_end, start)) + va = mtod(m, vm_offset_t); + next = pmap_kextract(va); + nsegs += sglist_count(m->m_data, len); + if (lastb + 1 == next) nsegs--; - prev_end = start + len; + lastb = pmap_kextract(va + len - 1); } MPASS(nsegs > 0); diff --git a/sys/dev/cyapa/cyapa.c b/sys/dev/cyapa/cyapa.c index 035121d7468b..d3a0ef18d83e 100644 --- a/sys/dev/cyapa/cyapa.c +++ b/sys/dev/cyapa/cyapa.c @@ -122,11 +122,11 @@ __FBSDID("$FreeBSD$"); #include <sys/uio.h> #include <sys/vnode.h> -#include <dev/smbus/smbconf.h> -#include <dev/smbus/smbus.h> +#include <dev/iicbus/iiconf.h> +#include <dev/iicbus/iicbus.h> #include <dev/cyapa/cyapa.h> -#include "smbus_if.h" +#include "iicbus_if.h" #include "bus_if.h" #include "device_if.h" @@ -149,7 +149,6 @@ struct cyapa_fifo { struct cyapa_softc { device_t dev; int count; /* >0 if device opened */ - int addr; struct cdev *devnode; struct selinfo selinfo; struct mtx mutex; @@ -273,6 +272,30 @@ static int cyapa_reset = 0; SYSCTL_INT(_debug, OID_AUTO, cyapa_reset, CTLFLAG_RW, &cyapa_reset, 0, "Reset track pad"); +static int +cyapa_read_bytes(device_t dev, uint8_t reg, uint8_t *val, int cnt) +{ + uint16_t addr = iicbus_get_addr(dev); + struct iic_msg msgs[] = { + { addr, IIC_M_WR | IIC_M_NOSTOP, 1, ® }, + { addr, IIC_M_RD, cnt, val }, + }; + + return (iicbus_transfer(dev, msgs, nitems(msgs))); +} + +static int +cyapa_write_bytes(device_t dev, uint8_t reg, const uint8_t *val, int cnt) +{ + uint16_t addr = iicbus_get_addr(dev); + struct iic_msg msgs[] = { + { addr, IIC_M_WR | IIC_M_NOSTOP, 1, ® }, + { addr, IIC_M_WR | IIC_M_NOSTART, cnt, __DECONST(uint8_t *, val) }, + }; + + return (iicbus_transfer(dev, msgs, nitems(msgs))); +} + static void cyapa_lock(struct cyapa_softc *sc) { @@ -318,7 +341,7 @@ cyapa_notify(struct cyapa_softc *sc) * Initialize the device */ static int -init_device(device_t dev, struct cyapa_cap *cap, int addr, int probe) +init_device(device_t dev, struct cyapa_cap *cap, int probe) { static char bl_exit[] = { 0x00, 0xff, 0xa5, 0x00, 0x01, @@ -326,17 +349,13 @@ init_device(device_t dev, struct cyapa_cap *cap, int addr, int probe) static char bl_deactivate[] = { 0x00, 0xff, 0x3b, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }; - device_t bus; struct cyapa_boot_regs boot; int error; int retries; - bus = device_get_parent(dev); /* smbus */ - /* Get status */ - error = smbus_trans(bus, addr, CMD_BOOT_STATUS, - SMB_TRANS_NOCNT | SMB_TRANS_7BIT, - NULL, 0, (void *)&boot, sizeof(boot), NULL); + error = cyapa_read_bytes(dev, CMD_BOOT_STATUS, + (void *)&boot, sizeof(boot)); if (error) goto done; @@ -350,25 +369,21 @@ init_device(device_t dev, struct cyapa_cap *cap, int addr, int probe) /* Busy, wait loop. */ } else if (boot.error & CYAPA_ERROR_BOOTLOADER) { /* Magic */ - error = smbus_trans(bus, addr, CMD_BOOT_STATUS, - SMB_TRANS_NOCNT | SMB_TRANS_7BIT, - bl_deactivate, sizeof(bl_deactivate), - NULL, 0, NULL); + error = cyapa_write_bytes(dev, CMD_BOOT_STATUS, + bl_deactivate, sizeof(bl_deactivate)); if (error) goto done; } else { /* Magic */ - error = smbus_trans(bus, addr, CMD_BOOT_STATUS, - SMB_TRANS_NOCNT | SMB_TRANS_7BIT, - bl_exit, sizeof(bl_exit), NULL, 0, NULL); + error = cyapa_write_bytes(dev, CMD_BOOT_STATUS, + bl_exit, sizeof(bl_exit)); if (error) goto done; } pause("cyapab1", (hz * 2) / 10); --retries; - error = smbus_trans(bus, addr, CMD_BOOT_STATUS, - SMB_TRANS_NOCNT | SMB_TRANS_7BIT, - NULL, 0, (void *)&boot, sizeof(boot), NULL); + error = cyapa_read_bytes(dev, CMD_BOOT_STATUS, + (void *)&boot, sizeof(boot)); if (error) goto done; } @@ -381,9 +396,8 @@ init_device(device_t dev, struct cyapa_cap *cap, int addr, int probe) /* Check identity */ if (cap) { - error = smbus_trans(bus, addr, CMD_QUERY_CAPABILITIES, - SMB_TRANS_NOCNT | SMB_TRANS_7BIT, - NULL, 0, (void *)cap, sizeof(*cap), NULL); + error = cyapa_read_bytes(dev, CMD_QUERY_CAPABILITIES, + (void *)cap, sizeof(*cap)); if (strncmp(cap->prod_ida, "CYTRA", 5) != 0) { device_printf(dev, "Product ID \"%5.5s\" mismatch\n", @@ -391,9 +405,8 @@ init_device(device_t dev, struct cyapa_cap *cap, int addr, int probe) error = ENXIO; } } - error = smbus_trans(bus, addr, CMD_BOOT_STATUS, - SMB_TRANS_NOCNT | SMB_TRANS_7BIT, - NULL, 0, (void *)&boot, sizeof(boot), NULL); + error = cyapa_read_bytes(dev, CMD_BOOT_STATUS, + (void *)&boot, sizeof(boot)); if (probe == 0) /* official init */ device_printf(dev, "cyapa init status %02x\n", boot.stat); @@ -452,16 +465,16 @@ cyapa_probe(device_t dev) int addr; int error; - addr = smbus_get_addr(dev); + addr = iicbus_get_addr(dev); /* * 0x67 - cypress trackpad on the acer c720 * (other devices might use other ids). */ - if (addr != 0x67) + if (addr != 0xce) return (ENXIO); - error = init_device(dev, &cap, addr, 1); + error = init_device(dev, &cap, 1); if (error != 0) return (ENXIO); @@ -482,15 +495,14 @@ cyapa_attach(device_t dev) sc->reporting_mode = 1; unit = device_get_unit(dev); - addr = smbus_get_addr(dev); + addr = iicbus_get_addr(dev); - if (init_device(dev, &cap, addr, 0)) + if (init_device(dev, &cap, 0)) return (ENXIO); mtx_init(&sc->mutex, "cyapa", NULL, MTX_DEF); sc->dev = dev; - sc->addr = addr; knlist_init_mtx(&sc->selinfo.si_note, &sc->mutex); @@ -1159,7 +1171,7 @@ cyapa_poll_thread(void *arg) { struct cyapa_softc *sc; struct cyapa_regs regs; - device_t bus; /* smbus */ + device_t bus; /* iicbus */ int error; int freq; int isidle; @@ -1180,12 +1192,10 @@ cyapa_poll_thread(void *arg) while (!sc->detaching) { cyapa_unlock(sc); - error = smbus_request_bus(bus, sc->dev, SMB_WAIT); + error = iicbus_request_bus(bus, sc->dev, IIC_WAIT); if (error == 0) { - error = smbus_trans(bus, sc->addr, CMD_DEV_STATUS, - SMB_TRANS_NOCNT | SMB_TRANS_7BIT, - NULL, 0, - (void *)®s, sizeof(regs), NULL); + error = cyapa_read_bytes(sc->dev, CMD_DEV_STATUS, + (void *)®s, sizeof(regs)); if (error == 0) { isidle = cyapa_raw_input(sc, ®s, freq); } @@ -1200,9 +1210,9 @@ cyapa_poll_thread(void *arg) (unsigned)(ticks - last_reset) > TIME_TO_RESET)) { cyapa_reset = 0; last_reset = ticks; - init_device(sc->dev, NULL, sc->addr, 2); + init_device(sc->dev, NULL, 2); } - smbus_release_bus(bus, sc->dev); + iicbus_release_bus(bus, sc->dev); } pause("cyapw", hz / freq); ++sc->poll_ticks; @@ -1531,18 +1541,16 @@ cyapa_set_power_mode(struct cyapa_softc *sc, int mode) int error; bus = device_get_parent(sc->dev); - error = smbus_request_bus(bus, sc->dev, SMB_WAIT); + error = iicbus_request_bus(bus, sc->dev, IIC_WAIT); if (error == 0) { - error = smbus_trans(bus, sc->addr, CMD_POWER_MODE, - SMB_TRANS_NOCNT | SMB_TRANS_7BIT, - NULL, 0, (void *)&data, 1, NULL); + error = cyapa_read_bytes(sc->dev, CMD_POWER_MODE, + &data, 1); data = (data & ~0xFC) | mode; if (error == 0) { - error = smbus_trans(bus, sc->addr, CMD_POWER_MODE, - SMB_TRANS_NOCNT | SMB_TRANS_7BIT, - (void *)&data, 1, NULL, 0, NULL); + error = cyapa_write_bytes(sc->dev, CMD_POWER_MODE, + &data, 1); } - smbus_release_bus(bus, sc->dev); + iicbus_release_bus(bus, sc->dev); } } @@ -1697,6 +1705,6 @@ cyapa_fuzz(int delta, int *fuzzp) return (delta); } -DRIVER_MODULE(cyapa, smbus, cyapa_driver, cyapa_devclass, NULL, NULL); -MODULE_DEPEND(cyapa, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); +DRIVER_MODULE(cyapa, iicbus, cyapa_driver, cyapa_devclass, NULL, NULL); +MODULE_DEPEND(cyapa, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); MODULE_VERSION(cyapa, 1); diff --git a/sys/dev/dpaa/bman_fdt.c b/sys/dev/dpaa/bman_fdt.c index 691a8dea9538..c7950270e34d 100644 --- a/sys/dev/dpaa/bman_fdt.c +++ b/sys/dev/dpaa/bman_fdt.c @@ -143,7 +143,7 @@ bman_portals_fdt_attach(device_t dev) ihandle_t cpu; int cpu_num, cpus, intr_rid; struct dpaa_portals_devinfo di; - struct ofw_bus_devinfo ofw_di; + struct ofw_bus_devinfo ofw_di = {}; cpus = 0; sc = device_get_softc(dev); diff --git a/sys/dev/dpaa/qman_fdt.c b/sys/dev/dpaa/qman_fdt.c index 4df8c05b84e0..69f128cf649f 100644 --- a/sys/dev/dpaa/qman_fdt.c +++ b/sys/dev/dpaa/qman_fdt.c @@ -91,7 +91,7 @@ qman_fdt_probe(device_t dev) static device_probe_t qman_portals_fdt_probe; static device_attach_t qman_portals_fdt_attach; -static device_method_t bm_portals_methods[] = { +static device_method_t qm_portals_methods[] = { /* Device interface */ DEVMETHOD(device_probe, qman_portals_fdt_probe), DEVMETHOD(device_attach, qman_portals_fdt_attach), @@ -100,14 +100,14 @@ static device_method_t bm_portals_methods[] = { { 0, 0 } }; -static driver_t bm_portals_driver = { +static driver_t qm_portals_driver = { "qman-portals", - bm_portals_methods, + qm_portals_methods, sizeof(struct dpaa_portals_softc), }; -static devclass_t bm_portals_devclass; -DRIVER_MODULE(qman_portals, ofwbus, bm_portals_driver, bm_portals_devclass, 0, 0); +static devclass_t qm_portals_devclass; +DRIVER_MODULE(qman_portals, ofwbus, qm_portals_driver, qm_portals_devclass, 0, 0); static void get_addr_props(phandle_t node, uint32_t *addrp, uint32_t *sizep) @@ -143,7 +143,7 @@ qman_portals_fdt_attach(device_t dev) ihandle_t cpu; int cpu_num, cpus, intr_rid; struct dpaa_portals_devinfo di; - struct ofw_bus_devinfo ofw_di; + struct ofw_bus_devinfo ofw_di = {}; cpus = 0; sc = device_get_softc(dev); diff --git a/sys/dev/evdev/evdev.c b/sys/dev/evdev/evdev.c index 63e04966fec9..4de5dee8db04 100644 --- a/sys/dev/evdev/evdev.c +++ b/sys/dev/evdev/evdev.c @@ -822,21 +822,6 @@ push: return (ret); } -inline int -evdev_sync(struct evdev_dev *evdev) -{ - - return (evdev_push_event(evdev, EV_SYN, SYN_REPORT, 1)); -} - - -inline int -evdev_mt_sync(struct evdev_dev *evdev) -{ - - return (evdev_push_event(evdev, EV_SYN, SYN_MT_REPORT, 1)); -} - int evdev_register_client(struct evdev_dev *evdev, struct evdev_client *client) { diff --git a/sys/dev/evdev/evdev.h b/sys/dev/evdev/evdev.h index 7287e739e832..34808b4b563c 100644 --- a/sys/dev/evdev/evdev.h +++ b/sys/dev/evdev/evdev.h @@ -97,8 +97,6 @@ int evdev_register(struct evdev_dev *); int evdev_register_mtx(struct evdev_dev *, struct mtx *); int evdev_unregister(struct evdev_dev *); int evdev_push_event(struct evdev_dev *, uint16_t, uint16_t, int32_t); -int evdev_sync(struct evdev_dev *); -int evdev_mt_sync(struct evdev_dev *); void evdev_support_prop(struct evdev_dev *, uint16_t); void evdev_support_event(struct evdev_dev *, uint16_t); void evdev_support_key(struct evdev_dev *, uint16_t); @@ -129,4 +127,68 @@ void evdev_push_leds(struct evdev_dev *, int); void evdev_push_repeats(struct evdev_dev *, keyboard_t *); evdev_event_t evdev_ev_kbd_event; +/* Event reporting shortcuts: */ +static __inline int +evdev_sync(struct evdev_dev *evdev) +{ + + return (evdev_push_event(evdev, EV_SYN, SYN_REPORT, 1)); +} + +static __inline int +evdev_mt_sync(struct evdev_dev *evdev) +{ + + return (evdev_push_event(evdev, EV_SYN, SYN_MT_REPORT, 1)); +} + +static __inline int +evdev_push_key(struct evdev_dev *evdev, uint16_t code, int32_t value) +{ + + return (evdev_push_event(evdev, EV_KEY, code, value != 0)); +} + +static __inline int +evdev_push_rel(struct evdev_dev *evdev, uint16_t code, int32_t value) +{ + + return (evdev_push_event(evdev, EV_REL, code, value)); +} + +static __inline int +evdev_push_abs(struct evdev_dev *evdev, uint16_t code, int32_t value) +{ + + return (evdev_push_event(evdev, EV_ABS, code, value)); +} + +static __inline int +evdev_push_msc(struct evdev_dev *evdev, uint16_t code, int32_t value) +{ + + return (evdev_push_event(evdev, EV_MSC, code, value)); +} + +static __inline int +evdev_push_led(struct evdev_dev *evdev, uint16_t code, int32_t value) +{ + + return (evdev_push_event(evdev, EV_LED, code, value != 0)); +} + +static __inline int +evdev_push_snd(struct evdev_dev *evdev, uint16_t code, int32_t value) +{ + + return (evdev_push_event(evdev, EV_SND, code, value != 0)); +} + +static __inline int +evdev_push_sw(struct evdev_dev *evdev, uint16_t code, int32_t value) +{ + + return (evdev_push_event(evdev, EV_SW, code, value != 0)); +} + #endif /* _DEV_EVDEV_EVDEV_H */ diff --git a/sys/dev/evdev/evdev_utils.c b/sys/dev/evdev/evdev_utils.c index 47e5e64a3c42..860634299270 100644 --- a/sys/dev/evdev/evdev_utils.c +++ b/sys/dev/evdev/evdev_utils.c @@ -271,8 +271,8 @@ evdev_push_mouse_btn(struct evdev_dev *evdev, int buttons) size_t i; for (i = 0; i < nitems(evdev_mouse_button_codes); i++) - evdev_push_event(evdev, EV_KEY, evdev_mouse_button_codes[i], - (buttons & (1 << i)) != 0); + evdev_push_key(evdev, evdev_mouse_button_codes[i], + buttons & (1 << i)); } void @@ -285,8 +285,7 @@ evdev_push_leds(struct evdev_dev *evdev, int leds) return; for (i = 0; i < nitems(evdev_led_codes); i++) - evdev_push_event(evdev, EV_LED, evdev_led_codes[i], - (leds & (1 << i)) != 0); + evdev_push_led(evdev, evdev_led_codes[i], leds & (1 << i)); } void diff --git a/sys/dev/fdt/fdt_intr.h b/sys/dev/fdt/fdt_intr.h new file mode 100644 index 000000000000..b8568e1399f0 --- /dev/null +++ b/sys/dev/fdt/fdt_intr.h @@ -0,0 +1,44 @@ +/*- + * Copyright (c) 2016 Andrew Turner <andrew@FreeBSD.org> + * All rights reserved. + * + * This software was developed by SRI International and the University of + * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237 + * ("CTSRD"), as part of the DARPA CRASH research programme. + * + * 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$ + */ + +#ifndef _FDT_INTR_H_ +#define _FDT_INTR_H_ + +#define FDT_INTR_EDGE_RISING 1 +#define FDT_INTR_EDGE_FALLING 2 +#define FDT_INTR_LEVEL_HIGH 4 +#define FDT_INTR_LEVEL_LOW 8 +#define FDT_INTR_LOW_MASK (FDT_INTR_EDGE_FALLING | FDT_INTR_LEVEL_LOW) +#define FDT_INTR_EDGE_MASK (FDT_INTR_EDGE_RISING | FDT_INTR_EDGE_FALLING) +#define FDT_INTR_MASK 0xf + +#endif /* _FDT_INTR_H_ */ diff --git a/sys/dev/gpio/gpiobusvar.h b/sys/dev/gpio/gpiobusvar.h index 4717bf3fa32e..b6fb44cf5866 100644 --- a/sys/dev/gpio/gpiobusvar.h +++ b/sys/dev/gpio/gpiobusvar.h @@ -38,7 +38,6 @@ #ifdef FDT #include <dev/ofw/ofw_bus_subr.h> -#include <gnu/dts/include/dt-bindings/gpio/gpio.h> #endif #ifdef INTRNG diff --git a/sys/dev/gpio/ofw_gpiobus.c b/sys/dev/gpio/ofw_gpiobus.c index 96775dc0eb00..f8fcbea95078 100644 --- a/sys/dev/gpio/ofw_gpiobus.c +++ b/sys/dev/gpio/ofw_gpiobus.c @@ -41,6 +41,8 @@ __FBSDID("$FreeBSD$"); #include "gpiobus_if.h" +#define GPIO_ACTIVE_LOW 1 + static struct ofw_gpiobus_devinfo *ofw_gpiobus_setup_devinfo(device_t, device_t, phandle_t); static void ofw_gpiobus_destroy_devinfo(device_t, struct ofw_gpiobus_devinfo *); diff --git a/sys/dev/hwpmc/hwpmc_amd.c b/sys/dev/hwpmc/hwpmc_amd.c index 14d708aee949..7221071de0f8 100644 --- a/sys/dev/hwpmc/hwpmc_amd.c +++ b/sys/dev/hwpmc/hwpmc_amd.c @@ -689,12 +689,13 @@ amd_intr(int cpu, struct trapframe *tf) error = pmc_process_interrupt(cpu, PMC_HR, pm, tf, TRAPF_USERMODE(tf)); if (error == 0) - wrmsr(evsel, config | AMD_PMC_ENABLE); + wrmsr(evsel, config); } atomic_add_int(retval ? &pmc_stats.pm_intr_processed : &pmc_stats.pm_intr_ignored, 1); + PMCDBG1(MDP,INT,2, "retval=%d", retval); return (retval); } diff --git a/sys/dev/hyperv/netvsc/hv_net_vsc.c b/sys/dev/hyperv/netvsc/hn_nvs.c index 2676222541e3..0d8c75cbc079 100644 --- a/sys/dev/hyperv/netvsc/hv_net_vsc.c +++ b/sys/dev/hyperv/netvsc/hn_nvs.c @@ -24,49 +24,59 @@ * 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$ */ -/** - * HyperV vmbus network VSC (virtual services client) module - * +/* + * Network Virtualization Service. */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "opt_inet6.h" +#include "opt_inet.h" #include <sys/param.h> #include <sys/kernel.h> -#include <sys/socket.h> #include <sys/limits.h> -#include <sys/lock.h> +#include <sys/socket.h> +#include <sys/systm.h> +#include <sys/taskqueue.h> + #include <net/if.h> #include <net/if_var.h> -#include <net/if_arp.h> -#include <machine/bus.h> -#include <machine/atomic.h> +#include <net/if_media.h> + +#include <netinet/in.h> +#include <netinet/tcp_lro.h> #include <dev/hyperv/include/hyperv.h> +#include <dev/hyperv/include/hyperv_busdma.h> +#include <dev/hyperv/include/vmbus.h> #include <dev/hyperv/include/vmbus_xact.h> -#include <dev/hyperv/netvsc/hv_net_vsc.h> -#include <dev/hyperv/netvsc/hv_rndis_filter.h> + +#include <dev/hyperv/netvsc/ndis.h> #include <dev/hyperv/netvsc/if_hnreg.h> #include <dev/hyperv/netvsc/if_hnvar.h> +#include <dev/hyperv/netvsc/hn_nvs.h> -MALLOC_DEFINE(M_NETVSC, "netvsc", "Hyper-V netvsc driver"); - -/* - * Forward declarations - */ -static int hn_nvs_conn_chim(struct hn_softc *sc); -static int hn_nvs_conn_rxbuf(struct hn_softc *); -static int hn_nvs_disconn_chim(struct hn_softc *sc); -static int hn_nvs_disconn_rxbuf(struct hn_softc *sc); -static void hn_nvs_sent_none(struct hn_send_ctx *sndc, - struct hn_softc *, struct vmbus_channel *chan, - const void *, int); +static int hn_nvs_conn_chim(struct hn_softc *); +static int hn_nvs_conn_rxbuf(struct hn_softc *); +static int hn_nvs_disconn_chim(struct hn_softc *); +static int hn_nvs_disconn_rxbuf(struct hn_softc *); +static int hn_nvs_conf_ndis(struct hn_softc *, int); +static int hn_nvs_init_ndis(struct hn_softc *); +static int hn_nvs_doinit(struct hn_softc *, uint32_t); +static int hn_nvs_init(struct hn_softc *); +static const void *hn_nvs_xact_execute(struct hn_softc *, + struct vmbus_xact *, void *, int, + size_t *, uint32_t); +static void hn_nvs_sent_none(struct hn_nvs_sendctx *, + struct hn_softc *, struct vmbus_channel *, + const void *, int); -struct hn_send_ctx hn_send_ctx_none = - HN_SEND_CTX_INITIALIZER(hn_nvs_sent_none, NULL); +struct hn_nvs_sendctx hn_nvs_sendctx_none = + HN_NVS_SENDCTX_INITIALIZER(hn_nvs_sent_none, NULL); static const uint32_t hn_nvs_version[] = { HN_NVS_VERSION_5, @@ -75,38 +85,11 @@ static const uint32_t hn_nvs_version[] = { HN_NVS_VERSION_1 }; -uint32_t -hn_chim_alloc(struct hn_softc *sc) -{ - int i, bmap_cnt = sc->hn_chim_bmap_cnt; - u_long *bmap = sc->hn_chim_bmap; - uint32_t ret = HN_NVS_CHIM_IDX_INVALID; - - for (i = 0; i < bmap_cnt; ++i) { - int idx; - - idx = ffsl(~bmap[i]); - if (idx == 0) - continue; - - --idx; /* ffsl is 1-based */ - KASSERT(i * LONG_BIT + idx < sc->hn_chim_cnt, - ("invalid i %d and idx %d", i, idx)); - - if (atomic_testandset_long(&bmap[i], idx)) - continue; - - ret = i * LONG_BIT + idx; - break; - } - return (ret); -} - static const void * hn_nvs_xact_execute(struct hn_softc *sc, struct vmbus_xact *xact, void *req, int reqlen, size_t *resplen0, uint32_t type) { - struct hn_send_ctx sndc; + struct hn_nvs_sendctx sndc; size_t resplen, min_resplen = *resplen0; const struct hn_nvs_hdr *hdr; int error; @@ -117,7 +100,7 @@ hn_nvs_xact_execute(struct hn_softc *sc, struct vmbus_xact *xact, /* * Execute the xact setup by the caller. */ - hn_send_ctx_init_simple(&sndc, hn_nvs_sent_xact, xact); + hn_nvs_sendctx_init(&sndc, hn_nvs_sent_xact, xact); vmbus_xact_activate(xact); error = hn_nvs_send(sc->hn_prichan, VMBUS_CHANPKT_FLAG_RC, @@ -150,7 +133,7 @@ hn_nvs_req_send(struct hn_softc *sc, void *req, int reqlen) { return (hn_nvs_send(sc->hn_prichan, VMBUS_CHANPKT_FLAG_NONE, - req, reqlen, &hn_send_ctx_none)); + req, reqlen, &hn_nvs_sendctx_none)); } static int @@ -167,9 +150,9 @@ hn_nvs_conn_rxbuf(struct hn_softc *sc) * Limit RXBUF size for old NVS. */ if (sc->hn_nvs_ver <= HN_NVS_VERSION_2) - rxbuf_size = NETVSC_RECEIVE_BUFFER_SIZE_LEGACY; + rxbuf_size = HN_RXBUF_SIZE_COMPAT; else - rxbuf_size = NETVSC_RECEIVE_BUFFER_SIZE; + rxbuf_size = HN_RXBUF_SIZE; /* * Connect the RXBUF GPADL to the primary channel. @@ -248,8 +231,7 @@ hn_nvs_conn_chim(struct hn_softc *sc) * Sub-channels just share this chimney sending buffer. */ error = vmbus_chan_gpadl_connect(sc->hn_prichan, - sc->hn_chim_dma.hv_paddr, NETVSC_SEND_BUFFER_SIZE, - &sc->hn_chim_gpadl); + sc->hn_chim_dma.hv_paddr, HN_CHIM_SIZE, &sc->hn_chim_gpadl); if (error) { if_printf(sc->hn_ifp, "chim gpadl conn failed: %d\n", error); goto cleanup; @@ -296,8 +278,8 @@ hn_nvs_conn_chim(struct hn_softc *sc) } sc->hn_chim_szmax = sectsz; - sc->hn_chim_cnt = NETVSC_SEND_BUFFER_SIZE / sc->hn_chim_szmax; - if (NETVSC_SEND_BUFFER_SIZE % sc->hn_chim_szmax != 0) { + sc->hn_chim_cnt = HN_CHIM_SIZE / sc->hn_chim_szmax; + if (HN_CHIM_SIZE % sc->hn_chim_szmax != 0) { if_printf(sc->hn_ifp, "chimney sending sections are " "not properly aligned\n"); } @@ -308,7 +290,7 @@ hn_nvs_conn_chim(struct hn_softc *sc) sc->hn_chim_bmap_cnt = sc->hn_chim_cnt / LONG_BIT; sc->hn_chim_bmap = malloc(sc->hn_chim_bmap_cnt * sizeof(u_long), - M_NETVSC, M_WAITOK | M_ZERO); + M_DEVBUF, M_WAITOK | M_ZERO); /* Done! */ sc->hn_flags |= HN_FLAG_CHIM_CONNECTED; @@ -427,7 +409,7 @@ hn_nvs_disconn_chim(struct hn_softc *sc) } if (sc->hn_chim_bmap != NULL) { - free(sc->hn_chim_bmap, M_NETVSC); + free(sc->hn_chim_bmap, M_DEVBUF); sc->hn_chim_bmap = NULL; } return (0); @@ -634,7 +616,7 @@ hn_nvs_detach(struct hn_softc *sc) } void -hn_nvs_sent_xact(struct hn_send_ctx *sndc, +hn_nvs_sent_xact(struct hn_nvs_sendctx *sndc, struct hn_softc *sc __unused, struct vmbus_channel *chan __unused, const void *data, int dlen) { @@ -643,60 +625,13 @@ hn_nvs_sent_xact(struct hn_send_ctx *sndc, } static void -hn_nvs_sent_none(struct hn_send_ctx *sndc __unused, +hn_nvs_sent_none(struct hn_nvs_sendctx *sndc __unused, struct hn_softc *sc __unused, struct vmbus_channel *chan __unused, const void *data __unused, int dlen __unused) { /* EMPTY */ } -void -hn_chim_free(struct hn_softc *sc, uint32_t chim_idx) -{ - u_long mask; - uint32_t idx; - - idx = chim_idx / LONG_BIT; - KASSERT(idx < sc->hn_chim_bmap_cnt, - ("invalid chimney index 0x%x", chim_idx)); - - mask = 1UL << (chim_idx % LONG_BIT); - KASSERT(sc->hn_chim_bmap[idx] & mask, - ("index bitmap 0x%lx, chimney index %u, " - "bitmap idx %d, bitmask 0x%lx", - sc->hn_chim_bmap[idx], chim_idx, idx, mask)); - - atomic_clear_long(&sc->hn_chim_bmap[idx], mask); -} - -/* - * Net VSC on send - * Sends a packet on the specified Hyper-V device. - * Returns 0 on success, non-zero on failure. - */ -int -hv_nv_on_send(struct vmbus_channel *chan, uint32_t rndis_mtype, - struct hn_send_ctx *sndc, struct vmbus_gpa *gpa, int gpa_cnt) -{ - struct hn_nvs_rndis rndis; - int ret; - - rndis.nvs_type = HN_NVS_TYPE_RNDIS; - rndis.nvs_rndis_mtype = rndis_mtype; - rndis.nvs_chim_idx = sndc->hn_chim_idx; - rndis.nvs_chim_sz = sndc->hn_chim_sz; - - if (gpa_cnt) { - ret = hn_nvs_send_sglist(chan, gpa, gpa_cnt, - &rndis, sizeof(rndis), sndc); - } else { - ret = hn_nvs_send(chan, VMBUS_CHANPKT_FLAG_RC, - &rndis, sizeof(rndis), sndc); - } - - return (ret); -} - int hn_nvs_alloc_subchans(struct hn_softc *sc, int *nsubch0) { @@ -747,3 +682,12 @@ done: vmbus_xact_put(xact); return (error); } + +int +hn_nvs_send_rndis_ctrl(struct vmbus_channel *chan, + struct hn_nvs_sendctx *sndc, struct vmbus_gpa *gpa, int gpa_cnt) +{ + + return hn_nvs_send_rndis_sglist(chan, HN_NVS_RNDIS_MTYPE_CTRL, + sndc, gpa, gpa_cnt); +} diff --git a/sys/dev/hyperv/netvsc/hn_nvs.h b/sys/dev/hyperv/netvsc/hn_nvs.h new file mode 100644 index 000000000000..49b03e09b808 --- /dev/null +++ b/sys/dev/hyperv/netvsc/hn_nvs.h @@ -0,0 +1,106 @@ +/*- + * Copyright (c) 2009-2012,2016 Microsoft Corp. + * Copyright (c) 2010-2012 Citrix Inc. + * Copyright (c) 2012 NetApp Inc. + * 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 ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _HN_NVS_H_ +#define _HN_NVS_H_ + +struct hn_nvs_sendctx; +struct vmbus_channel; +struct hn_softc; + +typedef void (*hn_nvs_sent_t) + (struct hn_nvs_sendctx *, struct hn_softc *, + struct vmbus_channel *, const void *, int); + +struct hn_nvs_sendctx { + hn_nvs_sent_t hn_cb; + void *hn_cbarg; +}; + +#define HN_NVS_SENDCTX_INITIALIZER(cb, cbarg) \ +{ \ + .hn_cb = cb, \ + .hn_cbarg = cbarg \ +} + +static __inline void +hn_nvs_sendctx_init(struct hn_nvs_sendctx *sndc, hn_nvs_sent_t cb, void *cbarg) +{ + + sndc->hn_cb = cb; + sndc->hn_cbarg = cbarg; +} + +static __inline int +hn_nvs_send(struct vmbus_channel *chan, uint16_t flags, + void *nvs_msg, int nvs_msglen, struct hn_nvs_sendctx *sndc) +{ + + return (vmbus_chan_send(chan, VMBUS_CHANPKT_TYPE_INBAND, flags, + nvs_msg, nvs_msglen, (uint64_t)(uintptr_t)sndc)); +} + +static __inline int +hn_nvs_send_sglist(struct vmbus_channel *chan, struct vmbus_gpa sg[], int sglen, + void *nvs_msg, int nvs_msglen, struct hn_nvs_sendctx *sndc) +{ + + return (vmbus_chan_send_sglist(chan, sg, sglen, nvs_msg, nvs_msglen, + (uint64_t)(uintptr_t)sndc)); +} + +static __inline int +hn_nvs_send_rndis_sglist(struct vmbus_channel *chan, uint32_t rndis_mtype, + struct hn_nvs_sendctx *sndc, struct vmbus_gpa *gpa, int gpa_cnt) +{ + struct hn_nvs_rndis rndis; + + rndis.nvs_type = HN_NVS_TYPE_RNDIS; + rndis.nvs_rndis_mtype = rndis_mtype; + rndis.nvs_chim_idx = HN_NVS_CHIM_IDX_INVALID; + rndis.nvs_chim_sz = 0; + + return (hn_nvs_send_sglist(chan, gpa, gpa_cnt, + &rndis, sizeof(rndis), sndc)); +} + +int hn_nvs_attach(struct hn_softc *sc, int mtu); +void hn_nvs_detach(struct hn_softc *sc); +int hn_nvs_alloc_subchans(struct hn_softc *sc, int *nsubch); +void hn_nvs_sent_xact(struct hn_nvs_sendctx *sndc, + struct hn_softc *sc, struct vmbus_channel *chan, + const void *data, int dlen); +int hn_nvs_send_rndis_ctrl(struct vmbus_channel *chan, + struct hn_nvs_sendctx *sndc, struct vmbus_gpa *gpa, + int gpa_cnt); + +extern struct hn_nvs_sendctx hn_nvs_sendctx_none; + +#endif /* !_HN_NVS_H_ */ diff --git a/sys/dev/hyperv/netvsc/hv_rndis_filter.c b/sys/dev/hyperv/netvsc/hn_rndis.c index 16f292a22bba..08314a25e290 100644 --- a/sys/dev/hyperv/netvsc/hv_rndis_filter.c +++ b/sys/dev/hyperv/netvsc/hn_rndis.c @@ -29,41 +29,36 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); +#include "opt_inet6.h" +#include "opt_inet.h" + #include <sys/param.h> -#include <sys/mbuf.h> #include <sys/socket.h> -#include <sys/lock.h> -#include <sys/mutex.h> +#include <sys/systm.h> +#include <sys/taskqueue.h> + +#include <machine/atomic.h> + +#include <net/ethernet.h> #include <net/if.h> -#include <net/if_arp.h> #include <net/if_var.h> -#include <net/ethernet.h> +#include <net/if_media.h> #include <net/rndis.h> + #include <netinet/in.h> #include <netinet/ip.h> -#include <sys/types.h> -#include <machine/atomic.h> -#include <sys/sema.h> -#include <vm/vm.h> -#include <vm/vm_param.h> -#include <vm/pmap.h> +#include <netinet/tcp_lro.h> #include <dev/hyperv/include/hyperv.h> +#include <dev/hyperv/include/hyperv_busdma.h> +#include <dev/hyperv/include/vmbus.h> #include <dev/hyperv/include/vmbus_xact.h> -#include <dev/hyperv/netvsc/hv_net_vsc.h> -#include <dev/hyperv/netvsc/hv_rndis_filter.h> -#include <dev/hyperv/netvsc/if_hnreg.h> -#include <dev/hyperv/netvsc/ndis.h> -#define HV_RF_RECVINFO_VLAN 0x1 -#define HV_RF_RECVINFO_CSUM 0x2 -#define HV_RF_RECVINFO_HASHINF 0x4 -#define HV_RF_RECVINFO_HASHVAL 0x8 -#define HV_RF_RECVINFO_ALL \ - (HV_RF_RECVINFO_VLAN | \ - HV_RF_RECVINFO_CSUM | \ - HV_RF_RECVINFO_HASHINF | \ - HV_RF_RECVINFO_HASHVAL) +#include <dev/hyperv/netvsc/ndis.h> +#include <dev/hyperv/netvsc/if_hnreg.h> +#include <dev/hyperv/netvsc/if_hnvar.h> +#include <dev/hyperv/netvsc/hn_nvs.h> +#include <dev/hyperv/netvsc/hn_rndis.h> #define HN_RNDIS_RID_COMPAT_MASK 0xffff #define HN_RNDIS_RID_COMPAT_MAX HN_RNDIS_RID_COMPAT_MASK @@ -82,24 +77,23 @@ __FBSDID("$FreeBSD$"); #define HN_NDIS_LSOV2_CAP_IP6 \ (NDIS_LSOV2_CAP_IP6EXT | NDIS_LSOV2_CAP_TCP6OPT) -/* - * Forward declarations - */ -static void hv_rf_receive_indicate_status(struct hn_softc *sc, - const void *data, int dlen); -static void hv_rf_receive_data(struct hn_rx_ring *rxr, - const void *data, int dlen); - -static int hn_rndis_query(struct hn_softc *sc, uint32_t oid, - const void *idata, size_t idlen, void *odata, size_t *odlen0); -static int hn_rndis_query2(struct hn_softc *sc, uint32_t oid, - const void *idata, size_t idlen, void *odata, size_t *odlen0, - size_t min_odlen); -static int hn_rndis_set(struct hn_softc *sc, uint32_t oid, const void *data, - size_t dlen); -static int hn_rndis_conf_offload(struct hn_softc *sc, int mtu); -static int hn_rndis_query_hwcaps(struct hn_softc *sc, - struct ndis_offload *caps); +static const void *hn_rndis_xact_exec1(struct hn_softc *, + struct vmbus_xact *, size_t, + struct hn_nvs_sendctx *, size_t *); +static const void *hn_rndis_xact_execute(struct hn_softc *, + struct vmbus_xact *, uint32_t, size_t, size_t *, + uint32_t); +static int hn_rndis_query(struct hn_softc *, uint32_t, + const void *, size_t, void *, size_t *); +static int hn_rndis_query2(struct hn_softc *, uint32_t, + const void *, size_t, void *, size_t *, size_t); +static int hn_rndis_set(struct hn_softc *, uint32_t, + const void *, size_t); +static int hn_rndis_init(struct hn_softc *); +static int hn_rndis_halt(struct hn_softc *); +static int hn_rndis_conf_offload(struct hn_softc *, int); +static int hn_rndis_query_hwcaps(struct hn_softc *, + struct ndis_offload *); static __inline uint32_t hn_rndis_rid(struct hn_softc *sc) @@ -115,371 +109,22 @@ again: return ((rid & 0xffff) << 16); } -void * -hn_rndis_pktinfo_append(struct rndis_packet_msg *pkt, size_t pktsize, - size_t pi_dlen, uint32_t pi_type) -{ - const size_t pi_size = HN_RNDIS_PKTINFO_SIZE(pi_dlen); - struct rndis_pktinfo *pi; - - KASSERT((pi_size & RNDIS_PACKET_MSG_OFFSET_ALIGNMASK) == 0, - ("unaligned pktinfo size %zu, pktinfo dlen %zu", pi_size, pi_dlen)); - - /* - * Per-packet-info does not move; it only grows. - * - * NOTE: - * rm_pktinfooffset in this phase counts from the beginning - * of rndis_packet_msg. - */ - KASSERT(pkt->rm_pktinfooffset + pkt->rm_pktinfolen + pi_size <= pktsize, - ("%u pktinfo overflows RNDIS packet msg", pi_type)); - pi = (struct rndis_pktinfo *)((uint8_t *)pkt + pkt->rm_pktinfooffset + - pkt->rm_pktinfolen); - pkt->rm_pktinfolen += pi_size; - - pi->rm_size = pi_size; - pi->rm_type = pi_type; - pi->rm_pktinfooffset = RNDIS_PKTINFO_OFFSET; - - /* Data immediately follow per-packet-info. */ - pkt->rm_dataoffset += pi_size; - - /* Update RNDIS packet msg length */ - pkt->rm_len += pi_size; - - return (pi->rm_data); -} - -/* - * RNDIS filter receive indicate status - */ -static void -hv_rf_receive_indicate_status(struct hn_softc *sc, const void *data, int dlen) -{ - const struct rndis_status_msg *msg; - int ofs; - - if (dlen < sizeof(*msg)) { - if_printf(sc->hn_ifp, "invalid RNDIS status\n"); - return; - } - msg = data; - - switch (msg->rm_status) { - case RNDIS_STATUS_MEDIA_CONNECT: - case RNDIS_STATUS_MEDIA_DISCONNECT: - hn_link_status_update(sc); - break; - - case RNDIS_STATUS_TASK_OFFLOAD_CURRENT_CONFIG: - /* Not really useful; ignore. */ - break; - - case RNDIS_STATUS_NETWORK_CHANGE: - ofs = RNDIS_STBUFOFFSET_ABS(msg->rm_stbufoffset); - if (dlen < ofs + msg->rm_stbuflen || - msg->rm_stbuflen < sizeof(uint32_t)) { - if_printf(sc->hn_ifp, "network changed\n"); - } else { - uint32_t change; - - memcpy(&change, ((const uint8_t *)msg) + ofs, - sizeof(change)); - if_printf(sc->hn_ifp, "network changed, change %u\n", - change); - } - hn_network_change(sc); - break; - - default: - /* TODO: */ - if_printf(sc->hn_ifp, "unknown RNDIS status 0x%08x\n", - msg->rm_status); - break; - } -} - -static int -hn_rndis_rxinfo(const void *info_data, int info_dlen, struct hn_recvinfo *info) -{ - const struct rndis_pktinfo *pi = info_data; - uint32_t mask = 0; - - while (info_dlen != 0) { - const void *data; - uint32_t dlen; - - if (__predict_false(info_dlen < sizeof(*pi))) - return (EINVAL); - if (__predict_false(info_dlen < pi->rm_size)) - return (EINVAL); - info_dlen -= pi->rm_size; - - if (__predict_false(pi->rm_size & RNDIS_PKTINFO_SIZE_ALIGNMASK)) - return (EINVAL); - if (__predict_false(pi->rm_size < pi->rm_pktinfooffset)) - return (EINVAL); - dlen = pi->rm_size - pi->rm_pktinfooffset; - data = pi->rm_data; - - switch (pi->rm_type) { - case NDIS_PKTINFO_TYPE_VLAN: - if (__predict_false(dlen < NDIS_VLAN_INFO_SIZE)) - return (EINVAL); - info->vlan_info = *((const uint32_t *)data); - mask |= HV_RF_RECVINFO_VLAN; - break; - - case NDIS_PKTINFO_TYPE_CSUM: - if (__predict_false(dlen < NDIS_RXCSUM_INFO_SIZE)) - return (EINVAL); - info->csum_info = *((const uint32_t *)data); - mask |= HV_RF_RECVINFO_CSUM; - break; - - case HN_NDIS_PKTINFO_TYPE_HASHVAL: - if (__predict_false(dlen < HN_NDIS_HASH_VALUE_SIZE)) - return (EINVAL); - info->hash_value = *((const uint32_t *)data); - mask |= HV_RF_RECVINFO_HASHVAL; - break; - - case HN_NDIS_PKTINFO_TYPE_HASHINF: - if (__predict_false(dlen < HN_NDIS_HASH_INFO_SIZE)) - return (EINVAL); - info->hash_info = *((const uint32_t *)data); - mask |= HV_RF_RECVINFO_HASHINF; - break; - - default: - goto next; - } - - if (mask == HV_RF_RECVINFO_ALL) { - /* All found; done */ - break; - } -next: - pi = (const struct rndis_pktinfo *) - ((const uint8_t *)pi + pi->rm_size); - } - - /* - * Final fixup. - * - If there is no hash value, invalidate the hash info. - */ - if ((mask & HV_RF_RECVINFO_HASHVAL) == 0) - info->hash_info = HN_NDIS_HASH_INFO_INVALID; - return (0); -} - -static __inline bool -hn_rndis_check_overlap(int off, int len, int check_off, int check_len) -{ - - if (off < check_off) { - if (__predict_true(off + len <= check_off)) - return (false); - } else if (off > check_off) { - if (__predict_true(check_off + check_len <= off)) - return (false); - } - return (true); -} - -/* - * RNDIS filter receive data - */ -static void -hv_rf_receive_data(struct hn_rx_ring *rxr, const void *data, int dlen) -{ - const struct rndis_packet_msg *pkt; - struct hn_recvinfo info; - int data_off, pktinfo_off, data_len, pktinfo_len; - - /* - * Check length. - */ - if (__predict_false(dlen < sizeof(*pkt))) { - if_printf(rxr->hn_ifp, "invalid RNDIS packet msg\n"); - return; - } - pkt = data; - - if (__predict_false(dlen < pkt->rm_len)) { - if_printf(rxr->hn_ifp, "truncated RNDIS packet msg, " - "dlen %d, msglen %u\n", dlen, pkt->rm_len); - return; - } - if (__predict_false(pkt->rm_len < - pkt->rm_datalen + pkt->rm_oobdatalen + pkt->rm_pktinfolen)) { - if_printf(rxr->hn_ifp, "invalid RNDIS packet msglen, " - "msglen %u, data %u, oob %u, pktinfo %u\n", - pkt->rm_len, pkt->rm_datalen, pkt->rm_oobdatalen, - pkt->rm_pktinfolen); - return; - } - if (__predict_false(pkt->rm_datalen == 0)) { - if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, no data\n"); - return; - } - - /* - * Check offests. - */ -#define IS_OFFSET_INVALID(ofs) \ - ((ofs) < RNDIS_PACKET_MSG_OFFSET_MIN || \ - ((ofs) & RNDIS_PACKET_MSG_OFFSET_ALIGNMASK)) - - /* XXX Hyper-V does not meet data offset alignment requirement */ - if (__predict_false(pkt->rm_dataoffset < RNDIS_PACKET_MSG_OFFSET_MIN)) { - if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " - "data offset %u\n", pkt->rm_dataoffset); - return; - } - if (__predict_false(pkt->rm_oobdataoffset > 0 && - IS_OFFSET_INVALID(pkt->rm_oobdataoffset))) { - if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " - "oob offset %u\n", pkt->rm_oobdataoffset); - return; - } - if (__predict_true(pkt->rm_pktinfooffset > 0) && - __predict_false(IS_OFFSET_INVALID(pkt->rm_pktinfooffset))) { - if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " - "pktinfo offset %u\n", pkt->rm_pktinfooffset); - return; - } - -#undef IS_OFFSET_INVALID - - data_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_dataoffset); - data_len = pkt->rm_datalen; - pktinfo_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_pktinfooffset); - pktinfo_len = pkt->rm_pktinfolen; - - /* - * Check OOB coverage. - */ - if (__predict_false(pkt->rm_oobdatalen != 0)) { - int oob_off, oob_len; - - if_printf(rxr->hn_ifp, "got oobdata\n"); - oob_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_oobdataoffset); - oob_len = pkt->rm_oobdatalen; - - if (__predict_false(oob_off + oob_len > pkt->rm_len)) { - if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " - "oob overflow, msglen %u, oob abs %d len %d\n", - pkt->rm_len, oob_off, oob_len); - return; - } - - /* - * Check against data. - */ - if (hn_rndis_check_overlap(oob_off, oob_len, - data_off, data_len)) { - if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " - "oob overlaps data, oob abs %d len %d, " - "data abs %d len %d\n", - oob_off, oob_len, data_off, data_len); - return; - } - - /* - * Check against pktinfo. - */ - if (pktinfo_len != 0 && - hn_rndis_check_overlap(oob_off, oob_len, - pktinfo_off, pktinfo_len)) { - if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " - "oob overlaps pktinfo, oob abs %d len %d, " - "pktinfo abs %d len %d\n", - oob_off, oob_len, pktinfo_off, pktinfo_len); - return; - } - } - - /* - * Check per-packet-info coverage and find useful per-packet-info. - */ - info.vlan_info = HN_NDIS_VLAN_INFO_INVALID; - info.csum_info = HN_NDIS_RXCSUM_INFO_INVALID; - info.hash_info = HN_NDIS_HASH_INFO_INVALID; - if (__predict_true(pktinfo_len != 0)) { - bool overlap; - int error; - - if (__predict_false(pktinfo_off + pktinfo_len > pkt->rm_len)) { - if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " - "pktinfo overflow, msglen %u, " - "pktinfo abs %d len %d\n", - pkt->rm_len, pktinfo_off, pktinfo_len); - return; - } - - /* - * Check packet info coverage. - */ - overlap = hn_rndis_check_overlap(pktinfo_off, pktinfo_len, - data_off, data_len); - if (__predict_false(overlap)) { - if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " - "pktinfo overlap data, pktinfo abs %d len %d, " - "data abs %d len %d\n", - pktinfo_off, pktinfo_len, data_off, data_len); - return; - } - - /* - * Find useful per-packet-info. - */ - error = hn_rndis_rxinfo(((const uint8_t *)pkt) + pktinfo_off, - pktinfo_len, &info); - if (__predict_false(error)) { - if_printf(rxr->hn_ifp, "invalid RNDIS packet msg " - "pktinfo\n"); - return; - } - } - - if (__predict_false(data_off + data_len > pkt->rm_len)) { - if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " - "data overflow, msglen %u, data abs %d len %d\n", - pkt->rm_len, data_off, data_len); - return; - } - hn_rxpkt(rxr, ((const uint8_t *)pkt) + data_off, data_len, &info); -} - -/* - * RNDIS filter on receive - */ void -hv_rf_on_receive(struct hn_softc *sc, struct hn_rx_ring *rxr, - const void *data, int dlen) +hn_rndis_rx_ctrl(struct hn_softc *sc, const void *data, int dlen) { const struct rndis_comp_hdr *comp; const struct rndis_msghdr *hdr; - if (__predict_false(dlen < sizeof(*hdr))) { - if_printf(rxr->hn_ifp, "invalid RNDIS msg\n"); - return; - } + KASSERT(dlen >= sizeof(*hdr), ("invalid RNDIS msg\n")); hdr = data; switch (hdr->rm_type) { - case REMOTE_NDIS_PACKET_MSG: - hv_rf_receive_data(rxr, data, dlen); - break; - case REMOTE_NDIS_INITIALIZE_CMPLT: case REMOTE_NDIS_QUERY_CMPLT: case REMOTE_NDIS_SET_CMPLT: case REMOTE_NDIS_KEEPALIVE_CMPLT: /* unused */ if (dlen < sizeof(*comp)) { - if_printf(rxr->hn_ifp, "invalid RNDIS cmplt\n"); + if_printf(sc->hn_ifp, "invalid RNDIS cmplt\n"); return; } comp = data; @@ -489,10 +134,6 @@ hv_rf_on_receive(struct hn_softc *sc, struct hn_rx_ring *rxr, vmbus_xact_ctx_wakeup(sc->hn_xact, comp, dlen); break; - case REMOTE_NDIS_INDICATE_STATUS_MSG: - hv_rf_receive_indicate_status(sc, data, dlen); - break; - case REMOTE_NDIS_RESET_CMPLT: /* * Reset completed, no rid. @@ -501,11 +142,11 @@ hv_rf_on_receive(struct hn_softc *sc, struct hn_rx_ring *rxr, * RESET is not issued by hn(4), so this message should * _not_ be observed. */ - if_printf(rxr->hn_ifp, "RESET cmplt received\n"); + if_printf(sc->hn_ifp, "RESET cmplt received\n"); break; default: - if_printf(rxr->hn_ifp, "unknown RNDIS msg 0x%x\n", + if_printf(sc->hn_ifp, "unknown RNDIS msg 0x%x\n", hdr->rm_type); break; } @@ -549,7 +190,7 @@ hn_rndis_get_linkstatus(struct hn_softc *sc, uint32_t *link_status) static const void * hn_rndis_xact_exec1(struct hn_softc *sc, struct vmbus_xact *xact, size_t reqlen, - struct hn_send_ctx *sndc, size_t *comp_len) + struct hn_nvs_sendctx *sndc, size_t *comp_len) { struct vmbus_gpa gpa[HN_XACT_REQ_PGCNT]; int gpa_cnt, error; @@ -585,8 +226,7 @@ hn_rndis_xact_exec1(struct hn_softc *sc, struct vmbus_xact *xact, size_t reqlen, * message. */ vmbus_xact_activate(xact); - error = hv_nv_on_send(sc->hn_prichan, HN_NVS_RNDIS_MTYPE_CTRL, sndc, - gpa, gpa_cnt); + error = hn_nvs_send_rndis_ctrl(sc->hn_prichan, sndc, gpa, gpa_cnt); if (error) { vmbus_xact_deactivate(xact); if_printf(sc->hn_ifp, "RNDIS ctrl send failed: %d\n", error); @@ -609,7 +249,7 @@ hn_rndis_xact_execute(struct hn_softc *sc, struct vmbus_xact *xact, uint32_t rid /* * Execute the xact setup by the caller. */ - comp = hn_rndis_xact_exec1(sc, xact, reqlen, &hn_send_ctx_none, + comp = hn_rndis_xact_exec1(sc, xact, reqlen, &hn_nvs_sendctx_none, &comp_len); if (comp == NULL) return (NULL); @@ -748,13 +388,14 @@ done: } int -hn_rndis_query_rsscaps(struct hn_softc *sc, int *rxr_cnt) +hn_rndis_query_rsscaps(struct hn_softc *sc, int *rxr_cnt0) { struct ndis_rss_caps in, caps; size_t caps_len; - int error; + int error, indsz, rxr_cnt, hash_fnidx; + uint32_t hash_func = 0, hash_types = 0; - *rxr_cnt = 0; + *rxr_cnt0 = 0; if (sc->hn_ndis_ver < HN_NDIS_VERSION_6_20) return (EOPNOTSUPP); @@ -793,18 +434,73 @@ hn_rndis_query_rsscaps(struct hn_softc *sc, int *rxr_cnt) return (EINVAL); } + /* + * Save information for later RSS configuration. + */ if (caps.ndis_nrxr == 0) { if_printf(sc->hn_ifp, "0 RX rings!?\n"); return (EINVAL); } - *rxr_cnt = caps.ndis_nrxr; + if (bootverbose) + if_printf(sc->hn_ifp, "%u RX rings\n", caps.ndis_nrxr); + rxr_cnt = caps.ndis_nrxr; + + if (caps.ndis_hdr.ndis_size == NDIS_RSS_CAPS_SIZE && + caps.ndis_hdr.ndis_rev >= NDIS_RSS_CAPS_REV_2) { + if (caps.ndis_nind > NDIS_HASH_INDCNT) { + if_printf(sc->hn_ifp, + "too many RSS indirect table entries %u\n", + caps.ndis_nind); + return (EOPNOTSUPP); + } + if (!powerof2(caps.ndis_nind)) { + if_printf(sc->hn_ifp, "RSS indirect table size is not " + "power-of-2 %u\n", caps.ndis_nind); + } - if (caps.ndis_hdr.ndis_size == NDIS_RSS_CAPS_SIZE) { if (bootverbose) { if_printf(sc->hn_ifp, "RSS indirect table size %u\n", caps.ndis_nind); } + indsz = caps.ndis_nind; + } else { + indsz = NDIS_HASH_INDCNT; } + if (indsz < rxr_cnt) { + if_printf(sc->hn_ifp, "# of RX rings (%d) > " + "RSS indirect table size %d\n", rxr_cnt, indsz); + rxr_cnt = indsz; + } + + /* + * NOTE: + * Toeplitz is at the lowest bit, and it is prefered; so ffs(), + * instead of fls(), is used here. + */ + hash_fnidx = ffs(caps.ndis_caps & NDIS_RSS_CAP_HASHFUNC_MASK); + if (hash_fnidx == 0) { + if_printf(sc->hn_ifp, "no hash functions, caps 0x%08x\n", + caps.ndis_caps); + return (EOPNOTSUPP); + } + hash_func = 1 << (hash_fnidx - 1); /* ffs is 1-based */ + + if (caps.ndis_caps & NDIS_RSS_CAP_IPV4) + hash_types |= NDIS_HASH_IPV4 | NDIS_HASH_TCP_IPV4; + if (caps.ndis_caps & NDIS_RSS_CAP_IPV6) + hash_types |= NDIS_HASH_IPV6 | NDIS_HASH_TCP_IPV6; + if (caps.ndis_caps & NDIS_RSS_CAP_IPV6_EX) + hash_types |= NDIS_HASH_IPV6_EX | NDIS_HASH_TCP_IPV6_EX; + if (hash_types == 0) { + if_printf(sc->hn_ifp, "no hash types, caps 0x%08x\n", + caps.ndis_caps); + return (EOPNOTSUPP); + } + + /* Commit! */ + sc->hn_rss_ind_size = indsz; + sc->hn_rss_hash = hash_func | hash_types; + *rxr_cnt0 = rxr_cnt; return (0); } @@ -1034,7 +730,7 @@ hn_rndis_conf_rss(struct hn_softc *sc, uint16_t flags) { struct ndis_rssprm_toeplitz *rss = &sc->hn_rss; struct ndis_rss_params *prm = &rss->rss_params; - int error; + int error, rss_size; /* * Only NDIS 6.20+ is supported: @@ -1044,21 +740,29 @@ hn_rndis_conf_rss(struct hn_softc *sc, uint16_t flags) KASSERT(sc->hn_ndis_ver >= HN_NDIS_VERSION_6_20, ("NDIS 6.20+ is required, NDIS version 0x%08x", sc->hn_ndis_ver)); + /* XXX only one can be specified through, popcnt? */ + KASSERT((sc->hn_rss_hash & NDIS_HASH_FUNCTION_MASK), ("no hash func")); + KASSERT((sc->hn_rss_hash & NDIS_HASH_TYPE_MASK), ("no hash types")); + KASSERT(sc->hn_rss_ind_size > 0, ("no indirect table size")); + + if (bootverbose) { + if_printf(sc->hn_ifp, "RSS indirect table size %d, " + "hash 0x%08x\n", sc->hn_rss_ind_size, sc->hn_rss_hash); + } + /* * NOTE: * DO NOT whack rss_key and rss_ind, which are setup by the caller. */ memset(prm, 0, sizeof(*prm)); + rss_size = NDIS_RSSPRM_TOEPLITZ_SIZE(sc->hn_rss_ind_size); prm->ndis_hdr.ndis_type = NDIS_OBJTYPE_RSS_PARAMS; prm->ndis_hdr.ndis_rev = NDIS_RSS_PARAMS_REV_2; - prm->ndis_hdr.ndis_size = sizeof(*rss); + prm->ndis_hdr.ndis_size = rss_size; prm->ndis_flags = flags; - prm->ndis_hash = NDIS_HASH_FUNCTION_TOEPLITZ | - NDIS_HASH_IPV4 | NDIS_HASH_TCP_IPV4 | - NDIS_HASH_IPV6 | NDIS_HASH_TCP_IPV6; - /* TODO: Take ndis_rss_caps.ndis_nind into account */ - prm->ndis_indsize = sizeof(rss->rss_ind); + prm->ndis_hash = sc->hn_rss_hash; + prm->ndis_indsize = sizeof(rss->rss_ind[0]) * sc->hn_rss_ind_size; prm->ndis_indoffset = __offsetof(struct ndis_rssprm_toeplitz, rss_ind[0]); prm->ndis_keysize = sizeof(rss->rss_key); @@ -1066,7 +770,7 @@ hn_rndis_conf_rss(struct hn_softc *sc, uint16_t flags) __offsetof(struct ndis_rssprm_toeplitz, rss_key[0]); error = hn_rndis_set(sc, OID_GEN_RECEIVE_SCALE_PARAMETERS, - rss, sizeof(*rss)); + rss, rss_size); if (error) { if_printf(sc->hn_ifp, "RSS config failed: %d\n", error); } else { @@ -1151,7 +855,7 @@ hn_rndis_halt(struct hn_softc *sc) { struct vmbus_xact *xact; struct rndis_halt_req *halt; - struct hn_send_ctx sndc; + struct hn_nvs_sendctx sndc; size_t comp_len; xact = vmbus_xact_get(sc->hn_xact, sizeof(*halt)); @@ -1165,7 +869,7 @@ hn_rndis_halt(struct hn_softc *sc) halt->rm_rid = hn_rndis_rid(sc); /* No RNDIS completion; rely on NVS message send completion */ - hn_send_ctx_init_simple(&sndc, hn_nvs_sent_xact, xact); + hn_nvs_sendctx_init(&sndc, hn_nvs_sent_xact, xact); hn_rndis_xact_exec1(sc, xact, sizeof(*halt), &sndc, &comp_len); vmbus_xact_put(xact); @@ -1285,10 +989,3 @@ hn_rndis_detach(struct hn_softc *sc) /* Halt the RNDIS. */ hn_rndis_halt(sc); } - -void -hv_rf_channel_rollup(struct hn_rx_ring *rxr, struct hn_tx_ring *txr) -{ - - hn_chan_rollup(rxr, txr); -} diff --git a/sys/dev/hyperv/netvsc/hv_rndis_filter.h b/sys/dev/hyperv/netvsc/hn_rndis.h index 3ecda3b80d03..736df341f642 100644 --- a/sys/dev/hyperv/netvsc/hv_rndis_filter.h +++ b/sys/dev/hyperv/netvsc/hn_rndis.h @@ -28,21 +28,22 @@ * $FreeBSD$ */ -#ifndef __HV_RNDIS_FILTER_H__ -#define __HV_RNDIS_FILTER_H__ +#ifndef _HN_RNDIS_H_ +#define _HN_RNDIS_H_ -#include <sys/param.h> -#include <net/ethernet.h> -#include <dev/hyperv/netvsc/if_hnvar.h> +struct hn_softc; -/* - * Externs - */ -struct hn_rx_ring; - -void hv_rf_on_receive(struct hn_softc *sc, struct hn_rx_ring *rxr, - const void *data, int dlen); -void hv_rf_channel_rollup(struct hn_rx_ring *rxr, struct hn_tx_ring *txr); - -#endif /* __HV_RNDIS_FILTER_H__ */ +int hn_rndis_attach(struct hn_softc *sc, int mtu); +void hn_rndis_detach(struct hn_softc *sc); +int hn_rndis_conf_rss(struct hn_softc *sc, uint16_t flags); +int hn_rndis_query_rsscaps(struct hn_softc *sc, int *rxr_cnt); +int hn_rndis_get_eaddr(struct hn_softc *sc, uint8_t *eaddr); +/* link_status: NDIS_MEDIA_STATE_ */ +int hn_rndis_get_linkstatus(struct hn_softc *sc, + uint32_t *link_status); +/* filter: NDIS_PACKET_TYPE_. */ +int hn_rndis_set_rxfilter(struct hn_softc *sc, uint32_t filter); +void hn_rndis_rx_ctrl(struct hn_softc *sc, const void *data, + int dlen); +#endif /* !_HN_RNDIS_H_ */ diff --git a/sys/dev/hyperv/netvsc/hv_net_vsc.h b/sys/dev/hyperv/netvsc/hv_net_vsc.h deleted file mode 100644 index 06a6dfe8c98b..000000000000 --- a/sys/dev/hyperv/netvsc/hv_net_vsc.h +++ /dev/null @@ -1,288 +0,0 @@ -/*- - * Copyright (c) 2009-2012,2016 Microsoft Corp. - * Copyright (c) 2010-2012 Citrix Inc. - * Copyright (c) 2012 NetApp Inc. - * 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 ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD$ - */ - -/* - * HyperV vmbus (virtual machine bus) network VSC (virtual services client) - * header file - * - * (Updated from unencumbered NvspProtocol.h) - */ - -#ifndef __HV_NET_VSC_H__ -#define __HV_NET_VSC_H__ - -#include <sys/param.h> -#include <sys/kernel.h> -#include <sys/lock.h> -#include <sys/malloc.h> -#include <sys/queue.h> -#include <sys/taskqueue.h> -#include <sys/sema.h> -#include <sys/sx.h> - -#include <machine/bus.h> -#include <sys/bus.h> -#include <sys/bus_dma.h> - -#include <netinet/in.h> -#include <netinet/tcp_lro.h> - -#include <net/ethernet.h> -#include <net/if.h> -#include <net/if_media.h> - -#include <dev/hyperv/include/hyperv.h> -#include <dev/hyperv/include/hyperv_busdma.h> -#include <dev/hyperv/include/vmbus.h> - -#include <dev/hyperv/netvsc/ndis.h> - -#define HN_USE_TXDESC_BUFRING - -MALLOC_DECLARE(M_NETVSC); - -/* - * The following arguably belongs in a separate header file - */ - -/* - * Defines - */ - -#define NETVSC_SEND_BUFFER_SIZE (1024*1024*15) /* 15M */ - -#define NETVSC_RECEIVE_BUFFER_SIZE_LEGACY (1024*1024*15) /* 15MB */ -#define NETVSC_RECEIVE_BUFFER_SIZE (1024*1024*16) /* 16MB */ - -/* - * Maximum MTU we permit to be configured for a netvsc interface. - * When the code was developed, a max MTU of 12232 was tested and - * proven to work. 9K is a reasonable maximum for an Ethernet. - */ -#define NETVSC_MAX_CONFIGURABLE_MTU (9 * 1024) - -#define NETVSC_PACKET_SIZE PAGE_SIZE - -/* - * Data types - */ - -struct vmbus_channel; - -#define NETVSC_DEVICE_RING_BUFFER_SIZE (128 * PAGE_SIZE) -#define NETVSC_PACKET_MAXPAGE 32 - -#define HN_XACT_REQ_PGCNT 2 -#define HN_XACT_RESP_PGCNT 2 -#define HN_XACT_REQ_SIZE (HN_XACT_REQ_PGCNT * PAGE_SIZE) -#define HN_XACT_RESP_SIZE (HN_XACT_RESP_PGCNT * PAGE_SIZE) - -#ifndef HN_USE_TXDESC_BUFRING -struct hn_txdesc; -SLIST_HEAD(hn_txdesc_list, hn_txdesc); -#else -struct buf_ring; -#endif - -struct hn_tx_ring; - -struct hn_rx_ring { - struct ifnet *hn_ifp; - struct hn_tx_ring *hn_txr; - void *hn_rdbuf; - uint8_t *hn_rxbuf; /* shadow sc->hn_rxbuf */ - int hn_rx_idx; - - /* Trust csum verification on host side */ - int hn_trust_hcsum; /* HN_TRUST_HCSUM_ */ - struct lro_ctrl hn_lro; - - u_long hn_csum_ip; - u_long hn_csum_tcp; - u_long hn_csum_udp; - u_long hn_csum_trusted; - u_long hn_lro_tried; - u_long hn_small_pkts; - u_long hn_pkts; - u_long hn_rss_pkts; - - /* Rarely used stuffs */ - struct sysctl_oid *hn_rx_sysctl_tree; - int hn_rx_flags; - - void *hn_br; /* TX/RX bufring */ - struct hyperv_dma hn_br_dma; -} __aligned(CACHE_LINE_SIZE); - -#define HN_TRUST_HCSUM_IP 0x0001 -#define HN_TRUST_HCSUM_TCP 0x0002 -#define HN_TRUST_HCSUM_UDP 0x0004 - -#define HN_RX_FLAG_ATTACHED 0x1 - -struct hn_tx_ring { -#ifndef HN_USE_TXDESC_BUFRING - struct mtx hn_txlist_spin; - struct hn_txdesc_list hn_txlist; -#else - struct buf_ring *hn_txdesc_br; -#endif - int hn_txdesc_cnt; - int hn_txdesc_avail; - u_short hn_has_txeof; - u_short hn_txdone_cnt; - - int hn_sched_tx; - void (*hn_txeof)(struct hn_tx_ring *); - struct taskqueue *hn_tx_taskq; - struct task hn_tx_task; - struct task hn_txeof_task; - - struct buf_ring *hn_mbuf_br; - int hn_oactive; - int hn_tx_idx; - int hn_tx_flags; - - struct mtx hn_tx_lock; - struct hn_softc *hn_sc; - struct vmbus_channel *hn_chan; - - int hn_direct_tx_size; - int hn_chim_size; - bus_dma_tag_t hn_tx_data_dtag; - uint64_t hn_csum_assist; - - int hn_suspended; - int hn_gpa_cnt; - struct vmbus_gpa hn_gpa[NETVSC_PACKET_MAXPAGE]; - - u_long hn_no_txdescs; - u_long hn_send_failed; - u_long hn_txdma_failed; - u_long hn_tx_collapsed; - u_long hn_tx_chimney_tried; - u_long hn_tx_chimney; - u_long hn_pkts; - - /* Rarely used stuffs */ - struct hn_txdesc *hn_txdesc; - bus_dma_tag_t hn_tx_rndis_dtag; - struct sysctl_oid *hn_tx_sysctl_tree; -} __aligned(CACHE_LINE_SIZE); - -#define HN_TX_FLAG_ATTACHED 0x1 -#define HN_TX_FLAG_HASHVAL 0x2 /* support HASHVAL pktinfo */ - -/* - * Device-specific softc structure - */ -struct hn_softc { - struct ifnet *hn_ifp; - struct ifmedia hn_media; - device_t hn_dev; - int hn_if_flags; - struct sx hn_lock; - struct vmbus_channel *hn_prichan; - - int hn_rx_ring_cnt; - int hn_rx_ring_inuse; - struct hn_rx_ring *hn_rx_ring; - - int hn_tx_ring_cnt; - int hn_tx_ring_inuse; - struct hn_tx_ring *hn_tx_ring; - - uint8_t *hn_chim; - u_long *hn_chim_bmap; - int hn_chim_bmap_cnt; - int hn_chim_cnt; - int hn_chim_szmax; - - int hn_cpu; - struct taskqueue *hn_tx_taskq; - struct sysctl_oid *hn_tx_sysctl_tree; - struct sysctl_oid *hn_rx_sysctl_tree; - struct vmbus_xact_ctx *hn_xact; - uint32_t hn_nvs_ver; - - struct taskqueue *hn_mgmt_taskq; - struct taskqueue *hn_mgmt_taskq0; - struct task hn_link_task; - struct task hn_netchg_init; - struct timeout_task hn_netchg_status; - uint32_t hn_link_flags; /* HN_LINK_FLAG_ */ - - uint32_t hn_caps; /* HN_CAP_ */ - uint32_t hn_flags; /* HN_FLAG_ */ - void *hn_rxbuf; - uint32_t hn_rxbuf_gpadl; - struct hyperv_dma hn_rxbuf_dma; - - uint32_t hn_chim_gpadl; - struct hyperv_dma hn_chim_dma; - - uint32_t hn_rndis_rid; - uint32_t hn_ndis_ver; - int hn_ndis_tso_szmax; - int hn_ndis_tso_sgmin; - - struct ndis_rssprm_toeplitz hn_rss; -}; - -#define HN_FLAG_RXBUF_CONNECTED 0x0001 -#define HN_FLAG_CHIM_CONNECTED 0x0002 -#define HN_FLAG_HAS_RSSKEY 0x0004 -#define HN_FLAG_HAS_RSSIND 0x0008 -#define HN_FLAG_SYNTH_ATTACHED 0x0010 - -#define HN_CAP_VLAN 0x0001 -#define HN_CAP_MTU 0x0002 -#define HN_CAP_IPCS 0x0004 -#define HN_CAP_TCP4CS 0x0008 -#define HN_CAP_TCP6CS 0x0010 -#define HN_CAP_UDP4CS 0x0020 -#define HN_CAP_UDP6CS 0x0040 -#define HN_CAP_TSO4 0x0080 -#define HN_CAP_TSO6 0x0100 -#define HN_CAP_HASHVAL 0x0200 - -#define HN_LINK_FLAG_LINKUP 0x0001 -#define HN_LINK_FLAG_NETCHG 0x0002 - -/* - * Externs - */ -struct hn_send_ctx; - -int hv_nv_on_send(struct vmbus_channel *chan, uint32_t rndis_mtype, - struct hn_send_ctx *sndc, struct vmbus_gpa *gpa, int gpa_cnt); - -#endif /* __HV_NET_VSC_H__ */ - diff --git a/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c b/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c index 336fee8b3b4e..6e2fb28ab380 100644 --- a/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c +++ b/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c @@ -61,6 +61,7 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/systm.h> #include <sys/sockio.h> +#include <sys/limits.h> #include <sys/mbuf.h> #include <sys/malloc.h> #include <sys/module.h> @@ -72,6 +73,7 @@ __FBSDID("$FreeBSD$"); #include <sys/smp.h> #include <sys/sysctl.h> #include <sys/buf_ring.h> +#include <sys/taskqueue.h> #include <net/if.h> #include <net/if_arp.h> @@ -90,6 +92,7 @@ __FBSDID("$FreeBSD$"); #include <netinet/ip.h> #include <netinet/if_ether.h> #include <netinet/tcp.h> +#include <netinet/tcp_lro.h> #include <netinet/udp.h> #include <netinet/ip6.h> @@ -115,11 +118,14 @@ __FBSDID("$FreeBSD$"); #include <dev/hyperv/include/hyperv.h> #include <dev/hyperv/include/hyperv_busdma.h> +#include <dev/hyperv/include/vmbus.h> #include <dev/hyperv/include/vmbus_xact.h> -#include <dev/hyperv/netvsc/hv_net_vsc.h> -#include <dev/hyperv/netvsc/hv_rndis_filter.h> #include <dev/hyperv/netvsc/ndis.h> +#include <dev/hyperv/netvsc/if_hnreg.h> +#include <dev/hyperv/netvsc/if_hnvar.h> +#include <dev/hyperv/netvsc/hn_nvs.h> +#include <dev/hyperv/netvsc/hn_rndis.h> #include "vmbus_if.h" @@ -154,12 +160,24 @@ __FBSDID("$FreeBSD$"); #define HN_TX_DATA_MAXSIZE IP_MAXPACKET #define HN_TX_DATA_SEGSIZE PAGE_SIZE /* -1 for RNDIS packet message */ -#define HN_TX_DATA_SEGCNT_MAX (NETVSC_PACKET_MAXPAGE - 1) +#define HN_TX_DATA_SEGCNT_MAX (HN_GPACNT_MAX - 1) #define HN_DIRECT_TX_SIZE_DEF 128 #define HN_EARLY_TXEOF_THRESH 8 +#define HN_RXINFO_VLAN 0x0001 +#define HN_RXINFO_CSUM 0x0002 +#define HN_RXINFO_HASHINF 0x0004 +#define HN_RXINFO_HASHVAL 0x0008 +#define HN_RXINFO_ALL \ + (HN_RXINFO_VLAN | \ + HN_RXINFO_CSUM | \ + HN_RXINFO_HASHINF | \ + HN_RXINFO_HASHVAL) + +#define HN_PKTBUF_LEN_DEF (16 * 1024) + struct hn_txdesc { #ifndef HN_USE_TXDESC_BUFRING SLIST_ENTRY(hn_txdesc) link; @@ -168,7 +186,9 @@ struct hn_txdesc { struct hn_tx_ring *txr; int refs; uint32_t flags; /* HN_TXD_FLAG_ */ - struct hn_send_ctx send_ctx; + struct hn_nvs_sendctx send_ctx; + uint32_t chim_index; + int chim_size; bus_dmamap_t data_dmap; @@ -180,6 +200,17 @@ struct hn_txdesc { #define HN_TXD_FLAG_ONLIST 0x1 #define HN_TXD_FLAG_DMAMAP 0x2 +#define HN_NDIS_VLAN_INFO_INVALID 0xffffffff +#define HN_NDIS_RXCSUM_INFO_INVALID 0 +#define HN_NDIS_HASH_INFO_INVALID 0 + +struct hn_rxinfo { + uint32_t vlan_info; + uint32_t csum_info; + uint32_t hash_info; + uint32_t hash_value; +}; + #define HN_LRO_LENLIM_MULTIRX_DEF (12 * ETHERMTU) #define HN_LRO_LENLIM_DEF (25 * ETHERMTU) /* YYY 2*MTU is a bit rough, but should be good enough. */ @@ -324,8 +355,10 @@ static int hn_tx_conf_int_sysctl(SYSCTL_HANDLER_ARGS); static int hn_ndis_version_sysctl(SYSCTL_HANDLER_ARGS); static int hn_caps_sysctl(SYSCTL_HANDLER_ARGS); static int hn_hwassist_sysctl(SYSCTL_HANDLER_ARGS); +static int hn_rxfilter_sysctl(SYSCTL_HANDLER_ARGS); static int hn_rss_key_sysctl(SYSCTL_HANDLER_ARGS); static int hn_rss_ind_sysctl(SYSCTL_HANDLER_ARGS); +static int hn_rss_hash_sysctl(SYSCTL_HANDLER_ARGS); static int hn_check_iplen(const struct mbuf *, int); static int hn_create_tx_ring(struct hn_softc *, int); static void hn_destroy_tx_ring(struct hn_tx_ring *); @@ -348,6 +381,7 @@ static void hn_chan_detach(struct hn_softc *, struct vmbus_channel *); static int hn_attach_subchans(struct hn_softc *); static void hn_detach_allchans(struct hn_softc *); static void hn_chan_callback(struct vmbus_channel *chan, void *xrxr); +static void hn_chan_rollup(struct hn_rx_ring *, struct hn_tx_ring *); static void hn_set_ring_inuse(struct hn_softc *, int); static int hn_synth_attach(struct hn_softc *, int); static void hn_synth_detach(struct hn_softc *); @@ -363,15 +397,25 @@ static void hn_tx_resume(struct hn_softc *, int); static void hn_tx_ring_qflush(struct hn_tx_ring *); static int netvsc_detach(device_t dev); static void hn_link_status(struct hn_softc *); +static int hn_sendpkt_rndis_sglist(struct hn_tx_ring *, struct hn_txdesc *); +static int hn_sendpkt_rndis_chim(struct hn_tx_ring *, struct hn_txdesc *); +static int hn_set_rxfilter(struct hn_softc *); +static void hn_link_status_update(struct hn_softc *); +static void hn_network_change(struct hn_softc *); + +static int hn_rndis_rxinfo(const void *, int, struct hn_rxinfo *); +static void hn_rndis_rx_data(struct hn_rx_ring *, const void *, int); +static void hn_rndis_rx_status(struct hn_softc *, const void *, int); static void hn_nvs_handle_notify(struct hn_softc *sc, const struct vmbus_chanpkt_hdr *pkt); static void hn_nvs_handle_comp(struct hn_softc *sc, struct vmbus_channel *chan, const struct vmbus_chanpkt_hdr *pkt); -static void hn_nvs_handle_rxbuf(struct hn_softc *sc, struct hn_rx_ring *rxr, +static void hn_nvs_handle_rxbuf(struct hn_rx_ring *rxr, struct vmbus_channel *chan, const struct vmbus_chanpkt_hdr *pkthdr); -static void hn_nvs_ack_rxbuf(struct vmbus_channel *chan, uint64_t tid); +static void hn_nvs_ack_rxbuf(struct hn_rx_ring *, struct vmbus_channel *, + uint64_t); static int hn_transmit(struct ifnet *, struct mbuf *); static void hn_xmit_qflush(struct ifnet *); @@ -400,6 +444,116 @@ hn_set_lro_lenlim(struct hn_softc *sc, int lenlim) #endif static int +hn_sendpkt_rndis_sglist(struct hn_tx_ring *txr, struct hn_txdesc *txd) +{ + + KASSERT(txd->chim_index == HN_NVS_CHIM_IDX_INVALID && + txd->chim_size == 0, ("invalid rndis sglist txd")); + return (hn_nvs_send_rndis_sglist(txr->hn_chan, HN_NVS_RNDIS_MTYPE_DATA, + &txd->send_ctx, txr->hn_gpa, txr->hn_gpa_cnt)); +} + +static int +hn_sendpkt_rndis_chim(struct hn_tx_ring *txr, struct hn_txdesc *txd) +{ + struct hn_nvs_rndis rndis; + + KASSERT(txd->chim_index != HN_NVS_CHIM_IDX_INVALID && + txd->chim_size > 0, ("invalid rndis chim txd")); + + rndis.nvs_type = HN_NVS_TYPE_RNDIS; + rndis.nvs_rndis_mtype = HN_NVS_RNDIS_MTYPE_DATA; + rndis.nvs_chim_idx = txd->chim_index; + rndis.nvs_chim_sz = txd->chim_size; + + return (hn_nvs_send(txr->hn_chan, VMBUS_CHANPKT_FLAG_RC, + &rndis, sizeof(rndis), &txd->send_ctx)); +} + +static __inline uint32_t +hn_chim_alloc(struct hn_softc *sc) +{ + int i, bmap_cnt = sc->hn_chim_bmap_cnt; + u_long *bmap = sc->hn_chim_bmap; + uint32_t ret = HN_NVS_CHIM_IDX_INVALID; + + for (i = 0; i < bmap_cnt; ++i) { + int idx; + + idx = ffsl(~bmap[i]); + if (idx == 0) + continue; + + --idx; /* ffsl is 1-based */ + KASSERT(i * LONG_BIT + idx < sc->hn_chim_cnt, + ("invalid i %d and idx %d", i, idx)); + + if (atomic_testandset_long(&bmap[i], idx)) + continue; + + ret = i * LONG_BIT + idx; + break; + } + return (ret); +} + +static __inline void +hn_chim_free(struct hn_softc *sc, uint32_t chim_idx) +{ + u_long mask; + uint32_t idx; + + idx = chim_idx / LONG_BIT; + KASSERT(idx < sc->hn_chim_bmap_cnt, + ("invalid chimney index 0x%x", chim_idx)); + + mask = 1UL << (chim_idx % LONG_BIT); + KASSERT(sc->hn_chim_bmap[idx] & mask, + ("index bitmap 0x%lx, chimney index %u, " + "bitmap idx %d, bitmask 0x%lx", + sc->hn_chim_bmap[idx], chim_idx, idx, mask)); + + atomic_clear_long(&sc->hn_chim_bmap[idx], mask); +} + +static int +hn_set_rxfilter(struct hn_softc *sc) +{ + struct ifnet *ifp = sc->hn_ifp; + uint32_t filter; + int error = 0; + + HN_LOCK_ASSERT(sc); + + if (ifp->if_flags & IFF_PROMISC) { + filter = NDIS_PACKET_TYPE_PROMISCUOUS; + } else { + filter = NDIS_PACKET_TYPE_DIRECTED; + if (ifp->if_flags & IFF_BROADCAST) + filter |= NDIS_PACKET_TYPE_BROADCAST; +#ifdef notyet + /* + * See the comment in SIOCADDMULTI/SIOCDELMULTI. + */ + /* TODO: support multicast list */ + if ((ifp->if_flags & IFF_ALLMULTI) || + !TAILQ_EMPTY(&ifp->if_multiaddrs)) + filter |= NDIS_PACKET_TYPE_ALL_MULTICAST; +#else + /* Always enable ALLMULTI */ + filter |= NDIS_PACKET_TYPE_ALL_MULTICAST; +#endif + } + + if (sc->hn_rx_filter != filter) { + error = hn_rndis_set_rxfilter(sc, filter); + if (!error) + sc->hn_rx_filter = filter; + } + return (error); +} + +static int hn_get_txswq_depth(const struct hn_tx_ring *txr) { @@ -673,6 +827,14 @@ netvsc_attach(device_t dev) SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "hwassist", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0, hn_hwassist_sysctl, "A", "hwassist"); + SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rxfilter", + CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0, + hn_rxfilter_sysctl, "A", "rxfilter"); + SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rss_hash", + CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0, + hn_rss_hash_sysctl, "A", "RSS hash"); + SYSCTL_ADD_INT(ctx, child, OID_AUTO, "rss_ind_size", + CTLFLAG_RD, &sc->hn_rss_ind_size, 0, "RSS indirect entry count"); SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rss_key", CTLTYPE_OPAQUE | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, 0, hn_rss_key_sysctl, "IU", "RSS key"); @@ -692,6 +854,7 @@ netvsc_attach(device_t dev) * Setup the ifnet for this interface. */ + ifp->if_baudrate = IF_Gbps(10); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = hn_ioctl; ifp->if_init = hn_init; @@ -873,7 +1036,7 @@ hn_netchg_status_taskfunc(void *xsc, int pending __unused) hn_link_status(sc); } -void +static void hn_link_status_update(struct hn_softc *sc) { @@ -881,7 +1044,7 @@ hn_link_status_update(struct hn_softc *sc) taskqueue_enqueue(sc->hn_mgmt_taskq, &sc->hn_link_task); } -void +static void hn_network_change(struct hn_softc *sc) { @@ -896,6 +1059,8 @@ hn_txdesc_dmamap_load(struct hn_tx_ring *txr, struct hn_txdesc *txd, struct mbuf *m = *m_head; int error; + KASSERT(txd->chim_index == HN_NVS_CHIM_IDX_INVALID, ("txd uses chim")); + error = bus_dmamap_load_mbuf_sg(txr->hn_tx_data_dtag, txd->data_dmap, m, segs, nsegs, BUS_DMA_NOWAIT); if (error == EFBIG) { @@ -919,19 +1084,6 @@ hn_txdesc_dmamap_load(struct hn_tx_ring *txr, struct hn_txdesc *txd, return error; } -static __inline void -hn_txdesc_dmamap_unload(struct hn_tx_ring *txr, struct hn_txdesc *txd) -{ - - if (txd->flags & HN_TXD_FLAG_DMAMAP) { - bus_dmamap_sync(txr->hn_tx_data_dtag, - txd->data_dmap, BUS_DMASYNC_POSTWRITE); - bus_dmamap_unload(txr->hn_tx_data_dtag, - txd->data_dmap); - txd->flags &= ~HN_TXD_FLAG_DMAMAP; - } -} - static __inline int hn_txdesc_put(struct hn_tx_ring *txr, struct hn_txdesc *txd) { @@ -943,14 +1095,25 @@ hn_txdesc_put(struct hn_tx_ring *txr, struct hn_txdesc *txd) if (atomic_fetchadd_int(&txd->refs, -1) != 1) return 0; - hn_txdesc_dmamap_unload(txr, txd); + if (txd->chim_index != HN_NVS_CHIM_IDX_INVALID) { + KASSERT((txd->flags & HN_TXD_FLAG_DMAMAP) == 0, + ("chim txd uses dmamap")); + hn_chim_free(txr->hn_sc, txd->chim_index); + txd->chim_index = HN_NVS_CHIM_IDX_INVALID; + } else if (txd->flags & HN_TXD_FLAG_DMAMAP) { + bus_dmamap_sync(txr->hn_tx_data_dtag, + txd->data_dmap, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(txr->hn_tx_data_dtag, + txd->data_dmap); + txd->flags &= ~HN_TXD_FLAG_DMAMAP; + } + if (txd->m != NULL) { m_freem(txd->m); txd->m = NULL; } txd->flags |= HN_TXD_FLAG_ONLIST; - #ifndef HN_USE_TXDESC_BUFRING mtx_lock_spin(&txr->hn_txlist_spin); KASSERT(txr->hn_txdesc_avail >= 0 && @@ -991,7 +1154,9 @@ hn_txdesc_get(struct hn_tx_ring *txr) atomic_subtract_int(&txr->hn_txdesc_avail, 1); #endif KASSERT(txd->m == NULL && txd->refs == 0 && - (txd->flags & HN_TXD_FLAG_ONLIST), ("invalid txd")); + txd->chim_index == HN_NVS_CHIM_IDX_INVALID && + (txd->flags & HN_TXD_FLAG_ONLIST) && + (txd->flags & HN_TXD_FLAG_DMAMAP) == 0, ("invalid txd")); txd->flags &= ~HN_TXD_FLAG_ONLIST; txd->refs = 1; } @@ -1032,15 +1197,12 @@ hn_txeof(struct hn_tx_ring *txr) } static void -hn_tx_done(struct hn_send_ctx *sndc, struct hn_softc *sc, +hn_tx_done(struct hn_nvs_sendctx *sndc, struct hn_softc *sc, struct vmbus_channel *chan, const void *data __unused, int dlen __unused) { struct hn_txdesc *txd = sndc->hn_cbarg; struct hn_tx_ring *txr; - if (sndc->hn_chim_idx != HN_NVS_CHIM_IDX_INVALID) - hn_chim_free(sc, sndc->hn_chim_idx); - txr = txd->txr; KASSERT(txr->hn_chan == chan, ("channel mismatch, on chan%u, should be chan%u", @@ -1057,7 +1219,7 @@ hn_tx_done(struct hn_send_ctx *sndc, struct hn_softc *sc, } } -void +static void hn_chan_rollup(struct hn_rx_ring *rxr, struct hn_tx_ring *txr) { #if defined(INET) || defined(INET6) @@ -1085,6 +1247,42 @@ hn_rndis_pktmsg_offset(uint32_t ofs) return (ofs - __offsetof(struct rndis_packet_msg, rm_dataoffset)); } +static __inline void * +hn_rndis_pktinfo_append(struct rndis_packet_msg *pkt, size_t pktsize, + size_t pi_dlen, uint32_t pi_type) +{ + const size_t pi_size = HN_RNDIS_PKTINFO_SIZE(pi_dlen); + struct rndis_pktinfo *pi; + + KASSERT((pi_size & RNDIS_PACKET_MSG_OFFSET_ALIGNMASK) == 0, + ("unaligned pktinfo size %zu, pktinfo dlen %zu", pi_size, pi_dlen)); + + /* + * Per-packet-info does not move; it only grows. + * + * NOTE: + * rm_pktinfooffset in this phase counts from the beginning + * of rndis_packet_msg. + */ + KASSERT(pkt->rm_pktinfooffset + pkt->rm_pktinfolen + pi_size <= pktsize, + ("%u pktinfo overflows RNDIS packet msg", pi_type)); + pi = (struct rndis_pktinfo *)((uint8_t *)pkt + pkt->rm_pktinfooffset + + pkt->rm_pktinfolen); + pkt->rm_pktinfolen += pi_size; + + pi->rm_size = pi_size; + pi->rm_type = pi_type; + pi->rm_pktinfooffset = RNDIS_PKTINFO_OFFSET; + + /* Data immediately follow per-packet-info. */ + pkt->rm_dataoffset += pi_size; + + /* Update RNDIS packet msg length */ + pkt->rm_len += pi_size; + + return (pi->rm_data); +} + /* * NOTE: * If this function fails, then both txd and m_head0 will be freed. @@ -1096,9 +1294,8 @@ hn_encap(struct hn_tx_ring *txr, struct hn_txdesc *txd, struct mbuf **m_head0) int error, nsegs, i; struct mbuf *m_head = *m_head0; struct rndis_packet_msg *pkt; - uint32_t send_buf_section_idx; - int send_buf_section_size, pktlen; uint32_t *pi_data; + int pktlen; /* * extension points to the area reserved for the @@ -1211,18 +1408,19 @@ hn_encap(struct hn_tx_ring *txr, struct hn_txdesc *txd, struct mbuf **m_head0) */ if (pkt->rm_len < txr->hn_chim_size) { txr->hn_tx_chimney_tried++; - send_buf_section_idx = hn_chim_alloc(txr->hn_sc); - if (send_buf_section_idx != HN_NVS_CHIM_IDX_INVALID) { + txd->chim_index = hn_chim_alloc(txr->hn_sc); + if (txd->chim_index != HN_NVS_CHIM_IDX_INVALID) { uint8_t *dest = txr->hn_sc->hn_chim + - (send_buf_section_idx * txr->hn_sc->hn_chim_szmax); + (txd->chim_index * txr->hn_sc->hn_chim_szmax); memcpy(dest, pkt, pktlen); dest += pktlen; m_copydata(m_head, 0, m_head->m_pkthdr.len, dest); - send_buf_section_size = pkt->rm_len; + txd->chim_size = pkt->rm_len; txr->hn_gpa_cnt = 0; txr->hn_tx_chimney++; + txr->hn_sendpkt = hn_sendpkt_rndis_chim; goto done; } } @@ -1267,14 +1465,14 @@ hn_encap(struct hn_tx_ring *txr, struct hn_txdesc *txd, struct mbuf **m_head0) gpa->gpa_len = segs[i].ds_len; } - send_buf_section_idx = HN_NVS_CHIM_IDX_INVALID; - send_buf_section_size = 0; + txd->chim_index = HN_NVS_CHIM_IDX_INVALID; + txd->chim_size = 0; + txr->hn_sendpkt = hn_sendpkt_rndis_sglist; done: txd->m = m_head; /* Set the completion routine */ - hn_send_ctx_init(&txd->send_ctx, hn_tx_done, txd, - send_buf_section_idx, send_buf_section_size); + hn_nvs_sendctx_init(&txd->send_ctx, hn_tx_done, txd); return 0; } @@ -1294,8 +1492,7 @@ again: * Make sure that txd is not freed before ETHER_BPF_MTAP. */ hn_txdesc_hold(txd); - error = hv_nv_on_send(txr->hn_chan, HN_NVS_RNDIS_MTYPE_DATA, - &txd->send_ctx, txr->hn_gpa, txr->hn_gpa_cnt); + error = txr->hn_sendpkt(txr, txd); if (!error) { ETHER_BPF_MTAP(ifp, txd->m); if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); @@ -1484,15 +1681,9 @@ hn_lro_rx(struct lro_ctrl *lc, struct mbuf *m) } #endif -/* - * Called when we receive a data packet from the "wire" on the - * specified device - * - * Note: This is no longer used as a callback - */ -int +static int hn_rxpkt(struct hn_rx_ring *rxr, const void *data, int dlen, - const struct hn_recvinfo *info) + const struct hn_rxinfo *info) { struct ifnet *ifp = rxr->hn_ifp; struct mbuf *m_new; @@ -1563,6 +1754,13 @@ hn_rxpkt(struct hn_rx_ring *rxr, const void *data, int dlen, rxr->hn_csum_udp++; } + /* + * XXX + * As of this write (Oct 28th, 2016), host side will turn + * on only TCPCS_OK and IPCS_OK even for UDP datagrams, so + * the do_lro setting here is actually _not_ accurate. We + * depend on the RSS hash type check to reset do_lro. + */ if ((info->csum_info & (NDIS_RXCSUM_INFO_TCPCS_OK | NDIS_RXCSUM_INFO_IPCS_OK)) == (NDIS_RXCSUM_INFO_TCPCS_OK | NDIS_RXCSUM_INFO_IPCS_OK)) @@ -1637,9 +1835,16 @@ skip: NDIS_HASH_FUNCTION_TOEPLITZ) { uint32_t type = (info->hash_info & NDIS_HASH_TYPE_MASK); + /* + * NOTE: + * do_lro is resetted, if the hash types are not TCP + * related. See the comment in the above csum_flags + * setup section. + */ switch (type) { case NDIS_HASH_IPV4: hash_type = M_HASHTYPE_RSS_IPV4; + do_lro = 0; break; case NDIS_HASH_TCP_IPV4: @@ -1648,10 +1853,12 @@ skip: case NDIS_HASH_IPV6: hash_type = M_HASHTYPE_RSS_IPV6; + do_lro = 0; break; case NDIS_HASH_IPV6_EX: hash_type = M_HASHTYPE_RSS_IPV6_EX; + do_lro = 0; break; case NDIS_HASH_TCP_IPV6: @@ -1706,7 +1913,7 @@ hn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) switch (cmd) { case SIOCSIFMTU: - if (ifr->ifr_mtu > NETVSC_MAX_CONFIGURABLE_MTU) { + if (ifr->ifr_mtu > HN_MTU_MAX) { error = EINVAL; break; } @@ -1730,19 +1937,6 @@ hn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) break; } - /* Obtain and record requested MTU */ - ifp->if_mtu = ifr->ifr_mtu; - -#if __FreeBSD_version >= 1100099 - /* - * Make sure that LRO aggregation length limit is still - * valid, after the MTU change. - */ - if (sc->hn_rx_ring[0].hn_lro.lro_length_lim < - HN_LRO_LENLIM_MIN(ifp)) - hn_set_lro_lenlim(sc, HN_LRO_LENLIM_MIN(ifp)); -#endif - /* * Suspend this interface before the synthetic parts * are ripped. @@ -1757,13 +1951,31 @@ hn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) /* * Reattach the synthetic parts, i.e. NVS and RNDIS, * with the new MTU setting. - * XXX check error. */ - hn_synth_attach(sc, ifr->ifr_mtu); + error = hn_synth_attach(sc, ifr->ifr_mtu); + if (error) { + HN_UNLOCK(sc); + break; + } + + /* + * Commit the requested MTU, after the synthetic parts + * have been successfully attached. + */ + ifp->if_mtu = ifr->ifr_mtu; + /* + * Make sure that various parameters based on MTU are + * still valid, after the MTU change. + */ if (sc->hn_tx_ring[0].hn_chim_size > sc->hn_chim_szmax) hn_set_chim_size(sc, sc->hn_chim_szmax); - hn_set_tso_maxsize(sc, hn_tso_maxlen, ifr->ifr_mtu); + hn_set_tso_maxsize(sc, hn_tso_maxlen, ifp->if_mtu); +#if __FreeBSD_version >= 1100099 + if (sc->hn_rx_ring[0].hn_lro.lro_length_lim < + HN_LRO_LENLIM_MIN(ifp)) + hn_set_lro_lenlim(sc, HN_LRO_LENLIM_MIN(ifp)); +#endif /* * All done! Resume the interface now. @@ -1782,31 +1994,13 @@ hn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) } if (ifp->if_flags & IFF_UP) { - /* - * If only the state of the PROMISC flag changed, - * then just use the 'set promisc mode' command - * instead of reinitializing the entire NIC. Doing - * a full re-init means reloading the firmware and - * waiting for it to start up, which may take a - * second or two. - */ -#ifdef notyet - /* Fixme: Promiscuous mode? */ - if (ifp->if_drv_flags & IFF_DRV_RUNNING && - ifp->if_flags & IFF_PROMISC && - !(sc->hn_if_flags & IFF_PROMISC)) { - /* do something here for Hyper-V */ - } else if (ifp->if_drv_flags & IFF_DRV_RUNNING && - !(ifp->if_flags & IFF_PROMISC) && - sc->hn_if_flags & IFF_PROMISC) { - /* do something here for Hyper-V */ - } else -#endif + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + hn_set_rxfilter(sc); + else hn_init_locked(sc); } else { - if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) hn_stop(sc); - } } sc->hn_if_flags = ifp->if_flags; @@ -1864,12 +2058,27 @@ hn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) case SIOCADDMULTI: case SIOCDELMULTI: - /* Always all-multi */ +#ifdef notyet /* - * TODO: - * Enable/disable all-multi according to the emptiness of - * the mcast address list. + * XXX + * Multicast uses mutex, while RNDIS RX filter setting + * sleeps. We workaround this by always enabling + * ALLMULTI. ALLMULTI would actually always be on, even + * if we supported the SIOCADDMULTI/SIOCDELMULTI, since + * we don't support multicast address list configuration + * for this driver. */ + HN_LOCK(sc); + + if ((sc->hn_flags & HN_FLAG_SYNTH_ATTACHED) == 0) { + HN_UNLOCK(sc); + break; + } + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + hn_set_rxfilter(sc); + + HN_UNLOCK(sc); +#endif break; case SIOCSIFMEDIA: @@ -1977,8 +2186,8 @@ hn_init_locked(struct hn_softc *sc) if (ifp->if_drv_flags & IFF_DRV_RUNNING) return; - /* TODO: add hn_rx_filter */ - hn_rndis_set_rxfilter(sc, NDIS_PACKET_TYPE_PROMISCUOUS); + /* Configure RX filter */ + hn_set_rxfilter(sc); /* Clear OACTIVE bit. */ atomic_clear_int(&ifp->if_drv_flags, IFF_DRV_OACTIVE); @@ -2275,18 +2484,7 @@ hn_caps_sysctl(SYSCTL_HANDLER_ARGS) HN_LOCK(sc); caps = sc->hn_caps; HN_UNLOCK(sc); - snprintf(caps_str, sizeof(caps_str), "%b", caps, - "\020" - "\001VLAN" - "\002MTU" - "\003IPCS" - "\004TCP4CS" - "\005TCP6CS" - "\006UDP4CS" - "\007UDP6CS" - "\010TSO4" - "\011TSO6" - "\012HASHVAL"); + snprintf(caps_str, sizeof(caps_str), "%b", caps, HN_CAP_BITS); return sysctl_handle_string(oidp, caps_str, sizeof(caps_str), req); } @@ -2305,6 +2503,21 @@ hn_hwassist_sysctl(SYSCTL_HANDLER_ARGS) } static int +hn_rxfilter_sysctl(SYSCTL_HANDLER_ARGS) +{ + struct hn_softc *sc = arg1; + char filter_str[128]; + uint32_t filter; + + HN_LOCK(sc); + filter = sc->hn_rx_filter; + HN_UNLOCK(sc); + snprintf(filter_str, sizeof(filter_str), "%b", filter, + NDIS_PACKET_TYPES); + return sysctl_handle_string(oidp, filter_str, sizeof(filter_str), req); +} + +static int hn_rss_key_sysctl(SYSCTL_HANDLER_ARGS) { struct hn_softc *sc = arg1; @@ -2366,6 +2579,20 @@ back: } static int +hn_rss_hash_sysctl(SYSCTL_HANDLER_ARGS) +{ + struct hn_softc *sc = arg1; + char hash_str[128]; + uint32_t hash; + + HN_LOCK(sc); + hash = sc->hn_rss_hash; + HN_UNLOCK(sc); + snprintf(hash_str, sizeof(hash_str), "%b", hash, NDIS_HASH_BITS); + return sysctl_handle_string(oidp, hash_str, sizeof(hash_str), req); +} + +static int hn_check_iplen(const struct mbuf *m, int hoff) { const struct ip *ip; @@ -2462,7 +2689,7 @@ hn_create_rx_data(struct hn_softc *sc, int ring_cnt) * may further limit the usable space. */ sc->hn_rxbuf = hyperv_dmamem_alloc(bus_get_dma_tag(dev), - PAGE_SIZE, 0, NETVSC_RECEIVE_BUFFER_SIZE, &sc->hn_rxbuf_dma, + PAGE_SIZE, 0, HN_RXBUF_SIZE, &sc->hn_rxbuf_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO); if (sc->hn_rxbuf == NULL) { device_printf(sc->hn_dev, "allocate rxbuf failed\n"); @@ -2473,7 +2700,7 @@ hn_create_rx_data(struct hn_softc *sc, int ring_cnt) sc->hn_rx_ring_inuse = sc->hn_rx_ring_cnt; sc->hn_rx_ring = malloc(sizeof(struct hn_rx_ring) * sc->hn_rx_ring_cnt, - M_NETVSC, M_WAITOK | M_ZERO); + M_DEVBUF, M_WAITOK | M_ZERO); #if defined(INET) || defined(INET6) #if __FreeBSD_version >= 1100095 @@ -2496,9 +2723,7 @@ hn_create_rx_data(struct hn_softc *sc, int ring_cnt) struct hn_rx_ring *rxr = &sc->hn_rx_ring[i]; rxr->hn_br = hyperv_dmamem_alloc(bus_get_dma_tag(dev), - PAGE_SIZE, 0, - NETVSC_DEVICE_RING_BUFFER_SIZE + - NETVSC_DEVICE_RING_BUFFER_SIZE, + PAGE_SIZE, 0, HN_TXBR_SIZE + HN_RXBR_SIZE, &rxr->hn_br_dma, BUS_DMA_WAITOK); if (rxr->hn_br == NULL) { device_printf(dev, "allocate bufring failed\n"); @@ -2514,7 +2739,8 @@ hn_create_rx_data(struct hn_softc *sc, int ring_cnt) rxr->hn_ifp = sc->hn_ifp; if (i < sc->hn_tx_ring_cnt) rxr->hn_txr = &sc->hn_tx_ring[i]; - rxr->hn_rdbuf = malloc(NETVSC_PACKET_SIZE, M_NETVSC, M_WAITOK); + rxr->hn_pktbuf_len = HN_PKTBUF_LEN_DEF; + rxr->hn_pktbuf = malloc(rxr->hn_pktbuf_len, M_DEVBUF, M_WAITOK); rxr->hn_rx_idx = i; rxr->hn_rxbuf = sc->hn_rxbuf; @@ -2557,6 +2783,11 @@ hn_create_rx_data(struct hn_softc *sc, int ring_cnt) OID_AUTO, "rss_pkts", CTLFLAG_RW, &rxr->hn_rss_pkts, "# of packets w/ RSS info received"); + SYSCTL_ADD_INT(ctx, + SYSCTL_CHILDREN(rxr->hn_rx_sysctl_tree), + OID_AUTO, "pktbuf_len", CTLFLAG_RD, + &rxr->hn_pktbuf_len, 0, + "Temporary channel packet buffer length"); } } } @@ -2629,6 +2860,10 @@ hn_create_rx_data(struct hn_softc *sc, int ring_cnt) CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, __offsetof(struct hn_rx_ring, hn_small_pkts), hn_rx_stat_ulong_sysctl, "LU", "# of small packets received"); + SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rx_ack_failed", + CTLTYPE_ULONG | CTLFLAG_RW | CTLFLAG_MPSAFE, sc, + __offsetof(struct hn_rx_ring, hn_ack_failed), + hn_rx_stat_ulong_sysctl, "LU", "# of RXBUF ack failures"); SYSCTL_ADD_INT(ctx, child, OID_AUTO, "rx_ring_cnt", CTLFLAG_RD, &sc->hn_rx_ring_cnt, 0, "# created RX rings"); SYSCTL_ADD_INT(ctx, child, OID_AUTO, "rx_ring_inuse", @@ -2661,9 +2896,9 @@ hn_destroy_rx_data(struct hn_softc *sc) #if defined(INET) || defined(INET6) tcp_lro_free(&rxr->hn_lro); #endif - free(rxr->hn_rdbuf, M_NETVSC); + free(rxr->hn_pktbuf, M_DEVBUF); } - free(sc->hn_rx_ring, M_NETVSC); + free(sc->hn_rx_ring, M_DEVBUF); sc->hn_rx_ring = NULL; sc->hn_rx_ring_cnt = 0; @@ -2688,11 +2923,11 @@ hn_create_tx_ring(struct hn_softc *sc, int id) txr->hn_txdesc_cnt = HN_TX_DESC_CNT; txr->hn_txdesc = malloc(sizeof(struct hn_txdesc) * txr->hn_txdesc_cnt, - M_NETVSC, M_WAITOK | M_ZERO); + M_DEVBUF, M_WAITOK | M_ZERO); #ifndef HN_USE_TXDESC_BUFRING SLIST_INIT(&txr->hn_txlist); #else - txr->hn_txdesc_br = buf_ring_alloc(txr->hn_txdesc_cnt, M_NETVSC, + txr->hn_txdesc_br = buf_ring_alloc(txr->hn_txdesc_cnt, M_DEVBUF, M_WAITOK, &txr->hn_tx_lock); #endif @@ -2710,7 +2945,7 @@ hn_create_tx_ring(struct hn_softc *sc, int id) TASK_INIT(&txr->hn_txeof_task, 0, hn_xmit_txeof_taskfunc, txr); br_depth = hn_get_txswq_depth(txr); - txr->hn_mbuf_br = buf_ring_alloc(br_depth, M_NETVSC, + txr->hn_mbuf_br = buf_ring_alloc(br_depth, M_DEVBUF, M_WAITOK, &txr->hn_tx_lock); } @@ -2766,6 +3001,7 @@ hn_create_tx_ring(struct hn_softc *sc, int id) struct hn_txdesc *txd = &txr->hn_txdesc[i]; txd->txr = txr; + txd->chim_index = HN_NVS_CHIM_IDX_INVALID; /* * Allocate and load RNDIS packet message. @@ -2892,14 +3128,14 @@ hn_destroy_tx_ring(struct hn_tx_ring *txr) bus_dma_tag_destroy(txr->hn_tx_rndis_dtag); #ifdef HN_USE_TXDESC_BUFRING - buf_ring_free(txr->hn_txdesc_br, M_NETVSC); + buf_ring_free(txr->hn_txdesc_br, M_DEVBUF); #endif - free(txr->hn_txdesc, M_NETVSC); + free(txr->hn_txdesc, M_DEVBUF); txr->hn_txdesc = NULL; if (txr->hn_mbuf_br != NULL) - buf_ring_free(txr->hn_mbuf_br, M_NETVSC); + buf_ring_free(txr->hn_mbuf_br, M_DEVBUF); #ifndef HN_USE_TXDESC_BUFRING mtx_destroy(&txr->hn_txlist_spin); @@ -2920,7 +3156,7 @@ hn_create_tx_data(struct hn_softc *sc, int ring_cnt) * NOTE: It is shared by all channels. */ sc->hn_chim = hyperv_dmamem_alloc(bus_get_dma_tag(sc->hn_dev), - PAGE_SIZE, 0, NETVSC_SEND_BUFFER_SIZE, &sc->hn_chim_dma, + PAGE_SIZE, 0, HN_CHIM_SIZE, &sc->hn_chim_dma, BUS_DMA_WAITOK | BUS_DMA_ZERO); if (sc->hn_chim == NULL) { device_printf(sc->hn_dev, "allocate txbuf failed\n"); @@ -2931,7 +3167,7 @@ hn_create_tx_data(struct hn_softc *sc, int ring_cnt) sc->hn_tx_ring_inuse = sc->hn_tx_ring_cnt; sc->hn_tx_ring = malloc(sizeof(struct hn_tx_ring) * sc->hn_tx_ring_cnt, - M_NETVSC, M_WAITOK | M_ZERO); + M_DEVBUF, M_WAITOK | M_ZERO); ctx = device_get_sysctl_ctx(sc->hn_dev); child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->hn_dev)); @@ -3091,7 +3327,7 @@ hn_destroy_tx_data(struct hn_softc *sc) for (i = 0; i < sc->hn_tx_ring_cnt; ++i) hn_destroy_tx_ring(&sc->hn_tx_ring[i]); - free(sc->hn_tx_ring, M_NETVSC); + free(sc->hn_tx_ring, M_DEVBUF); sc->hn_tx_ring = NULL; sc->hn_tx_ring_cnt = 0; @@ -3338,8 +3574,8 @@ hn_chan_attach(struct hn_softc *sc, struct vmbus_channel *chan) */ cbr.cbr = rxr->hn_br; cbr.cbr_paddr = rxr->hn_br_dma.hv_paddr; - cbr.cbr_txsz = NETVSC_DEVICE_RING_BUFFER_SIZE; - cbr.cbr_rxsz = NETVSC_DEVICE_RING_BUFFER_SIZE; + cbr.cbr_txsz = HN_TXBR_SIZE; + cbr.cbr_rxsz = HN_RXBR_SIZE; error = vmbus_chan_open_br(chan, &cbr, NULL, 0, hn_chan_callback, rxr); if (error) { if_printf(sc->hn_ifp, "open chan%u failed: %d\n", @@ -3528,6 +3764,10 @@ hn_synth_attach(struct hn_softc *sc, int mtu) old_caps = sc->hn_caps; sc->hn_caps = 0; + /* Clear RSS stuffs. */ + sc->hn_rss_ind_size = 0; + sc->hn_rss_hash = 0; + /* * Attach the primary channel _before_ attaching NVS and RNDIS. */ @@ -3602,7 +3842,6 @@ hn_synth_attach(struct hn_softc *sc, int mtu) if_printf(sc->hn_ifp, "setup default RSS indirect " "table\n"); } - /* TODO: Take ndis_rss_caps.ndis_nind into account. */ for (i = 0; i < NDIS_HASH_INDCNT; ++i) rss->rss_ind[i] = i % nchan; sc->hn_flags |= HN_FLAG_HAS_RSSIND; @@ -3723,7 +3962,8 @@ hn_suspend_data(struct hn_softc *sc) /* * Disable RX by clearing RX filter. */ - hn_rndis_set_rxfilter(sc, 0); + sc->hn_rx_filter = NDIS_PACKET_TYPE_NONE; + hn_rndis_set_rxfilter(sc, sc->hn_rx_filter); /* * Give RNDIS enough time to flush all pending data packets. @@ -3811,9 +4051,8 @@ hn_resume_data(struct hn_softc *sc) /* * Re-enable RX. - * TODO: add hn_rx_filter. */ - hn_rndis_set_rxfilter(sc, NDIS_PACKET_TYPE_PROMISCUOUS); + hn_set_rxfilter(sc); /* * Make sure to clear suspend status on "all" TX rings, @@ -3849,12 +4088,18 @@ static void hn_resume_mgmt(struct hn_softc *sc) { + sc->hn_mgmt_taskq = sc->hn_mgmt_taskq0; + /* - * Kick off network change detection, which will - * do link status check too. + * Kick off network change detection, if it was pending. + * If no network change was pending, start link status + * checks, which is more lightweight than network change + * detection. */ - sc->hn_mgmt_taskq = sc->hn_mgmt_taskq0; - hn_network_change(sc); + if (sc->hn_link_flags & HN_LINK_FLAG_NETCHG) + hn_network_change(sc); + else + hn_link_status_update(sc); } static void @@ -3866,6 +4111,325 @@ hn_resume(struct hn_softc *sc) hn_resume_mgmt(sc); } +static void +hn_rndis_rx_status(struct hn_softc *sc, const void *data, int dlen) +{ + const struct rndis_status_msg *msg; + int ofs; + + if (dlen < sizeof(*msg)) { + if_printf(sc->hn_ifp, "invalid RNDIS status\n"); + return; + } + msg = data; + + switch (msg->rm_status) { + case RNDIS_STATUS_MEDIA_CONNECT: + case RNDIS_STATUS_MEDIA_DISCONNECT: + hn_link_status_update(sc); + break; + + case RNDIS_STATUS_TASK_OFFLOAD_CURRENT_CONFIG: + /* Not really useful; ignore. */ + break; + + case RNDIS_STATUS_NETWORK_CHANGE: + ofs = RNDIS_STBUFOFFSET_ABS(msg->rm_stbufoffset); + if (dlen < ofs + msg->rm_stbuflen || + msg->rm_stbuflen < sizeof(uint32_t)) { + if_printf(sc->hn_ifp, "network changed\n"); + } else { + uint32_t change; + + memcpy(&change, ((const uint8_t *)msg) + ofs, + sizeof(change)); + if_printf(sc->hn_ifp, "network changed, change %u\n", + change); + } + hn_network_change(sc); + break; + + default: + if_printf(sc->hn_ifp, "unknown RNDIS status 0x%08x\n", + msg->rm_status); + break; + } +} + +static int +hn_rndis_rxinfo(const void *info_data, int info_dlen, struct hn_rxinfo *info) +{ + const struct rndis_pktinfo *pi = info_data; + uint32_t mask = 0; + + while (info_dlen != 0) { + const void *data; + uint32_t dlen; + + if (__predict_false(info_dlen < sizeof(*pi))) + return (EINVAL); + if (__predict_false(info_dlen < pi->rm_size)) + return (EINVAL); + info_dlen -= pi->rm_size; + + if (__predict_false(pi->rm_size & RNDIS_PKTINFO_SIZE_ALIGNMASK)) + return (EINVAL); + if (__predict_false(pi->rm_size < pi->rm_pktinfooffset)) + return (EINVAL); + dlen = pi->rm_size - pi->rm_pktinfooffset; + data = pi->rm_data; + + switch (pi->rm_type) { + case NDIS_PKTINFO_TYPE_VLAN: + if (__predict_false(dlen < NDIS_VLAN_INFO_SIZE)) + return (EINVAL); + info->vlan_info = *((const uint32_t *)data); + mask |= HN_RXINFO_VLAN; + break; + + case NDIS_PKTINFO_TYPE_CSUM: + if (__predict_false(dlen < NDIS_RXCSUM_INFO_SIZE)) + return (EINVAL); + info->csum_info = *((const uint32_t *)data); + mask |= HN_RXINFO_CSUM; + break; + + case HN_NDIS_PKTINFO_TYPE_HASHVAL: + if (__predict_false(dlen < HN_NDIS_HASH_VALUE_SIZE)) + return (EINVAL); + info->hash_value = *((const uint32_t *)data); + mask |= HN_RXINFO_HASHVAL; + break; + + case HN_NDIS_PKTINFO_TYPE_HASHINF: + if (__predict_false(dlen < HN_NDIS_HASH_INFO_SIZE)) + return (EINVAL); + info->hash_info = *((const uint32_t *)data); + mask |= HN_RXINFO_HASHINF; + break; + + default: + goto next; + } + + if (mask == HN_RXINFO_ALL) { + /* All found; done */ + break; + } +next: + pi = (const struct rndis_pktinfo *) + ((const uint8_t *)pi + pi->rm_size); + } + + /* + * Final fixup. + * - If there is no hash value, invalidate the hash info. + */ + if ((mask & HN_RXINFO_HASHVAL) == 0) + info->hash_info = HN_NDIS_HASH_INFO_INVALID; + return (0); +} + +static __inline bool +hn_rndis_check_overlap(int off, int len, int check_off, int check_len) +{ + + if (off < check_off) { + if (__predict_true(off + len <= check_off)) + return (false); + } else if (off > check_off) { + if (__predict_true(check_off + check_len <= off)) + return (false); + } + return (true); +} + +static void +hn_rndis_rx_data(struct hn_rx_ring *rxr, const void *data, int dlen) +{ + const struct rndis_packet_msg *pkt; + struct hn_rxinfo info; + int data_off, pktinfo_off, data_len, pktinfo_len; + + /* + * Check length. + */ + if (__predict_false(dlen < sizeof(*pkt))) { + if_printf(rxr->hn_ifp, "invalid RNDIS packet msg\n"); + return; + } + pkt = data; + + if (__predict_false(dlen < pkt->rm_len)) { + if_printf(rxr->hn_ifp, "truncated RNDIS packet msg, " + "dlen %d, msglen %u\n", dlen, pkt->rm_len); + return; + } + if (__predict_false(pkt->rm_len < + pkt->rm_datalen + pkt->rm_oobdatalen + pkt->rm_pktinfolen)) { + if_printf(rxr->hn_ifp, "invalid RNDIS packet msglen, " + "msglen %u, data %u, oob %u, pktinfo %u\n", + pkt->rm_len, pkt->rm_datalen, pkt->rm_oobdatalen, + pkt->rm_pktinfolen); + return; + } + if (__predict_false(pkt->rm_datalen == 0)) { + if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, no data\n"); + return; + } + + /* + * Check offests. + */ +#define IS_OFFSET_INVALID(ofs) \ + ((ofs) < RNDIS_PACKET_MSG_OFFSET_MIN || \ + ((ofs) & RNDIS_PACKET_MSG_OFFSET_ALIGNMASK)) + + /* XXX Hyper-V does not meet data offset alignment requirement */ + if (__predict_false(pkt->rm_dataoffset < RNDIS_PACKET_MSG_OFFSET_MIN)) { + if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " + "data offset %u\n", pkt->rm_dataoffset); + return; + } + if (__predict_false(pkt->rm_oobdataoffset > 0 && + IS_OFFSET_INVALID(pkt->rm_oobdataoffset))) { + if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " + "oob offset %u\n", pkt->rm_oobdataoffset); + return; + } + if (__predict_true(pkt->rm_pktinfooffset > 0) && + __predict_false(IS_OFFSET_INVALID(pkt->rm_pktinfooffset))) { + if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " + "pktinfo offset %u\n", pkt->rm_pktinfooffset); + return; + } + +#undef IS_OFFSET_INVALID + + data_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_dataoffset); + data_len = pkt->rm_datalen; + pktinfo_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_pktinfooffset); + pktinfo_len = pkt->rm_pktinfolen; + + /* + * Check OOB coverage. + */ + if (__predict_false(pkt->rm_oobdatalen != 0)) { + int oob_off, oob_len; + + if_printf(rxr->hn_ifp, "got oobdata\n"); + oob_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_oobdataoffset); + oob_len = pkt->rm_oobdatalen; + + if (__predict_false(oob_off + oob_len > pkt->rm_len)) { + if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " + "oob overflow, msglen %u, oob abs %d len %d\n", + pkt->rm_len, oob_off, oob_len); + return; + } + + /* + * Check against data. + */ + if (hn_rndis_check_overlap(oob_off, oob_len, + data_off, data_len)) { + if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " + "oob overlaps data, oob abs %d len %d, " + "data abs %d len %d\n", + oob_off, oob_len, data_off, data_len); + return; + } + + /* + * Check against pktinfo. + */ + if (pktinfo_len != 0 && + hn_rndis_check_overlap(oob_off, oob_len, + pktinfo_off, pktinfo_len)) { + if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " + "oob overlaps pktinfo, oob abs %d len %d, " + "pktinfo abs %d len %d\n", + oob_off, oob_len, pktinfo_off, pktinfo_len); + return; + } + } + + /* + * Check per-packet-info coverage and find useful per-packet-info. + */ + info.vlan_info = HN_NDIS_VLAN_INFO_INVALID; + info.csum_info = HN_NDIS_RXCSUM_INFO_INVALID; + info.hash_info = HN_NDIS_HASH_INFO_INVALID; + if (__predict_true(pktinfo_len != 0)) { + bool overlap; + int error; + + if (__predict_false(pktinfo_off + pktinfo_len > pkt->rm_len)) { + if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " + "pktinfo overflow, msglen %u, " + "pktinfo abs %d len %d\n", + pkt->rm_len, pktinfo_off, pktinfo_len); + return; + } + + /* + * Check packet info coverage. + */ + overlap = hn_rndis_check_overlap(pktinfo_off, pktinfo_len, + data_off, data_len); + if (__predict_false(overlap)) { + if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " + "pktinfo overlap data, pktinfo abs %d len %d, " + "data abs %d len %d\n", + pktinfo_off, pktinfo_len, data_off, data_len); + return; + } + + /* + * Find useful per-packet-info. + */ + error = hn_rndis_rxinfo(((const uint8_t *)pkt) + pktinfo_off, + pktinfo_len, &info); + if (__predict_false(error)) { + if_printf(rxr->hn_ifp, "invalid RNDIS packet msg " + "pktinfo\n"); + return; + } + } + + if (__predict_false(data_off + data_len > pkt->rm_len)) { + if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, " + "data overflow, msglen %u, data abs %d len %d\n", + pkt->rm_len, data_off, data_len); + return; + } + hn_rxpkt(rxr, ((const uint8_t *)pkt) + data_off, data_len, &info); +} + +static __inline void +hn_rndis_rxpkt(struct hn_rx_ring *rxr, const void *data, int dlen) +{ + const struct rndis_msghdr *hdr; + + if (__predict_false(dlen < sizeof(*hdr))) { + if_printf(rxr->hn_ifp, "invalid RNDIS msg\n"); + return; + } + hdr = data; + + if (__predict_true(hdr->rm_type == REMOTE_NDIS_PACKET_MSG)) { + /* Hot data path. */ + hn_rndis_rx_data(rxr, data, dlen); + /* Done! */ + return; + } + + if (hdr->rm_type == REMOTE_NDIS_INDICATE_STATUS_MSG) + hn_rndis_rx_status(rxr->hn_ifp->if_softc, data, dlen); + else + hn_rndis_rx_ctrl(rxr->hn_ifp->if_softc, data, dlen); +} + static void hn_nvs_handle_notify(struct hn_softc *sc, const struct vmbus_chanpkt_hdr *pkt) { @@ -3888,9 +4452,9 @@ static void hn_nvs_handle_comp(struct hn_softc *sc, struct vmbus_channel *chan, const struct vmbus_chanpkt_hdr *pkt) { - struct hn_send_ctx *sndc; + struct hn_nvs_sendctx *sndc; - sndc = (struct hn_send_ctx *)(uintptr_t)pkt->cph_xactid; + sndc = (struct hn_nvs_sendctx *)(uintptr_t)pkt->cph_xactid; sndc->hn_cb(sndc, sc, chan, VMBUS_CHANPKT_CONST_DATA(pkt), VMBUS_CHANPKT_DATALEN(pkt)); /* @@ -3901,8 +4465,8 @@ hn_nvs_handle_comp(struct hn_softc *sc, struct vmbus_channel *chan, } static void -hn_nvs_handle_rxbuf(struct hn_softc *sc, struct hn_rx_ring *rxr, - struct vmbus_channel *chan, const struct vmbus_chanpkt_hdr *pkthdr) +hn_nvs_handle_rxbuf(struct hn_rx_ring *rxr, struct vmbus_channel *chan, + const struct vmbus_chanpkt_hdr *pkthdr) { const struct vmbus_chanpkt_rxbuf *pkt; const struct hn_nvs_hdr *nvs_hdr; @@ -3947,52 +4511,52 @@ hn_nvs_handle_rxbuf(struct hn_softc *sc, struct hn_rx_ring *rxr, ofs = pkt->cp_rxbuf[i].rb_ofs; len = pkt->cp_rxbuf[i].rb_len; - if (__predict_false(ofs + len > NETVSC_RECEIVE_BUFFER_SIZE)) { + if (__predict_false(ofs + len > HN_RXBUF_SIZE)) { if_printf(rxr->hn_ifp, "%dth RNDIS msg overflow rxbuf, " "ofs %d, len %d\n", i, ofs, len); continue; } - hv_rf_on_receive(sc, rxr, rxr->hn_rxbuf + ofs, len); + hn_rndis_rxpkt(rxr, rxr->hn_rxbuf + ofs, len); } - + /* - * Moved completion call back here so that all received - * messages (not just data messages) will trigger a response - * message back to the host. + * Ack the consumed RXBUF associated w/ this channel packet, + * so that this RXBUF can be recycled by the hypervisor. */ - hn_nvs_ack_rxbuf(chan, pkt->cp_hdr.cph_xactid); + hn_nvs_ack_rxbuf(rxr, chan, pkt->cp_hdr.cph_xactid); } -/* - * Net VSC on receive completion - * - * Send a receive completion packet to RNDIS device (ie NetVsp) - */ static void -hn_nvs_ack_rxbuf(struct vmbus_channel *chan, uint64_t tid) +hn_nvs_ack_rxbuf(struct hn_rx_ring *rxr, struct vmbus_channel *chan, + uint64_t tid) { struct hn_nvs_rndis_ack ack; - int retries = 0; - int ret = 0; + int retries, error; ack.nvs_type = HN_NVS_TYPE_RNDIS_ACK; ack.nvs_status = HN_NVS_STATUS_OK; -retry_send_cmplt: - /* Send the completion */ - ret = vmbus_chan_send(chan, VMBUS_CHANPKT_TYPE_COMP, + retries = 0; +again: + error = vmbus_chan_send(chan, VMBUS_CHANPKT_TYPE_COMP, VMBUS_CHANPKT_FLAG_NONE, &ack, sizeof(ack), tid); - if (ret == 0) { - /* success */ - /* no-op */ - } else if (ret == EAGAIN) { - /* no more room... wait a bit and attempt to retry 3 times */ + if (__predict_false(error == EAGAIN)) { + /* + * NOTE: + * This should _not_ happen in real world, since the + * consumption of the TX bufring from the TX path is + * controlled. + */ + if (rxr->hn_ack_failed == 0) + if_printf(rxr->hn_ifp, "RXBUF ack retry\n"); + rxr->hn_ack_failed++; retries++; - - if (retries < 4) { + if (retries < 10) { DELAY(100); - goto retry_send_cmplt; + goto again; } + /* RXBUF leaks! */ + if_printf(rxr->hn_ifp, "RXBUF ack failed\n"); } } @@ -4001,66 +4565,72 @@ hn_chan_callback(struct vmbus_channel *chan, void *xrxr) { struct hn_rx_ring *rxr = xrxr; struct hn_softc *sc = rxr->hn_ifp->if_softc; - void *buffer; - int bufferlen = NETVSC_PACKET_SIZE; - buffer = rxr->hn_rdbuf; - do { - struct vmbus_chanpkt_hdr *pkt = buffer; - uint32_t bytes_rxed; - int ret; + for (;;) { + struct vmbus_chanpkt_hdr *pkt = rxr->hn_pktbuf; + int error, pktlen; - bytes_rxed = bufferlen; - ret = vmbus_chan_recv_pkt(chan, pkt, &bytes_rxed); - if (ret == 0) { - switch (pkt->cph_type) { - case VMBUS_CHANPKT_TYPE_COMP: - hn_nvs_handle_comp(sc, chan, pkt); - break; - case VMBUS_CHANPKT_TYPE_RXBUF: - hn_nvs_handle_rxbuf(sc, rxr, chan, pkt); - break; - case VMBUS_CHANPKT_TYPE_INBAND: - hn_nvs_handle_notify(sc, pkt); - break; - default: - if_printf(rxr->hn_ifp, - "unknown chan pkt %u\n", - pkt->cph_type); - break; - } - } else if (ret == ENOBUFS) { - /* Handle large packet */ - if (bufferlen > NETVSC_PACKET_SIZE) { - free(buffer, M_NETVSC); - buffer = NULL; - } + pktlen = rxr->hn_pktbuf_len; + error = vmbus_chan_recv_pkt(chan, pkt, &pktlen); + if (__predict_false(error == ENOBUFS)) { + void *nbuf; + int nlen; - /* alloc new buffer */ - buffer = malloc(bytes_rxed, M_NETVSC, M_NOWAIT); - if (buffer == NULL) { - if_printf(rxr->hn_ifp, - "hv_cb malloc buffer failed, len=%u\n", - bytes_rxed); - bufferlen = 0; - break; - } - bufferlen = bytes_rxed; - } else { - /* No more packets */ + /* + * Expand channel packet buffer. + * + * XXX + * Use M_WAITOK here, since allocation failure + * is fatal. + */ + nlen = rxr->hn_pktbuf_len * 2; + while (nlen < pktlen) + nlen *= 2; + nbuf = malloc(nlen, M_DEVBUF, M_WAITOK); + + if_printf(rxr->hn_ifp, "expand pktbuf %d -> %d\n", + rxr->hn_pktbuf_len, nlen); + + free(rxr->hn_pktbuf, M_DEVBUF); + rxr->hn_pktbuf = nbuf; + rxr->hn_pktbuf_len = nlen; + /* Retry! */ + continue; + } else if (__predict_false(error == EAGAIN)) { + /* No more channel packets; done! */ break; } - } while (1); + KASSERT(!error, ("vmbus_chan_recv_pkt failed: %d", error)); - if (bufferlen > NETVSC_PACKET_SIZE) - free(buffer, M_NETVSC); + switch (pkt->cph_type) { + case VMBUS_CHANPKT_TYPE_COMP: + hn_nvs_handle_comp(sc, chan, pkt); + break; - hv_rf_channel_rollup(rxr, rxr->hn_txr); + case VMBUS_CHANPKT_TYPE_RXBUF: + hn_nvs_handle_rxbuf(rxr, chan, pkt); + break; + + case VMBUS_CHANPKT_TYPE_INBAND: + hn_nvs_handle_notify(sc, pkt); + break; + + default: + if_printf(rxr->hn_ifp, "unknown chan pkt %u\n", + pkt->cph_type); + break; + } + } + hn_chan_rollup(rxr, rxr->hn_txr); } static void hn_tx_taskq_create(void *arg __unused) { + + if (vm_guest != VM_GUEST_HV) + return; + if (!hn_share_tx_taskq) return; @@ -4079,16 +4649,17 @@ hn_tx_taskq_create(void *arg __unused) taskqueue_start_threads(&hn_tx_taskq, 1, PI_NET, "hn tx"); } } -SYSINIT(hn_txtq_create, SI_SUB_DRIVERS, SI_ORDER_FIRST, +SYSINIT(hn_txtq_create, SI_SUB_DRIVERS, SI_ORDER_SECOND, hn_tx_taskq_create, NULL); static void hn_tx_taskq_destroy(void *arg __unused) { + if (hn_tx_taskq != NULL) taskqueue_free(hn_tx_taskq); } -SYSUNINIT(hn_txtq_destroy, SI_SUB_DRIVERS, SI_ORDER_FIRST, +SYSUNINIT(hn_txtq_destroy, SI_SUB_DRIVERS, SI_ORDER_SECOND, hn_tx_taskq_destroy, NULL); static device_method_t netvsc_methods[] = { diff --git a/sys/dev/hyperv/netvsc/if_hnvar.h b/sys/dev/hyperv/netvsc/if_hnvar.h index d25d5f2b7d53..ea47be85848a 100644 --- a/sys/dev/hyperv/netvsc/if_hnvar.h +++ b/sys/dev/hyperv/netvsc/if_hnvar.h @@ -29,118 +29,206 @@ #ifndef _IF_HNVAR_H_ #define _IF_HNVAR_H_ -#include <sys/param.h> +#define HN_USE_TXDESC_BUFRING -#include <dev/hyperv/include/vmbus.h> -#include <dev/hyperv/netvsc/if_hnreg.h> +#define HN_CHIM_SIZE (15 * 1024 * 1024) -struct hn_softc; +#define HN_RXBUF_SIZE (16 * 1024 * 1024) +#define HN_RXBUF_SIZE_COMPAT (15 * 1024 * 1024) -struct vmbus_channel; -struct hn_send_ctx; +/* Claimed to be 12232B */ +#define HN_MTU_MAX (9 * 1024) -typedef void (*hn_sent_callback_t) - (struct hn_send_ctx *, struct hn_softc *, - struct vmbus_channel *, const void *, int); +#define HN_TXBR_SIZE (128 * PAGE_SIZE) +#define HN_RXBR_SIZE (128 * PAGE_SIZE) -struct hn_send_ctx { - hn_sent_callback_t hn_cb; - void *hn_cbarg; - uint32_t hn_chim_idx; - int hn_chim_sz; -}; +#define HN_XACT_REQ_PGCNT 2 +#define HN_XACT_RESP_PGCNT 2 +#define HN_XACT_REQ_SIZE (HN_XACT_REQ_PGCNT * PAGE_SIZE) +#define HN_XACT_RESP_SIZE (HN_XACT_RESP_PGCNT * PAGE_SIZE) -struct rndis_hash_info; -struct rndix_hash_value; -struct ndis_8021q_info_; -struct rndis_tcp_ip_csum_info_; +#define HN_GPACNT_MAX 32 -#define HN_NDIS_VLAN_INFO_INVALID 0xffffffff -#define HN_NDIS_RXCSUM_INFO_INVALID 0 -#define HN_NDIS_HASH_INFO_INVALID 0 +struct hn_txdesc; +#ifndef HN_USE_TXDESC_BUFRING +SLIST_HEAD(hn_txdesc_list, hn_txdesc); +#else +struct buf_ring; +#endif +struct hn_tx_ring; -struct hn_recvinfo { - uint32_t vlan_info; - uint32_t csum_info; - uint32_t hash_info; - uint32_t hash_value; -}; +struct hn_rx_ring { + struct ifnet *hn_ifp; + struct hn_tx_ring *hn_txr; + void *hn_pktbuf; + int hn_pktbuf_len; + uint8_t *hn_rxbuf; /* shadow sc->hn_rxbuf */ + int hn_rx_idx; + + /* Trust csum verification on host side */ + int hn_trust_hcsum; /* HN_TRUST_HCSUM_ */ + struct lro_ctrl hn_lro; + + u_long hn_csum_ip; + u_long hn_csum_tcp; + u_long hn_csum_udp; + u_long hn_csum_trusted; + u_long hn_lro_tried; + u_long hn_small_pkts; + u_long hn_pkts; + u_long hn_rss_pkts; + u_long hn_ack_failed; + + /* Rarely used stuffs */ + struct sysctl_oid *hn_rx_sysctl_tree; + int hn_rx_flags; + + void *hn_br; /* TX/RX bufring */ + struct hyperv_dma hn_br_dma; +} __aligned(CACHE_LINE_SIZE); + +#define HN_TRUST_HCSUM_IP 0x0001 +#define HN_TRUST_HCSUM_TCP 0x0002 +#define HN_TRUST_HCSUM_UDP 0x0004 + +#define HN_RX_FLAG_ATTACHED 0x1 + +struct hn_tx_ring { +#ifndef HN_USE_TXDESC_BUFRING + struct mtx hn_txlist_spin; + struct hn_txdesc_list hn_txlist; +#else + struct buf_ring *hn_txdesc_br; +#endif + int hn_txdesc_cnt; + int hn_txdesc_avail; + u_short hn_has_txeof; + u_short hn_txdone_cnt; -#define HN_SEND_CTX_INITIALIZER(cb, cbarg) \ -{ \ - .hn_cb = cb, \ - .hn_cbarg = cbarg, \ - .hn_chim_idx = HN_NVS_CHIM_IDX_INVALID, \ - .hn_chim_sz = 0 \ -} + int hn_sched_tx; + void (*hn_txeof)(struct hn_tx_ring *); + struct taskqueue *hn_tx_taskq; + struct task hn_tx_task; + struct task hn_txeof_task; -static __inline void -hn_send_ctx_init(struct hn_send_ctx *sndc, hn_sent_callback_t cb, - void *cbarg, uint32_t chim_idx, int chim_sz) -{ + struct buf_ring *hn_mbuf_br; + int hn_oactive; + int hn_tx_idx; + int hn_tx_flags; - sndc->hn_cb = cb; - sndc->hn_cbarg = cbarg; - sndc->hn_chim_idx = chim_idx; - sndc->hn_chim_sz = chim_sz; -} + struct mtx hn_tx_lock; + struct hn_softc *hn_sc; + struct vmbus_channel *hn_chan; -static __inline void -hn_send_ctx_init_simple(struct hn_send_ctx *sndc, hn_sent_callback_t cb, - void *cbarg) -{ + int hn_direct_tx_size; + int hn_chim_size; + bus_dma_tag_t hn_tx_data_dtag; + uint64_t hn_csum_assist; - hn_send_ctx_init(sndc, cb, cbarg, HN_NVS_CHIM_IDX_INVALID, 0); -} + int (*hn_sendpkt)(struct hn_tx_ring *, struct hn_txdesc *); + int hn_suspended; + int hn_gpa_cnt; + struct vmbus_gpa hn_gpa[HN_GPACNT_MAX]; -static __inline int -hn_nvs_send(struct vmbus_channel *chan, uint16_t flags, - void *nvs_msg, int nvs_msglen, struct hn_send_ctx *sndc) -{ + u_long hn_no_txdescs; + u_long hn_send_failed; + u_long hn_txdma_failed; + u_long hn_tx_collapsed; + u_long hn_tx_chimney_tried; + u_long hn_tx_chimney; + u_long hn_pkts; - return (vmbus_chan_send(chan, VMBUS_CHANPKT_TYPE_INBAND, flags, - nvs_msg, nvs_msglen, (uint64_t)(uintptr_t)sndc)); -} + /* Rarely used stuffs */ + struct hn_txdesc *hn_txdesc; + bus_dma_tag_t hn_tx_rndis_dtag; + struct sysctl_oid *hn_tx_sysctl_tree; +} __aligned(CACHE_LINE_SIZE); -static __inline int -hn_nvs_send_sglist(struct vmbus_channel *chan, struct vmbus_gpa sg[], int sglen, - void *nvs_msg, int nvs_msglen, struct hn_send_ctx *sndc) -{ +#define HN_TX_FLAG_ATTACHED 0x1 +#define HN_TX_FLAG_HASHVAL 0x2 /* support HASHVAL pktinfo */ - return (vmbus_chan_send_sglist(chan, sg, sglen, nvs_msg, nvs_msglen, - (uint64_t)(uintptr_t)sndc)); -} +/* + * Device-specific softc structure + */ +struct hn_softc { + struct ifnet *hn_ifp; + struct ifmedia hn_media; + device_t hn_dev; + int hn_if_flags; + struct sx hn_lock; + struct vmbus_channel *hn_prichan; + + int hn_rx_ring_cnt; + int hn_rx_ring_inuse; + struct hn_rx_ring *hn_rx_ring; + + int hn_tx_ring_cnt; + int hn_tx_ring_inuse; + struct hn_tx_ring *hn_tx_ring; + + uint8_t *hn_chim; + u_long *hn_chim_bmap; + int hn_chim_bmap_cnt; + int hn_chim_cnt; + int hn_chim_szmax; + + int hn_cpu; + struct taskqueue *hn_tx_taskq; + struct sysctl_oid *hn_tx_sysctl_tree; + struct sysctl_oid *hn_rx_sysctl_tree; + struct vmbus_xact_ctx *hn_xact; + uint32_t hn_nvs_ver; + uint32_t hn_rx_filter; -struct vmbus_xact; -struct rndis_packet_msg; + struct taskqueue *hn_mgmt_taskq; + struct taskqueue *hn_mgmt_taskq0; + struct task hn_link_task; + struct task hn_netchg_init; + struct timeout_task hn_netchg_status; + uint32_t hn_link_flags; /* HN_LINK_FLAG_ */ -uint32_t hn_chim_alloc(struct hn_softc *sc); -void hn_chim_free(struct hn_softc *sc, uint32_t chim_idx); + uint32_t hn_caps; /* HN_CAP_ */ + uint32_t hn_flags; /* HN_FLAG_ */ + void *hn_rxbuf; + uint32_t hn_rxbuf_gpadl; + struct hyperv_dma hn_rxbuf_dma; + + uint32_t hn_chim_gpadl; + struct hyperv_dma hn_chim_dma; + + uint32_t hn_rndis_rid; + uint32_t hn_ndis_ver; + int hn_ndis_tso_szmax; + int hn_ndis_tso_sgmin; + + int hn_rss_ind_size; + uint32_t hn_rss_hash; /* NDIS_HASH_ */ + struct ndis_rssprm_toeplitz hn_rss; +}; -int hn_rndis_attach(struct hn_softc *sc, int mtu); -void hn_rndis_detach(struct hn_softc *sc); -int hn_rndis_conf_rss(struct hn_softc *sc, uint16_t flags); -void *hn_rndis_pktinfo_append(struct rndis_packet_msg *, - size_t pktsize, size_t pi_dlen, uint32_t pi_type); -int hn_rndis_query_rsscaps(struct hn_softc *sc, int *rxr_cnt); -int hn_rndis_get_eaddr(struct hn_softc *sc, uint8_t *eaddr); -int hn_rndis_get_linkstatus(struct hn_softc *sc, - uint32_t *link_status); -/* filter: NDIS_PACKET_TYPE_ or 0. */ -int hn_rndis_set_rxfilter(struct hn_softc *sc, uint32_t filter); +#define HN_FLAG_RXBUF_CONNECTED 0x0001 +#define HN_FLAG_CHIM_CONNECTED 0x0002 +#define HN_FLAG_HAS_RSSKEY 0x0004 +#define HN_FLAG_HAS_RSSIND 0x0008 +#define HN_FLAG_SYNTH_ATTACHED 0x0010 -int hn_nvs_attach(struct hn_softc *sc, int mtu); -void hn_nvs_detach(struct hn_softc *sc); -int hn_nvs_alloc_subchans(struct hn_softc *sc, int *nsubch); -void hn_nvs_sent_xact(struct hn_send_ctx *sndc, struct hn_softc *sc, - struct vmbus_channel *chan, const void *data, int dlen); +#define HN_CAP_VLAN 0x0001 +#define HN_CAP_MTU 0x0002 +#define HN_CAP_IPCS 0x0004 +#define HN_CAP_TCP4CS 0x0008 +#define HN_CAP_TCP6CS 0x0010 +#define HN_CAP_UDP4CS 0x0020 +#define HN_CAP_UDP6CS 0x0040 +#define HN_CAP_TSO4 0x0080 +#define HN_CAP_TSO6 0x0100 +#define HN_CAP_HASHVAL 0x0200 -int hn_rxpkt(struct hn_rx_ring *rxr, const void *data, int dlen, - const struct hn_recvinfo *info); -void hn_chan_rollup(struct hn_rx_ring *rxr, struct hn_tx_ring *txr); -void hn_link_status_update(struct hn_softc *sc); -void hn_network_change(struct hn_softc *sc); +/* Capability description for use with printf(9) %b identifier. */ +#define HN_CAP_BITS \ + "\020\1VLAN\2MTU\3IPCS\4TCP4CS\5TCP6CS" \ + "\6UDP4CS\7UDP6CS\10TSO4\11TSO6\12HASHVAL" -extern struct hn_send_ctx hn_send_ctx_none; +#define HN_LINK_FLAG_LINKUP 0x0001 +#define HN_LINK_FLAG_NETCHG 0x0002 #endif /* !_IF_HNVAR_H_ */ diff --git a/sys/dev/hyperv/netvsc/ndis.h b/sys/dev/hyperv/netvsc/ndis.h index 5c741c6db330..9d01471553c6 100644 --- a/sys/dev/hyperv/netvsc/ndis.h +++ b/sys/dev/hyperv/netvsc/ndis.h @@ -57,6 +57,10 @@ #define NDIS_HASH_TCP_IPV6 0x00001000 #define NDIS_HASH_TCP_IPV6_EX 0x00002000 +/* Hash description for use with printf(9) %b identifier. */ +#define NDIS_HASH_BITS \ + "\20\1TOEPLITZ\11IP4\12TCP4\13IP6\14IP6EX\15TCP6\16TCP6EX" + #define NDIS_HASH_KEYSIZE_TOEPLITZ 40 #define NDIS_HASH_INDCNT 128 @@ -142,7 +146,7 @@ struct ndis_offload_params { */ struct ndis_rss_caps { struct ndis_object_hdr ndis_hdr; - uint32_t ndis_flags; /* NDIS_RSS_CAP_ */ + uint32_t ndis_caps; /* NDIS_RSS_CAP_ */ uint32_t ndis_nmsi; /* # of MSIs */ uint32_t ndis_nrxr; /* # of RX rings */ /* NDIS >= 6.30 */ @@ -165,7 +169,8 @@ struct ndis_rss_caps { #define NDIS_RSS_CAP_IPV4 0x00000100 #define NDIS_RSS_CAP_IPV6 0x00000200 #define NDIS_RSS_CAP_IPV6_EX 0x00000400 -#define NDIS_RSS_CAP_HASH_TOEPLITZ 0x00000001 +#define NDIS_RSS_CAP_HASH_TOEPLITZ NDIS_HASH_FUNCTION_TOEPLITZ +#define NDIS_RSS_CAP_HASHFUNC_MASK NDIS_HASH_FUNCTION_MASK /* * OID_GEN_RECEIVE_SCALE_PARAMETERS @@ -209,6 +214,9 @@ struct ndis_rssprm_toeplitz { uint32_t rss_ind[NDIS_HASH_INDCNT]; }; +#define NDIS_RSSPRM_TOEPLITZ_SIZE(nind) \ + __offsetof(struct ndis_rssprm_toeplitz, rss_ind[nind]) + /* * OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES * ndis_type: NDIS_OBJTYPE_OFFLOAD diff --git a/sys/dev/hyperv/utilities/hv_heartbeat.c b/sys/dev/hyperv/utilities/hv_heartbeat.c index 37867148b538..8fc7a0944eaf 100644 --- a/sys/dev/hyperv/utilities/hv_heartbeat.c +++ b/sys/dev/hyperv/utilities/hv_heartbeat.c @@ -40,6 +40,14 @@ __FBSDID("$FreeBSD$"); #include "vmbus_if.h" +#define VMBUS_HEARTBEAT_FWVER_MAJOR 3 +#define VMBUS_HEARTBEAT_FWVER \ + VMBUS_IC_VERSION(VMBUS_HEARTBEAT_FWVER_MAJOR, 0) + +#define VMBUS_HEARTBEAT_MSGVER_MAJOR 3 +#define VMBUS_HEARTBEAT_MSGVER \ + VMBUS_IC_VERSION(VMBUS_HEARTBEAT_MSGVER_MAJOR, 0) + static const struct vmbus_ic_desc vmbus_heartbeat_descs[] = { { .ic_guid = { .hv_guid = { @@ -80,7 +88,8 @@ vmbus_heartbeat_cb(struct vmbus_channel *chan, void *xsc) */ switch (hdr->ic_type) { case VMBUS_ICMSG_TYPE_NEGOTIATE: - error = vmbus_ic_negomsg(sc, data, &dlen); + error = vmbus_ic_negomsg(sc, data, &dlen, + VMBUS_HEARTBEAT_FWVER, VMBUS_HEARTBEAT_MSGVER); if (error) return; break; diff --git a/sys/dev/hyperv/utilities/hv_kvp.c b/sys/dev/hyperv/utilities/hv_kvp.c index 9d1abb4c42b9..593b4226bc52 100644 --- a/sys/dev/hyperv/utilities/hv_kvp.c +++ b/sys/dev/hyperv/utilities/hv_kvp.c @@ -61,7 +61,9 @@ __FBSDID("$FreeBSD$"); #include <sys/mutex.h> #include <dev/hyperv/include/hyperv.h> +#include <dev/hyperv/include/vmbus.h> #include <dev/hyperv/utilities/hv_utilreg.h> +#include <dev/hyperv/utilities/vmbus_icreg.h> #include "hv_util.h" #include "unicode.h" @@ -74,6 +76,12 @@ __FBSDID("$FreeBSD$"); #define KVP_ERROR 1 #define kvp_hdr hdr.kvp_hdr +#define KVP_FWVER_MAJOR 3 +#define KVP_FWVER VMBUS_IC_VERSION(KVP_FWVER_MAJOR, 0) + +#define KVP_MSGVER_MAJOR 4 +#define KVP_MSGVER VMBUS_IC_VERSION(KVP_MSGVER_MAJOR, 0) + /* hv_kvp debug control */ static int hv_kvp_log = 0; @@ -208,52 +216,10 @@ hv_kvp_transaction_init(hv_kvp_sc *sc, uint32_t rcv_len, sc->host_msg_id = request_id; sc->rcv_buf = rcv_buf; sc->host_kvp_msg = (struct hv_kvp_msg *)&rcv_buf[ - sizeof(struct hv_vmbus_pipe_hdr) + - sizeof(struct hv_vmbus_icmsg_hdr)]; + sizeof(struct hv_vmbus_pipe_hdr) + + sizeof(struct hv_vmbus_icmsg_hdr)]; } - -/* - * hv_kvp - version neogtiation function - */ -static void -hv_kvp_negotiate_version(struct hv_vmbus_icmsg_hdr *icmsghdrp, uint8_t *buf) -{ - struct hv_vmbus_icmsg_negotiate *negop; - int icframe_vercnt; - int icmsg_vercnt; - - icmsghdrp->icmsgsize = 0x10; - - negop = (struct hv_vmbus_icmsg_negotiate *)&buf[ - sizeof(struct hv_vmbus_pipe_hdr) + - sizeof(struct hv_vmbus_icmsg_hdr)]; - icframe_vercnt = negop->icframe_vercnt; - icmsg_vercnt = negop->icmsg_vercnt; - - /* - * Select the framework version number we will support - */ - if ((icframe_vercnt >= 2) && (negop->icversion_data[1].major == 3)) { - icframe_vercnt = 3; - if (icmsg_vercnt > 2) - icmsg_vercnt = 4; - else - icmsg_vercnt = 3; - } else { - icframe_vercnt = 1; - icmsg_vercnt = 1; - } - - negop->icframe_vercnt = 1; - negop->icmsg_vercnt = 1; - negop->icversion_data[0].major = icframe_vercnt; - negop->icversion_data[0].minor = 0; - negop->icversion_data[1].major = icmsg_vercnt; - negop->icversion_data[1].minor = 0; -} - - /* * Convert ip related info in umsg from utf8 to utf16 and store in hmsg */ @@ -578,7 +544,8 @@ hv_kvp_respond_host(hv_kvp_sc *sc, int error) error = HV_KVP_E_FAIL; hv_icmsg_hdrp->status = error; - hv_icmsg_hdrp->icflags = HV_ICMSGHDRFLAG_TRANSACTION | HV_ICMSGHDRFLAG_RESPONSE; + hv_icmsg_hdrp->icflags = HV_ICMSGHDRFLAG_TRANSACTION | + HV_ICMSGHDRFLAG_RESPONSE; error = vmbus_chan_send(vmbus_get_channel(sc->dev), VMBUS_CHANPKT_TYPE_INBAND, 0, sc->rcv_buf, sc->host_msg_len, @@ -622,8 +589,8 @@ hv_kvp_process_request(void *context, int pending) uint32_t recvlen = 0; uint64_t requestid; struct hv_vmbus_icmsg_hdr *icmsghdrp; - int ret = 0; - hv_kvp_sc *sc; + int ret = 0, error; + hv_kvp_sc *sc; hv_kvp_log_info("%s: entering hv_kvp_process_request\n", __func__); @@ -637,14 +604,15 @@ hv_kvp_process_request(void *context, int pending) /* XXX check recvlen to make sure that it contains enough data */ while ((ret == 0) && (recvlen > 0)) { - icmsghdrp = (struct hv_vmbus_icmsg_hdr *) - &kvp_buf[sizeof(struct hv_vmbus_pipe_hdr)]; + &kvp_buf[sizeof(struct hv_vmbus_pipe_hdr)]; hv_kvp_transaction_init(sc, recvlen, requestid, kvp_buf); if (icmsghdrp->icmsgtype == HV_ICMSGTYPE_NEGOTIATE) { - hv_kvp_negotiate_version(icmsghdrp, kvp_buf); - hv_kvp_respond_host(sc, ret); + error = vmbus_ic_negomsg(&sc->util_sc, + kvp_buf, &recvlen, KVP_FWVER, KVP_MSGVER); + /* XXX handle vmbus_ic_negomsg failure. */ + hv_kvp_respond_host(sc, error); /* * It is ok to not acquire the mutex before setting diff --git a/sys/dev/hyperv/utilities/hv_kvp.h b/sys/dev/hyperv/utilities/hv_kvp.h index 6474e1825677..c391da0d5a5d 100644 --- a/sys/dev/hyperv/utilities/hv_kvp.h +++ b/sys/dev/hyperv/utilities/hv_kvp.h @@ -28,7 +28,6 @@ #ifndef _KVP_H #define _KVP_H - /* * An implementation of HyperV key value pair (KVP) functionality for FreeBSD * @@ -178,9 +177,9 @@ struct hv_kvp_ipaddr_value { }__attribute__((packed)); struct hv_kvp_hdr { - uint8_t operation; - uint8_t pool; - uint16_t pad; + uint8_t operation; + uint8_t pool; + uint16_t pad; } __attribute__((packed)); struct hv_kvp_exchg_msg_value { diff --git a/sys/dev/hyperv/utilities/hv_shutdown.c b/sys/dev/hyperv/utilities/hv_shutdown.c index 458009381b27..e511b21a070c 100644 --- a/sys/dev/hyperv/utilities/hv_shutdown.c +++ b/sys/dev/hyperv/utilities/hv_shutdown.c @@ -41,6 +41,14 @@ __FBSDID("$FreeBSD$"); #include "vmbus_if.h" +#define VMBUS_SHUTDOWN_FWVER_MAJOR 3 +#define VMBUS_SHUTDOWN_FWVER \ + VMBUS_IC_VERSION(VMBUS_SHUTDOWN_FWVER_MAJOR, 0) + +#define VMBUS_SHUTDOWN_MSGVER_MAJOR 3 +#define VMBUS_SHUTDOWN_MSGVER \ + VMBUS_IC_VERSION(VMBUS_SHUTDOWN_MSGVER_MAJOR, 0) + static const struct vmbus_ic_desc vmbus_shutdown_descs[] = { { .ic_guid = { .hv_guid = { @@ -82,7 +90,8 @@ vmbus_shutdown_cb(struct vmbus_channel *chan, void *xsc) */ switch (hdr->ic_type) { case VMBUS_ICMSG_TYPE_NEGOTIATE: - error = vmbus_ic_negomsg(sc, data, &dlen); + error = vmbus_ic_negomsg(sc, data, &dlen, + VMBUS_SHUTDOWN_FWVER, VMBUS_SHUTDOWN_MSGVER); if (error) return; break; diff --git a/sys/dev/hyperv/utilities/hv_timesync.c b/sys/dev/hyperv/utilities/hv_timesync.c index 2d440263ec0c..5bd51b08a91f 100644 --- a/sys/dev/hyperv/utilities/hv_timesync.c +++ b/sys/dev/hyperv/utilities/hv_timesync.c @@ -42,6 +42,14 @@ __FBSDID("$FreeBSD$"); #include "vmbus_if.h" +#define VMBUS_TIMESYNC_FWVER_MAJOR 3 +#define VMBUS_TIMESYNC_FWVER \ + VMBUS_IC_VERSION(VMBUS_TIMESYNC_FWVER_MAJOR, 0) + +#define VMBUS_TIMESYNC_MSGVER_MAJOR 3 +#define VMBUS_TIMESYNC_MSGVER \ + VMBUS_IC_VERSION(VMBUS_TIMESYNC_MSGVER_MAJOR, 0) + static const struct vmbus_ic_desc vmbus_timesync_descs[] = { { .ic_guid = { .hv_guid = { @@ -162,7 +170,8 @@ vmbus_timesync_cb(struct vmbus_channel *chan, void *xsc) */ switch (hdr->ic_type) { case VMBUS_ICMSG_TYPE_NEGOTIATE: - error = vmbus_ic_negomsg(sc, data, &dlen); + error = vmbus_ic_negomsg(sc, data, &dlen, + VMBUS_TIMESYNC_FWVER, VMBUS_TIMESYNC_MSGVER); if (error) return; break; diff --git a/sys/dev/hyperv/utilities/hv_util.c b/sys/dev/hyperv/utilities/hv_util.c index 3fc16c951d4a..20b8f84da94f 100644 --- a/sys/dev/hyperv/utilities/hv_util.c +++ b/sys/dev/hyperv/utilities/hv_util.c @@ -37,6 +37,7 @@ #include <sys/module.h> #include <sys/reboot.h> #include <sys/systm.h> +#include <sys/sysctl.h> #include <sys/timetc.h> #include <dev/hyperv/include/hyperv.h> @@ -53,52 +54,145 @@ __offsetof(struct vmbus_icmsg_negotiate, ic_ver[VMBUS_IC_VERCNT]) CTASSERT(VMBUS_IC_NEGOSZ < VMBUS_IC_BRSIZE); +static int vmbus_ic_fwver_sysctl(SYSCTL_HANDLER_ARGS); +static int vmbus_ic_msgver_sysctl(SYSCTL_HANDLER_ARGS); + int -vmbus_ic_negomsg(struct hv_util_sc *sc, void *data, int *dlen0) +vmbus_ic_negomsg(struct hv_util_sc *sc, void *data, int *dlen0, + uint32_t fw_ver, uint32_t msg_ver) { struct vmbus_icmsg_negotiate *nego; - int cnt, major, dlen = *dlen0; + int i, cnt, dlen = *dlen0, error; + uint32_t sel_fw_ver, sel_msg_ver; + bool has_fw_ver, has_msg_ver; /* - * Preliminary message size verification + * Preliminary message verification. */ if (dlen < sizeof(*nego)) { device_printf(sc->ic_dev, "truncated ic negotiate, len %d\n", dlen); - return EINVAL; + return (EINVAL); } nego = data; + if (nego->ic_fwver_cnt == 0) { + device_printf(sc->ic_dev, "ic negotiate does not contain " + "framework version %u\n", nego->ic_fwver_cnt); + return (EINVAL); + } + if (nego->ic_msgver_cnt == 0) { + device_printf(sc->ic_dev, "ic negotiate does not contain " + "message version %u\n", nego->ic_msgver_cnt); + return (EINVAL); + } + cnt = nego->ic_fwver_cnt + nego->ic_msgver_cnt; if (dlen < __offsetof(struct vmbus_icmsg_negotiate, ic_ver[cnt])) { device_printf(sc->ic_dev, "ic negotiate does not contain " "versions %d\n", dlen); - return EINVAL; + return (EINVAL); + } + + error = EOPNOTSUPP; + + /* + * Find the best match framework version. + */ + has_fw_ver = false; + for (i = 0; i < nego->ic_fwver_cnt; ++i) { + if (VMBUS_ICVER_LE(nego->ic_ver[i], fw_ver)) { + if (!has_fw_ver) { + sel_fw_ver = nego->ic_ver[i]; + has_fw_ver = true; + } else if (VMBUS_ICVER_GT(nego->ic_ver[i], + sel_fw_ver)) { + sel_fw_ver = nego->ic_ver[i]; + } + } + } + if (!has_fw_ver) { + device_printf(sc->ic_dev, "failed to select framework " + "version\n"); + goto done; + } + + /* + * Fine the best match message version. + */ + has_msg_ver = false; + for (i = nego->ic_fwver_cnt; + i < nego->ic_fwver_cnt + nego->ic_msgver_cnt; ++i) { + if (VMBUS_ICVER_LE(nego->ic_ver[i], msg_ver)) { + if (!has_msg_ver) { + sel_msg_ver = nego->ic_ver[i]; + has_msg_ver = true; + } else if (VMBUS_ICVER_GT(nego->ic_ver[i], + sel_msg_ver)) { + sel_msg_ver = nego->ic_ver[i]; + } + } + } + if (!has_msg_ver) { + device_printf(sc->ic_dev, "failed to select message " + "version\n"); + goto done; } - /* Select major version; XXX looks wrong. */ - if (nego->ic_fwver_cnt >= 2 && VMBUS_ICVER_MAJOR(nego->ic_ver[1]) == 3) - major = 3; - else - major = 1; + error = 0; +done: + if (bootverbose || !has_fw_ver || !has_msg_ver) { + if (has_fw_ver) { + device_printf(sc->ic_dev, "sel framework version: " + "%u.%u\n", + VMBUS_ICVER_MAJOR(sel_fw_ver), + VMBUS_ICVER_MINOR(sel_fw_ver)); + } + for (i = 0; i < nego->ic_fwver_cnt; i++) { + device_printf(sc->ic_dev, "supp framework version: " + "%u.%u\n", + VMBUS_ICVER_MAJOR(nego->ic_ver[i]), + VMBUS_ICVER_MINOR(nego->ic_ver[i])); + } + + if (has_msg_ver) { + device_printf(sc->ic_dev, "sel message version: " + "%u.%u\n", + VMBUS_ICVER_MAJOR(sel_msg_ver), + VMBUS_ICVER_MINOR(sel_msg_ver)); + } + for (i = nego->ic_fwver_cnt; + i < nego->ic_fwver_cnt + nego->ic_msgver_cnt; i++) { + device_printf(sc->ic_dev, "supp message version: " + "%u.%u\n", + VMBUS_ICVER_MAJOR(nego->ic_ver[i]), + VMBUS_ICVER_MINOR(nego->ic_ver[i])); + } + } + if (error) + return (error); + + /* Record the selected versions. */ + sc->ic_fwver = sel_fw_ver; + sc->ic_msgver = sel_msg_ver; - /* One framework version */ + /* One framework version. */ nego->ic_fwver_cnt = 1; - nego->ic_ver[0] = VMBUS_IC_VERSION(major, 0); + nego->ic_ver[0] = sel_fw_ver; - /* One message version */ + /* One message version. */ nego->ic_msgver_cnt = 1; - nego->ic_ver[1] = VMBUS_IC_VERSION(major, 0); + nego->ic_ver[1] = sel_msg_ver; - /* Update data size */ + /* Update data size. */ nego->ic_hdr.ic_dsize = VMBUS_IC_NEGOSZ - sizeof(struct vmbus_icmsg_hdr); - /* Update total size, if necessary */ + /* Update total size, if necessary. */ if (dlen < VMBUS_IC_NEGOSZ) *dlen0 = VMBUS_IC_NEGOSZ; - return 0; + return (0); } int @@ -124,6 +218,8 @@ hv_util_attach(device_t dev, vmbus_chan_callback_t cb) { struct hv_util_sc *sc = device_get_softc(dev); struct vmbus_channel *chan = vmbus_get_channel(dev); + struct sysctl_oid_list *child; + struct sysctl_ctx_list *ctx; int error; sc->ic_dev = dev; @@ -146,9 +242,41 @@ hv_util_attach(device_t dev, vmbus_chan_callback_t cb) free(sc->receive_buffer, M_DEVBUF); return (error); } + + ctx = device_get_sysctl_ctx(dev); + child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); + SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "fw_version", + CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0, + vmbus_ic_fwver_sysctl, "A", "framework version"); + SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "msg_version", + CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0, + vmbus_ic_msgver_sysctl, "A", "message version"); + return (0); } +static int +vmbus_ic_fwver_sysctl(SYSCTL_HANDLER_ARGS) +{ + struct hv_util_sc *sc = arg1; + char verstr[16]; + + snprintf(verstr, sizeof(verstr), "%u.%u", + VMBUS_ICVER_MAJOR(sc->ic_fwver), VMBUS_ICVER_MINOR(sc->ic_fwver)); + return sysctl_handle_string(oidp, verstr, sizeof(verstr), req); +} + +static int +vmbus_ic_msgver_sysctl(SYSCTL_HANDLER_ARGS) +{ + struct hv_util_sc *sc = arg1; + char verstr[16]; + + snprintf(verstr, sizeof(verstr), "%u.%u", + VMBUS_ICVER_MAJOR(sc->ic_msgver), VMBUS_ICVER_MINOR(sc->ic_msgver)); + return sysctl_handle_string(oidp, verstr, sizeof(verstr), req); +} + int hv_util_detach(device_t dev) { diff --git a/sys/dev/hyperv/utilities/hv_util.h b/sys/dev/hyperv/utilities/hv_util.h index 012cdeec3ed6..7cf8d3171826 100644 --- a/sys/dev/hyperv/utilities/hv_util.h +++ b/sys/dev/hyperv/utilities/hv_util.h @@ -42,6 +42,8 @@ typedef struct hv_util_sc { device_t ic_dev; uint8_t *receive_buffer; int ic_buflen; + uint32_t ic_fwver; /* framework version */ + uint32_t ic_msgver; /* message version */ } hv_util_sc; struct vmbus_ic_desc { @@ -54,6 +56,7 @@ struct vmbus_ic_desc { int hv_util_attach(device_t dev, vmbus_chan_callback_t cb); int hv_util_detach(device_t dev); int vmbus_ic_probe(device_t dev, const struct vmbus_ic_desc descs[]); -int vmbus_ic_negomsg(struct hv_util_sc *, void *data, int *dlen); +int vmbus_ic_negomsg(struct hv_util_sc *sc, void *data, int *dlen, + uint32_t fw_ver, uint32_t msg_ver); #endif diff --git a/sys/dev/hyperv/utilities/hv_utilreg.h b/sys/dev/hyperv/utilities/hv_utilreg.h index 124de412e95f..9358776409ee 100644 --- a/sys/dev/hyperv/utilities/hv_utilreg.h +++ b/sys/dev/hyperv/utilities/hv_utilreg.h @@ -76,16 +76,4 @@ typedef struct hv_vmbus_icmsg_negotiate { hv_vmbus_ic_version icversion_data[1]; /* any size array */ } __packed hv_vmbus_icmsg_negotiate; -typedef struct hv_vmbus_shutdown_msg_data { - uint32_t reason_code; - uint32_t timeout_seconds; - uint32_t flags; - uint8_t display_message[2048]; -} __packed hv_vmbus_shutdown_msg_data; - -typedef struct hv_vmbus_heartbeat_msg_data { - uint64_t seq_num; - uint32_t reserved[8]; -} __packed hv_vmbus_heartbeat_msg_data; - #endif /* !_HV_UTILREG_H_ */ diff --git a/sys/dev/hyperv/utilities/vmbus_icreg.h b/sys/dev/hyperv/utilities/vmbus_icreg.h index 683e2f83b591..34359622b6c2 100644 --- a/sys/dev/hyperv/utilities/vmbus_icreg.h +++ b/sys/dev/hyperv/utilities/vmbus_icreg.h @@ -42,6 +42,12 @@ #define VMBUS_IC_VERSION(major, minor) ((major) | (((uint32_t)(minor)) << 16)) #define VMBUS_ICVER_MAJOR(ver) ((ver) & 0xffff) #define VMBUS_ICVER_MINOR(ver) (((ver) & 0xffff0000) >> 16) +#define VMBUS_ICVER_SWAP(ver) \ + ((VMBUS_ICVER_MAJOR((ver)) << 16) | VMBUS_ICVER_MINOR((ver))) +#define VMBUS_ICVER_LE(v1, v2) \ + (VMBUS_ICVER_SWAP((v1)) <= VMBUS_ICVER_SWAP((v2))) +#define VMBUS_ICVER_GT(v1, v2) \ + (VMBUS_ICVER_SWAP((v1)) > VMBUS_ICVER_SWAP((v2))) struct vmbus_pipe_hdr { uint32_t ph_flags; diff --git a/sys/dev/hyperv/vmbus/vmbus.c b/sys/dev/hyperv/vmbus/vmbus.c index d9cb81ba3f66..6c153335c57c 100644 --- a/sys/dev/hyperv/vmbus/vmbus.c +++ b/sys/dev/hyperv/vmbus/vmbus.c @@ -863,7 +863,7 @@ vmbus_intr_setup(struct vmbus_softc *sc) device_printf(sc->vmbus_dev, "cannot find free IDT vector\n"); return ENXIO; } - if(bootverbose) { + if (bootverbose) { device_printf(sc->vmbus_dev, "vmbus IDT vector %d\n", sc->vmbus_idtvec); } diff --git a/sys/dev/hyperv/vmbus/vmbus_chan.c b/sys/dev/hyperv/vmbus/vmbus_chan.c index a889962b0b49..1a86efdaf09e 100644 --- a/sys/dev/hyperv/vmbus/vmbus_chan.c +++ b/sys/dev/hyperv/vmbus/vmbus_chan.c @@ -30,6 +30,7 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> +#include <sys/bus.h> #include <sys/kernel.h> #include <sys/lock.h> #include <sys/malloc.h> @@ -39,6 +40,7 @@ __FBSDID("$FreeBSD$"); #include <sys/systm.h> #include <machine/atomic.h> +#include <machine/stdarg.h> #include <dev/hyperv/include/hyperv_busdma.h> #include <dev/hyperv/vmbus/hyperv_var.h> @@ -90,6 +92,9 @@ static void vmbus_chan_msgproc_chrescind( struct vmbus_softc *, const struct vmbus_message *); +static int vmbus_chan_printf(const struct vmbus_channel *, + const char *, ...) __printflike(2, 3); + /* * Vmbus channel message processing. */ @@ -304,7 +309,7 @@ vmbus_chan_open(struct vmbus_channel *chan, int txbr_size, int rxbr_size, PAGE_SIZE, 0, txbr_size + rxbr_size, &chan->ch_bufring_dma, BUS_DMA_WAITOK); if (chan->ch_bufring == NULL) { - device_printf(chan->ch_dev, "bufring allocation failed\n"); + vmbus_chan_printf(chan, "bufring allocation failed\n"); return (ENOMEM); } @@ -336,7 +341,7 @@ vmbus_chan_open_br(struct vmbus_channel *chan, const struct vmbus_chan_br *cbr, uint8_t *br; if (udlen > VMBUS_CHANMSG_CHOPEN_UDATA_SIZE) { - device_printf(sc->vmbus_dev, + vmbus_chan_printf(chan, "invalid udata len %d for chan%u\n", udlen, chan->ch_id); return EINVAL; } @@ -386,7 +391,7 @@ vmbus_chan_open_br(struct vmbus_channel *chan, const struct vmbus_chan_br *cbr, error = vmbus_chan_gpadl_connect(chan, cbr->cbr_paddr, txbr_size + rxbr_size, &chan->ch_bufring_gpadl); if (error) { - device_printf(sc->vmbus_dev, + vmbus_chan_printf(chan, "failed to connect bufring GPADL to chan%u\n", chan->ch_id); goto failed; } @@ -402,7 +407,7 @@ vmbus_chan_open_br(struct vmbus_channel *chan, const struct vmbus_chan_br *cbr, */ mh = vmbus_msghc_get(sc, sizeof(*req)); if (mh == NULL) { - device_printf(sc->vmbus_dev, + vmbus_chan_printf(chan, "can not get msg hypercall for chopen(chan%u)\n", chan->ch_id); error = ENXIO; @@ -421,7 +426,7 @@ vmbus_chan_open_br(struct vmbus_channel *chan, const struct vmbus_chan_br *cbr, error = vmbus_msghc_exec(sc, mh); if (error) { - device_printf(sc->vmbus_dev, + vmbus_chan_printf(chan, "chopen(chan%u) msg hypercall exec failed: %d\n", chan->ch_id, error); vmbus_msghc_put(sc, mh); @@ -436,13 +441,12 @@ vmbus_chan_open_br(struct vmbus_channel *chan, const struct vmbus_chan_br *cbr, if (status == 0) { if (bootverbose) { - device_printf(sc->vmbus_dev, "chan%u opened\n", - chan->ch_id); + vmbus_chan_printf(chan, "chan%u opened\n", chan->ch_id); } return 0; } - device_printf(sc->vmbus_dev, "failed to open chan%u\n", chan->ch_id); + vmbus_chan_printf(chan, "failed to open chan%u\n", chan->ch_id); error = ENXIO; failed: @@ -485,7 +489,7 @@ vmbus_chan_gpadl_connect(struct vmbus_channel *chan, bus_addr_t paddr, * We don't support multiple GPA ranges. */ if (range_len > UINT16_MAX) { - device_printf(sc->vmbus_dev, "GPA too large, %d pages\n", + vmbus_chan_printf(chan, "GPA too large, %d pages\n", page_count); return EOPNOTSUPP; } @@ -514,8 +518,8 @@ vmbus_chan_gpadl_connect(struct vmbus_channel *chan, bus_addr_t paddr, chm_range.gpa_page[cnt]); mh = vmbus_msghc_get(sc, reqsz); if (mh == NULL) { - device_printf(sc->vmbus_dev, - "can not get msg hypercall for gpadl->chan%u\n", + vmbus_chan_printf(chan, + "can not get msg hypercall for gpadl_conn(chan%u)\n", chan->ch_id); return EIO; } @@ -533,8 +537,8 @@ vmbus_chan_gpadl_connect(struct vmbus_channel *chan, bus_addr_t paddr, error = vmbus_msghc_exec(sc, mh); if (error) { - device_printf(sc->vmbus_dev, - "gpadl->chan%u msg hypercall exec failed: %d\n", + vmbus_chan_printf(chan, + "gpadl_conn(chan%u) msg hypercall exec failed: %d\n", chan->ch_id, error); vmbus_msghc_put(sc, mh); return error; @@ -570,13 +574,13 @@ vmbus_chan_gpadl_connect(struct vmbus_channel *chan, bus_addr_t paddr, vmbus_msghc_put(sc, mh); if (status != 0) { - device_printf(sc->vmbus_dev, "gpadl->chan%u failed: " - "status %u\n", chan->ch_id, status); + vmbus_chan_printf(chan, "gpadl_conn(chan%u) failed: %u\n", + chan->ch_id, status); return EIO; } else { if (bootverbose) { - device_printf(sc->vmbus_dev, "gpadl->chan%u " - "succeeded\n", chan->ch_id); + vmbus_chan_printf(chan, + "gpadl_conn(chan%u) succeeded\n", chan->ch_id); } } return 0; @@ -595,8 +599,8 @@ vmbus_chan_gpadl_disconnect(struct vmbus_channel *chan, uint32_t gpadl) mh = vmbus_msghc_get(sc, sizeof(*req)); if (mh == NULL) { - device_printf(sc->vmbus_dev, - "can not get msg hypercall for gpa x->chan%u\n", + vmbus_chan_printf(chan, + "can not get msg hypercall for gpadl_disconn(chan%u)\n", chan->ch_id); return EBUSY; } @@ -608,8 +612,8 @@ vmbus_chan_gpadl_disconnect(struct vmbus_channel *chan, uint32_t gpadl) error = vmbus_msghc_exec(sc, mh); if (error) { - device_printf(sc->vmbus_dev, - "gpa x->chan%u msg hypercall exec failed: %d\n", + vmbus_chan_printf(chan, + "gpadl_disconn(chan%u) msg hypercall exec failed: %d\n", chan->ch_id, error); vmbus_msghc_put(sc, mh); return error; @@ -681,7 +685,7 @@ vmbus_chan_close_internal(struct vmbus_channel *chan) */ mh = vmbus_msghc_get(sc, sizeof(*req)); if (mh == NULL) { - device_printf(sc->vmbus_dev, + vmbus_chan_printf(chan, "can not get msg hypercall for chclose(chan%u)\n", chan->ch_id); return; @@ -695,12 +699,12 @@ vmbus_chan_close_internal(struct vmbus_channel *chan) vmbus_msghc_put(sc, mh); if (error) { - device_printf(sc->vmbus_dev, + vmbus_chan_printf(chan, "chclose(chan%u) msg hypercall exec failed: %d\n", chan->ch_id, error); return; } else if (bootverbose) { - device_printf(sc->vmbus_dev, "close chan%u\n", chan->ch_id); + vmbus_chan_printf(chan, "close chan%u\n", chan->ch_id); } /* @@ -890,13 +894,12 @@ vmbus_chan_recv(struct vmbus_channel *chan, void *data, int *dlen0, return (error); if (__predict_false(pkt.cph_hlen < VMBUS_CHANPKT_HLEN_MIN)) { - device_printf(chan->ch_dev, "invalid hlen %u\n", - pkt.cph_hlen); + vmbus_chan_printf(chan, "invalid hlen %u\n", pkt.cph_hlen); /* XXX this channel is dead actually. */ return (EIO); } if (__predict_false(pkt.cph_hlen > pkt.cph_tlen)) { - device_printf(chan->ch_dev, "invalid hlen %u and tlen %u\n", + vmbus_chan_printf(chan, "invalid hlen %u and tlen %u\n", pkt.cph_hlen, pkt.cph_tlen); /* XXX this channel is dead actually. */ return (EIO); @@ -933,13 +936,12 @@ vmbus_chan_recv_pkt(struct vmbus_channel *chan, return (error); if (__predict_false(pkt.cph_hlen < VMBUS_CHANPKT_HLEN_MIN)) { - device_printf(chan->ch_dev, "invalid hlen %u\n", - pkt.cph_hlen); + vmbus_chan_printf(chan, "invalid hlen %u\n", pkt.cph_hlen); /* XXX this channel is dead actually. */ return (EIO); } if (__predict_false(pkt.cph_hlen > pkt.cph_tlen)) { - device_printf(chan->ch_dev, "invalid hlen %u and tlen %u\n", + vmbus_chan_printf(chan, "invalid hlen %u and tlen %u\n", pkt.cph_hlen, pkt.cph_tlen); /* XXX this channel is dead actually. */ return (EIO); @@ -1082,8 +1084,8 @@ vmbus_chan_update_evtflagcnt(struct vmbus_softc *sc, break; if (atomic_cmpset_int(flag_cnt_ptr, old_flag_cnt, flag_cnt)) { if (bootverbose) { - device_printf(sc->vmbus_dev, - "channel%u update cpu%d flag_cnt to %d\n", + vmbus_chan_printf(chan, + "chan%u update cpu%d flag_cnt to %d\n", chan->ch_id, chan->ch_cpuid, flag_cnt); } break; @@ -1154,11 +1156,6 @@ vmbus_chan_add(struct vmbus_channel *newchan) return EINVAL; } - if (bootverbose) { - device_printf(sc->vmbus_dev, "chan%u subidx%u offer\n", - newchan->ch_id, newchan->ch_subidx); - } - mtx_lock(&sc->vmbus_prichan_lock); TAILQ_FOREACH(prichan, &sc->vmbus_prichans, ch_prilink) { /* @@ -1179,15 +1176,15 @@ vmbus_chan_add(struct vmbus_channel *newchan) goto done; } else { mtx_unlock(&sc->vmbus_prichan_lock); - device_printf(sc->vmbus_dev, "duplicated primary " - "chan%u\n", newchan->ch_id); + device_printf(sc->vmbus_dev, + "duplicated primary chan%u\n", newchan->ch_id); return EINVAL; } } else { /* Sub-channel */ if (prichan == NULL) { mtx_unlock(&sc->vmbus_prichan_lock); - device_printf(sc->vmbus_dev, "no primary chan for " - "chan%u\n", newchan->ch_id); + device_printf(sc->vmbus_dev, + "no primary chan for chan%u\n", newchan->ch_id); return EINVAL; } /* @@ -1224,6 +1221,15 @@ done: mtx_lock(&sc->vmbus_chan_lock); vmbus_chan_ins_list(sc, newchan); mtx_unlock(&sc->vmbus_chan_lock); + + if (bootverbose) { + vmbus_chan_printf(newchan, "chan%u subidx%u offer\n", + newchan->ch_id, newchan->ch_subidx); + } + + /* Select default cpu for this channel. */ + vmbus_chan_cpu_default(newchan); + return 0; } @@ -1242,7 +1248,8 @@ vmbus_chan_cpu_set(struct vmbus_channel *chan, int cpu) chan->ch_vcpuid = VMBUS_PCPU_GET(chan->ch_vmbus, vcpuid, cpu); if (bootverbose) { - printf("vmbus_chan%u: assigned to cpu%u [vcpu%u]\n", + vmbus_chan_printf(chan, + "chan%u assigned to cpu%u [vcpu%u]\n", chan->ch_id, chan->ch_cpuid, chan->ch_vcpuid); } } @@ -1338,9 +1345,6 @@ vmbus_chan_msgproc_choffer(struct vmbus_softc *sc, TASK_INIT(&chan->ch_attach_task, 0, attach_fn, chan); TASK_INIT(&chan->ch_detach_task, 0, detach_fn, chan); - /* Select default cpu for this channel. */ - vmbus_chan_cpu_default(chan); - error = vmbus_chan_add(chan); if (error) { device_printf(sc->vmbus_dev, "add chan%u failed: %d\n", @@ -1365,11 +1369,6 @@ vmbus_chan_msgproc_chrescind(struct vmbus_softc *sc, return; } - if (bootverbose) { - device_printf(sc->vmbus_dev, "chan%u rescinded\n", - note->chm_chanid); - } - /* * Find and remove the target channel from the channel list. */ @@ -1400,6 +1399,9 @@ vmbus_chan_msgproc_chrescind(struct vmbus_softc *sc, mtx_unlock(&sc->vmbus_prichan_lock); } + if (bootverbose) + vmbus_chan_printf(chan, "chan%u rescinded\n", note->chm_chanid); + /* Detach the target channel. */ taskqueue_enqueue(chan->ch_mgmt_tq, &chan->ch_detach_task); } @@ -1414,8 +1416,9 @@ vmbus_chan_release(struct vmbus_channel *chan) mh = vmbus_msghc_get(sc, sizeof(*req)); if (mh == NULL) { - device_printf(sc->vmbus_dev, "can not get msg hypercall for " - "chfree(chan%u)\n", chan->ch_id); + vmbus_chan_printf(chan, + "can not get msg hypercall for chfree(chan%u)\n", + chan->ch_id); return (ENXIO); } @@ -1427,13 +1430,12 @@ vmbus_chan_release(struct vmbus_channel *chan) vmbus_msghc_put(sc, mh); if (error) { - device_printf(sc->vmbus_dev, "chfree(chan%u) failed: %d", + vmbus_chan_printf(chan, + "chfree(chan%u) msg hypercall exec failed: %d\n", chan->ch_id, error); } else { - if (bootverbose) { - device_printf(sc->vmbus_dev, "chan%u freed\n", - chan->ch_id); - } + if (bootverbose) + vmbus_chan_printf(chan, "chan%u freed\n", chan->ch_id); } return (error); } @@ -1714,6 +1716,26 @@ vmbus_chan_rx_empty(const struct vmbus_channel *chan) return (vmbus_rxbr_empty(&chan->ch_rxbr)); } +static int +vmbus_chan_printf(const struct vmbus_channel *chan, const char *fmt, ...) +{ + va_list ap; + device_t dev; + int retval; + + if (chan->ch_dev == NULL || !device_is_alive(chan->ch_dev)) + dev = chan->ch_vmbus->vmbus_dev; + else + dev = chan->ch_dev; + + retval = device_print_prettyname(dev); + va_start(ap, fmt); + retval += vprintf(fmt, ap); + va_end(ap); + + return (retval); +} + void vmbus_chan_run_task(struct vmbus_channel *chan, struct task *task) { diff --git a/sys/dev/ichiic/ig4_iic.c b/sys/dev/ichiic/ig4_iic.c index 44bc64ead3e9..87eee02ebe33 100644 --- a/sys/dev/ichiic/ig4_iic.c +++ b/sys/dev/ichiic/ig4_iic.c @@ -61,6 +61,8 @@ __FBSDID("$FreeBSD$"); #include <dev/pci/pcivar.h> #include <dev/pci/pcireg.h> #include <dev/smbus/smbconf.h> +#include <dev/iicbus/iicbus.h> +#include <dev/iicbus/iiconf.h> #include <dev/ichiic/ig4_reg.h> #include <dev/ichiic/ig4_var.h> @@ -120,7 +122,7 @@ set_controller(ig4iic_softc_t *sc, uint32_t ctl) reg_write(sc, IG4_REG_INTR_MASK, 0); reg_write(sc, IG4_REG_I2C_EN, ctl); - error = SMB_ETIMEOUT; + error = IIC_ETIMEOUT; for (retry = 100; retry > 0; --retry) { v = reg_read(sc, IG4_REG_ENABLE_STATUS); @@ -148,7 +150,7 @@ wait_status(ig4iic_softc_t *sc, uint32_t status) u_int count_us = 0; u_int limit_us = 25000; /* 25ms */ - error = SMB_ETIMEOUT; + error = IIC_ETIMEOUT; for (;;) { /* @@ -484,6 +486,236 @@ done: } /* + * IICBUS API FUNCTIONS + */ +static int +ig4iic_xfer_start(ig4iic_softc_t *sc, uint16_t slave) +{ + /* XXX 10-bit address support? */ + set_slave_addr(sc, slave >> 1, 0); + return (0); +} + +static int +ig4iic_read(ig4iic_softc_t *sc, uint8_t *buf, uint16_t len, + bool repeated_start, bool stop) +{ + uint32_t cmd; + uint16_t i; + int error; + + if (len == 0) + return (0); + + cmd = IG4_DATA_COMMAND_RD; + cmd |= repeated_start ? IG4_DATA_RESTART : 0; + cmd |= stop && len == 1 ? IG4_DATA_STOP : 0; + + /* Issue request for the first byte (could be last as well). */ + reg_write(sc, IG4_REG_DATA_CMD, cmd); + + for (i = 0; i < len; i++) { + /* + * Maintain a pipeline by queueing the allowance for the next + * read before waiting for the current read. + */ + cmd = IG4_DATA_COMMAND_RD; + if (i < len - 1) { + cmd = IG4_DATA_COMMAND_RD; + cmd |= stop && i == len - 2 ? IG4_DATA_STOP : 0; + reg_write(sc, IG4_REG_DATA_CMD, cmd); + } + error = wait_status(sc, IG4_STATUS_RX_NOTEMPTY); + if (error) + break; + buf[i] = data_read(sc); + } + + (void)reg_read(sc, IG4_REG_TX_ABRT_SOURCE); + return (error); +} + +static int +ig4iic_write(ig4iic_softc_t *sc, uint8_t *buf, uint16_t len, + bool repeated_start, bool stop) +{ + uint32_t cmd; + uint16_t i; + int error; + + if (len == 0) + return (0); + + cmd = repeated_start ? IG4_DATA_RESTART : 0; + for (i = 0; i < len; i++) { + error = wait_status(sc, IG4_STATUS_TX_NOTFULL); + if (error) + break; + cmd |= buf[i]; + cmd |= stop && i == len - 1 ? IG4_DATA_STOP : 0; + reg_write(sc, IG4_REG_DATA_CMD, cmd); + cmd = 0; + } + + (void)reg_read(sc, IG4_REG_TX_ABRT_SOURCE); + return (error); +} + +int +ig4iic_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) +{ + ig4iic_softc_t *sc = device_get_softc(dev); + const char *reason = NULL; + uint32_t i; + int error; + int unit; + bool rpstart; + bool stop; + + /* + * The hardware interface imposes limits on allowed I2C messages. + * It is not possible to explicitly send a start or stop. + * They are automatically sent (or not sent, depending on the + * configuration) when a data byte is transferred. + * For this reason it's impossible to send a message with no data + * at all (like an SMBus quick message). + * The start condition is automatically generated after the stop + * condition, so it's impossible to not have a start after a stop. + * The repeated start condition is automatically sent if a change + * of the transfer direction happens, so it's impossible to have + * a change of direction without a (repeated) start. + * The repeated start can be forced even without the change of + * direction. + * Changing the target slave address requires resetting the hardware + * state, so it's impossible to do that without the stop followed + * by the start. + */ + for (i = 0; i < nmsgs; i++) { +#if 0 + if (i == 0 && (msgs[i].flags & IIC_M_NOSTART) != 0) { + reason = "first message without start"; + break; + } + if (i == nmsgs - 1 && (msgs[i].flags & IIC_M_NOSTOP) != 0) { + reason = "last message without stop"; + break; + } +#endif + if (msgs[i].len == 0) { + reason = "message with no data"; + break; + } + if (i > 0) { + if ((msgs[i].flags & IIC_M_NOSTART) != 0 && + (msgs[i - 1].flags & IIC_M_NOSTOP) == 0) { + reason = "stop not followed by start"; + break; + } + if ((msgs[i - 1].flags & IIC_M_NOSTOP) != 0 && + msgs[i].slave != msgs[i - 1].slave) { + reason = "change of slave without stop"; + break; + } + if ((msgs[i].flags & IIC_M_NOSTART) != 0 && + (msgs[i].flags & IIC_M_RD) != + (msgs[i - 1].flags & IIC_M_RD)) { + reason = "change of direction without repeated" + " start"; + break; + } + } + } + if (reason != NULL) { + if (bootverbose) + device_printf(dev, "%s\n", reason); + return (IIC_ENOTSUPP); + } + + sx_xlock(&sc->call_lock); + mtx_lock(&sc->io_lock); + + /* Debugging - dump registers. */ + if (ig4_dump) { + unit = device_get_unit(dev); + if (ig4_dump & (1 << unit)) { + ig4_dump &= ~(1 << unit); + ig4iic_dump(sc); + } + } + + /* + * Clear any previous abort condition that may have been holding + * the txfifo in reset. + */ + reg_read(sc, IG4_REG_CLR_TX_ABORT); + + /* + * Clean out any previously received data. + */ + if (sc->rpos != sc->rnext && bootverbose) { + device_printf(sc->dev, "discarding %d bytes of spurious data\n", + sc->rnext - sc->rpos); + } + sc->rpos = 0; + sc->rnext = 0; + + rpstart = false; + error = 0; + for (i = 0; i < nmsgs; i++) { + if ((msgs[i].flags & IIC_M_NOSTART) == 0) { + error = ig4iic_xfer_start(sc, msgs[i].slave); + } else { + if (!sc->slave_valid || + (msgs[i].slave >> 1) != sc->last_slave) { + device_printf(dev, "start condition suppressed" + "but slave address is not set up"); + error = EINVAL; + break; + } + rpstart = false; + } + if (error != 0) + break; + + stop = (msgs[i].flags & IIC_M_NOSTOP) == 0; + if (msgs[i].flags & IIC_M_RD) + error = ig4iic_read(sc, msgs[i].buf, msgs[i].len, + rpstart, stop); + else + error = ig4iic_write(sc, msgs[i].buf, msgs[i].len, + rpstart, stop); + if (error != 0) + break; + + rpstart = !stop; + } + + mtx_unlock(&sc->io_lock); + sx_unlock(&sc->call_lock); + return (error); +} + +int +ig4iic_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) +{ + ig4iic_softc_t *sc = device_get_softc(dev); + + sx_xlock(&sc->call_lock); + mtx_lock(&sc->io_lock); + + /* TODO handle speed configuration? */ + if (oldaddr != NULL) + *oldaddr = sc->last_slave << 1; + set_slave_addr(sc, addr >> 1, 0); + if (addr == IIC_UNKNOWN) + sc->slave_valid = false; + + mtx_unlock(&sc->io_lock); + sx_unlock(&sc->call_lock); + return (0); +} + +/* * SMBUS API FUNCTIONS * * Called from ig4iic_pci_attach/detach() @@ -549,9 +781,9 @@ ig4iic_attach(ig4iic_softc_t *sc) IG4_CTL_RESTARTEN | IG4_CTL_SPEED_STD); - sc->smb = device_add_child(sc->dev, "smbus", -1); - if (sc->smb == NULL) { - device_printf(sc->dev, "smbus driver not found\n"); + sc->iicbus = device_add_child(sc->dev, "iicbus", -1); + if (sc->iicbus == NULL) { + device_printf(sc->dev, "iicbus driver not found\n"); error = ENXIO; goto done; } @@ -624,15 +856,15 @@ ig4iic_detach(ig4iic_softc_t *sc) if (error) return (error); } - if (sc->smb) - device_delete_child(sc->dev, sc->smb); + if (sc->iicbus) + device_delete_child(sc->dev, sc->iicbus); if (sc->intr_handle) bus_teardown_intr(sc->dev, sc->intr_res, sc->intr_handle); sx_xlock(&sc->call_lock); mtx_lock(&sc->io_lock); - sc->smb = NULL; + sc->iicbus = NULL; sc->intr_handle = NULL; reg_write(sc, IG4_REG_INTR_MASK, 0); set_controller(sc, 0); @@ -976,4 +1208,4 @@ ig4iic_dump(ig4iic_softc_t *sc) } #undef REGDUMP -DRIVER_MODULE(smbus, ig4iic, smbus_driver, smbus_devclass, NULL, NULL); +DRIVER_MODULE(iicbus, ig4iic, iicbus_driver, iicbus_devclass, NULL, NULL); diff --git a/sys/dev/ichiic/ig4_pci.c b/sys/dev/ichiic/ig4_pci.c index 7038cae6478f..0d796e5980d7 100644 --- a/sys/dev/ichiic/ig4_pci.c +++ b/sys/dev/ichiic/ig4_pci.c @@ -60,6 +60,7 @@ __FBSDID("$FreeBSD$"); #include <dev/pci/pcivar.h> #include <dev/pci/pcireg.h> #include <dev/smbus/smbconf.h> +#include <dev/iicbus/iiconf.h> #include "smbus_if.h" @@ -180,6 +181,10 @@ static device_method_t ig4iic_pci_methods[] = { DEVMETHOD(smbus_bread, ig4iic_smb_bread), DEVMETHOD(smbus_trans, ig4iic_smb_trans), + DEVMETHOD(iicbus_transfer, ig4iic_transfer), + DEVMETHOD(iicbus_reset, ig4iic_reset), + DEVMETHOD(iicbus_callback, iicbus_null_callback), + DEVMETHOD_END }; @@ -191,7 +196,9 @@ static driver_t ig4iic_pci_driver = { static devclass_t ig4iic_pci_devclass; -DRIVER_MODULE(ig4iic, pci, ig4iic_pci_driver, ig4iic_pci_devclass, 0, 0); +DRIVER_MODULE_ORDERED(ig4iic, pci, ig4iic_pci_driver, ig4iic_pci_devclass, 0, 0, + SI_ORDER_ANY); MODULE_DEPEND(ig4iic, pci, 1, 1, 1); MODULE_DEPEND(ig4iic, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); +MODULE_DEPEND(ig4iic, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); MODULE_VERSION(ig4iic, 1); diff --git a/sys/dev/ichiic/ig4_var.h b/sys/dev/ichiic/ig4_var.h index bb1a357e2c32..b35f2f97296f 100644 --- a/sys/dev/ichiic/ig4_var.h +++ b/sys/dev/ichiic/ig4_var.h @@ -42,6 +42,7 @@ #include "device_if.h" #include "pci_if.h" #include "smbus_if.h" +#include "iicbus_if.h" #define IG4_RBUFSIZE 128 #define IG4_RBUFMASK (IG4_RBUFSIZE - 1) @@ -51,7 +52,7 @@ enum ig4_op { IG4_IDLE, IG4_READ, IG4_WRITE }; struct ig4iic_softc { device_t dev; struct intr_config_hook enum_hook; - device_t smb; + device_t iicbus; struct resource *regs_res; int regs_rid; struct resource *intr_res; @@ -115,5 +116,7 @@ extern smbus_pcall_t ig4iic_smb_pcall; extern smbus_bwrite_t ig4iic_smb_bwrite; extern smbus_bread_t ig4iic_smb_bread; extern smbus_trans_t ig4iic_smb_trans; +extern iicbus_transfer_t ig4iic_transfer; +extern iicbus_reset_t ig4iic_reset; #endif diff --git a/sys/dev/iicbus/iicbus.c b/sys/dev/iicbus/iicbus.c index 0e529fd2499b..8cb7383df243 100644 --- a/sys/dev/iicbus/iicbus.c +++ b/sys/dev/iicbus/iicbus.c @@ -208,7 +208,9 @@ iicbus_write_ivar(device_t bus, device_t child, int which, uintptr_t value) default: return (EINVAL); case IICBUS_IVAR_ADDR: - return (EINVAL); + if (devi->addr != 0) + return (EINVAL); + devi->addr = value; case IICBUS_IVAR_NOSTOP: devi->nostop = value; break; diff --git a/sys/dev/ioat/ioat.c b/sys/dev/ioat/ioat.c index ccf9166f8abb..21723b28f471 100644 --- a/sys/dev/ioat/ioat.c +++ b/sys/dev/ioat/ioat.c @@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$"); #include <sys/systm.h> #include <sys/bus.h> #include <sys/conf.h> +#include <sys/fail.h> #include <sys/ioccom.h> #include <sys/kernel.h> #include <sys/lock.h> @@ -676,7 +677,7 @@ ioat_process_events(struct ioat_softc *ioat) } completed = 0; - comp_update = ioat_get_chansts(ioat); + comp_update = *ioat->comp_update; status = comp_update & IOAT_CHANSTS_COMPLETED_DESCRIPTOR_MASK; if (status == ioat->last_seen) { @@ -690,11 +691,11 @@ ioat_process_events(struct ioat_softc *ioat) __func__, ioat->chan_idx, comp_update, ioat->last_seen); desc = ioat_get_ring_entry(ioat, ioat->tail - 1); - while (desc->hw_desc_bus_addr != status && ioat_get_active(ioat) > 0) { + while (desc->hw_desc_bus_addr != status) { desc = ioat_get_ring_entry(ioat, ioat->tail); dmadesc = &desc->bus_dmadesc; - CTR4(KTR_IOAT, "channel=%u completing desc %u ok cb %p(%p)", - ioat->chan_idx, ioat->tail, dmadesc->callback_fn, + CTR5(KTR_IOAT, "channel=%u completing desc idx %u (%p) ok cb %p(%p)", + ioat->chan_idx, ioat->tail, dmadesc, dmadesc->callback_fn, dmadesc->callback_arg); if (dmadesc->callback_fn != NULL) @@ -703,6 +704,8 @@ ioat_process_events(struct ioat_softc *ioat) completed++; ioat->tail++; } + CTR5(KTR_IOAT, "%s channel=%u head=%u tail=%u active=%u", __func__, + ioat->chan_idx, ioat->head, ioat->tail, ioat_get_active(ioat)); if (completed != 0) { ioat->last_seen = desc->hw_desc_bus_addr; @@ -760,8 +763,8 @@ out: while (ioat_get_active(ioat) > 0) { desc = ioat_get_ring_entry(ioat, ioat->tail); dmadesc = &desc->bus_dmadesc; - CTR4(KTR_IOAT, "channel=%u completing desc %u err cb %p(%p)", - ioat->chan_idx, ioat->tail, dmadesc->callback_fn, + CTR5(KTR_IOAT, "channel=%u completing desc idx %u (%p) err cb %p(%p)", + ioat->chan_idx, ioat->tail, dmadesc, dmadesc->callback_fn, dmadesc->callback_arg); if (dmadesc->callback_fn != NULL) @@ -773,6 +776,8 @@ out: ioat->stats.descriptors_processed++; ioat->stats.descriptors_error++; } + CTR5(KTR_IOAT, "%s channel=%u head=%u tail=%u active=%u", __func__, + ioat->chan_idx, ioat->head, ioat->tail, ioat_get_active(ioat)); if (ioat->is_completion_pending) { ioat->is_completion_pending = FALSE; @@ -947,7 +952,12 @@ ioat_release(bus_dmaengine_t dmaengine) struct ioat_softc *ioat; ioat = to_ioat_softc(dmaengine); - CTR2(KTR_IOAT, "%s channel=%u", __func__, ioat->chan_idx); + CTR4(KTR_IOAT, "%s channel=%u dispatch1 hw_head=%u head=%u", __func__, + ioat->chan_idx, ioat->hw_head & UINT16_MAX, ioat->head); + KFAIL_POINT_CODE(DEBUG_FP, ioat_release, /* do nothing */); + CTR4(KTR_IOAT, "%s channel=%u dispatch2 hw_head=%u head=%u", __func__, + ioat->chan_idx, ioat->hw_head & UINT16_MAX, ioat->head); + ioat_write_2(ioat, IOAT_DMACOUNT_OFFSET, (uint16_t)ioat->hw_head); if (!ioat->is_completion_pending) { @@ -1040,7 +1050,6 @@ ioat_copy(bus_dmaengine_t dmaengine, bus_addr_t dst, struct ioat_softc *ioat; ioat = to_ioat_softc(dmaengine); - CTR2(KTR_IOAT, "%s channel=%u", __func__, ioat->chan_idx); if (((src | dst) & (0xffffull << 48)) != 0) { ioat_log_message(0, "%s: High 16 bits of src/dst invalid\n", @@ -1058,6 +1067,8 @@ ioat_copy(bus_dmaengine_t dmaengine, bus_addr_t dst, dump_descriptor(hw_desc); ioat_submit_single(ioat); + CTR6(KTR_IOAT, "%s channel=%u desc=%p dest=%lx src=%lx len=%lx", + __func__, ioat->chan_idx, &desc->bus_dmadesc, dst, src, len); return (&desc->bus_dmadesc); } @@ -1414,11 +1425,16 @@ ioat_reserve_space(struct ioat_softc *ioat, uint32_t num_descs, int mflags) if (ioat_get_ring_space(ioat) >= num_descs) goto out; + CTR3(KTR_IOAT, "%s channel=%u starved (%u)", __func__, + ioat->chan_idx, num_descs); + if (!dug && !ioat->is_submitter_processing && (1 << ioat->ring_size_order) > num_descs) { ioat->is_submitter_processing = TRUE; mtx_unlock(&ioat->submit_lock); + CTR2(KTR_IOAT, "%s channel=%u attempting to process events", + __func__, ioat->chan_idx); ioat_process_events(ioat); mtx_lock(&ioat->submit_lock); @@ -1433,6 +1449,8 @@ ioat_reserve_space(struct ioat_softc *ioat, uint32_t num_descs, int mflags) order = ioat->ring_size_order; if (ioat->is_resize_pending || order == IOAT_MAX_ORDER) { if ((mflags & M_WAITOK) != 0) { + CTR2(KTR_IOAT, "%s channel=%u blocking on completions", + __func__, ioat->chan_idx); msleep(&ioat->tail, &ioat->submit_lock, 0, "ioat_rsz", 0); continue; @@ -1791,9 +1809,14 @@ static void ioat_submit_single(struct ioat_softc *ioat) { + mtx_assert(&ioat->submit_lock, MA_OWNED); + ioat_get(ioat, IOAT_ACTIVE_DESCR_REF); atomic_add_rel_int(&ioat->head, 1); atomic_add_rel_int(&ioat->hw_head, 1); + CTR5(KTR_IOAT, "%s channel=%u head=%u hw_head=%u tail=%u", __func__, + ioat->chan_idx, ioat->head, ioat->hw_head & UINT16_MAX, + ioat->tail); ioat->stats.descriptors_submitted++; } diff --git a/sys/dev/ioat/ioat_internal.h b/sys/dev/ioat/ioat_internal.h index a8507609c11d..419b1fdba270 100644 --- a/sys/dev/ioat/ioat_internal.h +++ b/sys/dev/ioat/ioat_internal.h @@ -523,6 +523,15 @@ struct ioat_softc { void ioat_test_attach(void); void ioat_test_detach(void); +/* + * XXX DO NOT USE this routine for obtaining the current completed descriptor. + * + * The double_4 read on ioat<3.3 appears to result in torn reads. And v3.2 + * hardware is still commonplace (Broadwell Xeon has it). Instead, use the + * device-pushed *comp_update. + * + * It is safe to use ioat_get_chansts() for the low status bits. + */ static inline uint64_t ioat_get_chansts(struct ioat_softc *ioat) { diff --git a/sys/dev/isl/isl.c b/sys/dev/isl/isl.c index 5531ee948125..ef77985df19c 100644 --- a/sys/dev/isl/isl.c +++ b/sys/dev/isl/isl.c @@ -52,14 +52,12 @@ __FBSDID("$FreeBSD$"); #include <sys/sysctl.h> #include <sys/systm.h> #include <sys/systm.h> -#include <sys/uio.h> -#include <sys/vnode.h> -#include <dev/smbus/smbconf.h> -#include <dev/smbus/smbus.h> +#include <dev/iicbus/iiconf.h> +#include <dev/iicbus/iicbus.h> #include <dev/isl/isl.h> -#include "smbus_if.h" +#include "iicbus_if.h" #include "bus_if.h" #include "device_if.h" @@ -71,46 +69,58 @@ __FBSDID("$FreeBSD$"); struct isl_softc { device_t dev; - int addr; - struct sx isl_sx; }; /* Returns < 0 on problem. */ -static int isl_read_sensor(device_t dev, int addr, uint8_t cmd_mask); +static int isl_read_sensor(device_t dev, uint8_t cmd_mask); + +static int +isl_read_byte(device_t dev, uint8_t reg, uint8_t *val) +{ + uint16_t addr = iicbus_get_addr(dev); + struct iic_msg msgs[] = { + { addr, IIC_M_WR | IIC_M_NOSTOP, 1, ® }, + { addr, IIC_M_RD, 1, val }, + }; + + return (iicbus_transfer(dev, msgs, nitems(msgs))); +} + +static int +isl_write_byte(device_t dev, uint8_t reg, uint8_t val) +{ + uint16_t addr = iicbus_get_addr(dev); + uint8_t bytes[] = { reg, val }; + struct iic_msg msgs[] = { + { addr, IIC_M_WR, nitems(bytes), bytes }, + }; + + return (iicbus_transfer(dev, msgs, nitems(msgs))); +} /* * Initialize the device */ static int -init_device(device_t dev, int addr, int probe) +init_device(device_t dev, int probe) { - static char bl_init[] = { 0x00 }; - - device_t bus; int error; - bus = device_get_parent(dev); /* smbus */ - /* * init procedure: send 0x00 to test ref and cmd reg 1 */ - error = smbus_trans(bus, addr, REG_TEST, - SMB_TRANS_NOCNT | SMB_TRANS_7BIT, - bl_init, sizeof(bl_init), NULL, 0, NULL); + error = isl_write_byte(dev, REG_TEST, 0); if (error) goto done; - - error = smbus_trans(bus, addr, REG_CMD1, - SMB_TRANS_NOCNT | SMB_TRANS_7BIT, - bl_init, sizeof(bl_init), NULL, 0, NULL); + error = isl_write_byte(dev, REG_CMD1, 0); if (error) goto done; pause("islinit", hz/100); done: - if (error) + if (error && !probe) device_printf(dev, "Unable to initialize\n"); return (error); } @@ -138,27 +148,33 @@ static driver_t isl_driver = { sizeof(struct isl_softc), }; +#if 0 +static void +isl_identify(driver_t *driver, device_t parent) +{ + + if (device_find_child(parent, "asl", -1)) { + if (bootverbose) + printf("asl: device(s) already created\n"); + return; + } + + /* Check if we can communicate to our slave. */ + if (init_device(dev, 0x88, 1) == 0) + BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE, "isl", -1); +} +#endif + static int isl_probe(device_t dev) { - int addr; - int error; - - addr = smbus_get_addr(dev); + uint32_t addr = iicbus_get_addr(dev); - /* - * 0x44 - isl ambient light sensor on the acer c720. - * (other devices might use other ids). - */ - if (addr != 0x44) + if (addr != 0x88) return (ENXIO); - - error = init_device(dev, addr, 1); - if (error) + if (init_device(dev, 1) != 0) return (ENXIO); - device_set_desc(dev, "ISL Digital Ambient Light Sensor"); - return (BUS_PROBE_VENDOR); } @@ -168,36 +184,28 @@ isl_attach(device_t dev) struct isl_softc *sc; struct sysctl_ctx_list *sysctl_ctx; struct sysctl_oid *sysctl_tree; - int addr; int use_als; int use_ir; int use_prox; sc = device_get_softc(dev); + sc->dev = dev; - if (!sc) - return (ENOMEM); - - addr = smbus_get_addr(dev); - - if (init_device(dev, addr, 0)) + if (init_device(dev, 0) != 0) return (ENXIO); sx_init(&sc->isl_sx, "ISL read lock"); - sc->dev = dev; - sc->addr = addr; - sysctl_ctx = device_get_sysctl_ctx(dev); sysctl_tree = device_get_sysctl_tree(dev); - use_als = isl_read_sensor(dev, addr, CMD1_MASK_ALS_ONCE) >= 0; - use_ir = isl_read_sensor(dev, addr, CMD1_MASK_IR_ONCE) >= 0; - use_prox = isl_read_sensor(dev, addr, CMD1_MASK_PROX_ONCE) >= 0; + use_als = isl_read_sensor(dev, CMD1_MASK_ALS_ONCE) >= 0; + use_ir = isl_read_sensor(dev, CMD1_MASK_IR_ONCE) >= 0; + use_prox = isl_read_sensor(dev, CMD1_MASK_PROX_ONCE) >= 0; if (use_als) { SYSCTL_ADD_PROC(sysctl_ctx, - SYSCTL_CHILDREN(sysctl_tree), OID_AUTO, + SYSCTL_CHILDREN(sysctl_tree), OID_AUTO, "als", CTLTYPE_INT | CTLFLAG_RD, sc, ISL_METHOD_ALS, isl_sysctl, "I", "Current ALS sensor read-out"); @@ -252,7 +260,6 @@ isl_sysctl(SYSCTL_HANDLER_ARGS) static int ranges[] = { 1000, 4000, 16000, 64000}; struct isl_softc *sc; - device_t bus; uint8_t rbyte; int arg; int resolution; @@ -262,10 +269,7 @@ isl_sysctl(SYSCTL_HANDLER_ARGS) arg = -1; sx_xlock(&sc->isl_sx); - bus = device_get_parent(sc->dev); /* smbus */ - if (smbus_trans(bus, sc->addr, REG_CMD2, - SMB_TRANS_NOCNT | SMB_TRANS_7BIT, - NULL, 0, &rbyte, sizeof(rbyte), NULL)) { + if (isl_read_byte(sc->dev, REG_CMD2, &rbyte) != 0) { sx_xunlock(&sc->isl_sx); return (-1); } @@ -275,16 +279,14 @@ isl_sysctl(SYSCTL_HANDLER_ARGS) switch (oidp->oid_arg2) { case ISL_METHOD_ALS: - arg = (isl_read_sensor(sc->dev, sc->addr, + arg = (isl_read_sensor(sc->dev, CMD1_MASK_ALS_ONCE) * range) >> resolution; break; case ISL_METHOD_IR: - arg = isl_read_sensor(sc->dev, sc->addr, - CMD1_MASK_IR_ONCE); + arg = isl_read_sensor(sc->dev, CMD1_MASK_IR_ONCE); break; case ISL_METHOD_PROX: - arg = isl_read_sensor(sc->dev, sc->addr, - CMD1_MASK_PROX_ONCE); + arg = isl_read_sensor(sc->dev, CMD1_MASK_PROX_ONCE); break; case ISL_METHOD_RESOLUTION: arg = (1 << resolution); @@ -300,18 +302,13 @@ isl_sysctl(SYSCTL_HANDLER_ARGS) } static int -isl_read_sensor(device_t dev, int addr, uint8_t cmd_mask) +isl_read_sensor(device_t dev, uint8_t cmd_mask) { - device_t bus; uint8_t rbyte; uint8_t cmd; int ret; - bus = device_get_parent(dev); /* smbus */ - - if (smbus_trans(bus, addr, REG_CMD1, - SMB_TRANS_NOCNT | SMB_TRANS_7BIT, - NULL, 0, &rbyte, sizeof(rbyte), NULL)) { + if (isl_read_byte(dev, REG_CMD1, &rbyte) != 0) { device_printf(dev, "Couldn't read first byte before issuing command %d\n", cmd_mask); @@ -319,27 +316,21 @@ isl_read_sensor(device_t dev, int addr, uint8_t cmd_mask) } cmd = (rbyte & 0x1f) | cmd_mask; - if (smbus_trans(bus, addr, REG_CMD1, - SMB_TRANS_NOCNT | SMB_TRANS_7BIT, - &cmd, sizeof(cmd), NULL, 0, NULL)) { + if (isl_write_byte(dev, REG_CMD1, cmd) != 0) { device_printf(dev, "Couldn't write command %d\n", cmd_mask); return (-1); } pause("islconv", hz/10); - if (smbus_trans(bus, addr, REG_DATA1, - SMB_TRANS_NOCNT | SMB_TRANS_7BIT, - NULL, 0, &rbyte, sizeof(rbyte), NULL)) { + if (isl_read_byte(dev, REG_DATA1, &rbyte) != 0) { device_printf(dev, "Couldn't read first byte after command %d\n", cmd_mask); return (-1); } ret = rbyte; - if (smbus_trans(bus, addr, REG_DATA2, - SMB_TRANS_NOCNT | SMB_TRANS_7BIT, - NULL, 0, &rbyte, sizeof(rbyte), NULL)) { + if (isl_read_byte(dev, REG_DATA2, &rbyte) != 0) { device_printf(dev, "Couldn't read second byte after command %d\n", cmd_mask); return (-1); } @@ -348,6 +339,6 @@ isl_read_sensor(device_t dev, int addr, uint8_t cmd_mask) return (ret); } -DRIVER_MODULE(isl, smbus, isl_driver, isl_devclass, NULL, NULL); -MODULE_DEPEND(isl, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); +DRIVER_MODULE(isl, iicbus, isl_driver, isl_devclass, NULL, NULL); +MODULE_DEPEND(isl, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER); MODULE_VERSION(isl, 1); diff --git a/sys/dev/jedec_ts/jedec_ts.c b/sys/dev/jedec_ts/jedec_ts.c new file mode 100644 index 000000000000..b65ef789173f --- /dev/null +++ b/sys/dev/jedec_ts/jedec_ts.c @@ -0,0 +1,179 @@ +/*- + * Copyright (c) 2016 Andriy Gapon <avg@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, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/endian.h> +#include <sys/module.h> +#include <sys/rman.h> +#include <sys/sysctl.h> +#include <sys/systm.h> + +#include <dev/smbus/smbconf.h> +#include <dev/smbus/smbus.h> + +#include "smbus_if.h" + + +/* + * SMBus specification defines little-endian byte order, + * but it seems that the JEDEC devices expect it to + * be big-endian. + */ +static int +ts_readw_be(device_t dev, uint8_t reg, uint16_t *val) +{ + device_t bus = device_get_parent(dev); + uint8_t addr = smbus_get_addr(dev); + int err; + + err = smbus_readw(bus, addr, reg, val); + if (err != 0) + return (err); + *val = be16toh(*val); + return (0); +} + +static int +ts_temp_sysctl(SYSCTL_HANDLER_ARGS) +{ + device_t dev = arg1; + int err; + int temp; + uint16_t val; + + err = ts_readw_be(dev, 5, &val); + if (err != 0) + return (EIO); + + /* + * Convert the reading to temperature in 0.0001 Kelvins. + * Three most significant bits are flags, the next + * most significant bit is a sign bit. + * Each step is 0.0625 degrees. + */ + temp = val & 0xfff; + if ((val & 0x1000) != 0) + temp = -temp; + temp = temp * 625 + 2731500; + err = sysctl_handle_int(oidp, &temp, 0, req); + return (err); +} + +static int +ts_probe(device_t dev) +{ + device_set_desc(dev, "DIMM memory sensor"); + return (BUS_PROBE_DEFAULT); +} + +static int +ts_attach(device_t dev) +{ + struct sysctl_ctx_list *ctx; + struct sysctl_oid_list *tree; + int err; + uint16_t vendorid; + uint16_t devid; + uint8_t addr; + + addr = smbus_get_addr(dev); + if ((addr & 0x30) != 0x30) { + /* Up to 8 slave devices starting at 0x30. */ + return (ENXIO); + } + + err = ts_readw_be(dev, 6, &vendorid); + if (err != 0) { + device_printf(dev, "failed to read Manufacturer ID\n"); + return (ENXIO); + } + err = ts_readw_be(dev, 6, &devid); + if (err != 0) { + device_printf(dev, "failed to read Device ID\n"); + return (ENXIO); + } + if ((devid & 0xff00) == 0x2200) { + /* + * Defined by JEDEC Standard No. 21-C, Release 26, + * Page 4.1.6 – 24 + */ + } else if (vendorid == 0x104a) { + /* + * STMicroelectronics datasheets say that + * device ID and revision can vary. + * E.g. STT424E02, Doc ID 13448 Rev 8, + * section 4.6, page 26. + */ + } else { + if (bootverbose) { + device_printf(dev, "Unknown Manufacturer and Device IDs" + ", 0x%x and 0x%x\n", vendorid, devid); + } + return (ENXIO); + } + + ctx = device_get_sysctl_ctx(dev); + tree = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); + + SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "temp", + CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, dev, 0, + ts_temp_sysctl, "IK4", "Current temperature"); + + return (0); +} + +static int +ts_detach(device_t dev) +{ + return (0); +} + + +static device_method_t jedec_ts_methods[] = { + /* Methods from the device interface */ + DEVMETHOD(device_probe, ts_probe), + DEVMETHOD(device_attach, ts_attach), + DEVMETHOD(device_detach, ts_detach), + + /* Terminate method list */ + { 0, 0 } +}; + +static driver_t jedec_ts_driver = { + "jedec_ts", + jedec_ts_methods, + 0 /* no softc */ +}; + +static devclass_t jedec_ts_devclass; + +DRIVER_MODULE(jedec_ts, smbus, jedec_ts_driver, jedec_ts_devclass, 0, 0); +MODULE_DEPEND(jedec_ts, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER); +MODULE_VERSION(jedec_ts, 1); diff --git a/sys/dev/mii/miidevs b/sys/dev/mii/miidevs index cdb02894192e..64818be60f19 100644 --- a/sys/dev/mii/miidevs +++ b/sys/dev/mii/miidevs @@ -316,7 +316,7 @@ model yyREALTEK RTL8201L 0x0020 RTL8201L 10/100 media interface model xxREALTEK RTL8169S 0x0011 RTL8169S/8110S/8211 1000BASE-T media interface model REALTEK RTL8305SC 0x0005 RTL8305SC 10/100 802.1q switch model REALTEK RTL8201E 0x0008 RTL8201E 10/100 media interface -model REALTEK RTL8251 0x0000 RTL8251 1000BASE-T media interface +model REALTEK RTL8251 0x0000 RTL8251/8153 1000BASE-T media interface model REALTEK RTL8169S 0x0011 RTL8169S/8110S/8211 1000BASE-T media interface /* Seeq Seeq PHYs */ diff --git a/sys/dev/mii/rgephy.c b/sys/dev/mii/rgephy.c index bb60fa92fe17..25cea3abdbaf 100644 --- a/sys/dev/mii/rgephy.c +++ b/sys/dev/mii/rgephy.c @@ -121,6 +121,8 @@ rgephy_attach(device_t dev) flags = 0; if (mii_dev_mac_match(dev, "re")) flags |= MIIF_PHYPRIV0; + else if (mii_dev_mac_match(dev, "ure")) + flags |= MIIF_PHYPRIV1; mii_phy_dev_attach(dev, flags, &rgephy_funcs, 0); /* RTL8169S do not report auto-sense; add manually. */ @@ -293,7 +295,10 @@ rgephy_linkup(struct mii_softc *sc) linkup++; } } else { - reg = PHY_READ(sc, RL_GMEDIASTAT); + if (sc->mii_flags & MIIF_PHYPRIV1) + reg = PHY_READ(sc, URE_GMEDIASTAT); + else + reg = PHY_READ(sc, RL_GMEDIASTAT); if (reg & RL_GMEDIASTAT_LINK) linkup++; } @@ -378,7 +383,10 @@ rgephy_status(struct mii_softc *sc) mii->mii_media_active |= IFM_HDX; } } else { - bmsr = PHY_READ(sc, RL_GMEDIASTAT); + if (sc->mii_flags & MIIF_PHYPRIV1) + bmsr = PHY_READ(sc, URE_GMEDIASTAT); + else + bmsr = PHY_READ(sc, RL_GMEDIASTAT); if (bmsr & RL_GMEDIASTAT_1000MBPS) mii->mii_media_active |= IFM_1000_T; else if (bmsr & RL_GMEDIASTAT_100MBPS) diff --git a/sys/dev/mii/rgephyreg.h b/sys/dev/mii/rgephyreg.h index 7c24a1f7cbb5..35917daa69c9 100644 --- a/sys/dev/mii/rgephyreg.h +++ b/sys/dev/mii/rgephyreg.h @@ -199,4 +199,7 @@ #define EEELPAR_1000T 0x0004 /* link partner 1000baseT EEE capable */ #define EEELPAR_100TX 0x0002 /* link partner 100baseTX EEE capable */ +/* RTL8153 */ +#define URE_GMEDIASTAT 0xe908 /* media status register */ + #endif /* _DEV_RGEPHY_MIIREG_H_ */ diff --git a/sys/dev/mlx4/mlx4_en/mlx4_en_tx.c b/sys/dev/mlx4/mlx4_en/mlx4_en_tx.c index d415ab65f790..9f83500314ac 100644 --- a/sys/dev/mlx4/mlx4_en/mlx4_en_tx.c +++ b/sys/dev/mlx4/mlx4_en/mlx4_en_tx.c @@ -707,20 +707,19 @@ static int mlx4_en_xmit(struct mlx4_en_priv *priv, int tx_ind, struct mbuf **mbp /* check if TX ring is full */ if (unlikely(mlx4_en_tx_ring_is_full(ring))) { - /* every full native Tx ring stops queue */ - if (ring->blocked == 0) - atomic_add_int(&priv->blocked, 1); - /* Set HW-queue-is-full flag */ - atomic_set_int(&ifp->if_drv_flags, IFF_DRV_OACTIVE); - priv->port_stats.queue_stopped++; - ring->blocked = 1; + /* every full native Tx ring stops queue */ + if (ring->blocked == 0) + atomic_add_int(&priv->blocked, 1); + /* Set HW-queue-is-full flag */ + atomic_set_int(&ifp->if_drv_flags, IFF_DRV_OACTIVE); priv->port_stats.queue_stopped++; + ring->blocked = 1; ring->queue_stopped++; /* Use interrupts to find out when queue opened */ mlx4_en_arm_cq(priv, priv->tx_cq[tx_ind]); return (ENOBUFS); - } + } /* sanity check we are not wrapping around */ KASSERT(((~ring->prod) & ring->size_mask) >= diff --git a/sys/dev/netmap/if_em_netmap.h b/sys/dev/netmap/if_em_netmap.h index 28f2dd4bbc64..1fe7563348c6 100644 --- a/sys/dev/netmap/if_em_netmap.h +++ b/sys/dev/netmap/if_em_netmap.h @@ -277,9 +277,9 @@ em_netmap_rxsync(struct netmap_kring *kring, int flags) if (addr == NETMAP_BUF_BASE(na)) /* bad buf */ goto ring_reset; + curr->read.buffer_addr = htole64(paddr); if (slot->flags & NS_BUF_CHANGED) { /* buffer has changed, reload map */ - curr->read.buffer_addr = htole64(paddr); netmap_reload_map(na, rxr->rxtag, rxbuf->map, addr); slot->flags &= ~NS_BUF_CHANGED; } diff --git a/sys/dev/netmap/if_ptnet.c b/sys/dev/netmap/if_ptnet.c index 90a90e984a5a..4c7072774df4 100644 --- a/sys/dev/netmap/if_ptnet.c +++ b/sys/dev/netmap/if_ptnet.c @@ -291,7 +291,7 @@ static inline void ptnet_kick(struct ptnet_queue *pq) static int ptnet_attach(device_t dev) { - uint32_t ptfeatures = PTNETMAP_F_BASE; + uint32_t ptfeatures = 0; unsigned int num_rx_rings, num_tx_rings; struct netmap_adapter na_arg; unsigned int nifp_offset; @@ -315,19 +315,12 @@ ptnet_attach(device_t dev) return (ENXIO); } - /* Check if we are supported by the hypervisor. If not, - * bail out immediately. */ + /* Negotiate features with the hypervisor. */ if (ptnet_vnet_hdr) { ptfeatures |= PTNETMAP_F_VNET_HDR; } bus_write_4(sc->iomem, PTNET_IO_PTFEAT, ptfeatures); /* wanted */ ptfeatures = bus_read_4(sc->iomem, PTNET_IO_PTFEAT); /* acked */ - if (!(ptfeatures & PTNETMAP_F_BASE)) { - device_printf(dev, "Hypervisor does not support netmap " - "passthorugh\n"); - err = ENXIO; - goto err_path; - } sc->ptfeatures = ptfeatures; /* Allocate CSB and carry out CSB allocation protocol (CSBBAH first, @@ -474,7 +467,8 @@ ptnet_attach(device_t dev) na_arg.nm_txsync = ptnet_nm_txsync; na_arg.nm_rxsync = ptnet_nm_rxsync; - netmap_pt_guest_attach(&na_arg, sc->csb, nifp_offset, ptnet_nm_ptctl); + netmap_pt_guest_attach(&na_arg, sc->csb, nifp_offset, + bus_read_4(sc->iomem, PTNET_IO_HOSTMEMID)); /* Now a netmap adapter for this ifp has been allocated, and it * can be accessed through NA(ifp). We also have to initialize the CSB @@ -1082,13 +1076,12 @@ static uint32_t ptnet_nm_ptctl(if_t ifp, uint32_t cmd) { struct ptnet_softc *sc = if_getsoftc(ifp); - int ret; - + /* + * Write a command and read back error status, + * with zero meaning success. + */ bus_write_4(sc->iomem, PTNET_IO_PTCTL, cmd); - ret = bus_read_4(sc->iomem, PTNET_IO_PTSTS); - device_printf(sc->dev, "PTCTL %u, ret %u\n", cmd, ret); - - return ret; + return bus_read_4(sc->iomem, PTNET_IO_PTCTL); } static int @@ -1196,7 +1189,7 @@ ptnet_nm_register(struct netmap_adapter *na, int onoff) /* Make sure the host adapter passed through is ready * for txsync/rxsync. */ - ret = ptnet_nm_ptctl(ifp, PTNETMAP_PTCTL_REGIF); + ret = ptnet_nm_ptctl(ifp, PTNETMAP_PTCTL_CREATE); if (ret) { return ret; } @@ -1246,7 +1239,7 @@ ptnet_nm_register(struct netmap_adapter *na, int onoff) } if (sc->ptna->backend_regifs == 0) { - ret = ptnet_nm_ptctl(ifp, PTNETMAP_PTCTL_UNREGIF); + ret = ptnet_nm_ptctl(ifp, PTNETMAP_PTCTL_DELETE); } } diff --git a/sys/dev/netmap/netmap.c b/sys/dev/netmap/netmap.c index 46aca2eab5e2..15e44815accc 100644 --- a/sys/dev/netmap/netmap.c +++ b/sys/dev/netmap/netmap.c @@ -2186,7 +2186,11 @@ netmap_ioctl(struct netmap_priv_d *priv, u_long cmd, caddr_t data, struct thread break; case NIOCREGIF: - /* possibly attach/detach NIC and VALE switch */ + /* + * If nmr->nr_cmd is not zero, this NIOCREGIF is not really + * a regif operation, but a different one, specified by the + * value of nmr->nr_cmd. + */ i = nmr->nr_cmd; if (i == NETMAP_BDG_ATTACH || i == NETMAP_BDG_DETACH || i == NETMAP_BDG_VNET_HDR @@ -2194,12 +2198,15 @@ netmap_ioctl(struct netmap_priv_d *priv, u_long cmd, caddr_t data, struct thread || i == NETMAP_BDG_DELIF || i == NETMAP_BDG_POLLING_ON || i == NETMAP_BDG_POLLING_OFF) { + /* possibly attach/detach NIC and VALE switch */ error = netmap_bdg_ctl(nmr, NULL); break; } else if (i == NETMAP_PT_HOST_CREATE || i == NETMAP_PT_HOST_DELETE) { + /* forward the command to the ptnetmap subsystem */ error = ptnetmap_ctl(nmr, priv->np_na); break; } else if (i == NETMAP_VNET_HDR_GET) { + /* get vnet-header length for this netmap port */ struct ifnet *ifp; NMG_LOCK(); @@ -2210,6 +2217,10 @@ netmap_ioctl(struct netmap_priv_d *priv, u_long cmd, caddr_t data, struct thread netmap_unget_na(na, ifp); NMG_UNLOCK(); break; + } else if (i == NETMAP_POOLS_INFO_GET) { + /* get information from the memory allocator */ + error = netmap_mem_pools_info_get(nmr, priv->np_na); + break; } else if (i != 0) { D("nr_cmd must be 0 not %d", i); error = EINVAL; @@ -2873,17 +2884,15 @@ netmap_attach(struct netmap_adapter *arg) #ifdef WITH_PTNETMAP_GUEST int -netmap_pt_guest_attach(struct netmap_adapter *arg, - void *csb, - unsigned int nifp_offset, - nm_pt_guest_ptctl_t ptctl) +netmap_pt_guest_attach(struct netmap_adapter *arg, void *csb, + unsigned int nifp_offset, unsigned int memid) { struct netmap_pt_guest_adapter *ptna; struct ifnet *ifp = arg ? arg->ifp : NULL; int error; /* get allocator */ - arg->nm_mem = netmap_mem_pt_guest_new(ifp, nifp_offset, ptctl); + arg->nm_mem = netmap_mem_pt_guest_new(ifp, nifp_offset, memid); if (arg->nm_mem == NULL) return ENOMEM; arg->na_flags |= NAF_MEM_OWNER; diff --git a/sys/dev/netmap/netmap_freebsd.c b/sys/dev/netmap/netmap_freebsd.c index d83f21e255ec..2aecb53b47e4 100644 --- a/sys/dev/netmap/netmap_freebsd.c +++ b/sys/dev/netmap/netmap_freebsd.c @@ -626,38 +626,26 @@ DRIVER_MODULE_ORDERED(ptn_memdev, pci, ptn_memdev_driver, ptnetmap_devclass, NULL, NULL, SI_ORDER_MIDDLE + 1); /* - * I/O port read/write wrappers. - * Some are not used, so we keep them commented out until needed - */ -#define ptn_ioread16(ptn_dev, reg) bus_read_2((ptn_dev)->pci_io, (reg)) -#define ptn_ioread32(ptn_dev, reg) bus_read_4((ptn_dev)->pci_io, (reg)) -#if 0 -#define ptn_ioread8(ptn_dev, reg) bus_read_1((ptn_dev)->pci_io, (reg)) -#define ptn_iowrite8(ptn_dev, reg, val) bus_write_1((ptn_dev)->pci_io, (reg), (val)) -#define ptn_iowrite16(ptn_dev, reg, val) bus_write_2((ptn_dev)->pci_io, (reg), (val)) -#define ptn_iowrite32(ptn_dev, reg, val) bus_write_4((ptn_dev)->pci_io, (reg), (val)) -#endif /* unused */ - -/* * Map host netmap memory through PCI-BAR in the guest OS, * returning physical (nm_paddr) and virtual (nm_addr) addresses * of the netmap memory mapped in the guest. */ int nm_os_pt_memdev_iomap(struct ptnetmap_memdev *ptn_dev, vm_paddr_t *nm_paddr, - void **nm_addr) + void **nm_addr, uint64_t *mem_size) { - uint32_t mem_size; int rid; D("ptn_memdev_driver iomap"); rid = PCIR_BAR(PTNETMAP_MEM_PCI_BAR); - mem_size = ptn_ioread32(ptn_dev, PTNETMAP_IO_PCI_MEMSIZE); + *mem_size = bus_read_4(ptn_dev->pci_io, PTNET_MDEV_IO_MEMSIZE_HI); + *mem_size = bus_read_4(ptn_dev->pci_io, PTNET_MDEV_IO_MEMSIZE_LO) | + (*mem_size << 32); /* map memory allocator */ ptn_dev->pci_mem = bus_alloc_resource(ptn_dev->dev, SYS_RES_MEMORY, - &rid, 0, ~0, mem_size, RF_ACTIVE); + &rid, 0, ~0, *mem_size, RF_ACTIVE); if (ptn_dev->pci_mem == NULL) { *nm_paddr = 0; *nm_addr = 0; @@ -667,14 +655,20 @@ nm_os_pt_memdev_iomap(struct ptnetmap_memdev *ptn_dev, vm_paddr_t *nm_paddr, *nm_paddr = rman_get_start(ptn_dev->pci_mem); *nm_addr = rman_get_virtual(ptn_dev->pci_mem); - D("=== BAR %d start %lx len %lx mem_size %x ===", + D("=== BAR %d start %lx len %lx mem_size %lx ===", PTNETMAP_MEM_PCI_BAR, (unsigned long)(*nm_paddr), (unsigned long)rman_get_size(ptn_dev->pci_mem), - mem_size); + (unsigned long)*mem_size); return (0); } +uint32_t +nm_os_pt_memdev_ioread(struct ptnetmap_memdev *ptn_dev, unsigned int reg) +{ + return bus_read_4(ptn_dev->pci_io, reg); +} + /* Unmap host netmap memory. */ void nm_os_pt_memdev_iounmap(struct ptnetmap_memdev *ptn_dev) @@ -730,7 +724,7 @@ ptn_memdev_attach(device_t dev) return (ENXIO); } - mem_id = ptn_ioread16(ptn_dev, PTNETMAP_IO_PCI_HOSTID); + mem_id = bus_read_4(ptn_dev->pci_io, PTNET_MDEV_IO_MEMID); /* create guest allocator */ ptn_dev->nm_mem = netmap_mem_pt_guest_attach(ptn_dev, mem_id); @@ -740,7 +734,7 @@ ptn_memdev_attach(device_t dev) } netmap_mem_get(ptn_dev->nm_mem); - D("ptn_memdev_driver probe OK - host_id: %d", mem_id); + D("ptn_memdev_driver probe OK - host_mem_id: %d", mem_id); return (0); } @@ -993,12 +987,7 @@ nm_os_ncpus(void) struct nm_kthread_ctx { struct thread *user_td; /* thread user-space (kthread creator) to send ioctl */ - /* notification to guest (interrupt) */ - int irq_fd; /* ioctl fd */ - struct nm_kth_ioctl irq_ioctl; /* ioctl arguments */ - - /* notification from guest */ - void *ioevent_file; /* tsleep() argument */ + struct ptnetmap_cfgentry_bhyve cfg; /* worker function and parameter */ nm_kthread_worker_fn_t worker_fn; @@ -1034,8 +1023,8 @@ nm_os_kthread_wakeup_worker(struct nm_kthread *nmk) */ mtx_lock(&nmk->worker_lock); nmk->scheduled++; - if (nmk->worker_ctx.ioevent_file) { - wakeup(nmk->worker_ctx.ioevent_file); + if (nmk->worker_ctx.cfg.wchan) { + wakeup((void *)nmk->worker_ctx.cfg.wchan); } mtx_unlock(&nmk->worker_lock); } @@ -1046,11 +1035,13 @@ nm_os_kthread_send_irq(struct nm_kthread *nmk) struct nm_kthread_ctx *ctx = &nmk->worker_ctx; int err; - if (ctx->user_td && ctx->irq_fd > 0) { - err = kern_ioctl(ctx->user_td, ctx->irq_fd, ctx->irq_ioctl.com, (caddr_t)&ctx->irq_ioctl.data.msix); + if (ctx->user_td && ctx->cfg.ioctl_fd > 0) { + err = kern_ioctl(ctx->user_td, ctx->cfg.ioctl_fd, ctx->cfg.ioctl_cmd, + (caddr_t)&ctx->cfg.ioctl_data); if (err) { - D("kern_ioctl error: %d ioctl parameters: fd %d com %ju data %p", - err, ctx->irq_fd, (uintmax_t)ctx->irq_ioctl.com, &ctx->irq_ioctl.data); + D("kern_ioctl error: %d ioctl parameters: fd %d com %lu data %p", + err, ctx->cfg.ioctl_fd, (unsigned long)ctx->cfg.ioctl_cmd, + &ctx->cfg.ioctl_data); } } } @@ -1082,10 +1073,10 @@ nm_kthread_worker(void *data) } /* - * if ioevent_file is not defined, we don't have notification + * if wchan is not defined, we don't have notification * mechanism and we continually execute worker_fn() */ - if (!ctx->ioevent_file) { + if (!ctx->cfg.wchan) { ctx->worker_fn(ctx->worker_private); /* worker body */ } else { /* checks if there is a pending notification */ @@ -1099,7 +1090,7 @@ nm_kthread_worker(void *data) continue; } else if (nmk->run) { /* wait on event with one second timeout */ - msleep_spin(ctx->ioevent_file, &nmk->worker_lock, + msleep_spin((void *)ctx->cfg.wchan, &nmk->worker_lock, "nmk_ev", hz); nmk->scheduled++; } @@ -1110,29 +1101,6 @@ nm_kthread_worker(void *data) kthread_exit(); } -static int -nm_kthread_open_files(struct nm_kthread *nmk, struct nm_kthread_cfg *cfg) -{ - /* send irq through ioctl to bhyve (vmm.ko) */ - if (cfg->event.irqfd) { - nmk->worker_ctx.irq_fd = cfg->event.irqfd; - nmk->worker_ctx.irq_ioctl = cfg->event.ioctl; - } - /* ring.ioeventfd contains the chan where do tsleep to wait events */ - if (cfg->event.ioeventfd) { - nmk->worker_ctx.ioevent_file = (void *)cfg->event.ioeventfd; - } - - return 0; -} - -static void -nm_kthread_close_files(struct nm_kthread *nmk) -{ - nmk->worker_ctx.irq_fd = 0; - nmk->worker_ctx.ioevent_file = NULL; -} - void nm_os_kthread_set_affinity(struct nm_kthread *nmk, int affinity) { @@ -1140,10 +1108,15 @@ nm_os_kthread_set_affinity(struct nm_kthread *nmk, int affinity) } struct nm_kthread * -nm_os_kthread_create(struct nm_kthread_cfg *cfg) +nm_os_kthread_create(struct nm_kthread_cfg *cfg, unsigned int cfgtype, + void *opaque) { struct nm_kthread *nmk = NULL; - int error; + + if (cfgtype != PTNETMAP_CFGTYPE_BHYVE) { + D("Unsupported cfgtype %u", cfgtype); + return NULL; + } nmk = malloc(sizeof(*nmk), M_DEVBUF, M_NOWAIT | M_ZERO); if (!nmk) @@ -1158,15 +1131,12 @@ nm_os_kthread_create(struct nm_kthread_cfg *cfg) /* attach kthread to user process (ptnetmap) */ nmk->attach_user = cfg->attach_user; - /* open event fd */ - error = nm_kthread_open_files(nmk, cfg); - if (error) - goto err; + /* store kick/interrupt configuration */ + if (opaque) { + nmk->worker_ctx.cfg = *((struct ptnetmap_cfgentry_bhyve *)opaque); + } return nmk; -err: - free(nmk, M_DEVBUF); - return NULL; } int @@ -1194,7 +1164,7 @@ nm_os_kthread_start(struct nm_kthread *nmk) goto err; } - D("nm_kthread started td 0x%p", nmk->worker); + D("nm_kthread started td %p", nmk->worker); return 0; err: @@ -1228,7 +1198,7 @@ nm_os_kthread_delete(struct nm_kthread *nmk) nm_os_kthread_stop(nmk); } - nm_kthread_close_files(nmk); + memset(&nmk->worker_ctx.cfg, 0, sizeof(nmk->worker_ctx.cfg)); free(nmk, M_DEVBUF); } diff --git a/sys/dev/netmap/netmap_kern.h b/sys/dev/netmap/netmap_kern.h index 28e69d7ab093..f904476721ba 100644 --- a/sys/dev/netmap/netmap_kern.h +++ b/sys/dev/netmap/netmap_kern.h @@ -2009,13 +2009,14 @@ typedef void (*nm_kthread_worker_fn_t)(void *data); /* kthread configuration */ struct nm_kthread_cfg { long type; /* kthread type/identifier */ - struct ptnet_ring_cfg event; /* event/ioctl fd */ nm_kthread_worker_fn_t worker_fn; /* worker function */ void *worker_private;/* worker parameter */ int attach_user; /* attach kthread to user process */ }; /* kthread configuration */ -struct nm_kthread *nm_os_kthread_create(struct nm_kthread_cfg *cfg); +struct nm_kthread *nm_os_kthread_create(struct nm_kthread_cfg *cfg, + unsigned int cfgtype, + void *opaque); int nm_os_kthread_start(struct nm_kthread *); void nm_os_kthread_stop(struct nm_kthread *); void nm_os_kthread_delete(struct nm_kthread *); @@ -2053,8 +2054,6 @@ nm_ptnetmap_host_on(struct netmap_adapter *na) #ifdef WITH_PTNETMAP_GUEST /* ptnetmap GUEST routines */ -typedef uint32_t (*nm_pt_guest_ptctl_t)(struct ifnet *, uint32_t); - /* * netmap adapter for guest ptnetmap ports */ @@ -2076,8 +2075,8 @@ struct netmap_pt_guest_adapter { }; -int netmap_pt_guest_attach(struct netmap_adapter *, void *, - unsigned int, nm_pt_guest_ptctl_t); +int netmap_pt_guest_attach(struct netmap_adapter *na, void *csb, + unsigned int nifp_offset, unsigned int memid); struct ptnet_ring; bool netmap_pt_guest_txsync(struct ptnet_ring *ptring, struct netmap_kring *kring, int flags); diff --git a/sys/dev/netmap/netmap_mem2.c b/sys/dev/netmap/netmap_mem2.c index bb0f9c8b6f39..ab89d3af65a5 100644 --- a/sys/dev/netmap/netmap_mem2.c +++ b/sys/dev/netmap/netmap_mem2.c @@ -147,39 +147,6 @@ struct netmap_mem_ops { typedef uint16_t nm_memid_t; -/* - * Shared info for netmap allocator - * - * Each allocator contains this structur as first netmap_if. - * In this way, we can share same details about allocator - * to the VM. - * Used in ptnetmap. - */ -struct netmap_mem_shared_info { -#ifndef _WIN32 - struct netmap_if up; /* ends with a 0-sized array, which VSC does not like */ -#else /* !_WIN32 */ - char up[sizeof(struct netmap_if)]; -#endif /* !_WIN32 */ - uint64_t features; -#define NMS_FEAT_BUF_POOL 0x0001 -#define NMS_FEAT_MEMSIZE 0x0002 - - uint32_t buf_pool_offset; - uint32_t buf_pool_objtotal; - uint32_t buf_pool_objsize; - uint32_t totalsize; -}; - -#define NMS_NAME "nms_info" -#define NMS_VERSION 1 -static const struct netmap_if nms_if_blueprint = { - .ni_name = NMS_NAME, - .ni_version = NMS_VERSION, - .ni_tx_rings = 0, - .ni_rx_rings = 0 -}; - struct netmap_mem_d { NMA_LOCK_T nm_mtx; /* protect the allocator */ u_int nm_totalsize; /* shorthand */ @@ -312,8 +279,6 @@ netmap_mem_finalize(struct netmap_mem_d *nmd, struct netmap_adapter *na) return nmd->lasterr; } -static int netmap_mem_init_shared_info(struct netmap_mem_d *nmd); - void netmap_mem_deref(struct netmap_mem_d *nmd, struct netmap_adapter *na) { @@ -362,13 +327,9 @@ netmap_mem_deref(struct netmap_mem_d *nmd, struct netmap_adapter *na) if (nmd->pools[NETMAP_BUF_POOL].bitmap) { /* XXX This check is a workaround that prevents a * NULL pointer crash which currently happens only - * with ptnetmap guests. Also, - * netmap_mem_init_shared_info must not be called - * by ptnetmap guest. */ + * with ptnetmap guests. + * Removed shared-info --> is the bug still there? */ nmd->pools[NETMAP_BUF_POOL].bitmap[0] = ~3; - - /* expose info to the ptnetmap guest */ - netmap_mem_init_shared_info(nmd); } } nmd->ops->nmd_deref(nmd); @@ -1391,30 +1352,6 @@ netmap_mem_map(struct netmap_obj_pool *p, struct netmap_adapter *na) } static int -netmap_mem_init_shared_info(struct netmap_mem_d *nmd) -{ - struct netmap_mem_shared_info *nms_info; - ssize_t base; - - /* Use the first slot in IF_POOL */ - nms_info = netmap_if_malloc(nmd, sizeof(*nms_info)); - if (nms_info == NULL) { - return ENOMEM; - } - - base = netmap_if_offset(nmd, nms_info); - - memcpy(&nms_info->up, &nms_if_blueprint, sizeof(nms_if_blueprint)); - nms_info->buf_pool_offset = nmd->pools[NETMAP_IF_POOL].memtotal + nmd->pools[NETMAP_RING_POOL].memtotal; - nms_info->buf_pool_objtotal = nmd->pools[NETMAP_BUF_POOL].objtotal; - nms_info->buf_pool_objsize = nmd->pools[NETMAP_BUF_POOL]._objsize; - nms_info->totalsize = nmd->nm_totalsize; - nms_info->features = NMS_FEAT_BUF_POOL | NMS_FEAT_MEMSIZE; - - return 0; -} - -static int netmap_mem_finalize_all(struct netmap_mem_d *nmd) { int i; @@ -1433,11 +1370,6 @@ netmap_mem_finalize_all(struct netmap_mem_d *nmd) nmd->pools[NETMAP_BUF_POOL].bitmap[0] = ~3; nmd->flags |= NETMAP_MEM_FINALIZED; - /* expose info to the ptnetmap guest */ - nmd->lasterr = netmap_mem_init_shared_info(nmd); - if (nmd->lasterr) - goto error; - if (netmap_verbose) D("interfaces %d KB, rings %d KB, buffers %d MB", nmd->pools[NETMAP_IF_POOL].memtotal >> 10, @@ -1929,12 +1861,54 @@ struct netmap_mem_ops netmap_mem_private_ops = { .nmd_rings_delete = netmap_mem2_rings_delete }; +int +netmap_mem_pools_info_get(struct nmreq *nmr, struct netmap_adapter *na) +{ + uintptr_t *pp = (uintptr_t *)&nmr->nr_arg1; + struct netmap_pools_info *upi = (struct netmap_pools_info *)(*pp); + struct netmap_mem_d *nmd = na->nm_mem; + struct netmap_pools_info pi; + unsigned int memsize; + uint16_t memid; + int ret; + + if (!nmd) { + return -1; + } + + ret = netmap_mem_get_info(nmd, &memsize, NULL, &memid); + if (ret) { + return ret; + } + + pi.memsize = memsize; + pi.memid = memid; + pi.if_pool_offset = 0; + pi.if_pool_objtotal = nmd->pools[NETMAP_IF_POOL].objtotal; + pi.if_pool_objsize = nmd->pools[NETMAP_IF_POOL]._objsize; + + pi.ring_pool_offset = nmd->pools[NETMAP_IF_POOL].memtotal; + pi.ring_pool_objtotal = nmd->pools[NETMAP_RING_POOL].objtotal; + pi.ring_pool_objsize = nmd->pools[NETMAP_RING_POOL]._objsize; + + pi.buf_pool_offset = nmd->pools[NETMAP_IF_POOL].memtotal + + nmd->pools[NETMAP_RING_POOL].memtotal; + pi.buf_pool_objtotal = nmd->pools[NETMAP_BUF_POOL].objtotal; + pi.buf_pool_objsize = nmd->pools[NETMAP_BUF_POOL]._objsize; + + ret = copyout(&pi, upi, sizeof(pi)); + if (ret) { + return ret; + } + + return 0; +} + #ifdef WITH_PTNETMAP_GUEST struct mem_pt_if { struct mem_pt_if *next; struct ifnet *ifp; unsigned int nifp_offset; - nm_pt_guest_ptctl_t ptctl; }; /* Netmap allocator for ptnetmap guests. */ @@ -1944,16 +1918,15 @@ struct netmap_mem_ptg { vm_paddr_t nm_paddr; /* physical address in the guest */ void *nm_addr; /* virtual address in the guest */ struct netmap_lut buf_lut; /* lookup table for BUF pool in the guest */ - nm_memid_t nm_host_id; /* allocator identifier in the host */ - struct ptnetmap_memdev *ptn_dev; + nm_memid_t host_mem_id; /* allocator identifier in the host */ + struct ptnetmap_memdev *ptn_dev;/* ptnetmap memdev */ struct mem_pt_if *pt_ifs; /* list of interfaces in passthrough */ }; /* Link a passthrough interface to a passthrough netmap allocator. */ static int netmap_mem_pt_guest_ifp_add(struct netmap_mem_d *nmd, struct ifnet *ifp, - unsigned int nifp_offset, - nm_pt_guest_ptctl_t ptctl) + unsigned int nifp_offset) { struct netmap_mem_ptg *ptnmd = (struct netmap_mem_ptg *)nmd; struct mem_pt_if *ptif = malloc(sizeof(*ptif), M_NETMAP, @@ -1967,7 +1940,6 @@ netmap_mem_pt_guest_ifp_add(struct netmap_mem_d *nmd, struct ifnet *ifp, ptif->ifp = ifp; ptif->nifp_offset = nifp_offset; - ptif->ptctl = ptctl; if (ptnmd->pt_ifs) { ptif->next = ptnmd->pt_ifs; @@ -2029,62 +2001,6 @@ netmap_mem_pt_guest_ifp_del(struct netmap_mem_d *nmd, struct ifnet *ifp) return ret; } -/* Read allocator info from the first netmap_if (only on finalize) */ -static int -netmap_mem_pt_guest_read_shared_info(struct netmap_mem_d *nmd) -{ - struct netmap_mem_ptg *ptnmd = (struct netmap_mem_ptg *)nmd; - struct netmap_mem_shared_info *nms_info; - uint32_t bufsize; - uint32_t nbuffers; - char *vaddr; - vm_paddr_t paddr; - int i; - - nms_info = (struct netmap_mem_shared_info *)ptnmd->nm_addr; - if (strncmp(nms_info->up.ni_name, NMS_NAME, sizeof(NMS_NAME)) != 0) { - D("error, the first slot does not contain shared info"); - return EINVAL; - } - /* check features mem_shared info */ - if ((nms_info->features & (NMS_FEAT_BUF_POOL | NMS_FEAT_MEMSIZE)) != - (NMS_FEAT_BUF_POOL | NMS_FEAT_MEMSIZE)) { - D("error, the shared info does not contain BUF_POOL and MEMSIZE"); - return EINVAL; - } - - bufsize = nms_info->buf_pool_objsize; - nbuffers = nms_info->buf_pool_objtotal; - - /* allocate the lut */ - if (ptnmd->buf_lut.lut == NULL) { - D("allocating lut"); - ptnmd->buf_lut.lut = nm_alloc_lut(nbuffers); - if (ptnmd->buf_lut.lut == NULL) { - D("lut allocation failed"); - return ENOMEM; - } - } - - /* we have physically contiguous memory mapped through PCI BAR */ - vaddr = (char *)(ptnmd->nm_addr) + nms_info->buf_pool_offset; - paddr = ptnmd->nm_paddr + nms_info->buf_pool_offset; - - for (i = 0; i < nbuffers; i++) { - ptnmd->buf_lut.lut[i].vaddr = vaddr; - ptnmd->buf_lut.lut[i].paddr = paddr; - vaddr += bufsize; - paddr += bufsize; - } - - ptnmd->buf_lut.objtotal = nbuffers; - ptnmd->buf_lut.objsize = bufsize; - - nmd->nm_totalsize = nms_info->totalsize; - - return 0; -} - static int netmap_mem_pt_guest_get_lut(struct netmap_mem_d *nmd, struct netmap_lut *lut) { @@ -2147,6 +2063,13 @@ static int netmap_mem_pt_guest_finalize(struct netmap_mem_d *nmd) { struct netmap_mem_ptg *ptnmd = (struct netmap_mem_ptg *)nmd; + uint64_t mem_size; + uint32_t bufsize; + uint32_t nbuffers; + uint32_t poolofs; + vm_paddr_t paddr; + char *vaddr; + int i; int error = 0; nmd->active++; @@ -2159,16 +2082,45 @@ netmap_mem_pt_guest_finalize(struct netmap_mem_d *nmd) error = ENOMEM; goto err; } - /* map memory through ptnetmap-memdev BAR */ + /* Map memory through ptnetmap-memdev BAR. */ error = nm_os_pt_memdev_iomap(ptnmd->ptn_dev, &ptnmd->nm_paddr, - &ptnmd->nm_addr); + &ptnmd->nm_addr, &mem_size); if (error) goto err; - /* read allcator info and create lut */ - error = netmap_mem_pt_guest_read_shared_info(nmd); - if (error) - goto err; + /* Initialize the lut using the information contained in the + * ptnetmap memory device. */ + bufsize = nm_os_pt_memdev_ioread(ptnmd->ptn_dev, + PTNET_MDEV_IO_BUF_POOL_OBJSZ); + nbuffers = nm_os_pt_memdev_ioread(ptnmd->ptn_dev, + PTNET_MDEV_IO_BUF_POOL_OBJNUM); + + /* allocate the lut */ + if (ptnmd->buf_lut.lut == NULL) { + D("allocating lut"); + ptnmd->buf_lut.lut = nm_alloc_lut(nbuffers); + if (ptnmd->buf_lut.lut == NULL) { + D("lut allocation failed"); + return ENOMEM; + } + } + + /* we have physically contiguous memory mapped through PCI BAR */ + poolofs = nm_os_pt_memdev_ioread(ptnmd->ptn_dev, + PTNET_MDEV_IO_BUF_POOL_OFS); + vaddr = (char *)(ptnmd->nm_addr) + poolofs; + paddr = ptnmd->nm_paddr + poolofs; + + for (i = 0; i < nbuffers; i++) { + ptnmd->buf_lut.lut[i].vaddr = vaddr; + ptnmd->buf_lut.lut[i].paddr = paddr; + vaddr += bufsize; + paddr += bufsize; + } + + ptnmd->buf_lut.objtotal = nbuffers; + ptnmd->buf_lut.objsize = bufsize; + nmd->nm_totalsize = (unsigned int)mem_size; nmd->flags |= NETMAP_MEM_FINALIZED; out: @@ -2248,15 +2200,10 @@ netmap_mem_pt_guest_if_delete(struct netmap_adapter *na, struct netmap_if *nifp) struct mem_pt_if *ptif; NMA_LOCK(na->nm_mem); - ptif = netmap_mem_pt_guest_ifp_lookup(na->nm_mem, na->ifp); if (ptif == NULL) { D("Error: interface %p is not in passthrough", na->ifp); - goto out; } - - ptif->ptctl(na->ifp, PTNETMAP_PTCTL_IFDELETE); -out: NMA_UNLOCK(na->nm_mem); } @@ -2295,7 +2242,6 @@ netmap_mem_pt_guest_rings_create(struct netmap_adapter *na) nifp->ring_ofs[i + na->num_tx_rings + 1]); } - //error = ptif->ptctl->nm_ptctl(ifp, PTNETMAP_PTCTL_RINGSCREATE); error = 0; out: NMA_UNLOCK(na->nm_mem); @@ -2331,7 +2277,7 @@ static struct netmap_mem_ops netmap_mem_pt_guest_ops = { /* Called with NMA_LOCK(&nm_mem) held. */ static struct netmap_mem_d * -netmap_mem_pt_guest_find_hostid(nm_memid_t host_id) +netmap_mem_pt_guest_find_memid(nm_memid_t mem_id) { struct netmap_mem_d *mem = NULL; struct netmap_mem_d *scan = netmap_last_mem_d; @@ -2339,7 +2285,7 @@ netmap_mem_pt_guest_find_hostid(nm_memid_t host_id) do { /* find ptnetmap allocator through host ID */ if (scan->ops->nmd_deref == netmap_mem_pt_guest_deref && - ((struct netmap_mem_ptg *)(scan))->nm_host_id == host_id) { + ((struct netmap_mem_ptg *)(scan))->host_mem_id == mem_id) { mem = scan; break; } @@ -2351,7 +2297,7 @@ netmap_mem_pt_guest_find_hostid(nm_memid_t host_id) /* Called with NMA_LOCK(&nm_mem) held. */ static struct netmap_mem_d * -netmap_mem_pt_guest_create(nm_memid_t host_id) +netmap_mem_pt_guest_create(nm_memid_t mem_id) { struct netmap_mem_ptg *ptnmd; int err = 0; @@ -2364,7 +2310,7 @@ netmap_mem_pt_guest_create(nm_memid_t host_id) } ptnmd->up.ops = &netmap_mem_pt_guest_ops; - ptnmd->nm_host_id = host_id; + ptnmd->host_mem_id = mem_id; ptnmd->pt_ifs = NULL; /* Assign new id in the guest (We have the lock) */ @@ -2388,14 +2334,14 @@ error: * if it is not there */ static struct netmap_mem_d * -netmap_mem_pt_guest_get(nm_memid_t host_id) +netmap_mem_pt_guest_get(nm_memid_t mem_id) { struct netmap_mem_d *nmd; NMA_LOCK(&nm_mem); - nmd = netmap_mem_pt_guest_find_hostid(host_id); + nmd = netmap_mem_pt_guest_find_memid(mem_id); if (nmd == NULL) { - nmd = netmap_mem_pt_guest_create(host_id); + nmd = netmap_mem_pt_guest_create(mem_id); } NMA_UNLOCK(&nm_mem); @@ -2404,7 +2350,7 @@ netmap_mem_pt_guest_get(nm_memid_t host_id) /* * The guest allocator can be created by ptnetmap_memdev (during the device - * attach) or by ptnetmap device (e1000/virtio), during the netmap_attach. + * attach) or by ptnetmap device (ptnet), during the netmap_attach. * * The order is not important (we have different order in LINUX and FreeBSD). * The first one, creates the device, and the second one simply attaches it. @@ -2413,12 +2359,12 @@ netmap_mem_pt_guest_get(nm_memid_t host_id) /* Called when ptnetmap_memdev is attaching, to attach a new allocator in * the guest */ struct netmap_mem_d * -netmap_mem_pt_guest_attach(struct ptnetmap_memdev *ptn_dev, nm_memid_t host_id) +netmap_mem_pt_guest_attach(struct ptnetmap_memdev *ptn_dev, nm_memid_t mem_id) { struct netmap_mem_d *nmd; struct netmap_mem_ptg *ptnmd; - nmd = netmap_mem_pt_guest_get(host_id); + nmd = netmap_mem_pt_guest_get(mem_id); /* assign this device to the guest allocator */ if (nmd) { @@ -2429,27 +2375,22 @@ netmap_mem_pt_guest_attach(struct ptnetmap_memdev *ptn_dev, nm_memid_t host_id) return nmd; } -/* Called when ptnetmap device (virtio/e1000) is attaching */ +/* Called when ptnet device is attaching */ struct netmap_mem_d * netmap_mem_pt_guest_new(struct ifnet *ifp, unsigned int nifp_offset, - nm_pt_guest_ptctl_t ptctl) + unsigned int memid) { struct netmap_mem_d *nmd; - nm_memid_t host_id; - if (ifp == NULL || ptctl == NULL) { + if (ifp == NULL) { return NULL; } - /* Get the host id allocator. */ - host_id = ptctl(ifp, PTNETMAP_PTCTL_HOSTMEMID); - - nmd = netmap_mem_pt_guest_get(host_id); + nmd = netmap_mem_pt_guest_get((nm_memid_t)memid); if (nmd) { - netmap_mem_pt_guest_ifp_add(nmd, ifp, nifp_offset, - ptctl); + netmap_mem_pt_guest_ifp_add(nmd, ifp, nifp_offset); } return nmd; diff --git a/sys/dev/netmap/netmap_mem2.h b/sys/dev/netmap/netmap_mem2.h index 7f4c5e9e9624..f170df9d5490 100644 --- a/sys/dev/netmap/netmap_mem2.h +++ b/sys/dev/netmap/netmap_mem2.h @@ -167,12 +167,14 @@ void netmap_mem_put(struct netmap_mem_d *); #ifdef WITH_PTNETMAP_GUEST struct netmap_mem_d* netmap_mem_pt_guest_new(struct ifnet *, unsigned int nifp_offset, - nm_pt_guest_ptctl_t); + unsigned int memid); struct ptnetmap_memdev; struct netmap_mem_d* netmap_mem_pt_guest_attach(struct ptnetmap_memdev *, uint16_t); int netmap_mem_pt_guest_ifp_del(struct netmap_mem_d *, struct ifnet *); #endif /* WITH_PTNETMAP_GUEST */ +int netmap_mem_pools_info_get(struct nmreq *, struct netmap_adapter *); + #define NETMAP_MEM_PRIVATE 0x2 /* allocator uses private address space */ #define NETMAP_MEM_IO 0x4 /* the underlying memory is mmapped I/O */ diff --git a/sys/dev/netmap/netmap_pt.c b/sys/dev/netmap/netmap_pt.c index 56434a236145..3913f4b957fd 100644 --- a/sys/dev/netmap/netmap_pt.c +++ b/sys/dev/netmap/netmap_pt.c @@ -560,13 +560,34 @@ ptnetmap_print_configuration(struct ptnetmap_cfg *cfg) { int k; - D("[PTN] configuration:"); - D(" CSB ptrings @%p, num_rings=%u, features %08x", cfg->ptrings, - cfg->num_rings, cfg->features); + D("ptnetmap configuration:"); + D(" CSB ptrings @%p, num_rings=%u, cfgtype %08x", cfg->ptrings, + cfg->num_rings, cfg->cfgtype); for (k = 0; k < cfg->num_rings; k++) { - D(" ring #%d: iofd=%llu, irqfd=%llu", k, - (unsigned long long)cfg->entries[k].ioeventfd, - (unsigned long long)cfg->entries[k].irqfd); + switch (cfg->cfgtype) { + case PTNETMAP_CFGTYPE_QEMU: { + struct ptnetmap_cfgentry_qemu *e = + (struct ptnetmap_cfgentry_qemu *)(cfg+1) + k; + D(" ring #%d: ioeventfd=%lu, irqfd=%lu", k, + (unsigned long)e->ioeventfd, + (unsigned long)e->irqfd); + break; + } + + case PTNETMAP_CFGTYPE_BHYVE: + { + struct ptnetmap_cfgentry_bhyve *e = + (struct ptnetmap_cfgentry_bhyve *)(cfg+1) + k; + D(" ring #%d: wchan=%lu, ioctl_fd=%lu, " + "ioctl_cmd=%lu, msix_msg_data=%lu, msix_addr=%lu", + k, (unsigned long)e->wchan, + (unsigned long)e->ioctl_fd, + (unsigned long)e->ioctl_cmd, + (unsigned long)e->ioctl_data.msg_data, + (unsigned long)e->ioctl_data.addr); + break; + } + } } } @@ -632,6 +653,7 @@ ptnetmap_create_kthreads(struct netmap_pt_host_adapter *pth_na, struct ptnetmap_state *ptns = pth_na->ptns; struct nm_kthread_cfg nmk_cfg; unsigned int num_rings; + uint8_t *cfg_entries = (uint8_t *)(cfg + 1); int k; num_rings = pth_na->up.num_tx_rings + @@ -640,7 +662,6 @@ ptnetmap_create_kthreads(struct netmap_pt_host_adapter *pth_na, for (k = 0; k < num_rings; k++) { nmk_cfg.attach_user = 1; /* attach kthread to user process */ nmk_cfg.worker_private = ptnetmap_kring(pth_na, k); - nmk_cfg.event = *(cfg->entries + k); nmk_cfg.type = k; if (k < pth_na->up.num_tx_rings) { nmk_cfg.worker_fn = ptnetmap_tx_handler; @@ -648,7 +669,8 @@ ptnetmap_create_kthreads(struct netmap_pt_host_adapter *pth_na, nmk_cfg.worker_fn = ptnetmap_rx_handler; } - ptns->kthreads[k] = nm_os_kthread_create(&nmk_cfg); + ptns->kthreads[k] = nm_os_kthread_create(&nmk_cfg, + cfg->cfgtype, cfg_entries + k * cfg->entry_size); if (ptns->kthreads[k] == NULL) { goto err; } @@ -727,7 +749,7 @@ ptnetmap_read_cfg(struct nmreq *nmr) return NULL; } - cfglen = sizeof(tmp) + tmp.num_rings * sizeof(struct ptnet_ring_cfg); + cfglen = sizeof(tmp) + tmp.num_rings * tmp.entry_size; cfg = malloc(cfglen, M_DEVBUF, M_NOWAIT | M_ZERO); if (!cfg) { return NULL; @@ -750,7 +772,6 @@ static int ptnetmap_create(struct netmap_pt_host_adapter *pth_na, struct ptnetmap_cfg *cfg) { - unsigned ft_mask = (PTNETMAP_CFG_FEAT_CSB | PTNETMAP_CFG_FEAT_EVENTFD); struct ptnetmap_state *ptns; unsigned int num_rings; int ret, i; @@ -761,12 +782,6 @@ ptnetmap_create(struct netmap_pt_host_adapter *pth_na, return EINVAL; } - if ((cfg->features & ft_mask) != ft_mask) { - D("ERROR ptnetmap_cfg(%x) does not contain CSB and EVENTFD", - cfg->features); - return EINVAL; - } - num_rings = pth_na->up.num_tx_rings + pth_na->up.num_rx_rings; if (num_rings != cfg->num_rings) { @@ -1240,9 +1255,9 @@ put_out_noputparent: #ifdef WITH_PTNETMAP_GUEST /* - * GUEST ptnetmap generic txsync()/rxsync() used in e1000/virtio-net device - * driver notify is set when we need to send notification to the host - * (driver-specific) + * Guest ptnetmap txsync()/rxsync() routines, used in ptnet device drivers. + * These routines are reused across the different operating systems supported + * by netmap. */ /* diff --git a/sys/dev/netmap/netmap_vale.c b/sys/dev/netmap/netmap_vale.c index 78c53409c0b3..71b3aedddd46 100644 --- a/sys/dev/netmap/netmap_vale.c +++ b/sys/dev/netmap/netmap_vale.c @@ -913,7 +913,7 @@ nm_bdg_create_kthreads(struct nm_bdg_polling_state *bps) kcfg.type = i; kcfg.worker_private = t; - t->nmk = nm_os_kthread_create(&kcfg); + t->nmk = nm_os_kthread_create(&kcfg, 0, NULL); if (t->nmk == NULL) { goto cleanup; } diff --git a/sys/dev/psci/psci.c b/sys/dev/psci/psci.c index c5fe0fbc443e..f7050976c8c6 100644 --- a/sys/dev/psci/psci.c +++ b/sys/dev/psci/psci.c @@ -189,12 +189,12 @@ psci_cpu_on(unsigned long cpu, unsigned long entry, unsigned long context_id) node = ofw_bus_find_compatible(OF_peer(0), "arm,psci-0.2"); if (node == 0) /* TODO: Handle psci 0.1 */ - return (PSCI_RETVAL_INTERNAL_FAILURE); + return (PSCI_MISSING); fnid = PSCI_FNID_CPU_ON; callfn = psci_get_callfn(node); if (callfn == NULL) - return (PSCI_RETVAL_INTERNAL_FAILURE); + return (PSCI_MISSING); } else { callfn = psci_softc->psci_call; fnid = psci_softc->psci_fnids[PSCI_FN_CPU_ON]; diff --git a/sys/dev/psci/psci.h b/sys/dev/psci/psci.h index 58f1e791d0c0..7f447abaf444 100644 --- a/sys/dev/psci/psci.h +++ b/sys/dev/psci/psci.h @@ -54,6 +54,10 @@ int psci_smc_despatch(register_t, register_t, register_t, register_t); #define PSCI_RETVAL_INTERNAL_FAILURE -6 #define PSCI_RETVAL_NOT_PRESENT -7 #define PSCI_RETVAL_DISABLED -8 +/* + * Used to signal PSCI is not available, e.g. to start a CPU. + */ +#define PSCI_MISSING 1 /* * PSCI function codes (as per PSCI v0.2). diff --git a/sys/dev/re/if_re.c b/sys/dev/re/if_re.c index 071b55b10146..00c27642b568 100644 --- a/sys/dev/re/if_re.c +++ b/sys/dev/re/if_re.c @@ -1358,15 +1358,17 @@ re_attach(device_t dev) CSR_WRITE_1(sc, RL_EECMD, RL_EEMODE_OFF); } - /* Disable ASPM L0S/L1. */ + /* Disable ASPM L0S/L1 and CLKREQ. */ if (sc->rl_expcap != 0) { cap = pci_read_config(dev, sc->rl_expcap + PCIER_LINK_CAP, 2); if ((cap & PCIEM_LINK_CAP_ASPM) != 0) { ctl = pci_read_config(dev, sc->rl_expcap + PCIER_LINK_CTL, 2); - if ((ctl & PCIEM_LINK_CTL_ASPMC) != 0) { - ctl &= ~PCIEM_LINK_CTL_ASPMC; + if ((ctl & (PCIEM_LINK_CTL_ECPM | + PCIEM_LINK_CTL_ASPMC))!= 0) { + ctl &= ~(PCIEM_LINK_CTL_ECPM | + PCIEM_LINK_CTL_ASPMC); pci_write_config(dev, sc->rl_expcap + PCIER_LINK_CTL, ctl, 2); device_printf(dev, "ASPM disabled\n"); diff --git a/sys/dev/usb/input/ums.c b/sys/dev/usb/input/ums.c index 4d60517e1b50..757a96a8b831 100644 --- a/sys/dev/usb/input/ums.c +++ b/sys/dev/usb/input/ums.c @@ -173,6 +173,8 @@ static usb_fifo_ioctl_t ums_fifo_ioctl; #ifdef EVDEV_SUPPORT static evdev_open_t ums_ev_open; static evdev_close_t ums_ev_close; +static void ums_evdev_push(struct ums_softc *, int32_t, int32_t, + int32_t, int32_t, int32_t); #endif static void ums_start_rx(struct ums_softc *); @@ -205,6 +207,9 @@ ums_put_queue_timeout(void *__sc) mtx_assert(&sc->sc_mtx, MA_OWNED); ums_put_queue(sc, 0, 0, 0, 0, 0); +#ifdef EVDEV_SUPPORT + ums_evdev_push(sc, 0, 0, 0, 0, 0); +#endif } static void @@ -216,6 +221,9 @@ ums_intr_callback(struct usb_xfer *xfer, usb_error_t error) uint8_t *buf = sc->sc_temp; int32_t buttons = 0; int32_t buttons_found = 0; +#ifdef EVDEV_SUPPORT + int32_t buttons_reported = 0; +#endif int32_t dw = 0; int32_t dx = 0; int32_t dy = 0; @@ -287,8 +295,11 @@ ums_intr_callback(struct usb_xfer *xfer, usb_error_t error) } if ((info->sc_flags & UMS_FLAG_T_AXIS) && - (id == info->sc_iid_t)) + (id == info->sc_iid_t)) { dt -= hid_get_data(buf, len, &info->sc_loc_t); + /* T-axis is translated into button presses */ + buttons_found |= (1UL << 5) | (1UL << 6); + } for (i = 0; i < info->sc_buttons; i++) { uint32_t mask; @@ -306,6 +317,9 @@ ums_intr_callback(struct usb_xfer *xfer, usb_error_t error) if (++info != &sc->sc_info[UMS_INFO_MAX]) goto repeat; +#ifdef EVDEV_SUPPORT + buttons_reported = buttons; +#endif /* keep old button value(s) for non-detected buttons */ buttons |= sc->sc_status.button & ~buttons_found; @@ -351,6 +365,11 @@ ums_intr_callback(struct usb_xfer *xfer, usb_error_t error) usb_callout_stop(&sc->sc_callout); ums_put_queue(sc, dx, dy, dz, dt, buttons); +#ifdef EVDEV_SUPPORT + ums_evdev_push(sc, dx, dy, dz, dt, + buttons_reported); +#endif + } } case USB_ST_SETUP: @@ -720,7 +739,7 @@ ums_attach(device_t dev) for (i = 0; i < info->sc_buttons; i++) evdev_support_key(sc->sc_evdev, BTN_MOUSE + i); - err = evdev_register(sc->sc_evdev); + err = evdev_register_mtx(sc->sc_evdev, &sc->sc_mtx); if (err) goto detach; #endif @@ -891,27 +910,32 @@ ums_put_queue(struct ums_softc *sc, int32_t dx, int32_t dy, } usb_fifo_put_data_linear(sc->sc_fifo.fp[USB_FIFO_RX], buf, sc->sc_mode.packetsize, 1); - -#ifdef EVDEV_SUPPORT - if (evdev_rcpt_mask & EVDEV_RCPT_HW_MOUSE) { - /* Push evdev event */ - evdev_push_event(sc->sc_evdev, EV_REL, REL_X, dx); - evdev_push_event(sc->sc_evdev, EV_REL, REL_Y, -dy); - evdev_push_event(sc->sc_evdev, EV_REL, REL_WHEEL, -dz); - evdev_push_event(sc->sc_evdev, EV_REL, REL_HWHEEL, dt); - evdev_push_mouse_btn(sc->sc_evdev, - (buttons & ~MOUSE_STDBUTTONS) | - (buttons & (1 << 2) ? MOUSE_BUTTON1DOWN : 0) | - (buttons & (1 << 1) ? MOUSE_BUTTON2DOWN : 0) | - (buttons & (1 << 0) ? MOUSE_BUTTON3DOWN : 0)); - evdev_sync(sc->sc_evdev); - } -#endif } else { DPRINTF("Buffer full, discarded packet\n"); } } +#ifdef EVDEV_SUPPORT +static void +ums_evdev_push(struct ums_softc *sc, int32_t dx, int32_t dy, + int32_t dz, int32_t dt, int32_t buttons) +{ + if (evdev_rcpt_mask & EVDEV_RCPT_HW_MOUSE) { + /* Push evdev event */ + evdev_push_rel(sc->sc_evdev, REL_X, dx); + evdev_push_rel(sc->sc_evdev, REL_Y, -dy); + evdev_push_rel(sc->sc_evdev, REL_WHEEL, -dz); + evdev_push_rel(sc->sc_evdev, REL_HWHEEL, dt); + evdev_push_mouse_btn(sc->sc_evdev, + (buttons & ~MOUSE_STDBUTTONS) | + (buttons & (1 << 2) ? MOUSE_BUTTON1DOWN : 0) | + (buttons & (1 << 1) ? MOUSE_BUTTON2DOWN : 0) | + (buttons & (1 << 0) ? MOUSE_BUTTON3DOWN : 0)); + evdev_sync(sc->sc_evdev); + } +} +#endif + static void ums_reset_buf(struct ums_softc *sc) { @@ -925,7 +949,7 @@ ums_ev_open(struct evdev_dev *evdev, void *ev_softc) { struct ums_softc *sc = (struct ums_softc *)ev_softc; - mtx_lock(&sc->sc_mtx); + mtx_assert(&sc->sc_mtx, MA_OWNED); sc->sc_evflags = UMS_EVDEV_OPENED; @@ -934,8 +958,6 @@ ums_ev_open(struct evdev_dev *evdev, void *ev_softc) ums_start_rx(sc); } - mtx_unlock(&sc->sc_mtx); - return (0); } @@ -944,14 +966,12 @@ ums_ev_close(struct evdev_dev *evdev, void *ev_softc) { struct ums_softc *sc = (struct ums_softc *)ev_softc; - mtx_lock(&sc->sc_mtx); + mtx_assert(&sc->sc_mtx, MA_OWNED); sc->sc_evflags = 0; if (sc->sc_fflags == 0) ums_stop_rx(sc); - - mtx_unlock(&sc->sc_mtx); } #endif diff --git a/sys/dev/usb/net/if_ure.c b/sys/dev/usb/net/if_ure.c index 9d7f47a576ee..59fa71f215d6 100644 --- a/sys/dev/usb/net/if_ure.c +++ b/sys/dev/usb/net/if_ure.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2015 Kevin Lo <kevlo@FreeBSD.org> + * Copyright (c) 2015-2016 Kevin Lo <kevlo@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -66,8 +66,9 @@ SYSCTL_INT(_hw_usb_ure, OID_AUTO, debug, CTLFLAG_RWTUN, &ure_debug, 0, * Various supported device vendors/products. */ static const STRUCT_USB_HOST_ID ure_devs[] = { -#define URE_DEV(v,p) { USB_VP(USB_VENDOR_##v, USB_PRODUCT_##v##_##p) } - URE_DEV(REALTEK, RTL8152), +#define URE_DEV(v,p,i) { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, i) } + URE_DEV(REALTEK, RTL8152, URE_FLAG_8152), + URE_DEV(REALTEK, RTL8153, 0), #undef URE_DEV }; @@ -87,8 +88,7 @@ static uether_fn_t ure_init; static uether_fn_t ure_stop; static uether_fn_t ure_start; static uether_fn_t ure_tick; -static uether_fn_t ure_setmulti; -static uether_fn_t ure_setpromisc; +static uether_fn_t ure_rxfilter; static int ure_ctl(struct ure_softc *, uint8_t, uint16_t, uint16_t, void *, int); @@ -112,6 +112,7 @@ static int ure_ifmedia_upd(struct ifnet *); static void ure_ifmedia_sts(struct ifnet *, struct ifmediareq *); static int ure_ioctl(struct ifnet *, u_long, caddr_t); static void ure_rtl8152_init(struct ure_softc *); +static void ure_rtl8153_init(struct ure_softc *); static void ure_disable_teredo(struct ure_softc *); static void ure_init_fifo(struct ure_softc *); @@ -129,7 +130,7 @@ static const struct usb_config ure_config[URE_N_TRANSFER] = { .type = UE_BULK, .endpoint = UE_ADDR_ANY, .direction = UE_DIR_IN, - .bufsize = MCLBYTES, + .bufsize = 16384, .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, .callback = ure_bulk_read_callback, .timeout = 0, /* no timeout */ @@ -173,8 +174,8 @@ static const struct usb_ether_methods ure_ue_methods = { .ue_init = ure_init, .ue_stop = ure_stop, .ue_tick = ure_tick, - .ue_setmulti = ure_setmulti, - .ue_setpromisc = ure_setpromisc, + .ue_setmulti = ure_rxfilter, + .ue_setpromisc = ure_rxfilter, .ue_mii_upd = ure_ifmedia_upd, .ue_mii_sts = ure_ifmedia_sts, }; @@ -343,6 +344,13 @@ ure_miibus_readreg(device_t dev, int phy, int reg) if (!locked) URE_LOCK(sc); + /* Let the rgephy driver read the URE_GMEDIASTAT register. */ + if (reg == URE_GMEDIASTAT) { + if (!locked) + URE_UNLOCK(sc); + return (ure_read_1(sc, URE_GMEDIASTAT, URE_MCU_TYPE_PLA)); + } + val = ure_ocp_reg_read(sc, URE_OCP_BASE_MII + reg * 2); if (!locked) @@ -398,6 +406,11 @@ ure_miibus_statchg(device_t dev) case IFM_100_TX: sc->sc_flags |= URE_FLAG_LINK; break; + case IFM_1000_T: + if ((sc->sc_flags & URE_FLAG_8152) != 0) + break; + sc->sc_flags |= URE_FLAG_LINK; + break; default: break; } @@ -412,7 +425,7 @@ done: } /* - * Probe for a RTL8152 chip. + * Probe for a RTL8152/RTL8153 chip. */ static int ure_probe(device_t dev) @@ -443,6 +456,7 @@ ure_attach(device_t dev) uint8_t iface_index; int error; + sc->sc_flags = USB_GET_DRIVER_INFO(uaa); device_set_usb_desc(dev); mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); @@ -617,6 +631,18 @@ ure_read_chipver(struct ure_softc *sc) case 0x4c10: sc->sc_chip |= URE_CHIP_VER_4C10; break; + case 0x5c00: + sc->sc_chip |= URE_CHIP_VER_5C00; + break; + case 0x5c10: + sc->sc_chip |= URE_CHIP_VER_5C10; + break; + case 0x5c20: + sc->sc_chip |= URE_CHIP_VER_5C20; + break; + case 0x5c30: + sc->sc_chip |= URE_CHIP_VER_5C30; + break; default: device_printf(sc->sc_ue.ue_dev, "unknown version 0x%04x\n", ver); @@ -635,7 +661,10 @@ ure_attach_post(struct usb_ether *ue) ure_read_chipver(sc); /* Initialize controller and get station address. */ - ure_rtl8152_init(sc); + if (sc->sc_flags & URE_FLAG_8152) + ure_rtl8152_init(sc); + else + ure_rtl8153_init(sc); if (sc->sc_chip & URE_CHIP_VER_4C00) ure_read_mem(sc, URE_PLA_IDR, URE_MCU_TYPE_PLA, @@ -676,7 +705,6 @@ ure_init(struct usb_ether *ue) { struct ure_softc *sc = uether_getsc(ue); struct ifnet *ifp = uether_getifp(ue); - uint32_t rxmode; URE_LOCK_ASSERT(sc, MA_OWNED); @@ -709,20 +737,8 @@ ure_init(struct usb_ether *ue) ure_read_2(sc, URE_PLA_MISC_1, URE_MCU_TYPE_PLA) & ~URE_RXDY_GATED_EN); - /* Set Rx mode. */ - rxmode = URE_RCR_APM; - - /* If we want promiscuous mode, set the allframes bit. */ - if (ifp->if_flags & IFF_PROMISC) - rxmode |= URE_RCR_AAP; - - if (ifp->if_flags & IFF_BROADCAST) - rxmode |= URE_RCR_AB; - - ure_write_4(sc, URE_PLA_RCR, URE_MCU_TYPE_PLA, rxmode); - - /* Load the multicast filter. */ - ure_setmulti(ue); + /* Configure RX filters. */ + ure_rxfilter(ue); usbd_xfer_set_stall(sc->sc_xfer[URE_BULK_DT_WR]); @@ -750,30 +766,11 @@ ure_tick(struct usb_ether *ue) } } -static void -ure_setpromisc(struct usb_ether *ue) -{ - struct ure_softc *sc = uether_getsc(ue); - struct ifnet *ifp = uether_getifp(ue); - uint32_t rxmode; - - rxmode = ure_read_4(sc, URE_PLA_RCR, URE_MCU_TYPE_PLA); - - if (ifp->if_flags & IFF_PROMISC) - rxmode |= URE_RCR_AAP; - else - rxmode &= ~URE_RCR_AAP; - - ure_write_4(sc, URE_PLA_RCR, URE_MCU_TYPE_PLA, rxmode); - - ure_setmulti(ue); -} - /* * Program the 64-bit multicast hash filter. */ static void -ure_setmulti(struct usb_ether *ue) +ure_rxfilter(struct usb_ether *ue) { struct ure_softc *sc = uether_getsc(ue); struct ifnet *ifp = uether_getifp(ue); @@ -783,7 +780,9 @@ ure_setmulti(struct usb_ether *ue) URE_LOCK_ASSERT(sc, MA_OWNED); - rxmode = ure_read_4(sc, URE_PLA_RCR, URE_MCU_TYPE_PLA); + rxmode = URE_RCR_APM; + if (ifp->if_flags & IFF_BROADCAST) + rxmode |= URE_RCR_AB; if (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) { if (ifp->if_flags & IFF_PROMISC) rxmode |= URE_RCR_AAP; @@ -792,6 +791,7 @@ ure_setmulti(struct usb_ether *ue) goto done; } + rxmode |= URE_RCR_AM; if_maddr_rlock(ifp); TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { if (ifma->ifma_addr->sa_family != AF_LINK) @@ -965,6 +965,156 @@ ure_rtl8152_init(struct ure_softc *sc) } static void +ure_rtl8153_init(struct ure_softc *sc) +{ + uint16_t val; + uint8_t u1u2[8]; + int i; + + /* Disable ALDPS. */ + ure_ocp_reg_write(sc, URE_OCP_POWER_CFG, + ure_ocp_reg_read(sc, URE_OCP_POWER_CFG) & ~URE_EN_ALDPS); + uether_pause(&sc->sc_ue, hz / 50); + + memset(u1u2, 0x00, sizeof(u1u2)); + ure_write_mem(sc, URE_USB_TOLERANCE, + URE_MCU_TYPE_USB | URE_BYTE_EN_SIX_BYTES, u1u2, sizeof(u1u2)); + + for (i = 0; i < URE_TIMEOUT; i++) { + if (ure_read_2(sc, URE_PLA_BOOT_CTRL, URE_MCU_TYPE_PLA) & + URE_AUTOLOAD_DONE) + break; + uether_pause(&sc->sc_ue, hz / 100); + } + if (i == URE_TIMEOUT) + device_printf(sc->sc_ue.ue_dev, + "timeout waiting for chip autoload\n"); + + for (i = 0; i < URE_TIMEOUT; i++) { + val = ure_ocp_reg_read(sc, URE_OCP_PHY_STATUS) & + URE_PHY_STAT_MASK; + if (val == URE_PHY_STAT_LAN_ON || val == URE_PHY_STAT_PWRDN) + break; + uether_pause(&sc->sc_ue, hz / 100); + } + if (i == URE_TIMEOUT) + device_printf(sc->sc_ue.ue_dev, + "timeout waiting for phy to stabilize\n"); + + ure_write_2(sc, URE_USB_U2P3_CTRL, URE_MCU_TYPE_USB, + ure_read_2(sc, URE_USB_U2P3_CTRL, URE_MCU_TYPE_USB) & + ~URE_U2P3_ENABLE); + + if (sc->sc_chip & URE_CHIP_VER_5C10) { + val = ure_read_2(sc, URE_USB_SSPHYLINK2, URE_MCU_TYPE_USB); + val &= ~URE_PWD_DN_SCALE_MASK; + val |= URE_PWD_DN_SCALE(96); + ure_write_2(sc, URE_USB_SSPHYLINK2, URE_MCU_TYPE_USB, val); + + ure_write_1(sc, URE_USB_USB2PHY, URE_MCU_TYPE_USB, + ure_read_1(sc, URE_USB_USB2PHY, URE_MCU_TYPE_USB) | + URE_USB2PHY_L1 | URE_USB2PHY_SUSPEND); + } else if (sc->sc_chip & URE_CHIP_VER_5C20) { + ure_write_1(sc, URE_PLA_DMY_REG0, URE_MCU_TYPE_PLA, + ure_read_1(sc, URE_PLA_DMY_REG0, URE_MCU_TYPE_PLA) & + ~URE_ECM_ALDPS); + } + if (sc->sc_chip & (URE_CHIP_VER_5C20 | URE_CHIP_VER_5C30)) { + val = ure_read_1(sc, URE_USB_CSR_DUMMY1, URE_MCU_TYPE_USB); + if (ure_read_2(sc, URE_USB_BURST_SIZE, URE_MCU_TYPE_USB) == + 0) + val &= ~URE_DYNAMIC_BURST; + else + val |= URE_DYNAMIC_BURST; + ure_write_1(sc, URE_USB_CSR_DUMMY1, URE_MCU_TYPE_USB, val); + } + + ure_write_1(sc, URE_USB_CSR_DUMMY2, URE_MCU_TYPE_USB, + ure_read_1(sc, URE_USB_CSR_DUMMY2, URE_MCU_TYPE_USB) | + URE_EP4_FULL_FC); + + ure_write_2(sc, URE_USB_WDT11_CTRL, URE_MCU_TYPE_USB, + ure_read_2(sc, URE_USB_WDT11_CTRL, URE_MCU_TYPE_USB) & + ~URE_TIMER11_EN); + + ure_write_2(sc, URE_PLA_LED_FEATURE, URE_MCU_TYPE_PLA, + ure_read_2(sc, URE_PLA_LED_FEATURE, URE_MCU_TYPE_PLA) & + ~URE_LED_MODE_MASK); + + if ((sc->sc_chip & URE_CHIP_VER_5C10) && + usbd_get_speed(sc->sc_ue.ue_udev) != USB_SPEED_SUPER) + val = URE_LPM_TIMER_500MS; + else + val = URE_LPM_TIMER_500US; + ure_write_1(sc, URE_USB_LPM_CTRL, URE_MCU_TYPE_USB, + val | URE_FIFO_EMPTY_1FB | URE_ROK_EXIT_LPM); + + val = ure_read_2(sc, URE_USB_AFE_CTRL2, URE_MCU_TYPE_USB); + val &= ~URE_SEN_VAL_MASK; + val |= URE_SEN_VAL_NORMAL | URE_SEL_RXIDLE; + ure_write_2(sc, URE_USB_AFE_CTRL2, URE_MCU_TYPE_USB, val); + + ure_write_2(sc, URE_USB_CONNECT_TIMER, URE_MCU_TYPE_USB, 0x0001); + + ure_write_2(sc, URE_USB_POWER_CUT, URE_MCU_TYPE_USB, + ure_read_2(sc, URE_USB_POWER_CUT, URE_MCU_TYPE_USB) & + ~(URE_PWR_EN | URE_PHASE2_EN)); + ure_write_2(sc, URE_USB_MISC_0, URE_MCU_TYPE_USB, + ure_read_2(sc, URE_USB_MISC_0, URE_MCU_TYPE_USB) & + ~URE_PCUT_STATUS); + + memset(u1u2, 0xff, sizeof(u1u2)); + ure_write_mem(sc, URE_USB_TOLERANCE, + URE_MCU_TYPE_USB | URE_BYTE_EN_SIX_BYTES, u1u2, sizeof(u1u2)); + + ure_write_2(sc, URE_PLA_MAC_PWR_CTRL, URE_MCU_TYPE_PLA, + URE_ALDPS_SPDWN_RATIO); + ure_write_2(sc, URE_PLA_MAC_PWR_CTRL2, URE_MCU_TYPE_PLA, + URE_EEE_SPDWN_RATIO); + ure_write_2(sc, URE_PLA_MAC_PWR_CTRL3, URE_MCU_TYPE_PLA, + URE_PKT_AVAIL_SPDWN_EN | URE_SUSPEND_SPDWN_EN | + URE_U1U2_SPDWN_EN | URE_L1_SPDWN_EN); + ure_write_2(sc, URE_PLA_MAC_PWR_CTRL4, URE_MCU_TYPE_PLA, + URE_PWRSAVE_SPDWN_EN | URE_RXDV_SPDWN_EN | URE_TX10MIDLE_EN | + URE_TP100_SPDWN_EN | URE_TP500_SPDWN_EN | URE_TP1000_SPDWN_EN | + URE_EEE_SPDWN_EN); + + val = ure_read_2(sc, URE_USB_U2P3_CTRL, URE_MCU_TYPE_USB); + if (!(sc->sc_chip & (URE_CHIP_VER_5C00 | URE_CHIP_VER_5C10))) + val |= URE_U2P3_ENABLE; + else + val &= ~URE_U2P3_ENABLE; + ure_write_2(sc, URE_USB_U2P3_CTRL, URE_MCU_TYPE_USB, val); + + memset(u1u2, 0x00, sizeof(u1u2)); + ure_write_mem(sc, URE_USB_TOLERANCE, + URE_MCU_TYPE_USB | URE_BYTE_EN_SIX_BYTES, u1u2, sizeof(u1u2)); + + /* Disable ALDPS. */ + ure_ocp_reg_write(sc, URE_OCP_POWER_CFG, + ure_ocp_reg_read(sc, URE_OCP_POWER_CFG) & ~URE_EN_ALDPS); + uether_pause(&sc->sc_ue, hz / 50); + + ure_init_fifo(sc); + + /* Disable Rx aggregation. */ + ure_write_2(sc, URE_USB_USB_CTRL, URE_MCU_TYPE_USB, + ure_read_2(sc, URE_USB_USB_CTRL, URE_MCU_TYPE_USB) | + URE_RX_AGG_DISABLE); + + val = ure_read_2(sc, URE_USB_U2P3_CTRL, URE_MCU_TYPE_USB); + if (!(sc->sc_chip & (URE_CHIP_VER_5C00 | URE_CHIP_VER_5C10))) + val |= URE_U2P3_ENABLE; + else + val &= ~URE_U2P3_ENABLE; + ure_write_2(sc, URE_USB_U2P3_CTRL, URE_MCU_TYPE_USB, val); + + memset(u1u2, 0xff, sizeof(u1u2)); + ure_write_mem(sc, URE_USB_TOLERANCE, + URE_MCU_TYPE_USB | URE_BYTE_EN_SIX_BYTES, u1u2, sizeof(u1u2)); +} + +static void ure_stop(struct usb_ether *ue) { struct ure_softc *sc = uether_getsc(ue); @@ -1011,6 +1161,43 @@ ure_init_fifo(struct ure_softc *sc) ure_read_4(sc, URE_PLA_RCR, URE_MCU_TYPE_PLA) & ~URE_RCR_ACPT_ALL); + if (!(sc->sc_flags & URE_FLAG_8152)) { + if (sc->sc_chip & (URE_CHIP_VER_5C00 | URE_CHIP_VER_5C10 | + URE_CHIP_VER_5C20)) { + ure_ocp_reg_write(sc, URE_OCP_ADC_CFG, + URE_CKADSEL_L | URE_ADC_EN | URE_EN_EMI_L); + } + if (sc->sc_chip & URE_CHIP_VER_5C00) { + ure_ocp_reg_write(sc, URE_OCP_EEE_CFG, + ure_ocp_reg_read(sc, URE_OCP_EEE_CFG) & + ~URE_CTAP_SHORT_EN); + } + ure_ocp_reg_write(sc, URE_OCP_POWER_CFG, + ure_ocp_reg_read(sc, URE_OCP_POWER_CFG) | + URE_EEE_CLKDIV_EN); + ure_ocp_reg_write(sc, URE_OCP_DOWN_SPEED, + ure_ocp_reg_read(sc, URE_OCP_DOWN_SPEED) | + URE_EN_10M_BGOFF); + ure_ocp_reg_write(sc, URE_OCP_POWER_CFG, + ure_ocp_reg_read(sc, URE_OCP_POWER_CFG) | + URE_EN_10M_PLLOFF); + ure_ocp_reg_write(sc, URE_OCP_SRAM_ADDR, URE_SRAM_IMPEDANCE); + ure_ocp_reg_write(sc, URE_OCP_SRAM_DATA, 0x0b13); + ure_write_2(sc, URE_PLA_PHY_PWR, URE_MCU_TYPE_PLA, + ure_read_2(sc, URE_PLA_PHY_PWR, URE_MCU_TYPE_PLA) | + URE_PFM_PWM_SWITCH); + + /* Enable LPF corner auto tune. */ + ure_ocp_reg_write(sc, URE_OCP_SRAM_ADDR, URE_SRAM_LPF_CFG); + ure_ocp_reg_write(sc, URE_OCP_SRAM_DATA, 0xf70f); + + /* Adjust 10M amplitude. */ + ure_ocp_reg_write(sc, URE_OCP_SRAM_ADDR, URE_SRAM_10M_AMP1); + ure_ocp_reg_write(sc, URE_OCP_SRAM_DATA, 0x00af); + ure_ocp_reg_write(sc, URE_OCP_SRAM_ADDR, URE_SRAM_10M_AMP2); + ure_ocp_reg_write(sc, URE_OCP_SRAM_DATA, 0x0208); + } + ure_reset(sc); ure_write_1(sc, URE_PLA_CR, URE_MCU_TYPE_PLA, 0); diff --git a/sys/dev/usb/net/if_urereg.h b/sys/dev/usb/net/if_urereg.h index 89c34f5a64ca..8eff1c25db4f 100644 --- a/sys/dev/usb/net/if_urereg.h +++ b/sys/dev/usb/net/if_urereg.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2015 Kevin Lo <kevlo@FreeBSD.org> + * Copyright (c) 2015-2016 Kevin Lo <kevlo@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -86,7 +86,7 @@ #define URE_PLA_OCP_GPHY_BASE 0xe86c #define URE_PLA_TELLYCNT 0xe890 #define URE_PLA_SFF_STS_7 0xe8de -#define URE_PLA_PHYSTATUS 0xe908 +#define URE_GMEDIASTAT 0xe908 #define URE_USB_USB2PHY 0xb41e #define URE_USB_SSPHYLINK2 0xb428 @@ -424,10 +424,15 @@ struct ure_softc { u_int sc_flags; #define URE_FLAG_LINK 0x0001 +#define URE_FLAG_8152 0x1000 /* RTL8152 */ u_int sc_chip; #define URE_CHIP_VER_4C00 0x01 #define URE_CHIP_VER_4C10 0x02 +#define URE_CHIP_VER_5C00 0x04 +#define URE_CHIP_VER_5C10 0x08 +#define URE_CHIP_VER_5C20 0x10 +#define URE_CHIP_VER_5C30 0x20 }; #define URE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) diff --git a/sys/dev/usb/net/uhso.c b/sys/dev/usb/net/uhso.c index c94e67625a94..d8db44986b53 100644 --- a/sys/dev/usb/net/uhso.c +++ b/sys/dev/usb/net/uhso.c @@ -1752,7 +1752,7 @@ uhso_if_rxflush(void *arg) * Allocate a new mbuf for this IP packet and * copy the IP-packet into it. */ - m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); + m = m_getcl(M_WAITOK, MT_DATA, M_PKTHDR); memcpy(mtod(m, uint8_t *), mtod(m0, uint8_t *), iplen); m->m_pkthdr.len = m->m_len = iplen; diff --git a/sys/dev/usb/usb_device.c b/sys/dev/usb/usb_device.c index bc0f50e5ead3..56a6095a16e9 100644 --- a/sys/dev/usb/usb_device.c +++ b/sys/dev/usb/usb_device.c @@ -1938,8 +1938,8 @@ config_done: udev->ugen_symlink = usb_alloc_symlink(udev->ugen_name); /* Announce device */ - printf("%s: <%s> at %s\n", udev->ugen_name, - usb_get_manufacturer(udev), + printf("%s: <%s %s> at %s\n", udev->ugen_name, + usb_get_manufacturer(udev), usb_get_product(udev), device_get_nameunit(udev->bus->bdev)); #endif @@ -2148,8 +2148,9 @@ usb_free_device(struct usb_device *udev, uint8_t flag) #if USB_HAVE_UGEN if (!rebooting) { - printf("%s: <%s> at %s (disconnected)\n", udev->ugen_name, - usb_get_manufacturer(udev), device_get_nameunit(bus->bdev)); + printf("%s: <%s %s> at %s (disconnected)\n", udev->ugen_name, + usb_get_manufacturer(udev), usb_get_product(udev), + device_get_nameunit(bus->bdev)); } /* Destroy UGEN symlink, if any */ diff --git a/sys/dev/xen/gntdev/gntdev.c b/sys/dev/xen/gntdev/gntdev.c new file mode 100644 index 000000000000..467a0c05df85 --- /dev/null +++ b/sys/dev/xen/gntdev/gntdev.c @@ -0,0 +1,1275 @@ +/*- + * Copyright (c) 2016 Akshay Jaggi <jaggi@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, 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. + * + * gntdev.c + * + * Interface to /dev/xen/gntdev. + * + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/uio.h> +#include <sys/bus.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/rwlock.h> +#include <sys/selinfo.h> +#include <sys/poll.h> +#include <sys/conf.h> +#include <sys/fcntl.h> +#include <sys/ioccom.h> +#include <sys/rman.h> +#include <sys/tree.h> +#include <sys/module.h> +#include <sys/proc.h> +#include <sys/bitset.h> +#include <sys/queue.h> +#include <sys/mman.h> +#include <sys/syslog.h> +#include <sys/taskqueue.h> + +#include <vm/vm.h> +#include <vm/vm_param.h> +#include <vm/vm_extern.h> +#include <vm/vm_kern.h> +#include <vm/vm_page.h> +#include <vm/vm_map.h> +#include <vm/vm_object.h> +#include <vm/vm_pager.h> +#include <vm/vm_phys.h> + +#include <machine/md_var.h> + +#include <xen/xen-os.h> +#include <xen/hypervisor.h> +#include <xen/error.h> +#include <xen/xen_intr.h> +#include <xen/gnttab.h> +#include <xen/gntdev.h> + +MALLOC_DEFINE(M_GNTDEV, "gntdev", "Xen grant-table user-space device"); + +#define MAX_OFFSET_COUNT ((0xffffffffffffffffull >> PAGE_SHIFT) + 1) + +static d_open_t gntdev_open; +static d_ioctl_t gntdev_ioctl; +static d_mmap_single_t gntdev_mmap_single; + +static struct cdevsw gntdev_devsw = { + .d_version = D_VERSION, + .d_open = gntdev_open, + .d_ioctl = gntdev_ioctl, + .d_mmap_single = gntdev_mmap_single, + .d_name = "gntdev", +}; + +static device_t gntdev_dev = NULL; + +struct gntdev_gref; +struct gntdev_gmap; +STAILQ_HEAD(gref_list_head, gntdev_gref); +STAILQ_HEAD(gmap_list_head, gntdev_gmap); +RB_HEAD(gref_tree_head, gntdev_gref); +RB_HEAD(gmap_tree_head, gntdev_gmap); + +struct file_offset_struct { + RB_ENTRY(file_offset_struct) next; + uint64_t file_offset; + uint64_t count; +}; + +static int +offset_cmp(struct file_offset_struct *f1, struct file_offset_struct *f2) +{ + return (f1->file_offset - f2->file_offset); +} + +RB_HEAD(file_offset_head, file_offset_struct); +RB_GENERATE_STATIC(file_offset_head, file_offset_struct, next, offset_cmp); + +struct per_user_data { + struct mtx user_data_lock; + struct gref_tree_head gref_tree; + struct gmap_tree_head gmap_tree; + struct file_offset_head file_offset; +}; + +/* + * Get offset into the file which will be used while mmapping the + * appropriate pages by the userspace program. + */ +static int +get_file_offset(struct per_user_data *priv_user, uint32_t count, + uint64_t *file_offset) +{ + struct file_offset_struct *offset, *offset_tmp; + + if (count == 0) + return (EINVAL); + mtx_lock(&priv_user->user_data_lock); + RB_FOREACH_SAFE(offset, file_offset_head, &priv_user->file_offset, + offset_tmp) { + if (offset->count >= count) { + offset->count -= count; + *file_offset = offset->file_offset + offset->count * + PAGE_SIZE; + if (offset->count == 0) { + RB_REMOVE(file_offset_head, + &priv_user->file_offset, offset); + free(offset, M_GNTDEV); + } + mtx_unlock(&priv_user->user_data_lock); + return (0); + } + } + mtx_unlock(&priv_user->user_data_lock); + + return (ENOSPC); +} + +static void +put_file_offset(struct per_user_data *priv_user, uint32_t count, + uint64_t file_offset) +{ + struct file_offset_struct *offset, *offset_nxt, *offset_prv; + + offset = malloc(sizeof(*offset), M_GNTDEV, M_WAITOK | M_ZERO); + offset->file_offset = file_offset; + offset->count = count; + + mtx_lock(&priv_user->user_data_lock); + RB_INSERT(file_offset_head, &priv_user->file_offset, offset); + offset_nxt = RB_NEXT(file_offset_head, &priv_user->file_offset, offset); + offset_prv = RB_PREV(file_offset_head, &priv_user->file_offset, offset); + if (offset_nxt != NULL && + offset_nxt->file_offset == offset->file_offset + offset->count * + PAGE_SIZE) { + offset->count += offset_nxt->count; + RB_REMOVE(file_offset_head, &priv_user->file_offset, + offset_nxt); + free(offset_nxt, M_GNTDEV); + } + if (offset_prv != NULL && + offset->file_offset == offset_prv->file_offset + offset_prv->count * + PAGE_SIZE) { + offset_prv->count += offset->count; + RB_REMOVE(file_offset_head, &priv_user->file_offset, offset); + free(offset, M_GNTDEV); + } + mtx_unlock(&priv_user->user_data_lock); +} + +static int gntdev_gmap_pg_ctor(void *handle, vm_ooffset_t size, + vm_prot_t prot, vm_ooffset_t foff, struct ucred *cred, u_short *color); +static void gntdev_gmap_pg_dtor(void *handle); +static int gntdev_gmap_pg_fault(vm_object_t object, vm_ooffset_t offset, + int prot, vm_page_t *mres); + +static struct cdev_pager_ops gntdev_gmap_pg_ops = { + .cdev_pg_fault = gntdev_gmap_pg_fault, + .cdev_pg_ctor = gntdev_gmap_pg_ctor, + .cdev_pg_dtor = gntdev_gmap_pg_dtor, +}; + +struct cleanup_data_struct { + struct mtx to_kill_grefs_mtx; + struct mtx to_kill_gmaps_mtx; + struct gref_list_head to_kill_grefs; + struct gmap_list_head to_kill_gmaps; +}; + +static struct cleanup_data_struct cleanup_data = { + .to_kill_grefs = STAILQ_HEAD_INITIALIZER(cleanup_data.to_kill_grefs), + .to_kill_gmaps = STAILQ_HEAD_INITIALIZER(cleanup_data.to_kill_gmaps), +}; +MTX_SYSINIT(to_kill_grefs_mtx, &cleanup_data.to_kill_grefs_mtx, + "gntdev to_kill_grefs mutex", MTX_DEF); +MTX_SYSINIT(to_kill_gmaps_mtx, &cleanup_data.to_kill_gmaps_mtx, + "gntdev to_kill_gmaps mutex", MTX_DEF); + +static void cleanup_function(void *arg, __unused int pending); +static struct task cleanup_task = TASK_INITIALIZER(0, cleanup_function, + &cleanup_data); + +struct notify_data { + uint64_t index; + uint32_t action; + uint32_t event_channel_port; + xen_intr_handle_t notify_evtchn_handle; +}; + +static void notify(struct notify_data *notify, vm_page_t page); + +/*-------------------- Grant Allocation Methods -----------------------------*/ + +struct gntdev_gref { + union gref_next_union { + STAILQ_ENTRY(gntdev_gref) list; + RB_ENTRY(gntdev_gref) tree; + } gref_next; + uint64_t file_index; + grant_ref_t gref_id; + vm_page_t page; + struct notify_data *notify; +}; + +static int +gref_cmp(struct gntdev_gref *g1, struct gntdev_gref *g2) +{ + return (g1->file_index - g2->file_index); +} + +RB_GENERATE_STATIC(gref_tree_head, gntdev_gref, gref_next.tree, gref_cmp); + +/* + * Traverse over the device-list of to-be-deleted grants allocated, and + * if all accesses, both local mmaps and foreign maps, to them have ended, + * destroy them. + */ +static void +gref_list_dtor(struct cleanup_data_struct *cleanup_data) +{ + struct gref_list_head tmp_grefs; + struct gntdev_gref *gref, *gref_tmp, *gref_previous; + + STAILQ_INIT(&tmp_grefs); + mtx_lock(&cleanup_data->to_kill_grefs_mtx); + STAILQ_SWAP(&cleanup_data->to_kill_grefs, &tmp_grefs, gntdev_gref); + mtx_unlock(&cleanup_data->to_kill_grefs_mtx); + + gref_previous = NULL; + STAILQ_FOREACH_SAFE(gref, &tmp_grefs, gref_next.list, gref_tmp) { + if (gref->page && gref->page->object == NULL) { + if (gref->notify) { + notify(gref->notify, gref->page); + } + if (gref->gref_id != GRANT_REF_INVALID) { + if (gnttab_query_foreign_access(gref->gref_id)) + continue; + if (gnttab_end_foreign_access_ref(gref->gref_id) + == 0) + continue; + gnttab_free_grant_reference(gref->gref_id); + } + vm_page_unwire(gref->page, PQ_NONE); + vm_page_free(gref->page); + gref->page = NULL; + } + if (gref->page == NULL) { + if (gref_previous == NULL) + STAILQ_REMOVE_HEAD(&tmp_grefs, gref_next.list); + else + STAILQ_REMOVE_AFTER(&tmp_grefs, gref_previous, + gref_next.list); + if (gref->notify) + free(gref->notify, M_GNTDEV); + free(gref, M_GNTDEV); + } + else + gref_previous = gref; + } + + if (!STAILQ_EMPTY(&tmp_grefs)) { + mtx_lock(&cleanup_data->to_kill_grefs_mtx); + STAILQ_CONCAT(&cleanup_data->to_kill_grefs, &tmp_grefs); + mtx_unlock(&cleanup_data->to_kill_grefs_mtx); + } +} + +/* + * Find count number of contiguous allocated grants for a given userspace + * program by file-offset (index). + */ +static struct gntdev_gref* +gntdev_find_grefs(struct per_user_data *priv_user, + uint64_t index, uint32_t count) +{ + struct gntdev_gref find_gref, *gref, *gref_start = NULL; + + find_gref.file_index = index; + + mtx_lock(&priv_user->user_data_lock); + gref_start = RB_FIND(gref_tree_head, &priv_user->gref_tree, &find_gref); + for (gref = gref_start; gref != NULL && count > 0; gref = + RB_NEXT(gref_tree_head, &priv_user->gref_tree, gref)) { + if (index != gref->file_index) + break; + index += PAGE_SIZE; + count--; + } + mtx_unlock(&priv_user->user_data_lock); + + if (count) + return (NULL); + return (gref_start); +} + +/* + * IOCTL_GNTDEV_ALLOC_GREF + * Allocate required number of wired pages for the request, grant foreign + * access to the physical frames for these pages, and add details about + * this allocation to the per user private data, so that these pages can + * be mmapped by the userspace program. + */ +static int +gntdev_alloc_gref(struct ioctl_gntdev_alloc_gref *arg) +{ + uint32_t i; + int error, readonly; + uint64_t file_offset; + struct gntdev_gref *grefs; + struct per_user_data *priv_user; + + readonly = !(arg->flags & GNTDEV_ALLOC_FLAG_WRITABLE); + + error = devfs_get_cdevpriv((void**) &priv_user); + if (error != 0) + return (EINVAL); + + /* Cleanup grefs and free pages. */ + taskqueue_enqueue(taskqueue_thread, &cleanup_task); + + /* Get file offset for this request. */ + error = get_file_offset(priv_user, arg->count, &file_offset); + if (error != 0) + return (error); + + /* Allocate grefs. */ + grefs = malloc(sizeof(*grefs) * arg->count, M_GNTDEV, M_WAITOK); + + for (i = 0; i < arg->count; i++) { + grefs[i].file_index = file_offset + i * PAGE_SIZE; + grefs[i].gref_id = GRANT_REF_INVALID; + grefs[i].notify = NULL; + grefs[i].page = vm_page_alloc(NULL, 0, VM_ALLOC_NORMAL + | VM_ALLOC_NOOBJ | VM_ALLOC_WIRED | VM_ALLOC_ZERO); + if (grefs[i].page == NULL) { + log(LOG_ERR, "Page allocation failed."); + error = ENOMEM; + break; + } + if ((grefs[i].page->flags & PG_ZERO) == 0) { + /* + * Zero the allocated page, as we don't want to + * leak our memory to other domains. + */ + pmap_zero_page(grefs[i].page); + } + grefs[i].page->valid = VM_PAGE_BITS_ALL; + + error = gnttab_grant_foreign_access(arg->domid, + (VM_PAGE_TO_PHYS(grefs[i].page) >> PAGE_SHIFT), + readonly, &grefs[i].gref_id); + if (error != 0) { + log(LOG_ERR, "Grant Table Hypercall failed."); + break; + } + } + + if (error != 0) { + /* + * If target domain maps the gref (by guessing the gref-id), + * then we can't clean it up yet and we have to leave the + * page in place so as to not leak our memory to that domain. + * Add it to a global list to be cleaned up later. + */ + mtx_lock(&cleanup_data.to_kill_grefs_mtx); + for (i = 0; i < arg->count; i++) + STAILQ_INSERT_TAIL(&cleanup_data.to_kill_grefs, + &grefs[i], gref_next.list); + mtx_unlock(&cleanup_data.to_kill_grefs_mtx); + + taskqueue_enqueue(taskqueue_thread, &cleanup_task); + + return (error); + } + + /* Copy the output values. */ + arg->index = file_offset; + for (i = 0; i < arg->count; i++) + arg->gref_ids[i] = grefs[i].gref_id; + + /* Modify the per user private data. */ + mtx_lock(&priv_user->user_data_lock); + for (i = 0; i < arg->count; i++) + RB_INSERT(gref_tree_head, &priv_user->gref_tree, &grefs[i]); + mtx_unlock(&priv_user->user_data_lock); + + return (error); +} + +/* + * IOCTL_GNTDEV_DEALLOC_GREF + * Remove grant allocation information from the per user private data, so + * that it can't be mmapped anymore by the userspace program, and add it + * to the to-be-deleted grants global device-list. + */ +static int +gntdev_dealloc_gref(struct ioctl_gntdev_dealloc_gref *arg) +{ + int error; + uint32_t count; + struct gntdev_gref *gref, *gref_tmp; + struct per_user_data *priv_user; + + error = devfs_get_cdevpriv((void**) &priv_user); + if (error != 0) + return (EINVAL); + + gref = gntdev_find_grefs(priv_user, arg->index, arg->count); + if (gref == NULL) { + log(LOG_ERR, "Can't find requested grant-refs."); + return (EINVAL); + } + + /* Remove the grefs from user private data. */ + count = arg->count; + mtx_lock(&priv_user->user_data_lock); + mtx_lock(&cleanup_data.to_kill_grefs_mtx); + for (; gref != NULL && count > 0; gref = gref_tmp) { + gref_tmp = RB_NEXT(gref_tree_head, &priv_user->gref_tree, gref); + RB_REMOVE(gref_tree_head, &priv_user->gref_tree, gref); + STAILQ_INSERT_TAIL(&cleanup_data.to_kill_grefs, gref, + gref_next.list); + count--; + } + mtx_unlock(&cleanup_data.to_kill_grefs_mtx); + mtx_unlock(&priv_user->user_data_lock); + + taskqueue_enqueue(taskqueue_thread, &cleanup_task); + put_file_offset(priv_user, arg->count, arg->index); + + return (0); +} + +/*-------------------- Grant Mapping Methods --------------------------------*/ + +struct gntdev_gmap_map { + vm_object_t mem; + struct resource *pseudo_phys_res; + int pseudo_phys_res_id; + vm_paddr_t phys_base_addr; +}; + +struct gntdev_gmap { + union gmap_next_union { + STAILQ_ENTRY(gntdev_gmap) list; + RB_ENTRY(gntdev_gmap) tree; + } gmap_next; + uint64_t file_index; + uint32_t count; + struct gnttab_map_grant_ref *grant_map_ops; + struct gntdev_gmap_map *map; + struct notify_data *notify; +}; + +static int +gmap_cmp(struct gntdev_gmap *g1, struct gntdev_gmap *g2) +{ + return (g1->file_index - g2->file_index); +} + +RB_GENERATE_STATIC(gmap_tree_head, gntdev_gmap, gmap_next.tree, gmap_cmp); + +/* + * Traverse over the device-list of to-be-deleted grant mappings, and if + * the region is no longer mmapped by anyone, free the memory used to + * store information about the mapping. + */ +static void +gmap_list_dtor(struct cleanup_data_struct *cleanup_data) +{ + struct gmap_list_head tmp_gmaps; + struct gntdev_gmap *gmap, *gmap_tmp, *gmap_previous; + + STAILQ_INIT(&tmp_gmaps); + mtx_lock(&cleanup_data->to_kill_gmaps_mtx); + STAILQ_SWAP(&cleanup_data->to_kill_gmaps, &tmp_gmaps, gntdev_gmap); + mtx_unlock(&cleanup_data->to_kill_gmaps_mtx); + + gmap_previous = NULL; + STAILQ_FOREACH_SAFE(gmap, &tmp_gmaps, gmap_next.list, gmap_tmp) { + if (gmap->map == NULL) { + if (gmap_previous == NULL) + STAILQ_REMOVE_HEAD(&tmp_gmaps, gmap_next.list); + else + STAILQ_REMOVE_AFTER(&tmp_gmaps, gmap_previous, + gmap_next.list); + + if (gmap->notify) + free(gmap->notify, M_GNTDEV); + free(gmap->grant_map_ops, M_GNTDEV); + free(gmap, M_GNTDEV); + } + else + gmap_previous = gmap; + } + + if (!STAILQ_EMPTY(&tmp_gmaps)) { + mtx_lock(&cleanup_data->to_kill_gmaps_mtx); + STAILQ_CONCAT(&cleanup_data->to_kill_gmaps, &tmp_gmaps); + mtx_unlock(&cleanup_data->to_kill_gmaps_mtx); + } +} + +/* + * Find mapped grants for a given userspace program, by file-offset (index) + * and count, as supplied during the map-ioctl. + */ +static struct gntdev_gmap* +gntdev_find_gmap(struct per_user_data *priv_user, + uint64_t index, uint32_t count) +{ + struct gntdev_gmap find_gmap, *gmap; + + find_gmap.file_index = index; + + mtx_lock(&priv_user->user_data_lock); + gmap = RB_FIND(gmap_tree_head, &priv_user->gmap_tree, &find_gmap); + mtx_unlock(&priv_user->user_data_lock); + + if (gmap != NULL && gmap->count == count) + return (gmap); + return (NULL); +} + +/* + * Remove the pages from the mgtdevice pager, call the unmap hypercall, + * free the xenmem resource. This function is called during the + * destruction of the mgtdevice pager, which happens when all mmaps to + * it have been removed, and the unmap-ioctl has been performed. + */ +static int +notify_unmap_cleanup(struct gntdev_gmap *gmap) +{ + uint32_t i; + int error, count; + vm_page_t m; + struct gnttab_unmap_grant_ref *unmap_ops; + + unmap_ops = malloc(sizeof(struct gnttab_unmap_grant_ref) * gmap->count, + M_GNTDEV, M_WAITOK); + + /* Enumerate freeable maps. */ + count = 0; + for (i = 0; i < gmap->count; i++) { + if (gmap->grant_map_ops[i].handle != -1) { + unmap_ops[count].handle = gmap->grant_map_ops[i].handle; + unmap_ops[count].host_addr = + gmap->grant_map_ops[i].host_addr; + unmap_ops[count].dev_bus_addr = 0; + count++; + } + } + + /* Perform notification. */ + if (count > 0 && gmap->notify) { + vm_page_t page; + uint64_t page_offset; + + page_offset = gmap->notify->index - gmap->file_index; + page = PHYS_TO_VM_PAGE(gmap->map->phys_base_addr + page_offset); + notify(gmap->notify, page); + } + + /* Free the pages. */ + VM_OBJECT_WLOCK(gmap->map->mem); +retry: + for (i = 0; i < gmap->count; i++) { + m = vm_page_lookup(gmap->map->mem, i); + if (m == NULL) + continue; + if (vm_page_sleep_if_busy(m, "pcmdum")) + goto retry; + cdev_pager_free_page(gmap->map->mem, m); + } + VM_OBJECT_WUNLOCK(gmap->map->mem); + + /* Perform unmap hypercall. */ + error = HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, + unmap_ops, count); + + for (i = 0; i < gmap->count; i++) { + gmap->grant_map_ops[i].handle = -1; + gmap->grant_map_ops[i].host_addr = 0; + } + + if (gmap->map) { + error = xenmem_free(gntdev_dev, gmap->map->pseudo_phys_res_id, + gmap->map->pseudo_phys_res); + KASSERT(error == 0, + ("Unable to release memory resource: %d", error)); + + free(gmap->map, M_GNTDEV); + gmap->map = NULL; + } + + free(unmap_ops, M_GNTDEV); + + return (error); +} + +/* + * IOCTL_GNTDEV_MAP_GRANT_REF + * Populate structures for mapping the grant reference in the per user + * private data. Actual resource allocation and map hypercall is performed + * during the mmap. + */ +static int +gntdev_map_grant_ref(struct ioctl_gntdev_map_grant_ref *arg) +{ + uint32_t i; + int error; + struct gntdev_gmap *gmap; + struct per_user_data *priv_user; + + error = devfs_get_cdevpriv((void**) &priv_user); + if (error != 0) + return (EINVAL); + + gmap = malloc(sizeof(*gmap), M_GNTDEV, M_WAITOK | M_ZERO); + gmap->count = arg->count; + gmap->grant_map_ops = + malloc(sizeof(struct gnttab_map_grant_ref) * arg->count, + M_GNTDEV, M_WAITOK | M_ZERO); + + error = get_file_offset(priv_user, arg->count, &gmap->file_index); + if (error != 0) + return (error); + + for (i = 0; i < arg->count; i++) { + gmap->grant_map_ops[i].dom = arg->refs[i].domid; + gmap->grant_map_ops[i].ref = arg->refs[i].ref; + gmap->grant_map_ops[i].handle = -1; + gmap->grant_map_ops[i].flags = GNTMAP_host_map; + } + + mtx_lock(&priv_user->user_data_lock); + RB_INSERT(gmap_tree_head, &priv_user->gmap_tree, gmap); + mtx_unlock(&priv_user->user_data_lock); + + arg->index = gmap->file_index; + + return (error); +} + +/* + * IOCTL_GNTDEV_UNMAP_GRANT_REF + * Remove the map information from the per user private data and add it + * to the global device-list of mappings to be deleted. A reference to + * the mgtdevice pager is also decreased, the reason for which is + * explained in mmap_gmap(). + */ +static int +gntdev_unmap_grant_ref(struct ioctl_gntdev_unmap_grant_ref *arg) +{ + int error; + struct gntdev_gmap *gmap; + struct per_user_data *priv_user; + + error = devfs_get_cdevpriv((void**) &priv_user); + if (error != 0) + return (EINVAL); + + gmap = gntdev_find_gmap(priv_user, arg->index, arg->count); + if (gmap == NULL) { + log(LOG_ERR, "Can't find requested grant-map."); + return (EINVAL); + } + + mtx_lock(&priv_user->user_data_lock); + mtx_lock(&cleanup_data.to_kill_gmaps_mtx); + RB_REMOVE(gmap_tree_head, &priv_user->gmap_tree, gmap); + STAILQ_INSERT_TAIL(&cleanup_data.to_kill_gmaps, gmap, gmap_next.list); + mtx_unlock(&cleanup_data.to_kill_gmaps_mtx); + mtx_unlock(&priv_user->user_data_lock); + + if (gmap->map) + vm_object_deallocate(gmap->map->mem); + + taskqueue_enqueue(taskqueue_thread, &cleanup_task); + put_file_offset(priv_user, arg->count, arg->index); + + return (0); +} + +/* + * IOCTL_GNTDEV_GET_OFFSET_FOR_VADDR + * Get file-offset and count for a given mapping, from the virtual address + * where the mapping is mmapped. + * Please note, this only works for grants mapped by this domain, and not + * grants allocated. Count doesn't make much sense in reference to grants + * allocated. Also, because this function is present in the linux gntdev + * device, but not in the linux gntalloc one, most userspace code only use + * it for mapped grants. + */ +static int +gntdev_get_offset_for_vaddr(struct ioctl_gntdev_get_offset_for_vaddr *arg, + struct thread *td) +{ + int error; + vm_map_t map; + vm_map_entry_t entry; + vm_object_t mem; + vm_pindex_t pindex; + vm_prot_t prot; + boolean_t wired; + struct gntdev_gmap *gmap; + + map = &td->td_proc->p_vmspace->vm_map; + error = vm_map_lookup(&map, arg->vaddr, VM_PROT_NONE, &entry, + &mem, &pindex, &prot, &wired); + if (error != KERN_SUCCESS) + return (EINVAL); + vm_map_lookup_done(map, entry); + + if ((mem->type != OBJT_MGTDEVICE) || + (mem->un_pager.devp.ops != &gntdev_gmap_pg_ops)) + return (EINVAL); + + gmap = mem->handle; + if (gmap == NULL || + (entry->end - entry->start) != (gmap->count * PAGE_SIZE)) + return (EINVAL); + + arg->count = gmap->count; + arg->offset = gmap->file_index; + return (0); +} + +/*-------------------- Grant Mapping Pager ----------------------------------*/ + +static int +gntdev_gmap_pg_ctor(void *handle, vm_ooffset_t size, vm_prot_t prot, + vm_ooffset_t foff, struct ucred *cred, u_short *color) +{ + + return (0); +} + +static void +gntdev_gmap_pg_dtor(void *handle) +{ + + notify_unmap_cleanup((struct gntdev_gmap *)handle); +} + +static int +gntdev_gmap_pg_fault(vm_object_t object, vm_ooffset_t offset, int prot, + vm_page_t *mres) +{ + struct gntdev_gmap *gmap = object->handle; + vm_pindex_t pidx, ridx; + vm_page_t page, oldm; + vm_ooffset_t relative_offset; + + if (gmap->map == NULL) + return (VM_PAGER_FAIL); + + relative_offset = offset - gmap->file_index; + + pidx = OFF_TO_IDX(offset); + ridx = OFF_TO_IDX(relative_offset); + if (ridx >= gmap->count || + gmap->grant_map_ops[ridx].status != GNTST_okay) + return (VM_PAGER_FAIL); + + page = PHYS_TO_VM_PAGE(gmap->map->phys_base_addr + relative_offset); + if (page == NULL) + return (VM_PAGER_FAIL); + + KASSERT((page->flags & PG_FICTITIOUS) != 0, + ("not fictitious %p", page)); + KASSERT(page->wire_count == 1, ("wire_count not 1 %p", page)); + KASSERT(vm_page_busied(page) == 0, ("page %p is busy", page)); + + if (*mres != NULL) { + oldm = *mres; + vm_page_lock(oldm); + vm_page_free(oldm); + vm_page_unlock(oldm); + *mres = NULL; + } + + vm_page_insert(page, object, pidx); + page->valid = VM_PAGE_BITS_ALL; + vm_page_xbusy(page); + *mres = page; + return (VM_PAGER_OK); +} + +/*------------------ Grant Table Methods ------------------------------------*/ + +static void +notify(struct notify_data *notify, vm_page_t page) +{ + if (notify->action & UNMAP_NOTIFY_CLEAR_BYTE) { + uint8_t *mem; + uint64_t offset; + + offset = notify->index & PAGE_MASK; + mem = (uint8_t *)pmap_quick_enter_page(page); + mem[offset] = 0; + pmap_quick_remove_page((vm_offset_t)mem); + } + if (notify->action & UNMAP_NOTIFY_SEND_EVENT) { + xen_intr_signal(notify->notify_evtchn_handle); + xen_intr_unbind(¬ify->notify_evtchn_handle); + } + notify->action = 0; +} + +/* + * Helper to copy new arguments from the notify ioctl into + * the existing notify data. + */ +static int +copy_notify_helper(struct notify_data *destination, + struct ioctl_gntdev_unmap_notify *source) +{ + xen_intr_handle_t handlep = NULL; + + /* + * "Get" before "Put"ting previous reference, as we might be + * holding the last reference to the event channel port. + */ + if (source->action & UNMAP_NOTIFY_SEND_EVENT) + if (xen_intr_get_evtchn_from_port(source->event_channel_port, + &handlep) != 0) + return (EINVAL); + + if (destination->action & UNMAP_NOTIFY_SEND_EVENT) + xen_intr_unbind(&destination->notify_evtchn_handle); + + destination->action = source->action; + destination->event_channel_port = source->event_channel_port; + destination->index = source->index; + destination->notify_evtchn_handle = handlep; + + return (0); +} + +/* + * IOCTL_GNTDEV_SET_UNMAP_NOTIFY + * Set unmap notification inside the appropriate grant. It sends a + * notification when the grant is completely munmapped by this domain + * and ready for destruction. + */ +static int +gntdev_set_unmap_notify(struct ioctl_gntdev_unmap_notify *arg) +{ + int error; + uint64_t index; + struct per_user_data *priv_user; + struct gntdev_gref *gref = NULL; + struct gntdev_gmap *gmap; + + error = devfs_get_cdevpriv((void**) &priv_user); + if (error != 0) + return (EINVAL); + + if (arg->action & ~(UNMAP_NOTIFY_CLEAR_BYTE|UNMAP_NOTIFY_SEND_EVENT)) + return (EINVAL); + + index = arg->index & ~PAGE_MASK; + gref = gntdev_find_grefs(priv_user, index, 1); + if (gref) { + if (gref->notify == NULL) + gref->notify = malloc(sizeof(*arg), M_GNTDEV, + M_WAITOK | M_ZERO); + return (copy_notify_helper(gref->notify, arg)); + } + + error = EINVAL; + mtx_lock(&priv_user->user_data_lock); + RB_FOREACH(gmap, gmap_tree_head, &priv_user->gmap_tree) { + if (arg->index >= gmap->file_index && + arg->index < gmap->file_index + gmap->count * PAGE_SIZE) { + if (gmap->notify == NULL) + gmap->notify = malloc(sizeof(*arg), M_GNTDEV, + M_WAITOK | M_ZERO); + error = copy_notify_helper(gmap->notify, arg); + break; + } + } + mtx_unlock(&priv_user->user_data_lock); + + return (error); +} + +/*------------------ Gntdev Char Device Methods -----------------------------*/ + +static void +cleanup_function(void *arg, __unused int pending) +{ + + gref_list_dtor((struct cleanup_data_struct *) arg); + gmap_list_dtor((struct cleanup_data_struct *) arg); +} + +static void +per_user_data_dtor(void *arg) +{ + struct gntdev_gref *gref, *gref_tmp; + struct gntdev_gmap *gmap, *gmap_tmp; + struct file_offset_struct *offset, *offset_tmp; + struct per_user_data *priv_user; + + priv_user = (struct per_user_data *) arg; + + mtx_lock(&priv_user->user_data_lock); + + mtx_lock(&cleanup_data.to_kill_grefs_mtx); + RB_FOREACH_SAFE(gref, gref_tree_head, &priv_user->gref_tree, gref_tmp) { + RB_REMOVE(gref_tree_head, &priv_user->gref_tree, gref); + STAILQ_INSERT_TAIL(&cleanup_data.to_kill_grefs, gref, + gref_next.list); + } + mtx_unlock(&cleanup_data.to_kill_grefs_mtx); + + mtx_lock(&cleanup_data.to_kill_gmaps_mtx); + RB_FOREACH_SAFE(gmap, gmap_tree_head, &priv_user->gmap_tree, gmap_tmp) { + RB_REMOVE(gmap_tree_head, &priv_user->gmap_tree, gmap); + STAILQ_INSERT_TAIL(&cleanup_data.to_kill_gmaps, gmap, + gmap_next.list); + if (gmap->map) + vm_object_deallocate(gmap->map->mem); + } + mtx_unlock(&cleanup_data.to_kill_gmaps_mtx); + + RB_FOREACH_SAFE(offset, file_offset_head, &priv_user->file_offset, + offset_tmp) { + RB_REMOVE(file_offset_head, &priv_user->file_offset, offset); + free(offset, M_GNTDEV); + } + + mtx_unlock(&priv_user->user_data_lock); + + taskqueue_enqueue(taskqueue_thread, &cleanup_task); + + mtx_destroy(&priv_user->user_data_lock); + free(priv_user, M_GNTDEV); +} + +static int +gntdev_open(struct cdev *dev, int flag, int otyp, struct thread *td) +{ + int error; + struct per_user_data *priv_user; + struct file_offset_struct *offset; + + priv_user = malloc(sizeof(*priv_user), M_GNTDEV, M_WAITOK | M_ZERO); + RB_INIT(&priv_user->gref_tree); + RB_INIT(&priv_user->gmap_tree); + RB_INIT(&priv_user->file_offset); + offset = malloc(sizeof(*offset), M_GNTDEV, M_WAITOK | M_ZERO); + offset->file_offset = 0; + offset->count = MAX_OFFSET_COUNT; + RB_INSERT(file_offset_head, &priv_user->file_offset, offset); + mtx_init(&priv_user->user_data_lock, + "per user data mutex", NULL, MTX_DEF); + + error = devfs_set_cdevpriv(priv_user, per_user_data_dtor); + if (error != 0) + per_user_data_dtor(priv_user); + + return (error); +} + +static int +gntdev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, + int fflag, struct thread *td) +{ + int error; + + switch (cmd) { + case IOCTL_GNTDEV_SET_UNMAP_NOTIFY: + error = gntdev_set_unmap_notify( + (struct ioctl_gntdev_unmap_notify*) data); + break; + case IOCTL_GNTDEV_ALLOC_GREF: + error = gntdev_alloc_gref( + (struct ioctl_gntdev_alloc_gref*) data); + break; + case IOCTL_GNTDEV_DEALLOC_GREF: + error = gntdev_dealloc_gref( + (struct ioctl_gntdev_dealloc_gref*) data); + break; + case IOCTL_GNTDEV_MAP_GRANT_REF: + error = gntdev_map_grant_ref( + (struct ioctl_gntdev_map_grant_ref*) data); + break; + case IOCTL_GNTDEV_UNMAP_GRANT_REF: + error = gntdev_unmap_grant_ref( + (struct ioctl_gntdev_unmap_grant_ref*) data); + break; + case IOCTL_GNTDEV_GET_OFFSET_FOR_VADDR: + error = gntdev_get_offset_for_vaddr( + (struct ioctl_gntdev_get_offset_for_vaddr*) data, td); + break; + default: + error = ENOSYS; + break; + } + + return (error); +} + +/* + * MMAP an allocated grant into user memory. + * Please note, that the grants must not already be mmapped, otherwise + * this function will fail. + */ +static int +mmap_gref(struct per_user_data *priv_user, struct gntdev_gref *gref_start, + uint32_t count, vm_size_t size, struct vm_object **object) +{ + vm_object_t mem_obj; + struct gntdev_gref *gref; + + mem_obj = vm_object_allocate(OBJT_PHYS, size); + if (mem_obj == NULL) + return (ENOMEM); + + mtx_lock(&priv_user->user_data_lock); + VM_OBJECT_WLOCK(mem_obj); + for (gref = gref_start; gref != NULL && count > 0; gref = + RB_NEXT(gref_tree_head, &priv_user->gref_tree, gref)) { + if (gref->page->object) + break; + + vm_page_insert(gref->page, mem_obj, + OFF_TO_IDX(gref->file_index)); + + count--; + } + VM_OBJECT_WUNLOCK(mem_obj); + mtx_unlock(&priv_user->user_data_lock); + + if (count) { + vm_object_deallocate(mem_obj); + return (EINVAL); + } + + *object = mem_obj; + + return (0); + +} + +/* + * MMAP a mapped grant into user memory. + */ +static int +mmap_gmap(struct per_user_data *priv_user, struct gntdev_gmap *gmap_start, + vm_ooffset_t *offset, vm_size_t size, struct vm_object **object, int nprot) +{ + uint32_t i; + int error; + + /* + * The grant map hypercall might already be done. + * If that is the case, increase a reference to the + * vm object and return the already allocated object. + */ + if (gmap_start->map) { + vm_object_reference(gmap_start->map->mem); + *object = gmap_start->map->mem; + return (0); + } + + gmap_start->map = malloc(sizeof(*(gmap_start->map)), M_GNTDEV, + M_WAITOK | M_ZERO); + + /* Allocate the xen pseudo physical memory resource. */ + gmap_start->map->pseudo_phys_res_id = 0; + gmap_start->map->pseudo_phys_res = xenmem_alloc(gntdev_dev, + &gmap_start->map->pseudo_phys_res_id, size); + if (gmap_start->map->pseudo_phys_res == NULL) { + free(gmap_start->map, M_GNTDEV); + gmap_start->map = NULL; + return (ENOMEM); + } + gmap_start->map->phys_base_addr = + rman_get_start(gmap_start->map->pseudo_phys_res); + + /* Allocate the mgtdevice pager. */ + gmap_start->map->mem = cdev_pager_allocate(gmap_start, OBJT_MGTDEVICE, + &gntdev_gmap_pg_ops, size, nprot, *offset, NULL); + if (gmap_start->map->mem == NULL) { + xenmem_free(gntdev_dev, gmap_start->map->pseudo_phys_res_id, + gmap_start->map->pseudo_phys_res); + free(gmap_start->map, M_GNTDEV); + gmap_start->map = NULL; + return (ENOMEM); + } + + for (i = 0; i < gmap_start->count; i++) { + gmap_start->grant_map_ops[i].host_addr = + gmap_start->map->phys_base_addr + i * PAGE_SIZE; + + if ((nprot & PROT_WRITE) == 0) + gmap_start->grant_map_ops[i].flags |= GNTMAP_readonly; + } + /* Make the MAP hypercall. */ + error = HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, + gmap_start->grant_map_ops, gmap_start->count); + if (error != 0) { + /* + * Deallocate pager. + * Pager deallocation will automatically take care of + * xenmem deallocation, etc. + */ + vm_object_deallocate(gmap_start->map->mem); + + return (EINVAL); + } + + /* Retry EAGAIN maps. */ + for (i = 0; i < gmap_start->count; i++) { + int delay = 1; + while (delay < 256 && + gmap_start->grant_map_ops[i].status == GNTST_eagain) { + HYPERVISOR_grant_table_op( GNTTABOP_map_grant_ref, + &gmap_start->grant_map_ops[i], 1); + pause(("gntmap"), delay * SBT_1MS); + delay++; + } + if (gmap_start->grant_map_ops[i].status == GNTST_eagain) + gmap_start->grant_map_ops[i].status = GNTST_bad_page; + + if (gmap_start->grant_map_ops[i].status != GNTST_okay) { + /* + * Deallocate pager. + * Pager deallocation will automatically take care of + * xenmem deallocation, notification, unmap hypercall, + * etc. + */ + vm_object_deallocate(gmap_start->map->mem); + + return (EINVAL); + } + } + + /* + * Add a reference to the vm object. We do not want + * the vm object to be deleted when all the mmaps are + * unmapped, because it may be re-mmapped. Instead, + * we want the object to be deleted, when along with + * munmaps, we have also processed the unmap-ioctl. + */ + vm_object_reference(gmap_start->map->mem); + + *object = gmap_start->map->mem; + + return (0); +} + +static int +gntdev_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, vm_size_t size, + struct vm_object **object, int nprot) +{ + int error; + uint32_t count; + struct gntdev_gref *gref_start; + struct gntdev_gmap *gmap_start; + struct per_user_data *priv_user; + + error = devfs_get_cdevpriv((void**) &priv_user); + if (error != 0) + return (EINVAL); + + count = OFF_TO_IDX(size); + + gref_start = gntdev_find_grefs(priv_user, *offset, count); + if (gref_start) { + error = mmap_gref(priv_user, gref_start, count, size, object); + return (error); + } + + gmap_start = gntdev_find_gmap(priv_user, *offset, count); + if (gmap_start) { + error = mmap_gmap(priv_user, gmap_start, offset, size, object, + nprot); + return (error); + } + + return (EINVAL); +} + +/*------------------ Private Device Attachment Functions --------------------*/ +static void +gntdev_identify(driver_t *driver, device_t parent) +{ + + KASSERT((xen_domain()), + ("Trying to attach gntdev device on non Xen domain")); + + if (BUS_ADD_CHILD(parent, 0, "gntdev", 0) == NULL) + panic("unable to attach gntdev user-space device"); +} + +static int +gntdev_probe(device_t dev) +{ + + gntdev_dev = dev; + device_set_desc(dev, "Xen grant-table user-space device"); + return (BUS_PROBE_NOWILDCARD); +} + +static int +gntdev_attach(device_t dev) +{ + + make_dev_credf(MAKEDEV_ETERNAL, &gntdev_devsw, 0, NULL, UID_ROOT, + GID_WHEEL, 0600, "xen/gntdev"); + return (0); +} + +/*-------------------- Private Device Attachment Data -----------------------*/ +static device_method_t gntdev_methods[] = { + DEVMETHOD(device_identify, gntdev_identify), + DEVMETHOD(device_probe, gntdev_probe), + DEVMETHOD(device_attach, gntdev_attach), + DEVMETHOD_END +}; + +static driver_t gntdev_driver = { + "gntdev", + gntdev_methods, + 0, +}; + +devclass_t gntdev_devclass; + +DRIVER_MODULE(gntdev, xenpv, gntdev_driver, gntdev_devclass, 0, 0); +MODULE_DEPEND(gntdev, xenpv, 1, 1, 1); diff --git a/sys/dev/xen/netfront/netfront.c b/sys/dev/xen/netfront/netfront.c index a68bc9675da5..3c080ef817f7 100644 --- a/sys/dev/xen/netfront/netfront.c +++ b/sys/dev/xen/netfront/netfront.c @@ -156,21 +156,6 @@ static int xn_get_responses(struct netfront_rxq *, #define virt_to_mfn(x) (vtophys(x) >> PAGE_SHIFT) #define INVALID_P2M_ENTRY (~0UL) - -struct xn_rx_stats -{ - u_long rx_packets; /* total packets received */ - u_long rx_bytes; /* total bytes received */ - u_long rx_errors; /* bad packets received */ -}; - -struct xn_tx_stats -{ - u_long tx_packets; /* total packets transmitted */ - u_long tx_bytes; /* total bytes transmitted */ - u_long tx_errors; /* packet transmit problems */ -}; - #define XN_QUEUE_NAME_LEN 8 /* xn{t,r}x_%u, allow for two digits */ struct netfront_rxq { struct netfront_info *info; @@ -190,8 +175,6 @@ struct netfront_rxq { struct lro_ctrl lro; struct callout rx_refill; - - struct xn_rx_stats stats; }; struct netfront_txq { @@ -215,8 +198,6 @@ struct netfront_txq { struct task defrtask; bool full; - - struct xn_tx_stats stats; }; struct netfront_info { @@ -1191,7 +1172,7 @@ xn_rxeof(struct netfront_rxq *rxq) if (__predict_false(err)) { if (m) (void )mbufq_enqueue(&mbufq_errq, m); - rxq->stats.rx_errors++; + if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); continue; } @@ -1216,9 +1197,6 @@ xn_rxeof(struct netfront_rxq *rxq) m->m_pkthdr.csum_flags |= CSUM_TSO; } - rxq->stats.rx_packets++; - rxq->stats.rx_bytes += m->m_pkthdr.len; - (void )mbufq_enqueue(&mbufq_rxq, m); rxq->ring.rsp_cons = i; } @@ -1304,12 +1282,6 @@ xn_txeof(struct netfront_txq *txq) "trying to free it again!")); M_ASSERTVALID(m); - /* - * Increment packet count if this is the last - * mbuf of the chain. - */ - if (!m->m_next) - if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); if (__predict_false(gnttab_query_foreign_access( txq->grant_ref[id]) != 0)) { panic("%s: grant id %u still in use by the " @@ -1701,10 +1673,12 @@ xn_assemble_tx_request(struct netfront_txq *txq, struct mbuf *m_head) } BPF_MTAP(ifp, m_head); - xn_txeof(txq); + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); + if_inc_counter(ifp, IFCOUNTER_OBYTES, m_head->m_pkthdr.len); + if (m_head->m_flags & M_MCAST) + if_inc_counter(ifp, IFCOUNTER_OMCASTS, 1); - txq->stats.tx_bytes += m_head->m_pkthdr.len; - txq->stats.tx_packets++; + xn_txeof(txq); return (0); } |
