diff --git a/sys/amd64/conf/GENERIC b/sys/amd64/conf/GENERIC --- a/sys/amd64/conf/GENERIC +++ b/sys/amd64/conf/GENERIC @@ -352,6 +352,7 @@ device virtio_blk # VirtIO Block device device virtio_scsi # VirtIO SCSI device device virtio_balloon # VirtIO Memory Balloon device +device virtio_fs # VirtIO File System device # Linux KVM paravirtualization support device kvm_clock # KVM paravirtual clock driver diff --git a/sys/conf/files b/sys/conf/files --- a/sys/conf/files +++ b/sys/conf/files @@ -3448,6 +3448,7 @@ dev/virtio/random/virtio_random.c optional virtio_random dev/virtio/scmi/virtio_scmi.c optional virtio_scmi dev/virtio/scsi/virtio_scsi.c optional virtio_scsi +dev/virtio/fs/virtio_fs.c optional virtio_fs dev/vkbd/vkbd.c optional vkbd dev/vmgenc/vmgenc_acpi.c optional acpi dev/vmware/vmxnet3/if_vmx.c optional vmx diff --git a/sys/dev/virtio/fs/virtio_fs.h b/sys/dev/virtio/fs/virtio_fs.h new file mode 100644 --- /dev/null +++ b/sys/dev/virtio/fs/virtio_fs.h @@ -0,0 +1,44 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * 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. + */ + +#ifndef _VIRTIO_FS_H +#define _VIRTIO_FS_H + +struct vtfs_softc; +typedef struct vtfs_softc *vtfs_instance; +typedef void (*vtfs_fuse_cb)(void *, uint32_t); +typedef void (*vtfs_teardown_cb)(void *); + +void vtfs_register_cb(vtfs_instance, vtfs_fuse_cb, vtfs_fuse_cb, + vtfs_teardown_cb, void *); +int vtfs_enqueue(vtfs_instance, void *, struct sglist *, int, int, bool); +int vtfs_find(char *, vtfs_instance *); +void vtfs_release(vtfs_instance); +void vtfs_drain(vtfs_instance); +void vtfs_unregister_cb(vtfs_instance); + +#endif /* _VIRTIO_FS_H */ diff --git a/sys/dev/virtio/fs/virtio_fs.c b/sys/dev/virtio/fs/virtio_fs.c new file mode 100644 --- /dev/null +++ b/sys/dev/virtio/fs/virtio_fs.c @@ -0,0 +1,606 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * 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. + */ + +/* Device for the VirtIO file system. */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "virtio_if.h" +#include "virtio_fs_internal.h" + +/* Methods for the vtfs device. */ + +struct vtfs_softc; + +/* Module-wide device list. */ + +LIST_HEAD(, vtfs_softc) vtfs_contexts = LIST_HEAD_INITIALIZER(vtfs_contexts); +struct mtx vtfs_mod_mtx; +MTX_SYSINIT(vtfs_mod_mtx, &vtfs_mod_mtx, "Virtio FS Global Lock", MTX_DEF); + +#define VTFS_LOCK() mtx_lock(&vtfs_mod_mtx) +#define VTFS_UNLOCK() mtx_unlock(&vtfs_mod_mtx) + +#define FSQ_LOCK(_fsq) mtx_lock(&(_fsq)->vtfsq_mtx) +#define FSQ_UNLOCK(_fsq) mtx_unlock(&(_fsq)->vtfsq_mtx) +#define FSQ_WAIT(_fsq) cv_wait(&(_fsq)->vtfsq_cv, &(_fsq)->vtfsq_mtx) +#define FSQ_KICK(_fsq) cv_broadcast(&(_fsq)->vtfsq_cv) + +static int vtfs_modevent(module_t, int, void *); + +static int vtfs_probe(device_t); +static int vtfs_attach(device_t); +static int vtfs_detach(device_t); + +static device_method_t vtfs_methods[] = { + DEVMETHOD(device_probe, vtfs_probe), + DEVMETHOD(device_attach, vtfs_attach), + DEVMETHOD(device_detach, vtfs_detach), + DEVMETHOD_END +}; + +static driver_t vtfs_driver = { + "vtfs", + vtfs_methods, + sizeof(struct vtfs_softc), +}; + +VIRTIO_DRIVER_MODULE(vtfs, vtfs_driver, vtfs_modevent, NULL); +MODULE_VERSION(vtfs, 1); +MODULE_DEPEND(vtfs, virtio, 1, 1, 1); + +VIRTIO_SIMPLE_PNPINFO(vtfs, VIRTIO_ID_FS, "VirtIO FS Adapter"); + +static int +vtfs_modevent(module_t mod, int type, void *unused) +{ + int error = 0; + + switch (type) { + case MOD_LOAD: + case MOD_UNLOAD: + case MOD_QUIESCE: + case MOD_SHUTDOWN: + break; + default: + error = EOPNOTSUPP; + break; + } + + return (error); +} + +static int +vtfs_probe(device_t dev) +{ + return (VIRTIO_SIMPLE_PROBE(dev, vtfs)); +} + +static int +vtfs_read_config(struct vtfs_softc *sc) +{ + device_t dev; + size_t taglen; +#ifdef INVARIANTS + struct vtfs_config fscfg; +#endif + + dev = sc->vtfs_dev; + + _Static_assert(sizeof(sc->vtfs_nqs) == sizeof(fscfg.num_request_queues), + "reading num_request_queues into wrongly typed struct"); + + virtio_read_device_config(dev, + offsetof(struct vtfs_config, num_request_queues), + &sc->vtfs_nqs, + sizeof(&sc->vtfs_nqs)); + + if (sc->vtfs_nqs <= 0) { + VTFS_ERR("read non-positive queue number from host"); + return (EINVAL); + } + + /* Account for the priority queue. */ + sc->vtfs_nqs += 1; + + virtio_read_device_config_array(dev, + offsetof(struct vtfs_config, tag), + sc->vtfs_tag, 1, TAG_SIZE); + + /* The read tag may not be NUL-terminated. */ + taglen = strnlen(sc->vtfs_tag, TAG_SIZE); + if (taglen == 0) { + VTFS_ERR("read empty tag"); + return (EINVAL); + } + + KASSERT(taglen < sizeof(sc->vtfs_tag), ("overran tag buffer")); + sc->vtfs_tag[taglen] = '\0'; + + VTFS_DEBUG("fs tag: %s, # vqs: %d", sc->vtfs_tag, sc->vtfs_nqs); + + return (0); +} + +static void +vtfs_vq_intr(void *xfsq) +{ + struct vtfs_fsq *fsq = xfsq; + struct virtqueue *vq = fsq->vtfsq_vq; + uint32_t len; + void *ftick; + + /* We turn off vtfs interrupts before removing the callback. */ + KASSERT(fsq->vtfsq_cb != NULL, ("missing fuse callback")); + + FSQ_LOCK(fsq); + +again: + /* Go through the tickets one by one, invoke the fuse callback. */ + while ((ftick = virtqueue_dequeue(vq, &len)) != NULL) + fsq->vtfsq_cb(ftick, len); + + /* If the host pushed another descriptor in the meantime, go again. */ + if (virtqueue_enable_intr(vq) != 0) { + virtqueue_disable_intr(vq); + goto again; + } + + FSQ_KICK(fsq); + FSQ_UNLOCK(fsq); +} + +static int +vtfs_init_fsq(struct vtfs_softc *sc, int id) +{ + struct vtfs_fsq *fsq = &sc->vtfs_fsqs[id]; + int error; + + if (id == 0) { + snprintf(fsq->vtfsq_name, sizeof(fsq->vtfsq_name), + "%s-priority", device_get_nameunit(sc->vtfs_dev)); + } else { + snprintf(fsq->vtfsq_name, sizeof(fsq->vtfsq_name), + "%s-queue%d", device_get_nameunit(sc->vtfs_dev), id); + } + + mtx_init(&fsq->vtfsq_mtx, fsq->vtfsq_name, NULL, MTX_DEF); + cv_init(&fsq->vtfsq_cv, "fsqvqcv"); + + fsq->vtfsq_tq = taskqueue_create("vtfsqtq", M_WAITOK, + taskqueue_thread_enqueue, &fsq->vtfsq_tq); + if (fsq->vtfsq_tq == NULL) + return (ENOMEM); + + error = taskqueue_start_threads(&fsq->vtfsq_tq, VTFS_TQTHREAD, + PVM, "VirtioFS device"); + if (error != 0) + return (error); + + fsq->vtfsq_sc = sc; + VTFS_DEBUG("vq %s online", fsq->vtfsq_name); + + return (0); +} + +static void +vtfs_fini_fsq(struct vtfs_fsq *fsq) +{ + + if (fsq->vtfsq_sg != NULL) { + sglist_free(fsq->vtfsq_sg); + fsq->vtfsq_sg = NULL; + } + + if (fsq->vtfsq_tq != NULL) { + taskqueue_drain_all(fsq->vtfsq_tq); + taskqueue_free(fsq->vtfsq_tq); + fsq->vtfsq_tq = NULL; + } + + if (mtx_initialized(&fsq->vtfsq_mtx) != 0) { + cv_destroy(&fsq->vtfsq_cv); + mtx_destroy(&fsq->vtfsq_mtx); + } + + VTFS_DEBUG("vq %s destroyed", fsq->vtfsq_name); +} + +static int +vtfs_alloc_fsqueues(struct vtfs_softc *sc) +{ + int error; + int nvqs; + int i, j; + + nvqs = sc->vtfs_nqs; + sc->vtfs_fsqs = malloc((sizeof(*sc->vtfs_fsqs)) * nvqs, M_DEVBUF, + M_NOWAIT | M_ZERO); + if (sc->vtfs_fsqs == NULL) + return (ENOMEM); + + for (i = 0; i < nvqs; i++) { + error = vtfs_init_fsq(sc, i); + if (error != 0) + goto fail; + } + + return (0); + +fail: + VTFS_DEBUG("failed while initializing vq %d", i); + + /* Clean up any initialized fsqs. */ + for (j = 0; j < i; j++) + vtfs_fini_fsq(&sc->vtfs_fsqs[j]); + + free(sc->vtfs_fsqs, M_DEVBUF); + sc->vtfs_fsqs = NULL; + + return (error); +} + +static void +vtfs_free_fsqueues(struct vtfs_softc *sc) +{ + int i; + + for (i = 0; i < sc->vtfs_nqs; i++) + vtfs_fini_fsq(&sc->vtfs_fsqs[i]); + + free(sc->vtfs_fsqs, M_DEVBUF); +} + +static int +vtfs_alloc_virtqueues(struct vtfs_softc *sc) +{ + struct vq_alloc_info *vq_info; + struct vtfs_fsq *fsq; + device_t dev; + int error; + int nqs; + int i; + + dev = sc->vtfs_dev; + nqs = sc->vtfs_nqs; + + /* + * We have num_request_queues regular queues and one high-priority + * queue for FORGET/general priority operations. + */ + KASSERT(nqs > 1, ("missing regular queues")); + + vq_info = malloc(sizeof(*vq_info) * nqs, M_TEMP, M_NOWAIT); + if (vq_info == NULL) + return (ENOMEM); + + for (i = 0; i < nqs; i++) { + fsq = &sc->vtfs_fsqs[i]; + VQ_ALLOC_INFO_INIT(&vq_info[i], VTFS_MAXSEGS, vtfs_vq_intr, + fsq, &fsq->vtfsq_vq, "%s", fsq->vtfsq_name); + } + + error = virtio_alloc_virtqueues(dev, nqs, vq_info); + free(vq_info, M_TEMP); + + VTFS_DEBUG("virtqueues online"); + + return (error); +} + +static int +vtfs_enable_intr(device_t dev) +{ + struct vtfs_softc *sc; + int error; + int i; + + sc = device_get_softc(dev); + + error = virtio_setup_intr(dev, INTR_TYPE_BIO); + if (error != 0) + return (error); + + for (i = 0; i < sc->vtfs_nqs; i++) + virtqueue_enable_intr(sc->vtfs_fsqs[i].vtfsq_vq); + + + return (0); +} + +static void +vtfs_stop(struct vtfs_softc *sc) +{ + struct virtqueue *vq; + int i; + + /* The file system should have cleared the virtqueues when umounting .*/ + for (i = 0; i < sc->vtfs_nqs; i++) { + vq = sc->vtfs_fsqs[i].vtfsq_vq; + virtqueue_disable_intr(vq); + KASSERT(virtqueue_empty(vq), ("virtqueue not empty")); + } + + virtio_stop(sc->vtfs_dev); + +} + +static void +vtfs_add(struct vtfs_softc *sc) +{ + VTFS_LOCK(); + LIST_INSERT_HEAD(&vtfs_contexts, sc, vtfs_link); + VTFS_UNLOCK(); +} +static void +vtfs_remove(struct vtfs_softc *sc) +{ + VTFS_LOCK(); + LIST_REMOVE(sc, vtfs_link); + VTFS_UNLOCK(); +} + +int +vtfs_find(char *tag, struct vtfs_softc **scp) +{ + struct vtfs_softc *sc; + + VTFS_LOCK(); + + LIST_FOREACH(sc, &vtfs_contexts, vtfs_link) { + if (strncmp(sc->vtfs_tag, tag, sizeof(sc->vtfs_tag)) != 0) + continue; + + if (sc->vtfs_inuse) { + VTFS_UNLOCK(); + return (EALREADY); + } + + sc->vtfs_inuse = true; + VTFS_UNLOCK(); + + *scp = sc; + + return (0); + } + + VTFS_UNLOCK(); + + return (EINVAL); +} + +void +vtfs_release(struct vtfs_softc *sc) +{ + VTFS_LOCK(); + sc->vtfs_inuse = false; + VTFS_UNLOCK(); +} + +static int +vtfs_attach(device_t dev) +{ + struct vtfs_softc *sc; + int error; + + sc = device_get_softc(dev); + sc->vtfs_dev = dev; + sc->vtfs_inuse = false; + + error = vtfs_read_config(sc); + if (error != 0) + goto fail; + + error = vtfs_alloc_fsqueues(sc); + if (error != 0) + goto fail; + + error = vtfs_alloc_virtqueues(sc); + if (error != 0) + goto fail; + + error = vtfs_enable_intr(dev); + if (error != 0) + goto fail; + + vtfs_add(sc); + + return (0); + +fail: + vtfs_detach(dev); + + return (error); +} + + +static int +vtfs_detach(device_t dev) +{ + struct vtfs_softc *sc; + + sc = device_get_softc(dev); + + vtfs_remove(sc); + + /* Kill any active FUSE session. */ + if (sc->vtfs_detach_cb) + sc->vtfs_detach_cb(sc->vtfs_detach_cb_arg); + + vtfs_drain(sc); + + vtfs_stop(sc); + vtfs_free_fsqueues(sc); + + VTFS_DEBUG("device detached"); + + return (0); +} + +void +vtfs_register_cb(struct vtfs_softc *sc, vtfs_fuse_cb forget_cb, + vtfs_fuse_cb regular_cb, vtfs_teardown_cb detach_cb, + void *detach_cb_arg) +{ + struct vtfs_fsq *fsq; + int i; + + sc->vtfs_detach_cb = detach_cb; + sc->vtfs_detach_cb_arg = detach_cb_arg; + + for (i = 0; i < sc->vtfs_nqs; i++) { + fsq = &sc->vtfs_fsqs[i]; + FSQ_LOCK(fsq); + if (i == VTFS_FORGET_FSQ) + fsq->vtfsq_cb = forget_cb; + else + fsq->vtfsq_cb = regular_cb; + FSQ_UNLOCK(fsq); + } + +} + +void +vtfs_unregister_cb(struct vtfs_softc *sc) +{ + struct vtfs_fsq *fsq; + int i; + + for (i = 0; i < sc->vtfs_nqs; i++) { + fsq = &sc->vtfs_fsqs[i]; + + FSQ_LOCK(fsq); + fsq->vtfsq_cb = NULL; + FSQ_UNLOCK(fsq); + } + + sc->vtfs_detach_cb_arg = NULL; + sc->vtfs_detach_cb = NULL; +} + +int +vtfs_enqueue(struct vtfs_softc *sc, void *ftick, struct sglist *sg, + int readable, int writable, bool urgent) +{ + struct virtqueue *vq; + struct vtfs_fsq *fsq; + int error; + + if (urgent) + fsq = &sc->vtfs_fsqs[VTFS_FORGET_FSQ]; + else + fsq = &sc->vtfs_fsqs[VTFS_REGULAR_FSQ]; + + FSQ_LOCK(fsq); + vq = fsq->vtfsq_vq; + + KASSERT(sg->sg_nseg == readable + writable, ("inconsistent segmentation")); + + error = virtqueue_enqueue(vq, ftick, sg, readable, writable); + while (error == ENOSPC) { + FSQ_WAIT(fsq); + error = virtqueue_enqueue(vq, ftick, sg, readable, writable); + } + + if (error != 0) + return (error); + + virtqueue_notify(vq); + + FSQ_UNLOCK(fsq); + sglist_free(sg); + + return (0); +} + +static void +vtfs_drain_vq(struct vtfs_fsq *fsq) +{ + struct virtqueue *vq = fsq->vtfsq_vq; + uint32_t len; + void *ftick; + + /* + * If there is no callback, we don't have an + * upper-level FUSE session. + */ + if (fsq->vtfsq_cb == NULL) { + KASSERT(virtqueue_empty(vq), ("virtqueue not empty")); + return; + } + + vq = fsq->vtfsq_vq; + + while ((ftick = virtqueue_dequeue(vq, &len)) != NULL) { + fsq->vtfsq_cb(ftick, len); + } + + KASSERT(virtqueue_empty(vq), ("virtqueue not empty")); +} + +void +vtfs_drain(struct vtfs_softc *sc) +{ + struct vtfs_fsq *fsq; + int i; + + for (i = 0; i < sc->vtfs_nqs; i++) { + fsq = &sc->vtfs_fsqs[i]; + FSQ_LOCK(fsq); + virtqueue_disable_intr(fsq->vtfsq_vq); + FSQ_UNLOCK(fsq); + } + + for (i = 0; i < sc->vtfs_nqs; i++) { + fsq = &sc->vtfs_fsqs[i]; + FSQ_LOCK(fsq); + vtfs_drain_vq(fsq); + FSQ_UNLOCK(fsq); + } + +} diff --git a/sys/dev/virtio/fs/virtio_fs_internal.h b/sys/dev/virtio/fs/virtio_fs_internal.h new file mode 100644 --- /dev/null +++ b/sys/dev/virtio/fs/virtio_fs_internal.h @@ -0,0 +1,60 @@ +#ifndef _VIRTIO_FS_INTERNAL_ +#define _VIRTIO_FS_INTERNAL_ + +/* Protocol-specified file system tag size. */ +#define TAG_SIZE (36) +#define FSQ_NAME_SIZE (16) + +#define VTFS_DEBUG(fmt, ...) \ + do { \ + printf("(%s:%d) " fmt "\n", __func__, __LINE__, ##__VA_ARGS__); \ + } while (0) + +#define VTFS_ERR(fmt, ...) \ + do { \ + printf("[ERROR] (%s:%d) " fmt "\n", __func__, __LINE__, ##__VA_ARGS__); \ + } while (0) + +/* Struct for the file system instance. Provided by the host. */ +struct vtfs_config { + /* UTF-8 File system tag. */ + uint8_t tag[TAG_SIZE]; + /* Number of request queues. */ + uint32_t num_request_queues; + /* Minimum # bytes for each buffer in the notification queue. */ + uint32_t notify_buf_size; +} __packed; + +/* A queue structure belonging to a virtio fs device. */ +struct vtfs_fsq { + struct mtx vtfsq_mtx; + struct cv vtfsq_cv; + struct virtqueue *vtfsq_vq; + struct taskqueue *vtfsq_tq; + struct sglist *vtfsq_sg; + char vtfsq_name[FSQ_NAME_SIZE]; + struct vtfs_softc *vtfsq_sc; + vtfs_fuse_cb vtfsq_cb; +}; + +/* A single virtio fs device instance. */ +struct vtfs_softc { + device_t vtfs_dev; + struct mtx vtfs_mtx; + struct vtfs_fsq *vtfs_fsqs; + bool vtfs_inuse; /* protected by the vtfs modulelock */ + vtfs_teardown_cb vtfs_detach_cb; + void *vtfs_detach_cb_arg; + /* Host-provided config state. */ + uint8_t vtfs_tag[TAG_SIZE + 1]; + uint32_t vtfs_nqs; + LIST_ENTRY(vtfs_softc) vtfs_link; +}; + +#define VTFS_FORGET_FSQ (0) +#define VTFS_REGULAR_FSQ (1) + +#define VTFS_MAXSEGS (16) +#define VTFS_TQTHREAD (4) + +#endif /* _VIRTIO_FS_INTERNAL_ */ diff --git a/sys/modules/virtio/Makefile b/sys/modules/virtio/Makefile --- a/sys/modules/virtio/Makefile +++ b/sys/modules/virtio/Makefile @@ -22,6 +22,6 @@ # SUCH DAMAGE. # -SUBDIR= virtio pci network block balloon scsi random console p9fs +SUBDIR= virtio pci network block balloon scsi random console p9fs fs .include diff --git a/sys/modules/virtio/Makefile b/sys/modules/virtio/fs/Makefile copy from sys/modules/virtio/Makefile copy to sys/modules/virtio/fs/Makefile --- a/sys/modules/virtio/Makefile +++ b/sys/modules/virtio/fs/Makefile @@ -1,5 +1,3 @@ -# -# # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: @@ -22,6 +20,11 @@ # SUCH DAMAGE. # -SUBDIR= virtio pci network block balloon scsi random console p9fs +.PATH: ${SRCTOP}/sys/dev/virtio/fs + +KMOD= vtfs +SRCS= virtio_fs.c +SRCS+= virtio_bus_if.h virtio_if.h +SRCS+= bus_if.h device_if.h -.include +.include