Page MenuHomeFreeBSD

D61216.1793939361.diff
No OneTemporary

Authored By
Unknown
Size
28 KB
Referenced Files
None
Subscribers
None

D61216.1793939361.diff

diff --git a/sys/compat/linuxkpi/common/include/linux/virtio.h b/sys/compat/linuxkpi/common/include/linux/virtio.h
new file mode 100644
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/virtio.h
@@ -0,0 +1,177 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Denis Borovikov <denis.borovikov@gmail.com>
+ *
+ * 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.
+ */
+
+/*
+ * Linux virtio driver API over FreeBSD virtio(4)/virtqueue(9).
+ * Implemented in linux_virtio.c, which #undef's the macros here that
+ * collide with dev/virtio.
+ */
+
+#ifndef _LINUXKPI_LINUX_VIRTIO_H_
+#define _LINUXKPI_LINUX_VIRTIO_H_
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/device/driver.h>
+#include <linux/err.h>
+#include <linux/gfp.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+
+#include <asm/uaccess.h>
+
+struct virtio_config_ops;
+
+struct linux_virtqueue {
+ void (*callback)(struct linux_virtqueue *vq);
+ struct virtio_device *vdev;
+ unsigned int num_free;
+ /* FreeBSD */
+ struct virtqueue *bsd_vq; /* virtqueue(9): before the #define */
+ struct sglist *bsd_sg; /* segments for virtqueue_enqueue() */
+};
+#define virtqueue linux_virtqueue
+
+int linuxkpi_virtqueue_add_sgs(struct linux_virtqueue *vq,
+ struct scatterlist *sgs[], unsigned int out_sgs,
+ unsigned int in_sgs, void *data, gfp_t gfp);
+bool linuxkpi_virtqueue_kick_prepare(struct linux_virtqueue *vq);
+void *linuxkpi_virtqueue_get_buf(struct linux_virtqueue *vq,
+ unsigned int *len);
+void linuxkpi_virtqueue_disable_cb(struct linux_virtqueue *vq);
+bool linuxkpi_virtqueue_enable_cb(struct linux_virtqueue *vq);
+#define virtqueue_add_sgs(...) linuxkpi_virtqueue_add_sgs(__VA_ARGS__)
+#define virtqueue_kick_prepare(vq) linuxkpi_virtqueue_kick_prepare(vq)
+#define virtqueue_notify(vq) linuxkpi_virtqueue_notify(vq)
+#define virtqueue_get_buf(vq, len) linuxkpi_virtqueue_get_buf(vq, len)
+#define virtqueue_disable_cb(vq) linuxkpi_virtqueue_disable_cb(vq)
+#define virtqueue_enable_cb(vq) linuxkpi_virtqueue_enable_cb(vq)
+
+/* virtqueue_kick_prepare() already notified the device. */
+static inline bool
+linuxkpi_virtqueue_notify(struct linux_virtqueue *vq)
+{
+ return (true);
+}
+
+/*
+ * virtqueue(9) indirect tables hold VIRTIO_MAX_INDIRECT descriptors,
+ * where Linux allocates any size, so that is as many segments as a
+ * request can count on. Data a driver hands the device that could span
+ * more pages goes in physically contiguous memory, which add_sgs
+ * coalesces into one segment (LinuxKPI's kmalloc() uses contigmalloc(9)
+ * above PAGE_SIZE). Three segments stay for the command, the response,
+ * and one more page when the data is not page-aligned.
+ */
+#define LKPI_VIRTQUEUE_MAX_SEGS (PAGE_SIZE / 16)
+
+static inline void *
+lkpi_virtqueue_kvmalloc(size_t size, gfp_t gfp)
+{
+ if (size > INT_MAX) /* as Linux's kvmalloc() */
+ return (NULL);
+ /* M_WAITOK contigmalloc(9) can retry forever on NUMA: fail instead. */
+ if (size > (LKPI_VIRTQUEUE_MAX_SEGS - 3) * PAGE_SIZE)
+ return (kmalloc(size, (gfp & ~M_WAITOK) | M_NOWAIT));
+ return (kvmalloc(size, gfp));
+}
+
+static inline void *
+lkpi_virtqueue_memdup_user(const void __user *src, size_t len)
+{
+ void *p;
+
+ p = lkpi_virtqueue_kvmalloc(len, GFP_KERNEL);
+ if (p == NULL)
+ return (ERR_PTR(-ENOMEM));
+ if (copy_from_user(p, src, len) != 0) {
+ kvfree(p);
+ return (ERR_PTR(-EFAULT));
+ }
+ return (p);
+}
+
+struct virtio_device {
+ struct device dev;
+ const struct virtio_config_ops *config;
+ uint64_t features;
+ void *priv;
+ /* FreeBSD */
+ struct linux_virtqueue *bsd_vqs;
+ unsigned int bsd_nvqs;
+ struct mtx bsd_lock; /* callbacks vs. reset and detach */
+ bool bsd_vqs_ready; /* queue callbacks allowed */
+ bool bsd_detaching; /* removed: no config callbacks */
+};
+
+void linuxkpi_virtio_reset_device(struct virtio_device *vdev);
+#define virtio_reset_device(vdev) linuxkpi_virtio_reset_device(vdev)
+
+#define VIRTIO_DEV_ANY_ID 0xffffffff
+struct virtio_device_id {
+ uint32_t device;
+ uint32_t vendor;
+};
+
+struct virtio_driver {
+ struct device_driver driver;
+ const struct virtio_device_id *id_table;
+ const unsigned int *feature_table;
+ unsigned int feature_table_size;
+ int (*probe)(struct virtio_device *vdev);
+ void (*remove)(struct virtio_device *vdev);
+ void (*config_changed)(struct virtio_device *vdev);
+ /* FreeBSD */
+ driver_t bsddriver;
+};
+
+int linuxkpi_register_virtio_driver(struct virtio_driver *drv);
+void linuxkpi_unregister_virtio_driver(struct virtio_driver *drv);
+#define register_virtio_driver(drv) linuxkpi_register_virtio_driver(drv)
+#define unregister_virtio_driver(drv) linuxkpi_unregister_virtio_driver(drv)
+#define module_virtio_driver(_drv) \
+ module_driver(_drv, register_virtio_driver, unregister_virtio_driver)
+
+/* The subvendor is Linux's virtio vendor ID. */
+#define MODULE_DEVICE_TABLE_BUS_virtio(_bus, _table) \
+ MODULE_PNP_INFO("U32:device_type;V32:subvendor", virtio_pci, \
+ lkpi_ ## _table, _table, nitems(_table) - 1)
+
+/* No cross-device virtio dma-buf sharing. */
+static inline bool
+is_virtio_device(struct device *dev)
+{
+ return (false);
+}
+
+#endif /* _LINUXKPI_LINUX_VIRTIO_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/virtio_config.h b/sys/compat/linuxkpi/common/include/linux/virtio_config.h
new file mode 100644
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/virtio_config.h
@@ -0,0 +1,95 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Denis Borovikov <denis.borovikov@gmail.com>
+ *
+ * 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.
+ */
+
+#ifndef _LINUXKPI_LINUX_VIRTIO_CONFIG_H_
+#define _LINUXKPI_LINUX_VIRTIO_CONFIG_H_
+
+#include <linux/virtio.h>
+
+/* Feature bit numbers, Linux-style (dev/virtio uses masks). */
+#define VIRTIO_F_VERSION_1 32
+#define VIRTIO_F_ACCESS_PLATFORM 33
+
+struct irq_affinity;
+
+struct virtio_shm_region {
+ uint64_t addr;
+ uint64_t len;
+};
+
+typedef void vq_callback_t(struct linux_virtqueue *);
+
+struct virtqueue_info {
+ const char *name;
+ vq_callback_t *callback;
+};
+
+struct virtio_config_ops {
+ void (*del_vqs)(struct virtio_device *vdev);
+};
+
+static inline bool
+virtio_has_feature(const struct virtio_device *vdev, unsigned int fbit)
+{
+ return ((vdev->features & (1ULL << fbit)) != 0);
+}
+
+static inline bool
+virtio_has_dma_quirk(const struct virtio_device *vdev)
+{
+ return (!virtio_has_feature(vdev, VIRTIO_F_ACCESS_PLATFORM));
+}
+
+int linuxkpi_virtio_find_vqs(struct virtio_device *vdev, unsigned int nvqs,
+ struct linux_virtqueue *vqs[], struct virtqueue_info vqs_info[],
+ struct irq_affinity *desc);
+void linuxkpi_virtio_device_ready(struct virtio_device *vdev);
+#define virtio_find_vqs(...) linuxkpi_virtio_find_vqs(__VA_ARGS__)
+#define virtio_device_ready(vdev) linuxkpi_virtio_device_ready(vdev)
+
+/* Shared memory regions (VIRTIO_PCI_CAP_SHARED_MEMORY_CFG) unsupported. */
+static inline bool
+virtio_get_shm_region(struct virtio_device *vdev,
+ struct virtio_shm_region *region, uint8_t id)
+{
+ return (false);
+}
+
+void linuxkpi_virtio_cread_bytes(struct virtio_device *vdev,
+ unsigned int offset, void *buf, int len);
+void linuxkpi_virtio_cwrite_bytes(struct virtio_device *vdev,
+ unsigned int offset, const void *buf, int len);
+
+/* virtio_read_device_config() already returns host byte order. */
+#define virtio_cread_le(vdev, structname, member, ptr) \
+ linuxkpi_virtio_cread_bytes((vdev), \
+ offsetof(structname, member), (ptr), sizeof(*(ptr)))
+#define virtio_cwrite_le(vdev, structname, member, ptr) \
+ linuxkpi_virtio_cwrite_bytes((vdev), \
+ offsetof(structname, member), (ptr), sizeof(*(ptr)))
+
+#endif /* _LINUXKPI_LINUX_VIRTIO_CONFIG_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/virtio_ids.h b/sys/compat/linuxkpi/common/include/linux/virtio_ids.h
new file mode 100644
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/virtio_ids.h
@@ -0,0 +1,33 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Denis Borovikov <denis.borovikov@gmail.com>
+ *
+ * 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.
+ */
+
+#ifndef _LINUXKPI_LINUX_VIRTIO_IDS_H_
+#define _LINUXKPI_LINUX_VIRTIO_IDS_H_
+
+#define VIRTIO_ID_GPU 16
+
+#endif /* _LINUXKPI_LINUX_VIRTIO_IDS_H_ */
diff --git a/sys/compat/linuxkpi/common/include/linux/virtio_ring.h b/sys/compat/linuxkpi/common/include/linux/virtio_ring.h
new file mode 100644
--- /dev/null
+++ b/sys/compat/linuxkpi/common/include/linux/virtio_ring.h
@@ -0,0 +1,36 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Denis Borovikov <denis.borovikov@gmail.com>
+ *
+ * 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.
+ */
+
+#ifndef _LINUXKPI_LINUX_VIRTIO_RING_H_
+#define _LINUXKPI_LINUX_VIRTIO_RING_H_
+
+#include <linux/virtio.h>
+
+/* Feature bit number, Linux-style (dev/virtio uses a mask). */
+#define VIRTIO_RING_F_INDIRECT_DESC 28
+
+#endif /* _LINUXKPI_LINUX_VIRTIO_RING_H_ */
diff --git a/sys/compat/linuxkpi/common/src/linux_virtio.c b/sys/compat/linuxkpi/common/src/linux_virtio.c
new file mode 100644
--- /dev/null
+++ b/sys/compat/linuxkpi/common/src/linux_virtio.c
@@ -0,0 +1,479 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Denis Borovikov <denis.borovikov@gmail.com>
+ *
+ * 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.
+ */
+
+/*
+ * LinuxKPI virtio: binds Linux virtio_driver's to FreeBSD virtio_pci(4)
+ * children (as linux_pci.c does for pci_driver's) and implements the
+ * Linux virtqueue API over virtqueue(9).
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/sglist.h>
+
+#include <linux/kernel.h>
+#include <linux/compat.h>
+#include <linux/pci.h>
+#include <linux/virtio.h>
+#include <linux/virtio_config.h>
+
+/* Linux names that collide with dev/virtio. */
+#undef virtqueue
+#undef virtqueue_notify
+#undef VIRTIO_F_VERSION_1
+
+#include <dev/virtio/virtio.h>
+#include <dev/virtio/virtqueue.h>
+
+#include "virtio_if.h"
+
+static MALLOC_DEFINE(M_LKPI_VIRTIO, "lkpivirtio", "LinuxKPI virtio");
+
+CTASSERT(LKPI_VIRTQUEUE_MAX_SEGS == VIRTIO_MAX_INDIRECT);
+
+/*
+ * As sglist_append_phys(), but merging only when asked: coalescing must
+ * not cross the readable/writable boundary. Drivers place small
+ * responses right after the command, and a merged segment would be
+ * readable-only, leaving the device nowhere to write the response.
+ */
+static int
+lkpi_append_sg(struct sglist *fsg, struct scatterlist *sg, bool merge)
+{
+ struct sglist_seg *ss;
+ vm_paddr_t paddr = sg_phys(sg);
+
+ if (merge && fsg->sg_nseg > 0) {
+ ss = &fsg->sg_segs[fsg->sg_nseg - 1];
+ if (paddr == ss->ss_paddr + ss->ss_len) {
+ ss->ss_len += sg->length;
+ return (0);
+ }
+ }
+ if (fsg->sg_nseg == fsg->sg_maxseg)
+ return (EFBIG);
+ ss = &fsg->sg_segs[fsg->sg_nseg++];
+ ss->ss_paddr = paddr;
+ ss->ss_len = sg->length;
+ return (0);
+}
+
+int
+linuxkpi_virtqueue_add_sgs(struct linux_virtqueue *vq,
+ struct scatterlist *sgs[], unsigned int out_sgs, unsigned int in_sgs,
+ void *data, gfp_t gfp)
+{
+ struct sglist *fsg = vq->bsd_sg; /* caller's queue lock */
+ struct scatterlist *sg;
+ unsigned int i;
+ int readable, error;
+ bool merge;
+
+ sglist_reset(fsg);
+ error = 0;
+ for (i = 0; i < out_sgs && error == 0; i++)
+ for (sg = sgs[i]; sg != NULL && error == 0; sg = sg_next(sg))
+ error = lkpi_append_sg(fsg, sg, true);
+ readable = fsg->sg_nseg;
+ merge = false;
+ for (; i < out_sgs + in_sgs && error == 0; i++)
+ for (sg = sgs[i]; sg != NULL && error == 0; sg = sg_next(sg)) {
+ error = lkpi_append_sg(fsg, sg, merge);
+ merge = true;
+ }
+ if (error == 0)
+ error = virtqueue_enqueue(vq->bsd_vq, data, fsg, readable,
+ fsg->sg_nseg - readable);
+ if (error == EMSGSIZE) /* Linux has only ENOSPC for "full" */
+ error = ENOSPC;
+ vq->num_free = virtqueue_nfree(vq->bsd_vq);
+ return (-error);
+}
+
+void *
+linuxkpi_virtqueue_get_buf(struct linux_virtqueue *vq, unsigned int *len)
+{
+ void *cookie;
+
+ cookie = virtqueue_dequeue(vq->bsd_vq, len);
+ vq->num_free = virtqueue_nfree(vq->bsd_vq);
+ return (cookie);
+}
+
+/*
+ * virtqueue(9) has no locking of its own, so notify here, under the
+ * caller's queue lock, rather than in virtqueue_notify(), which Linux
+ * drivers call after dropping it.
+ */
+bool
+linuxkpi_virtqueue_kick_prepare(struct linux_virtqueue *vq)
+{
+ virtqueue_notify(vq->bsd_vq);
+ return (false);
+}
+
+void
+linuxkpi_virtqueue_disable_cb(struct linux_virtqueue *vq)
+{
+ virtqueue_disable_intr(vq->bsd_vq);
+}
+
+bool
+linuxkpi_virtqueue_enable_cb(struct linux_virtqueue *vq)
+{
+ /* Linux: false = more work pending; FreeBSD: non-zero. */
+ return (virtqueue_enable_intr(vq->bsd_vq) == 0);
+}
+
+/*
+ * One handler for all queues. It takes the softc, so it can check
+ * bsd_vqs_ready under the lock before touching queues that del_vqs() may
+ * have freed, and calls every callback: queue state may only be read
+ * under the driver's lock, and Linux callbacks cope with finding no work.
+ */
+static void
+lkpi_virtio_intr(void *arg)
+{
+ struct virtio_device *vdev;
+ struct linux_virtqueue *vq;
+ unsigned int i;
+
+ vdev = arg;
+ if (linux_set_current_flags(curthread, M_NOWAIT) != 0)
+ return;
+ mtx_lock(&vdev->bsd_lock);
+ for (i = 0; vdev->bsd_vqs_ready && i < vdev->bsd_nvqs; i++) {
+ vq = &vdev->bsd_vqs[i];
+ if (vq->callback != NULL)
+ vq->callback(vq);
+ }
+ mtx_unlock(&vdev->bsd_lock);
+}
+
+void
+linuxkpi_virtio_cread_bytes(struct virtio_device *vdev, unsigned int offset,
+ void *buf, int len)
+{
+ virtio_read_device_config(vdev->dev.bsddev, offset, buf, len);
+}
+
+void
+linuxkpi_virtio_cwrite_bytes(struct virtio_device *vdev, unsigned int offset,
+ const void *buf, int len)
+{
+ virtio_write_device_config(vdev->dev.bsddev, offset, buf, len);
+}
+
+void
+linuxkpi_virtio_device_ready(struct virtio_device *vdev)
+{
+ virtio_reinit_complete(vdev->dev.bsddev);
+}
+
+/*
+ * No queue callbacks after a reset or del_vqs(), and one already
+ * running completes first.
+ */
+static void
+lkpi_virtio_stop_callbacks(struct virtio_device *vdev)
+{
+ mtx_lock(&vdev->bsd_lock);
+ vdev->bsd_vqs_ready = false;
+ mtx_unlock(&vdev->bsd_lock);
+}
+
+void
+linuxkpi_virtio_reset_device(struct virtio_device *vdev)
+{
+ lkpi_virtio_stop_callbacks(vdev);
+ virtio_stop(vdev->dev.bsddev);
+}
+
+static void
+lkpi_virtio_del_vqs(struct virtio_device *vdev)
+{
+ unsigned int i;
+
+ lkpi_virtio_stop_callbacks(vdev);
+ /* The virtqueue(9) side is freed when the newbus child detaches. */
+ for (i = 0; i < vdev->bsd_nvqs; i++)
+ sglist_free(vdev->bsd_vqs[i].bsd_sg);
+ free(vdev->bsd_vqs, M_LKPI_VIRTIO);
+ vdev->bsd_vqs = NULL;
+ vdev->bsd_nvqs = 0;
+}
+
+static const struct virtio_config_ops lkpi_virtio_config_ops = {
+ .del_vqs = lkpi_virtio_del_vqs,
+};
+
+int
+linuxkpi_virtio_find_vqs(struct virtio_device *vdev, unsigned int nvqs,
+ struct linux_virtqueue *vqs[], struct virtqueue_info vqs_info[],
+ struct irq_affinity *desc)
+{
+ device_t dev = vdev->dev.bsddev;
+ struct vq_alloc_info *info;
+ struct linux_virtqueue *vq;
+ unsigned int i;
+ int error;
+
+ vdev->bsd_vqs = malloc(nvqs * sizeof(*vdev->bsd_vqs), M_LKPI_VIRTIO,
+ M_WAITOK | M_ZERO);
+ info = malloc(nvqs * sizeof(*info), M_LKPI_VIRTIO, M_WAITOK);
+ vdev->bsd_nvqs = nvqs;
+
+ for (i = 0; i < nvqs; i++) {
+ vq = &vdev->bsd_vqs[i];
+ vq->vdev = vdev;
+ vq->callback = vqs_info[i].callback;
+ VQ_ALLOC_INFO_INIT(&info[i], VIRTIO_MAX_INDIRECT,
+ lkpi_virtio_intr, vdev, &vq->bsd_vq, "%s %s",
+ device_get_nameunit(dev), vqs_info[i].name);
+ }
+
+ error = virtio_alloc_virtqueues(dev, nvqs, info);
+ free(info, M_LKPI_VIRTIO);
+ if (error != 0)
+ return (-error);
+ error = virtio_setup_intr(dev, INTR_TYPE_TTY);
+ if (error != 0)
+ return (-error);
+
+ /* Before any queue interrupt is enabled. */
+ mtx_lock(&vdev->bsd_lock);
+ vdev->bsd_vqs_ready = true;
+ mtx_unlock(&vdev->bsd_lock);
+ for (i = 0; i < nvqs; i++) {
+ vq = &vdev->bsd_vqs[i];
+ vq->num_free = virtqueue_nfree(vq->bsd_vq);
+ /* An indirect chain may exceed the ring size. */
+ vq->bsd_sg = sglist_alloc(MAX(vq->num_free,
+ VIRTIO_MAX_INDIRECT), M_WAITOK);
+ /*
+ * FreeBSD virtqueues start with interrupts disabled and
+ * drivers arm them explicitly; Linux vrings start enabled
+ * and Linux drivers rely on that.
+ */
+ virtqueue_enable_intr(vq->bsd_vq);
+ vqs[i] = vq;
+ }
+ return (0);
+}
+
+static struct virtio_driver *
+lkpi_virtio_driver(device_t dev)
+{
+ return (container_of(device_get_driver(dev), struct virtio_driver,
+ bsddriver));
+}
+
+static int
+lkpi_virtio_probe(device_t dev)
+{
+ struct virtio_driver *drv;
+ const struct virtio_device_id *id;
+ uint32_t type, vendor;
+
+ drv = lkpi_virtio_driver(dev);
+ type = virtio_get_device_type(dev);
+ vendor = virtio_get_subvendor(dev);
+ /* As Linux's virtio_id_match(). */
+ for (id = drv->id_table; id->device != 0; id++) {
+ if ((id->device == type || id->device == VIRTIO_DEV_ANY_ID) &&
+ (id->vendor == vendor || id->vendor == VIRTIO_DEV_ANY_ID)) {
+ device_set_desc(dev, drv->driver.name);
+ /* Outrank base drivers for the same device type. */
+ return (BUS_PROBE_VENDOR);
+ }
+ }
+ return (ENXIO);
+}
+
+/* Undoes attach; on detach it runs after the driver's remove(). */
+static void
+lkpi_virtio_release(struct virtio_device *vdev)
+{
+ /* Stops the device's interrupts and clears its status. */
+ linuxkpi_virtio_reset_device(vdev);
+ lkpi_virtio_del_vqs(vdev);
+ if (vdev->dev.parent != NULL)
+ pci_dev_put(to_pci_dev(vdev->dev.parent));
+ mtx_destroy(&vdev->bsd_lock);
+}
+
+static int
+lkpi_virtio_attach(device_t dev)
+{
+ struct virtio_device *vdev = device_get_softc(dev);
+ struct virtio_driver *drv = lkpi_virtio_driver(dev);
+ struct pci_dev *pdev;
+ uint64_t mask;
+ unsigned int i;
+ int error;
+
+ linux_set_current(curthread);
+
+ vdev->dev.bsddev = dev;
+ mtx_init(&vdev->bsd_lock, device_get_nameunit(dev), "lkpi virtio",
+ MTX_DEF);
+ /*
+ * virtio_reinit() runs the whole status sequence, reset included:
+ * devctl(8) attach and enable bypass vtpci's probe-and-attach
+ * wrapper, which otherwise sets the initial status.
+ *
+ * Linux always negotiates indirect descriptors and drivers size
+ * their ring-space waits on that: without them a request with more
+ * segments than ring slots can never be queued.
+ */
+ mask = VIRTIO_RING_F_INDIRECT_DESC;
+ for (i = 0; i < drv->feature_table_size; i++)
+ mask |= 1ULL << drv->feature_table[i];
+ error = virtio_reinit(dev, mask);
+ if (error != 0) {
+ device_printf(dev, "virtio_reinit failed: %d\n", error);
+ goto fail;
+ }
+
+ /*
+ * drm_dev_alloc() and friends need a real parent struct device. It
+ * has no DMA tag: VIRTIO_F_ACCESS_PLATFORM is never requested, so
+ * drivers hand the device physical addresses.
+ */
+ pdev = lkpinew_pci_dev(device_get_parent(dev));
+ if (pdev == NULL) {
+ error = ENOMEM;
+ goto fail;
+ }
+ vdev->dev.parent = &pdev->dev;
+ vdev->config = &lkpi_virtio_config_ops;
+ for (i = 0; i < 64; i++)
+ if (virtio_with_feature(dev, 1ULL << i))
+ vdev->features |= 1ULL << i;
+
+ error = drv->probe(vdev);
+ if (error != 0) {
+ error = -error;
+ goto fail;
+ }
+ return (0);
+
+fail:
+ lkpi_virtio_release(vdev);
+ return (error);
+}
+
+static int
+lkpi_virtio_detach(device_t dev)
+{
+ struct virtio_device *vdev = device_get_softc(dev);
+
+ linux_set_current(curthread);
+
+ /* Already removed if an earlier detach returned EBUSY below. */
+ if (!vdev->bsd_detaching) {
+ /*
+ * As Linux, no config callbacks once remove() starts: the
+ * device stays attached until we return.
+ */
+ mtx_lock(&vdev->bsd_lock);
+ vdev->bsd_detaching = true;
+ mtx_unlock(&vdev->bsd_lock);
+ lkpi_virtio_driver(dev)->remove(vdev);
+ }
+ /*
+ * As linux_pci: refuse while others, such as a DRM device with open
+ * files, still hold the parent, whose bsddev is the transport.
+ */
+ if (kref_read(&vdev->dev.parent->kobj.kref) > 1) {
+ device_printf(dev, "%s failed due to %u other pending "
+ "references on the parent device.\n", __func__,
+ kref_read(&vdev->dev.parent->kobj.kref) - 1);
+ return (EBUSY);
+ }
+ lkpi_virtio_release(vdev);
+ return (0);
+}
+
+static int
+lkpi_virtio_config_change(device_t dev)
+{
+ struct virtio_device *vdev = device_get_softc(dev);
+ struct virtio_driver *drv = lkpi_virtio_driver(dev);
+
+ if (linux_set_current_flags(curthread, M_NOWAIT) != 0)
+ return (0);
+ /*
+ * Delivered between the end of probe and the start of detach.
+ * Unlike Linux, a change that arrives during probe is not replayed
+ * afterwards.
+ */
+ mtx_lock(&vdev->bsd_lock);
+ if (device_is_attached(dev) && !vdev->bsd_detaching &&
+ drv->config_changed != NULL)
+ drv->config_changed(vdev);
+ mtx_unlock(&vdev->bsd_lock);
+ return (0);
+}
+
+static device_method_t lkpi_virtio_methods[] = {
+ DEVMETHOD(device_probe, lkpi_virtio_probe),
+ DEVMETHOD(device_attach, lkpi_virtio_attach),
+ DEVMETHOD(device_detach, lkpi_virtio_detach),
+
+ DEVMETHOD(virtio_config_change, lkpi_virtio_config_change),
+
+ DEVMETHOD_END
+};
+
+int
+linuxkpi_register_virtio_driver(struct virtio_driver *drv)
+{
+ int error;
+
+ drv->bsddriver.name = drv->driver.name;
+ drv->bsddriver.methods = lkpi_virtio_methods;
+ drv->bsddriver.size = sizeof(struct virtio_device);
+ bus_topo_lock();
+ error = devclass_add_driver(devclass_create("virtio_pci"),
+ &drv->bsddriver, BUS_PASS_DEFAULT, NULL);
+ bus_topo_unlock();
+ return (-error);
+}
+
+void
+linuxkpi_unregister_virtio_driver(struct virtio_driver *drv)
+{
+ bus_topo_lock();
+ (void)devclass_delete_driver(devclass_find("virtio_pci"),
+ &drv->bsddriver);
+ bus_topo_unlock();
+}
diff --git a/sys/compat/linuxkpi/common/src/linuxkpi_virtiokmod.c b/sys/compat/linuxkpi/common/src/linuxkpi_virtiokmod.c
new file mode 100644
--- /dev/null
+++ b/sys/compat/linuxkpi/common/src/linuxkpi_virtiokmod.c
@@ -0,0 +1,8 @@
+/* Public domain. */
+
+#include <sys/param.h>
+#include <sys/module.h>
+
+MODULE_VERSION(linuxkpi_virtio, 1);
+MODULE_DEPEND(linuxkpi_virtio, linuxkpi, 1, 1, 1);
+MODULE_DEPEND(linuxkpi_virtio, virtio, 1, 1, 1);
diff --git a/sys/modules/Makefile b/sys/modules/Makefile
--- a/sys/modules/Makefile
+++ b/sys/modules/Makefile
@@ -228,6 +228,7 @@
linuxkpi \
linuxkpi_hdmi \
linuxkpi_video \
+ linuxkpi_virtio \
linuxkpi_wlan \
${_lio} \
lpt \
diff --git a/sys/modules/linuxkpi_virtio/Makefile b/sys/modules/linuxkpi_virtio/Makefile
new file mode 100644
--- /dev/null
+++ b/sys/modules/linuxkpi_virtio/Makefile
@@ -0,0 +1,14 @@
+.PATH: ${SRCTOP}/sys/compat/linuxkpi/common/src
+
+KMOD= linuxkpi_virtio
+SRCS= linux_virtio.c \
+ linuxkpi_virtiokmod.c
+
+SRCS+= ${LINUXKPI_GENSRCS}
+SRCS+= virtio_if.h
+
+CFLAGS+= ${LINUXKPI_INCLUDES}
+
+EXPORT_SYMS= YES
+
+.include <bsd.kmod.mk>

File Metadata

Mime Type
text/plain
Expires
Fri, Nov 6, 4:29 AM (4 h, 35 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41489147
Default Alt Text
D61216.1793939361.diff (28 KB)

Event Timeline