aboutsummaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2016-10-31 19:02:42 +0000
committerDimitry Andric <dim@FreeBSD.org>2016-10-31 19:02:42 +0000
commit02ebdc78239c4e929e42896931a4f04526e04440 (patch)
treeb2635ce18a57392f126c5599b6baaf68c4b92575 /sys/dev
parent5763f79695f9b1ffacce55a8594cb7be08c3f31c (diff)
parent130a08a362287342b83f8a78914db38a325145a3 (diff)
Notes
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/aacraid/aacraid_pci.c2
-rw-r--r--sys/dev/bfe/if_bfe.c2
-rw-r--r--sys/dev/bxe/bxe.c4
-rw-r--r--sys/dev/chromebook_platform/chromebook_platform.c102
-rw-r--r--sys/dev/cxgbe/common/t4_hw.c37
-rw-r--r--sys/dev/cxgbe/t4_sge.c33
-rw-r--r--sys/dev/cyapa/cyapa.c110
-rw-r--r--sys/dev/dpaa/bman_fdt.c2
-rw-r--r--sys/dev/dpaa/qman_fdt.c12
-rw-r--r--sys/dev/evdev/evdev.c15
-rw-r--r--sys/dev/evdev/evdev.h66
-rw-r--r--sys/dev/evdev/evdev_utils.c7
-rw-r--r--sys/dev/fdt/fdt_intr.h44
-rw-r--r--sys/dev/gpio/gpiobusvar.h1
-rw-r--r--sys/dev/gpio/ofw_gpiobus.c2
-rw-r--r--sys/dev/hwpmc/hwpmc_amd.c3
-rw-r--r--sys/dev/hyperv/netvsc/hn_nvs.c (renamed from sys/dev/hyperv/netvsc/hv_net_vsc.c)170
-rw-r--r--sys/dev/hyperv/netvsc/hn_nvs.h106
-rw-r--r--sys/dev/hyperv/netvsc/hn_rndis.c (renamed from sys/dev/hyperv/netvsc/hv_rndis_filter.c)549
-rw-r--r--sys/dev/hyperv/netvsc/hn_rndis.h (renamed from sys/dev/hyperv/netvsc/hv_rndis_filter.h)31
-rw-r--r--sys/dev/hyperv/netvsc/hv_net_vsc.h288
-rw-r--r--sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c997
-rw-r--r--sys/dev/hyperv/netvsc/if_hnvar.h268
-rw-r--r--sys/dev/hyperv/netvsc/ndis.h12
-rw-r--r--sys/dev/hyperv/utilities/hv_heartbeat.c11
-rw-r--r--sys/dev/hyperv/utilities/hv_kvp.c70
-rw-r--r--sys/dev/hyperv/utilities/hv_kvp.h7
-rw-r--r--sys/dev/hyperv/utilities/hv_shutdown.c11
-rw-r--r--sys/dev/hyperv/utilities/hv_timesync.c11
-rw-r--r--sys/dev/hyperv/utilities/hv_util.c162
-rw-r--r--sys/dev/hyperv/utilities/hv_util.h5
-rw-r--r--sys/dev/hyperv/utilities/hv_utilreg.h12
-rw-r--r--sys/dev/hyperv/utilities/vmbus_icreg.h6
-rw-r--r--sys/dev/hyperv/vmbus/vmbus.c2
-rw-r--r--sys/dev/hyperv/vmbus/vmbus_chan.c136
-rw-r--r--sys/dev/ichiic/ig4_iic.c250
-rw-r--r--sys/dev/ichiic/ig4_pci.c9
-rw-r--r--sys/dev/ichiic/ig4_var.h5
-rw-r--r--sys/dev/iicbus/iicbus.c4
-rw-r--r--sys/dev/ioat/ioat.c39
-rw-r--r--sys/dev/ioat/ioat_internal.h9
-rw-r--r--sys/dev/isl/isl.c147
-rw-r--r--sys/dev/jedec_ts/jedec_ts.c179
-rw-r--r--sys/dev/mii/miidevs2
-rw-r--r--sys/dev/mii/rgephy.c12
-rw-r--r--sys/dev/mii/rgephyreg.h3
-rw-r--r--sys/dev/mlx4/mlx4_en/mlx4_en_tx.c15
-rw-r--r--sys/dev/netmap/if_em_netmap.h2
-rw-r--r--sys/dev/netmap/if_ptnet.c29
-rw-r--r--sys/dev/netmap/netmap.c21
-rw-r--r--sys/dev/netmap/netmap_freebsd.c110
-rw-r--r--sys/dev/netmap/netmap_kern.h11
-rw-r--r--sys/dev/netmap/netmap_mem2.c269
-rw-r--r--sys/dev/netmap/netmap_mem2.h4
-rw-r--r--sys/dev/netmap/netmap_pt.c53
-rw-r--r--sys/dev/netmap/netmap_vale.c2
-rw-r--r--sys/dev/psci/psci.c4
-rw-r--r--sys/dev/psci/psci.h4
-rw-r--r--sys/dev/re/if_re.c8
-rw-r--r--sys/dev/usb/input/ums.c68
-rw-r--r--sys/dev/usb/net/if_ure.c279
-rw-r--r--sys/dev/usb/net/if_urereg.h9
-rw-r--r--sys/dev/usb/net/uhso.c2
-rw-r--r--sys/dev/usb/usb_device.c9
-rw-r--r--sys/dev/xen/gntdev/gntdev.c1275
-rw-r--r--sys/dev/xen/netfront/netfront.c38
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, &reg },
+ { 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, &reg },
+ { 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 *)&regs, sizeof(regs), NULL);
+ error = cyapa_read_bytes(sc->dev, CMD_DEV_STATUS,
+ (void *)&regs, sizeof(regs));
if (error == 0) {
isidle = cyapa_raw_input(sc, &regs, 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, &reg },
+ { 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(&notify->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);
}