From 100353cfbf882e23c911300ebd0cb458bd3ee975 Mon Sep 17 00:00:00 2001 From: Jakub Wojciech Klama Date: Sat, 3 Oct 2020 19:05:13 +0000 Subject: [PATCH] Add virtio-9p (aka VirtFS) filesystem sharing to bhyve. VirtFS allows sharing an arbitrary directory tree between bhyve virtual machine and the host. Current implementation has a fairly complete support for 9P2000.L protocol, except for the extended attribute support. It has been verified to work with the qemu-kvm hypervisor. Reviewed by: rgrimes, emaste, jhb, trasz Approved by: trasz (mentor) MFC after: 1 month Relnotes: yes Sponsored by: Conclusive Engineering (development), vStack.com (funding) Differential Revision: https://reviews.freebsd.org/D10335 --- etc/mtree/BSD.include.dist | 2 + lib/Makefile | 1 + lib/lib9p/Makefile | 28 +++ share/mk/bsd.libnames.mk | 1 + share/mk/src.libnames.mk | 2 + usr.sbin/bhyve/Makefile | 4 +- usr.sbin/bhyve/bhyve.8 | 15 ++ usr.sbin/bhyve/pci_virtio_9p.c | 344 +++++++++++++++++++++++++++++++++ usr.sbin/bhyve/virtio.h | 1 + 9 files changed, 397 insertions(+), 1 deletion(-) create mode 100644 lib/lib9p/Makefile create mode 100644 usr.sbin/bhyve/pci_virtio_9p.c diff --git a/etc/mtree/BSD.include.dist b/etc/mtree/BSD.include.dist index 22bbaa73662e..ec71a7f4509f 100644 --- a/etc/mtree/BSD.include.dist +++ b/etc/mtree/BSD.include.dist @@ -193,6 +193,8 @@ .. lib80211 .. + lib9p + .. libipt .. libmilter diff --git a/lib/Makefile b/lib/Makefile index ff3e36a7136e..f5006d4eb5f4 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -30,6 +30,7 @@ SUBDIR= ${SUBDIR_BOOTSTRAP} \ .WAIT \ libsqlite3 \ geom \ + lib9p \ libalias \ libarchive \ libauditd \ diff --git a/lib/lib9p/Makefile b/lib/lib9p/Makefile new file mode 100644 index 000000000000..c909bdb1f781 --- /dev/null +++ b/lib/lib9p/Makefile @@ -0,0 +1,28 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../contrib/lib9p +CFLAGS+= -DWITH_CASPER +CFLAGS+= -I${.CURDIR} +CFLAGS+= -I${.CURDIR}/../../contrib/lib9p + +LIB= 9p +PACKAGE= lib${LIB} +SHLIB_MAJOR= 1 +SRCS= connection.c \ + genacl.c \ + hashtable.c \ + log.c \ + pack.c \ + request.c \ + rfuncs.c \ + threadpool.c \ + utils.c \ + backend/fs.c \ + transport/socket.c + +INCSDIR= ${INCLUDEDIR}/lib9p +INCS= fid.h lib9p.h backend/fs.h + +LIBADD= sbuf + +.include diff --git a/share/mk/bsd.libnames.mk b/share/mk/bsd.libnames.mk index ad7d549cad4c..eba09ffa5fda 100644 --- a/share/mk/bsd.libnames.mk +++ b/share/mk/bsd.libnames.mk @@ -17,6 +17,7 @@ LIBDESTDIR= ${SYSROOT:U${DESTDIR}} LIBCRT0?= ${LIBDESTDIR}${LIBDIR_BASE}/crt0.o LIB80211?= ${LIBDESTDIR}${LIBDIR_BASE}/lib80211.a +LIB9P?= ${LIBDESTDIR}${LIBDIR_BASE}/lib9p.a LIBALIAS?= ${LIBDESTDIR}${LIBDIR_BASE}/libalias.a LIBARCHIVE?= ${LIBDESTDIR}${LIBDIR_BASE}/libarchive.a LIBASN1?= ${LIBDESTDIR}${LIBDIR_BASE}/libasn1.a diff --git a/share/mk/src.libnames.mk b/share/mk/src.libnames.mk index 612cd6604025..aa2ab42da62b 100644 --- a/share/mk/src.libnames.mk +++ b/share/mk/src.libnames.mk @@ -70,6 +70,7 @@ _LIBRARIES= \ ${_INTERNALLIBS} \ ${LOCAL_LIBRARIES} \ 80211 \ + 9p \ alias \ archive \ asn1 \ @@ -246,6 +247,7 @@ LIBVERIEXEC?= ${LIBVERIEXECDIR}/libveriexec.a # Each library's LIBADD needs to be duplicated here for static linkage of # 2nd+ order consumers. Auto-generating this would be better. _DP_80211= sbuf bsdxml +_DP_9p= sbuf _DP_archive= z bz2 lzma bsdxml zstd _DP_zstd= pthread .if ${MK_BLACKLIST} != "no" diff --git a/usr.sbin/bhyve/Makefile b/usr.sbin/bhyve/Makefile index 8898468c38cf..c1d4d2ee77f6 100644 --- a/usr.sbin/bhyve/Makefile +++ b/usr.sbin/bhyve/Makefile @@ -3,6 +3,7 @@ # .include +CFLAGS+=-I${.CURDIR}/../../contrib/lib9p CFLAGS+=-I${SRCTOP}/sys .PATH: ${SRCTOP}/sys/cam/ctl @@ -47,6 +48,7 @@ SRCS= \ pci_lpc.c \ pci_nvme.c \ pci_passthru.c \ + pci_virtio_9p.c \ pci_virtio_block.c \ pci_virtio_console.c \ pci_virtio_net.c \ @@ -82,7 +84,7 @@ CFLAGS.kernemu_dev.c+= -I${SRCTOP}/sys/amd64 .PATH: ${BHYVE_SYSDIR}/sys/amd64/vmm SRCS+= vmm_instruction_emul.c -LIBADD= vmmapi md pthread z util sbuf cam +LIBADD= vmmapi md pthread z util sbuf cam 9p casper cap_pwd cap_grp .if ${MK_BHYVE_SNAPSHOT} != "no" LIBADD+= ucl xo .endif diff --git a/usr.sbin/bhyve/bhyve.8 b/usr.sbin/bhyve/bhyve.8 index 2e91ed86d3a1..7bb3bbc9e20a 100644 --- a/usr.sbin/bhyve/bhyve.8 +++ b/usr.sbin/bhyve/bhyve.8 @@ -258,6 +258,8 @@ Virtio network interface. Virtio block storage interface. .It Li virtio-scsi Virtio SCSI interface. +.It Li virtio-9p +Virtio 9p (VirtFS) interface. .It Li virtio-rnd Virtio RNG interface. .It Li virtio-console @@ -374,6 +376,19 @@ Initiator ID to use when sending requests to specified CTL port. The default value is 0. .El .Pp +9P devices: +.Bl -tag -width 10n +.It Pa sharename=/path/to/share[,9p-device-options] +.El +.Pp +The +.Ar 9p-device-options +are: +.Bl -tag -width 10n +.It Li ro +Expose the share in read-only mode. +.El +.Pp TTY devices: .Bl -tag -width 10n .It Li stdio diff --git a/usr.sbin/bhyve/pci_virtio_9p.c b/usr.sbin/bhyve/pci_virtio_9p.c new file mode 100644 index 000000000000..7e8542a46e05 --- /dev/null +++ b/usr.sbin/bhyve/pci_virtio_9p.c @@ -0,0 +1,344 @@ +/*- + * Copyright (c) 2015 iXsystems Inc. + * Copyright (c) 2017-2018 Jakub Klama + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 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. + */ + +/* + * VirtIO filesystem passthrough using 9p protocol. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "bhyverun.h" +#include "pci_emul.h" +#include "virtio.h" + +#define VT9P_MAX_IOV 128 +#define VT9P_RINGSZ 256 +#define VT9P_MAXTAGSZ 256 +#define VT9P_CONFIGSPACESZ (VT9P_MAXTAGSZ + sizeof(uint16_t)) + +static int pci_vt9p_debug; +#define DPRINTF(params) if (pci_vt9p_debug) printf params +#define WPRINTF(params) printf params + +/* + * Per-device softc + */ +struct pci_vt9p_softc { + struct virtio_softc vsc_vs; + struct vqueue_info vsc_vq; + pthread_mutex_t vsc_mtx; + uint64_t vsc_cfg; + uint64_t vsc_features; + char * vsc_rootpath; + struct pci_vt9p_config * vsc_config; + struct l9p_backend * vsc_fs_backend; + struct l9p_server * vsc_server; + struct l9p_connection * vsc_conn; +}; + +struct pci_vt9p_request { + struct pci_vt9p_softc * vsr_sc; + struct iovec * vsr_iov; + size_t vsr_niov; + size_t vsr_respidx; + size_t vsr_iolen; + uint16_t vsr_idx; +}; + +struct pci_vt9p_config { + uint16_t tag_len; + char tag[0]; +} __attribute__((packed)); + +static int pci_vt9p_send(struct l9p_request *, const struct iovec *, + const size_t, const size_t, void *); +static void pci_vt9p_drop(struct l9p_request *, const struct iovec *, size_t, + void *); +static void pci_vt9p_reset(void *); +static void pci_vt9p_notify(void *, struct vqueue_info *); +static int pci_vt9p_cfgread(void *, int, int, uint32_t *); +static void pci_vt9p_neg_features(void *, uint64_t); + +static struct virtio_consts vt9p_vi_consts = { + "vt9p", /* our name */ + 1, /* we support 1 virtqueue */ + VT9P_CONFIGSPACESZ, /* config reg size */ + pci_vt9p_reset, /* reset */ + pci_vt9p_notify, /* device-wide qnotify */ + pci_vt9p_cfgread, /* read virtio config */ + NULL, /* write virtio config */ + pci_vt9p_neg_features, /* apply negotiated features */ + (1 << 0), /* our capabilities */ +}; + + +static void +pci_vt9p_reset(void *vsc) +{ + struct pci_vt9p_softc *sc; + + sc = vsc; + + DPRINTF(("vt9p: device reset requested !\n")); + vi_reset_dev(&sc->vsc_vs); +} + +static void +pci_vt9p_neg_features(void *vsc, uint64_t negotiated_features) +{ + struct pci_vt9p_softc *sc = vsc; + + sc->vsc_features = negotiated_features; +} + +static int +pci_vt9p_cfgread(void *vsc, int offset, int size, uint32_t *retval) +{ + struct pci_vt9p_softc *sc = vsc; + void *ptr; + + ptr = (uint8_t *)sc->vsc_config + offset; + memcpy(retval, ptr, size); + return (0); +} + +static int +pci_vt9p_get_buffer(struct l9p_request *req, struct iovec *iov, size_t *niov, + void *arg) +{ + struct pci_vt9p_request *preq = req->lr_aux; + size_t n = preq->vsr_niov - preq->vsr_respidx; + + memcpy(iov, preq->vsr_iov + preq->vsr_respidx, + n * sizeof(struct iovec)); + *niov = n; + return (0); +} + +static int +pci_vt9p_send(struct l9p_request *req, const struct iovec *iov, + const size_t niov, const size_t iolen, void *arg) +{ + struct pci_vt9p_request *preq = req->lr_aux; + struct pci_vt9p_softc *sc = preq->vsr_sc; + + preq->vsr_iolen = iolen; + + pthread_mutex_lock(&sc->vsc_mtx); + vq_relchain(&sc->vsc_vq, preq->vsr_idx, preq->vsr_iolen); + vq_endchains(&sc->vsc_vq, 1); + pthread_mutex_unlock(&sc->vsc_mtx); + free(preq); + return (0); +} + +static void +pci_vt9p_drop(struct l9p_request *req, const struct iovec *iov, size_t niov, + void *arg) +{ + struct pci_vt9p_request *preq = req->lr_aux; + struct pci_vt9p_softc *sc = preq->vsr_sc; + + pthread_mutex_lock(&sc->vsc_mtx); + vq_relchain(&sc->vsc_vq, preq->vsr_idx, 0); + vq_endchains(&sc->vsc_vq, 1); + pthread_mutex_unlock(&sc->vsc_mtx); + free(preq); +} + +static void +pci_vt9p_notify(void *vsc, struct vqueue_info *vq) +{ + struct iovec iov[VT9P_MAX_IOV]; + struct pci_vt9p_softc *sc; + struct pci_vt9p_request *preq; + uint16_t idx, n, i; + uint16_t flags[VT9P_MAX_IOV]; + + sc = vsc; + + while (vq_has_descs(vq)) { + n = vq_getchain(vq, &idx, iov, VT9P_MAX_IOV, flags); + preq = calloc(1, sizeof(struct pci_vt9p_request)); + preq->vsr_sc = sc; + preq->vsr_idx = idx; + preq->vsr_iov = iov; + preq->vsr_niov = n; + preq->vsr_respidx = 0; + + /* Count readable descriptors */ + for (i = 0; i < n; i++) { + if (flags[i] & VRING_DESC_F_WRITE) + break; + + preq->vsr_respidx++; + } + + for (int i = 0; i < n; i++) { + DPRINTF(("vt9p: vt9p_notify(): desc%d base=%p, " + "len=%zu, flags=0x%04x\r\n", i, iov[i].iov_base, + iov[i].iov_len, flags[i])); + } + + l9p_connection_recv(sc->vsc_conn, iov, preq->vsr_respidx, preq); + } +} + + +static int +pci_vt9p_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) +{ + struct pci_vt9p_softc *sc; + char *opt; + char *sharename = NULL; + char *rootpath = NULL; + int rootfd; + bool ro = false; + cap_rights_t rootcap; + + if (opts == NULL) { + printf("virtio-9p: share name and path required\n"); + return (1); + } + + while ((opt = strsep(&opts, ",")) != NULL) { + if (strchr(opt, '=') != NULL) { + if (sharename != NULL) { + printf("virtio-9p: more than one share name given\n"); + return (1); + } + + sharename = strsep(&opt, "="); + rootpath = opt; + continue; + } + + if (strcmp(opt, "ro") == 0) { + DPRINTF(("read-only mount requested\r\n")); + ro = true; + continue; + } + + printf("virtio-9p: invalid option '%s'\n", opt); + return (1); + } + + if (strlen(sharename) > VT9P_MAXTAGSZ) { + printf("virtio-9p: share name too long\n"); + return (1); + } + + rootfd = open(rootpath, O_DIRECTORY); + if (rootfd < 0) + return (-1); + + sc = calloc(1, sizeof(struct pci_vt9p_softc)); + sc->vsc_config = calloc(1, sizeof(struct pci_vt9p_config) + + VT9P_MAXTAGSZ); + + pthread_mutex_init(&sc->vsc_mtx, NULL); + + cap_rights_init(&rootcap, + CAP_LOOKUP, CAP_ACL_CHECK, CAP_ACL_DELETE, CAP_ACL_GET, + CAP_ACL_SET, CAP_READ, CAP_WRITE, CAP_SEEK, CAP_FSTAT, + CAP_CREATE, CAP_FCHMODAT, CAP_FCHOWNAT, CAP_FTRUNCATE, + CAP_LINKAT_SOURCE, CAP_LINKAT_TARGET, CAP_MKDIRAT, CAP_MKNODAT, + CAP_PREAD, CAP_PWRITE, CAP_RENAMEAT_SOURCE, CAP_RENAMEAT_TARGET, + CAP_SEEK, CAP_SYMLINKAT, CAP_UNLINKAT, CAP_EXTATTR_DELETE, + CAP_EXTATTR_GET, CAP_EXTATTR_LIST, CAP_EXTATTR_SET, + CAP_FUTIMES, CAP_FSTATFS, CAP_FSYNC, CAP_FPATHCONF); + + if (cap_rights_limit(rootfd, &rootcap) != 0) + return (1); + + sc->vsc_config->tag_len = (uint16_t)strlen(sharename); + memcpy(sc->vsc_config->tag, sharename, sc->vsc_config->tag_len); + + if (l9p_backend_fs_init(&sc->vsc_fs_backend, rootfd, ro) != 0) { + errno = ENXIO; + return (1); + } + + if (l9p_server_init(&sc->vsc_server, sc->vsc_fs_backend) != 0) { + errno = ENXIO; + return (1); + } + + if (l9p_connection_init(sc->vsc_server, &sc->vsc_conn) != 0) { + errno = EIO; + return (1); + } + + sc->vsc_conn->lc_msize = L9P_MAX_IOV * PAGE_SIZE; + sc->vsc_conn->lc_lt.lt_get_response_buffer = pci_vt9p_get_buffer; + sc->vsc_conn->lc_lt.lt_send_response = pci_vt9p_send; + sc->vsc_conn->lc_lt.lt_drop_response = pci_vt9p_drop; + + vi_softc_linkup(&sc->vsc_vs, &vt9p_vi_consts, sc, pi, &sc->vsc_vq); + sc->vsc_vs.vs_mtx = &sc->vsc_mtx; + sc->vsc_vq.vq_qsize = VT9P_RINGSZ; + + /* initialize config space */ + pci_set_cfgdata16(pi, PCIR_DEVICE, VIRTIO_DEV_9P); + pci_set_cfgdata16(pi, PCIR_VENDOR, VIRTIO_VENDOR); + pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_STORAGE); + pci_set_cfgdata16(pi, PCIR_SUBDEV_0, VIRTIO_TYPE_9P); + pci_set_cfgdata16(pi, PCIR_SUBVEND_0, VIRTIO_VENDOR); + + if (vi_intr_init(&sc->vsc_vs, 1, fbsdrun_virtio_msix())) + return (1); + vi_set_io_bar(&sc->vsc_vs, 0); + + return (0); +} + +struct pci_devemu pci_de_v9p = { + .pe_emu = "virtio-9p", + .pe_init = pci_vt9p_init, + .pe_barwrite = vi_pci_write, + .pe_barread = vi_pci_read +}; +PCI_EMUL_SET(pci_de_v9p); diff --git a/usr.sbin/bhyve/virtio.h b/usr.sbin/bhyve/virtio.h index e9432e012b27..b055f0c35941 100644 --- a/usr.sbin/bhyve/virtio.h +++ b/usr.sbin/bhyve/virtio.h @@ -216,6 +216,7 @@ struct vring_used { #define VIRTIO_DEV_CONSOLE 0x1003 #define VIRTIO_DEV_RANDOM 0x1005 #define VIRTIO_DEV_SCSI 0x1008 +#define VIRTIO_DEV_9P 0x1009 /* * PCI config space constants.