Page MenuHomeFreeBSD

D32184.id95841.diff
No OneTemporary

D32184.id95841.diff

diff --git a/sys/dev/hyperv/hvuio/hv_uio.c b/sys/dev/hyperv/hvuio/hv_uio.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/hyperv/hvuio/hv_uio.c
@@ -0,0 +1,778 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2013-2016 Brocade Communications Systems, Inc.
+ * Copyright (c) 2016 Microsoft Corp.
+ * Copyright (c) 2021 One Convergence, 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.
+ */
+
+/*
+ * hv_uio - generic UIO driver for VMBus Network Devices
+ *
+ * Bind/unbind hv_uio with VMBus network device using devctl.
+ * For example:
+ * devctl set driver -f hn1 hv_uio
+ */
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <sys/kernel.h>
+#include <sys/smp.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/errno.h>
+#include <sys/cdefs.h>
+#include <sys/sysctl.h>
+#include <sys/rwlock.h>
+#include <sys/lock.h>
+#include <sys/proc.h>
+#include <sys/selinfo.h>
+#include <sys/uio.h>
+#include <sys/poll.h>
+#include <sys/malloc.h>
+
+#include <machine/atomic.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/vm_object.h>
+#include <vm/vm_page.h>
+#include <vm/vm_pager.h>
+#include <vm/vm_phys.h>
+
+#include <dev/hyperv/include/hyperv.h>
+#include <dev/hyperv/vmbus/vmbus_reg.h>
+#include <dev/hyperv/vmbus/vmbus_chanvar.h>
+
+#include "vmbus_if.h"
+
+#define UH_DNAME "hv_uio"
+#define DRIVER_VERSION 1
+
+#define VMBUS_VPREF 1
+#define VMBUS_VMIN VMBUS_VPREF
+#define VMBUS_VMAX VMBUS_VPREF
+
+#define HV_RING_SIZE 512 /* pages */
+#define SEND_BUFFER_SIZE (16 * 1024 * 1024)
+#define RECV_BUFFER_SIZE (31 * 1024 * 1024)
+
+#define UH_NAMESIZE 32
+
+/* Macros to distinguish mmap request
+ * [7-0] - Device memory region
+ * [15-8]- Sub-channel id
+ */
+#define UH_MEM_MASK 0x00ff
+#define UH_SUBCHAN_MASK_SHIFT 8
+
+/* ioctl */
+#define HVIOOPENSUBCHAN _IOW('h', 14, uint32_t)
+
+/* Maximum number of VMBUS resources. */
+enum uio_hv_map {
+ HV_TXRX_RING_MAP = 0,
+ HV_INT_PAGE_MAP,
+ HV_MON_PAGE_MAP,
+ HV_RECV_BUF_MAP,
+ HV_SEND_BUF_MAP,
+ HV_MAX_RESOURCE
+};
+
+enum uh_cdev_st {
+ UH_ST_CLOSE,
+ UH_ST_OPEN,
+};
+
+/*
+ * Network GUID
+ */
+static const struct hyperv_guid hn_guid = {
+ .hv_guid = {
+ 0x63, 0x51, 0x61, 0xf8, 0x3e, 0xdf, 0xc5, 0x46,
+ 0x91, 0x3f, 0xf2, 0xd2, 0xf9, 0x65, 0xed, 0x0e }
+};
+
+struct uh_res {
+ struct hyperv_dma *hvdma;
+ void *buf;
+ uint32_t gpadl;
+ uint64_t size;
+ struct sysctl_oid *oid;
+ char name[UH_NAMESIZE];
+};
+
+struct uio_hv_softc {
+ device_t dev;
+ struct cdev *uh_cdev;
+
+ int cdev_st;
+ volatile uint32_t revent; /* total read events */
+ uint32_t uevent; /* events read by user */
+
+ struct vmbus_channel *chan;
+ uint32_t subchan_cnt;
+
+ uint32_t cbr_txsz;
+ uint32_t cbr_rxsz;
+
+ struct uh_res res[HV_MAX_RESOURCE];
+
+ struct selinfo uh_selinfo;
+ boolean_t allow_read;
+ struct sysctl_ctx_list *ctx;
+};
+
+static void
+uio_hv_sysctl_add(struct uio_hv_softc *sc, struct uh_res *res,
+ const char *descr, boolean_t publish_gpadl);
+
+static inline void
+uio_hv_event_notify(struct uio_hv_softc *sc)
+{
+ atomic_add_32(&sc->revent, 1);
+ selwakeup(&sc->uh_selinfo);
+}
+
+/*
+ * Callback from vmbus_event when something is in inbound ring.
+ */
+static void
+uio_hv_channel_cb(struct vmbus_channel *chan, void *arg)
+{
+ struct uio_hv_softc *sc = (struct uio_hv_softc *)arg;
+
+ vmbus_rxbr_intr_mask(&chan->ch_rxbr);
+ uio_hv_event_notify(sc);
+}
+
+static void
+uio_hv_rescind_cb(struct vmbus_channel *chan)
+{
+ struct uio_hv_softc *sc = device_get_softc(chan->ch_dev);
+
+ sc->allow_read = false;
+ uio_hv_event_notify(sc);
+}
+
+static int
+uio_hv_schan_open(struct uio_hv_softc *sc, struct vmbus_channel *schan)
+{
+ int ret;
+
+ vmbus_chan_cpu_set(schan, schan->ch_subidx % mp_ncpus);
+ ret = vmbus_chan_open(schan, sc->cbr_txsz, sc->cbr_rxsz, NULL, 0,
+ uio_hv_channel_cb, sc);
+ if (ret)
+ return ret;
+
+ /* Disable interrupts on sub channel */
+ vmbus_rxbr_intr_mask(&schan->ch_rxbr);
+ sc->subchan_cnt++;
+ return 0;
+}
+
+static int
+uio_hv_read(struct cdev *cdev, struct uio *uio, int ioflag __unused)
+{
+ struct uio_hv_softc *sc = cdev->si_drv1;
+ uint32_t evcount;
+ int ret;
+
+ if (sc->allow_read == false)
+ return EIO;
+
+ if (uio->uio_resid < sizeof(uint32_t))
+ return EINVAL;
+
+ evcount = atomic_load_32(&sc->revent);
+ ret = uiomove(&evcount, sizeof(uint32_t), uio);
+ if (ret)
+ device_printf(sc->dev, "Failed to read\n");
+ else
+ sc->uevent = evcount;
+
+ return ret;
+}
+
+/* Write is used only to disable/enable interrupt from user space processes */
+static int
+uio_hv_write(struct cdev *cdev, struct uio *uio, int ioflag __unused)
+{
+ struct uio_hv_softc *sc = cdev->si_drv1;
+ struct vmbus_channel *pchan = sc->chan;
+ uint32_t irq_state;
+ int ret;
+
+ ret = copyin(uio->uio_iov->iov_base, &irq_state, sizeof(uint32_t));
+ if (ret) {
+ device_printf(sc->dev, "Failed to write\n");
+ return ret;
+ }
+
+ pchan->ch_rxbr.rxbr_imask = !irq_state;
+ mb();
+
+ return 0;
+}
+
+static int
+uio_hv_open(struct cdev *cdev, int oflags, int devtype, struct thread *td __unused)
+{
+ struct uio_hv_softc *sc = cdev->si_drv1;
+ struct vmbus_channel *pchan = sc->chan;
+ struct uh_res *res = NULL;
+ int ret = 0;
+
+ if (!atomic_cmpset_int(&sc->cdev_st, UH_ST_CLOSE, UH_ST_OPEN))
+ return 0;
+
+ pchan->ch_rescind_cb = uio_hv_rescind_cb;
+
+ ret = vmbus_chan_open(pchan, sc->cbr_txsz, sc->cbr_rxsz, NULL, 0,
+ uio_hv_channel_cb, sc);
+ if (ret) {
+ device_printf(sc->dev, "failed opening primary chan%u\n",
+ vmbus_chan_id(pchan));
+ goto cleanup;
+ }
+ vmbus_rxbr_intr_mask(&pchan->ch_rxbr);
+
+ res = &sc->res[HV_TXRX_RING_MAP];
+ res->hvdma = &pchan->ch_bufring_dma;
+ uio_hv_sysctl_add(sc, res, "Channel Tx Rx rings size", false);
+
+ sc->allow_read = true;
+
+ if (bootverbose)
+ device_printf(sc->dev, "device opened\n");
+
+ return 0;
+cleanup:
+ device_printf(sc->dev, "device open failed\n");
+ atomic_set_int(&sc->cdev_st, UH_ST_CLOSE);
+
+ return ret;
+}
+
+static void
+uio_hv_reset(struct uio_hv_softc *sc)
+{
+ if (!atomic_cmpset_int(&sc->cdev_st, UH_ST_OPEN, UH_ST_CLOSE))
+ return;
+
+ sc->allow_read = false;
+ sysctl_remove_oid(sc->res[HV_TXRX_RING_MAP].oid, 1, 1);
+ sc->res[HV_TXRX_RING_MAP].hvdma = NULL;
+ vmbus_chan_close(sc->chan);
+ sc->subchan_cnt = 0;
+}
+
+/* Only the last close call to the userspace process object will reach here */
+static int
+uio_hv_close(struct cdev *cdev, int fflag __unused, int devtype __unused,
+ struct thread *td __unused)
+{
+ struct uio_hv_softc *sc = cdev->si_drv1;
+
+ if (bootverbose)
+ device_printf(sc->dev, "device close\n");
+
+ uio_hv_reset(sc);
+
+ return 0;
+}
+
+static int
+uio_hv_poll(struct cdev *cdev, int events, struct thread *td)
+{
+ struct uio_hv_softc *sc = cdev->si_drv1;
+ int ret = 0;
+
+ if (sc->uevent != atomic_load_32(&sc->revent))
+ ret = POLLIN;
+ else
+ selrecord(td, &sc->uh_selinfo);
+
+ return ret;
+}
+
+static int
+uio_hv_cdev_pg_ctor(void *handle, vm_ooffset_t size, vm_prot_t prot,
+ vm_ooffset_t foff, struct ucred *cred, u_short *color)
+{
+ struct uio_hv_softc *sc = handle;
+
+ if (color)
+ *color = 0;
+ dev_ref(sc->uh_cdev);
+ return 0;
+}
+
+static void
+uio_hv_cdev_pg_dtor(void *handle)
+{
+ struct uio_hv_softc *sc = handle;
+
+ dev_rel(sc->uh_cdev);
+}
+
+ static int
+uio_hv_cdev_pg_fault(vm_object_t object, vm_ooffset_t offset, int prot,
+ vm_page_t *mres)
+{
+ vm_memattr_t memattr, memattr1;
+ vm_page_t page, m_paddr;
+ vm_paddr_t paddr;
+
+ memattr = object->memattr;
+ VM_OBJECT_WUNLOCK(object);
+ paddr = offset;
+
+ m_paddr = vm_phys_paddr_to_vm_page(paddr);
+ if (m_paddr != NULL) {
+ memattr1 = pmap_page_get_memattr(m_paddr);
+ if (memattr1 != memattr)
+ memattr = memattr1;
+ }
+ if (((*mres)->flags & PG_FICTITIOUS) != 0) {
+ /*
+ * If the passed in result page is a fake page, update it with
+ * the new physical address.
+ */
+ page = *mres;
+ VM_OBJECT_WLOCK(object);
+ vm_page_updatefake(page, paddr, memattr);
+ } else {
+ /*
+ * Replace the passed in reqpage page with our own fake page and
+ * free up the original page.
+ */
+ page = vm_page_getfake(paddr, memattr);
+ VM_OBJECT_WLOCK(object);
+#if __FreeBSD__ >= 13
+ vm_page_replace(page, object, (*mres)->pindex, *mres);
+#else
+ vm_page_t mret = vm_page_replace(page, object, (*mres)->pindex);
+
+ KASSERT(mret == *mres,
+ ("invalid page replacement, old=%p, ret=%p", *mres, mret));
+ vm_page_lock(mret);
+ vm_page_free(mret);
+ vm_page_unlock(mret);
+#endif
+ *mres = page;
+ }
+ page->valid = VM_PAGE_BITS_ALL;
+ return VM_PAGER_OK;
+}
+
+static struct cdev_pager_ops uh_cdev_pager_ops = {
+ .cdev_pg_ctor = uio_hv_cdev_pg_ctor,
+ .cdev_pg_dtor = uio_hv_cdev_pg_dtor,
+ .cdev_pg_fault = uio_hv_cdev_pg_fault,
+};
+
+static int
+uio_hv_mmap_single(struct cdev *cdev, vm_ooffset_t *offset, vm_size_t size,
+ struct vm_object **obj, int nprot)
+{
+ struct uio_hv_softc *sc = cdev->si_drv1;
+ struct vmbus_channel *pchan = vmbus_get_channel(sc->dev);
+ struct vmbus_channel **schans;
+ uint32_t req = *offset/PAGE_SIZE; /* map region is encoded in the offset */
+ uint32_t reg = req & UH_MEM_MASK;
+ uint32_t subchan_id = req >> UH_SUBCHAN_MASK_SHIFT;
+ bool schan_found = false;
+ int i, ret = 0;
+
+ if ((pchan->ch_stflags & VMBUS_CHAN_ST_OPENED) == 0) {
+ ret = ENODEV;
+ goto end;
+ }
+
+ if (bootverbose)
+ device_printf(sc->dev, "mmap request for reg %u subchan_id %u\n",
+ reg, subchan_id);
+
+ if (!subchan_id) {
+ if (reg >= HV_MAX_RESOURCE) {
+ device_printf(sc->dev, "Invalid resource request\n");
+ ret = EINVAL;
+ goto end;
+ }
+
+ *offset = (vm_ooffset_t) sc->res[reg].hvdma->hv_paddr;
+ } else {
+ schans = vmbus_subchan_get(pchan, sc->subchan_cnt);
+
+ for (i = 0; i < sc->subchan_cnt; ++i) {
+ if (schans[i]->ch_id == subchan_id) {
+ schan_found = true;
+ *offset =
+ (vm_ooffset_t)schans[i]->ch_bufring_dma.hv_paddr;
+ break;
+ }
+ }
+
+ vmbus_subchan_rel(schans, sc->subchan_cnt);
+ if (schan_found == false) {
+ device_printf(sc->dev, "Invalid subchan idx\n");
+ ret = ENODEV;
+ goto end;
+ }
+ }
+
+ *obj = cdev_pager_allocate(sc, OBJT_DEVICE, &uh_cdev_pager_ops, size, nprot,
+ *offset, curthread->td_ucred);
+ if (*obj == NULL) {
+ device_printf(sc->dev, "vm_pager_allocate failed\n");
+ ret = ENOMEM;
+ }
+end:
+ return ret;
+}
+
+static int
+uio_hv_schan_open_all(struct uio_hv_softc *sc, uint32_t subchan_cnt)
+{
+ struct vmbus_channel *pchan = sc->chan;
+ struct vmbus_channel **subchans;
+ int ret, i, sc_fail = 0;
+
+ KASSERT(subchan_cnt > 0, ("subchan count should not be 0"));
+
+ if (subchan_cnt < pchan->ch_subchan_cnt) {
+ device_printf(sc->dev, "%u subchan open req received"
+ "pchan has %u subchannels", subchan_cnt,
+ pchan->ch_subchan_cnt);
+ return EINVAL;
+ }
+
+ /* Waits for all sub-channels to become ready */
+ subchans = vmbus_subchan_get(pchan, subchan_cnt);
+ for (i = 0; i < subchan_cnt; ++i) {
+ ret = uio_hv_schan_open(sc, subchans[i]);
+ /* On err, all subchans will be closed later */
+ if (ret)
+ sc_fail = ret;
+ }
+ vmbus_subchan_rel(subchans, subchan_cnt);
+
+ if (sc_fail) {
+ device_printf(sc->dev, "subchan open failed, device closing\n");
+ return sc_fail;
+ }
+
+ return 0;
+}
+
+static int
+uio_hv_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag, struct thread *td)
+{
+ struct uio_hv_softc *sc = cdev->si_drv1;
+ uint32_t nschan;
+ int ret = 0;
+
+ if (bootverbose)
+ device_printf(sc->dev, "ioctl cmd %lu\n", cmd);
+ switch (cmd) {
+ case HVIOOPENSUBCHAN:
+ /* userspace should pass the subchan cnt returned by NVS */
+ nschan = *(uint32_t *)data;
+ if (nschan == 0)
+ break;
+
+ ret = uio_hv_schan_open_all(sc, nschan);
+ break;
+ default:
+ if (bootverbose)
+ device_printf(sc->dev,
+ "invalid ioctl cmd %lu\n", cmd);
+ ret = EINVAL;
+ }
+ return ret;
+}
+
+static struct cdevsw uh_cdevsw = {
+ .d_name = UH_DNAME,
+ .d_version = D_VERSION,
+ .d_open = uio_hv_open,
+ .d_close = uio_hv_close,
+ .d_read = uio_hv_read,
+ .d_write = uio_hv_write,
+ .d_ioctl = uio_hv_ioctl,
+ .d_poll = uio_hv_poll,
+ .d_mmap_single = uio_hv_mmap_single,
+};
+
+static void
+uio_hv_sysctl_add(struct uio_hv_softc *sc, struct uh_res *res,
+ const char *descr, boolean_t publish_gpadl)
+{
+ struct sysctl_oid_list *child;
+ struct sysctl_oid_list *child2;
+
+ child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev));
+ res->oid = SYSCTL_ADD_NODE(sc->ctx, child, OID_AUTO, res->name,
+ CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "");
+ child2 = SYSCTL_CHILDREN(res->oid);
+ SYSCTL_ADD_U64(sc->ctx, child2, OID_AUTO, "size", CTLFLAG_RD, NULL,
+ res->size, descr);
+ if (publish_gpadl)
+ SYSCTL_ADD_U32(sc->ctx, child2, OID_AUTO, "gpadl", CTLFLAG_RD,
+ &res->gpadl, 0, "");
+}
+
+static void uio_hv_sysctl_create(struct uio_hv_softc *sc)
+{
+ struct uh_res *res = NULL;
+
+ sc->ctx = device_get_sysctl_ctx(sc->dev);
+
+ res = &sc->res[HV_INT_PAGE_MAP];
+ uio_hv_sysctl_add(sc, res, "VMBUS Interrupt Page size", false);
+
+ res = &sc->res[HV_MON_PAGE_MAP];
+ uio_hv_sysctl_add(sc, res, "VMBUS Monitor Page size", false);
+
+ res = &sc->res[HV_RECV_BUF_MAP];
+ uio_hv_sysctl_add(sc, res, "Channel Receive Buffer size", true);
+
+ res = &sc->res[HV_SEND_BUF_MAP];
+ uio_hv_sysctl_add(sc, res, "Channel Send Buffer size", true);
+
+ SYSCTL_ADD_U32(sc->ctx, SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)),
+ OID_AUTO, "subchan_cnt", CTLFLAG_RD, &sc->subchan_cnt, 0,
+ "sub channels count");
+}
+
+static void
+uio_hv_gpadl_free(struct uio_hv_softc *sc, enum uio_hv_map mr)
+{
+ struct uh_res *res = &sc->res[mr];
+
+ if (res->gpadl) {
+ vmbus_chan_gpadl_disconnect(sc->chan, res->gpadl);
+ res->gpadl = 0;
+ }
+
+ if (res->buf) {
+ hyperv_dmamem_free(res->hvdma, res->buf);
+ res->buf = NULL;
+ }
+
+ if (res->hvdma) {
+ free(res->hvdma, M_DEVBUF);
+ res->hvdma = NULL;
+ }
+}
+
+static int
+uio_hv_gpadl_alloc(struct uio_hv_softc *sc, enum uio_hv_map mr, uint64_t size)
+{
+ struct uh_res *res = &sc->res[mr];
+ int ret = 0;
+
+ res->hvdma = malloc(sizeof(struct hyperv_dma), M_DEVBUF, M_WAITOK);
+ if (!res->hvdma)
+ return ENOMEM;
+
+ res->buf = hyperv_dmamem_alloc(bus_get_dma_tag(sc->dev), PAGE_SIZE, 0,
+ size, res->hvdma,
+ BUS_DMA_WAITOK | BUS_DMA_ZERO);
+ if (!res->buf) {
+ ret = ENOMEM;
+ goto cleanup;
+ }
+
+ ret = vmbus_chan_gpadl_connect(sc->chan, res->hvdma->hv_paddr, size,
+ &res->gpadl);
+ if (ret)
+ goto cleanup;
+
+ return 0;
+cleanup:
+ uio_hv_gpadl_free(sc, mr);
+ return ret;
+}
+
+static int
+uio_hv_detach(device_t dev)
+{
+ struct uio_hv_softc *sc = device_get_softc(dev);
+
+ if (sc->uh_cdev) {
+ uio_hv_reset(sc);
+ destroy_dev(sc->uh_cdev);
+ }
+
+ uio_hv_gpadl_free(sc, HV_SEND_BUF_MAP);
+ uio_hv_gpadl_free(sc, HV_RECV_BUF_MAP);
+
+ sc->chan = NULL;
+
+ return 0;
+}
+
+static int
+uio_hv_attach(device_t dev)
+{
+ struct vmbus_channel *chan = vmbus_get_channel(dev);
+ struct uio_hv_softc *sc = device_get_softc(dev);
+ int ret;
+
+ /* Communicating with host has to be via shared memory not hypercall */
+ if (!(chan->ch_txflags & VMBUS_CHAN_TXF_HASMNF)) {
+ device_printf(dev, "vmbus channel requires shared memory communication\n");
+ return ENOTSUP;
+ }
+
+ sc->dev = dev;
+ sc->chan = chan;
+
+ sc->cbr_txsz = HV_RING_SIZE * PAGE_SIZE;
+ sc->cbr_rxsz = HV_RING_SIZE * PAGE_SIZE;
+
+ vmbus_chan_cpu_set(chan, chan->ch_subidx % mp_ncpus);
+
+ /* Channel TxRx rings are created while cdev open and not here
+ * since closing the channel destroys the rings
+ */
+ strncpy(sc->res[HV_TXRX_RING_MAP].name, "txrx_rings", UH_NAMESIZE);
+ sc->res[HV_TXRX_RING_MAP].size = sc->cbr_txsz + sc->cbr_rxsz;
+
+ strncpy(sc->res[HV_INT_PAGE_MAP].name, "int_page", UH_NAMESIZE);
+ sc->res[HV_INT_PAGE_MAP].hvdma = vmbus_get_mem_evtflags();
+ sc->res[HV_INT_PAGE_MAP].size = PAGE_SIZE;
+
+ strncpy(sc->res[HV_MON_PAGE_MAP].name, "monitor_page", UH_NAMESIZE);
+ sc->res[HV_MON_PAGE_MAP].hvdma = vmbus_get_mem_mnf2();
+ sc->res[HV_MON_PAGE_MAP].size = PAGE_SIZE;
+
+ ret = uio_hv_gpadl_alloc(sc, HV_RECV_BUF_MAP, RECV_BUFFER_SIZE);
+ if (ret) {
+ device_printf(dev, "Failed to get recv gpadl\n");
+ goto cleanup;
+ }
+ strncpy(sc->res[HV_RECV_BUF_MAP].name, "recv_buf", UH_NAMESIZE);
+ sc->res[HV_RECV_BUF_MAP].size = RECV_BUFFER_SIZE;
+
+ ret = uio_hv_gpadl_alloc(sc, HV_SEND_BUF_MAP, SEND_BUFFER_SIZE);
+ if (ret) {
+ device_printf(dev, "Failed to get send gpadl\n");
+ goto cleanup;
+ }
+ strncpy(sc->res[HV_SEND_BUF_MAP].name, "send_buf", UH_NAMESIZE);
+ sc->res[HV_SEND_BUF_MAP].size = SEND_BUFFER_SIZE;
+
+ ret = make_dev_p(MAKEDEV_CHECKNAME | MAKEDEV_WAITOK, &sc->uh_cdev,
+ &uh_cdevsw, NULL, UID_ROOT, GID_WHEEL, 0640, "%s",
+ device_get_nameunit(dev));
+ if (ret)
+ goto cleanup;
+
+ sc->uh_cdev->si_drv1 = sc;
+
+ /*Create sysctl tree for this device */
+ uio_hv_sysctl_create(sc);
+
+ return 0;
+cleanup:
+ uio_hv_detach(dev);
+ return ret;
+}
+
+static int
+uio_hv_probe(device_t dev)
+{
+ /* Presently only VMBus Network Device is driven */
+ if (VMBUS_PROBE_GUID(device_get_parent(dev), dev, &hn_guid) == 0) {
+ device_set_desc(dev, "Hyper-V UIO Network Interface");
+ return BUS_PROBE_DEFAULT;
+ }
+ return ENXIO;
+}
+
+static int
+uio_hv_shutdown(device_t dev)
+{
+ return 0;
+}
+
+static inline void
+uio_hv_load(void)
+{
+ printf("%s ver %d module loaded\n", UH_DNAME, DRIVER_VERSION);
+}
+
+static inline void
+uio_hv_unload(void)
+{
+ printf("%s module unloaded\n", UH_DNAME);
+}
+
+/* KLD event handler */
+static int
+uio_hv_modevh(module_t mod __unused, int event, void *arg __unused)
+{
+ int ret = 0;
+
+ switch (event) {
+ case MOD_LOAD:
+ uio_hv_load();
+ break;
+ case MOD_UNLOAD:
+ uio_hv_unload();
+ break;
+ case MOD_SHUTDOWN:
+ break;
+ default:
+ ret = EOPNOTSUPP;
+ break;
+ }
+ return ret;
+}
+
+static device_method_t uio_hv_methods[] = {
+ DEVMETHOD(device_probe, uio_hv_probe),
+ DEVMETHOD(device_shutdown, uio_hv_shutdown),
+ DEVMETHOD(device_attach, uio_hv_attach),
+ DEVMETHOD(device_detach, uio_hv_detach),
+ DEVMETHOD_END
+};
+
+static devclass_t uio_hv_devclass;
+
+static driver_t uio_hv_driver = {
+ UH_DNAME,
+ uio_hv_methods,
+ sizeof(struct uio_hv_softc)
+};
+
+DRIVER_MODULE(hv_uio, vmbus, uio_hv_driver, uio_hv_devclass, uio_hv_modevh, NULL);
+MODULE_VERSION(hv_uio, DRIVER_VERSION);
+MODULE_DEPEND(hv_uio, vmbus, VMBUS_VMIN, VMBUS_VPREF, VMBUS_VMAX);
diff --git a/sys/dev/hyperv/include/vmbus.h b/sys/dev/hyperv/include/vmbus.h
--- a/sys/dev/hyperv/include/vmbus.h
+++ b/sys/dev/hyperv/include/vmbus.h
@@ -132,6 +132,7 @@
typedef void (*vmbus_chan_callback_t)(struct vmbus_channel *, void *);
typedef int (*vmbus_br_copy_callback_t)(void *, int, void *);
+typedef void (*vmbus_chan_rescind_callback_t)(struct vmbus_channel *);
static __inline struct vmbus_channel *
vmbus_get_channel(device_t dev)
diff --git a/sys/dev/hyperv/vmbus/vmbus.c b/sys/dev/hyperv/vmbus/vmbus.c
--- a/sys/dev/hyperv/vmbus/vmbus.c
+++ b/sys/dev/hyperv/vmbus/vmbus.c
@@ -853,6 +853,28 @@
wrmsr(MSR_HV_SIEFP, (orig & MSR_HV_SIEFP_RSVD_MASK));
}
+struct hyperv_dma *
+vmbus_get_mem_evtflags(void)
+{
+ struct vmbus_softc *sc = vmbus_get_softc();
+
+ if (!sc)
+ return NULL;
+
+ return &sc->vmbus_evtflags_dma;
+}
+
+struct hyperv_dma *
+vmbus_get_mem_mnf2(void)
+{
+ struct vmbus_softc *sc = vmbus_get_softc();
+
+ if (!sc)
+ return NULL;
+
+ return &sc->vmbus_mnf2_dma;
+}
+
static int
vmbus_dma_alloc(struct vmbus_softc *sc)
{
diff --git a/sys/dev/hyperv/vmbus/vmbus_chan.c b/sys/dev/hyperv/vmbus/vmbus_chan.c
--- a/sys/dev/hyperv/vmbus/vmbus_chan.c
+++ b/sys/dev/hyperv/vmbus/vmbus_chan.c
@@ -273,9 +273,11 @@
/*
* Create dev.NAME.UNIT.channel.CHANID tree.
*/
- if (VMBUS_CHAN_ISPRIMARY(chan))
+ if (VMBUS_CHAN_ISPRIMARY(chan)) {
ch_id = chan->ch_id;
- else
+ SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(ch_tree), OID_AUTO,
+ "ch_id", CTLFLAG_RD, &chan->ch_id, 0, "owner channel id");
+ } else
ch_id = chan->ch_prichan->ch_id;
snprintf(name, sizeof(name), "%d", ch_id);
chid_tree = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(ch_tree),
@@ -308,6 +310,11 @@
"chanid", CTLFLAG_RD, &chan->ch_id, 0, "channel id");
}
+ SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(chid_tree), OID_AUTO,
+ "ch_subidx", CTLFLAG_RD, &chan->ch_subidx, 0, "subchan index");
+ SYSCTL_ADD_U8(ctx, SYSCTL_CHILDREN(chid_tree), OID_AUTO,
+ "monitor_id", CTLFLAG_RD, &chan->ch_monitor_id, 0,
+ "owner monitor id");
SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(chid_tree), OID_AUTO,
"cpu", CTLFLAG_RD, &chan->ch_cpuid, 0, "owner CPU id");
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(chid_tree), OID_AUTO,
@@ -1848,6 +1855,7 @@
chan->ch_subidx = offer->chm_subidx;
chan->ch_guid_type = offer->chm_chtype;
chan->ch_guid_inst = offer->chm_chinst;
+ chan->ch_monitor_id = offer->chm_montrig;
/* Batch reading is on by default */
chan->ch_flags |= VMBUS_CHAN_FLAG_BATCHREAD;
@@ -1954,6 +1962,14 @@
note->chm_chanid);
return;
}
+ mtx_unlock(&sc->vmbus_chan_lock);
+
+ if (chan->ch_dev && chan->ch_rescind_cb) {
+ chan->ch_rescind_cb(chan);
+ return;
+ }
+
+ mtx_lock(&sc->vmbus_chan_lock);
vmbus_chan_rem_list(sc, chan);
mtx_unlock(&sc->vmbus_chan_lock);
diff --git a/sys/dev/hyperv/vmbus/vmbus_chanvar.h b/sys/dev/hyperv/vmbus/vmbus_chanvar.h
--- a/sys/dev/hyperv/vmbus/vmbus_chanvar.h
+++ b/sys/dev/hyperv/vmbus/vmbus_chanvar.h
@@ -64,6 +64,7 @@
struct callout ch_poll_timeo;
vmbus_chan_callback_t ch_cb;
void *ch_cbarg;
+ vmbus_chan_rescind_callback_t ch_rescind_cb;
/*
* TX bufring; at the beginning of ch_bufring.
@@ -84,6 +85,7 @@
* These are based on the vmbus_chanmsg_choffer.chm_montrig.
* Save it here for easy access.
*/
+ uint8_t ch_monitor_id;
uint32_t ch_montrig_mask;/* MNF trig mask */
volatile uint32_t *ch_montrig; /* MNF trigger loc. */
@@ -191,5 +193,6 @@
void vmbus_chan_msgproc(struct vmbus_softc *,
const struct vmbus_message *);
void vmbus_chan_destroy_all(struct vmbus_softc *);
-
+struct hyperv_dma *vmbus_get_mem_evtflags(void);
+struct hyperv_dma *vmbus_get_mem_mnf2(void);
#endif /* !_VMBUS_CHANVAR_H_ */
diff --git a/sys/modules/hyperv/Makefile b/sys/modules/hyperv/Makefile
--- a/sys/modules/hyperv/Makefile
+++ b/sys/modules/hyperv/Makefile
@@ -1,5 +1,5 @@
# $FreeBSD$
-SUBDIR = vmbus netvsc storvsc utilities hvsock
+SUBDIR = vmbus netvsc storvsc utilities hvsock hvuio
.include <bsd.subdir.mk>
diff --git a/sys/modules/hyperv/hvuio/Makefile b/sys/modules/hyperv/hvuio/Makefile
new file mode 100644
--- /dev/null
+++ b/sys/modules/hyperv/hvuio/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+.PATH: ${SRCTOP}/sys/dev/hyperv/hvuio \
+ ${SRCTOP}/sys/dev/hyperv/vmbus
+
+KMOD= hv_uio
+SRCS= hv_uio.c
+SRCS+= device_if.h bus_if.h vmbus_if.h
+
+CFLAGS+= -I${SRCTOP}/sys/dev/hyperv/hvuio
+
+.include <bsd.kmod.mk>

File Metadata

Mime Type
text/plain
Expires
Thu, Sep 3, 4:44 PM (11 h, 19 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
38087290
Default Alt Text
D32184.id95841.diff (23 KB)

Event Timeline