From nobody Wed Sep 16 14:12:52 2026 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4hlLSj71dmz6rXCF for ; Wed, 16 Sep 2026 14:12:53 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "YR2" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4hlLSh3pvbz3Lw2 for ; Wed, 16 Sep 2026 14:12:52 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1789567972; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=tG0oxEHJP9XuYM+v+cOlAqVk6wn+WbRdR39RIBmjjtQ=; b=jumGt91R3yNiD9pGRQiozK4gUGaaIpyQ4EpXzkLwXl8wQY1j+myMmI+g5/VuSWBF7wseQ1 Rrs7F0IVvJxLE4uTY+C9gDoTkggiGRtxYHf/8lseMWo5TkA0yWEFP/CdonpE7q+LO+uamz 36B6l14AwHYNaPF/fDMjGaEu3R7i8jnQa7SVazUqHuyh/cOaGxRRHuMED/8NT7DtNeoMeo DE/sAQzOYdk15t8NmEqsMmY9OopGeKWkxD5FRHVUSGEKKTGVY+avzFP1tUwkvhkYZZYQ3u zcHk6PUob8PAvAOcagT7I4d1B/mC3Qp6WH+51MFHREYhSE9cwFhLy2mEBp4AwA== ARC-Seal: i=1; a=rsa-sha256; d=freebsd.org; s=dkim; cv=none; t=1789567972; b=VtiYVzs3O+w7CXz7qqTC1jVvOc04GwfhD2AoT6qJ4K63STAIgLQa2geVSmdesJrBWcRlHJ mdwrw5zjDLIlu1xWUOXFLieajiCqQ86ccCgRdOs9xneAA0Tm9b6zBuU0J2fgCHW01NheMC 47V25jg+QHFa0pbn1AaHhYPOlMT5JsTLoSlbBeNJrIiDuq1cRRLswau3ogP9gGiaM485TX jnK77d0a47F6RF3DTXNPGoav7qz6q3XZxNrtnxadyorS/SdwdXvZikqrmsuIO6YW9K62rz SoCz1cqLezaumPLBM3p2KIipDz78ulXHBVxxusVMEj7FDDGqDPh0WPiGs7dOEw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1789567972; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=tG0oxEHJP9XuYM+v+cOlAqVk6wn+WbRdR39RIBmjjtQ=; b=Zw7RDrGwaf3nsTLoN1OmtAsWSemif4unO09HvzLfpRoA9szPi53/9kLMHKnOK5c7Rkke8N XtSV76+Klp1WAIBaAcUhJPZrIm6iSaJq0X+cWHIzKhw8WWAxfqL7oi9QzVPPhyHRd06XYk sXa7dbRCj4192uIVrqFBV6mq3tZd8rxSJozeArlBwbqG+Ini/uRUNBZPPSBDNrBQE71HX2 NhCPRDqkvC3zWGl0Shft1Ss8x00lTDPhNX4fV1PytspgNW4aO4wJdnD2pyjGRCVnUk43nD 0A7F/fuZW/l+rMjjrhqUwcHuyfRw278TR2fthXPuM8e14FUo3T/XXFjvncCjpw== ARC-Authentication-Results: i=1; mx1.freebsd.org; none Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4hlLSh2t4LzqWh for ; Wed, 16 Sep 2026 14:12:52 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 3615b by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Wed, 16 Sep 2026 14:12:52 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org Cc: Vag Singh From: Konstantin Belousov Subject: git: 998acbc14051 - main - hvuio: Add HV_UIO driver List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: kib X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 998acbc14051c357a8b0a8f52c7f16885bf74fc6 Auto-Submitted: auto-generated Date: Wed, 16 Sep 2026 14:12:52 +0000 Message-Id: <6aaaa3e4.3615b.32608982@gitrepo.freebsd.org> The branch main has been updated by kib: URL: https://cgit.FreeBSD.org/src/commit/?id=998acbc14051c357a8b0a8f52c7f16885bf74fc6 commit 998acbc14051c357a8b0a8f52c7f16885bf74fc6 Author: Vag Singh AuthorDate: 2025-08-14 05:45:24 +0000 Commit: Konstantin Belousov CommitDate: 2026-09-16 14:12:07 +0000 hvuio: Add HV_UIO driver [PATCH 10/31] FreeBSD OFED support for DPDK MLX5 PMD Adding UIO driver that exposes the synthetic network device resources to userspace for application such as DPDK can drive it. Added rescind CB capability to the VMBUS driver. Whenever the hypervisor wants to close the device channel, VMBUS on receiving this msg, calls the CB of the device driver (hv_uio in our case). Differential revision: https://reviews.freebsd.org/D32184 MFC after: 1 month --- sys/dev/hyperv/hvuio/hv_uio.c | 779 +++++++++++++++++++++++++++++++++++ sys/dev/hyperv/include/vmbus.h | 1 + sys/dev/hyperv/vmbus/vmbus.c | 22 + sys/dev/hyperv/vmbus/vmbus_chan.c | 20 +- sys/dev/hyperv/vmbus/vmbus_chanvar.h | 5 +- sys/modules/hyperv/Makefile | 2 + sys/modules/hyperv/hvuio/Makefile | 12 + 7 files changed, 838 insertions(+), 3 deletions(-) diff --git a/sys/dev/hyperv/hvuio/hv_uio.c b/sys/dev/hyperv/hvuio/hv_uio.c new file mode 100644 index 000000000000..db5436bd21b1 --- /dev/null +++ b/sys/dev/hyperv/hvuio/hv_uio.c @@ -0,0 +1,779 @@ +/*- + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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 index ab67a7436b71..345212938060 100644 --- a/sys/dev/hyperv/include/vmbus.h +++ b/sys/dev/hyperv/include/vmbus.h @@ -130,6 +130,7 @@ struct taskqueue; 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 index 15683f21ea6a..895352d6debe 100644 --- a/sys/dev/hyperv/vmbus/vmbus.c +++ b/sys/dev/hyperv/vmbus/vmbus.c @@ -848,6 +848,28 @@ vmbus_synic_teardown(void *arg) 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 NULL; //&sc->vmbus_evtflags_dma; +} + +struct hyperv_dma * +vmbus_get_mem_mnf2(void) +{ + struct vmbus_softc *sc = vmbus_get_softc(); + + if (!sc) + return NULL; + + return NULL; //&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 index 7ea60a499c72..9adcb64de124 100644 --- a/sys/dev/hyperv/vmbus/vmbus_chan.c +++ b/sys/dev/hyperv/vmbus/vmbus_chan.c @@ -274,9 +274,11 @@ vmbus_chan_sysctl_create(struct vmbus_channel *chan) /* * 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), @@ -309,6 +311,11 @@ vmbus_chan_sysctl_create(struct vmbus_channel *chan) "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, @@ -1854,6 +1861,7 @@ vmbus_chan_msgproc_choffer(struct vmbus_softc *sc, 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; @@ -1960,6 +1968,14 @@ vmbus_chan_msgproc_chrescind(struct vmbus_softc *sc, 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 index c02ec24c70a1..0b254886c9d8 100644 --- a/sys/dev/hyperv/vmbus/vmbus_chanvar.h +++ b/sys/dev/hyperv/vmbus/vmbus_chanvar.h @@ -61,6 +61,7 @@ struct vmbus_channel { 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. @@ -81,6 +82,7 @@ struct vmbus_channel { * 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. */ @@ -186,5 +188,6 @@ void vmbus_event_proc_compat(struct vmbus_softc *, int); 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 index 170564a730ac..aaa059e8d7d1 100644 --- a/sys/modules/hyperv/Makefile +++ b/sys/modules/hyperv/Makefile @@ -1,3 +1,5 @@ SUBDIR = vmbus netvsc storvsc utilities hvsock hid +# XXXKIB +#SUBDIR+= hvuio .include diff --git a/sys/modules/hyperv/hvuio/Makefile b/sys/modules/hyperv/hvuio/Makefile new file mode 100644 index 000000000000..3fca2a916801 --- /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