ACRN: DM: Implement the vhost vsock

This patch supports the user space logic of vhost vsock which
is majorly for its initialization phase.

Tracked-On: #7456
Signed-off-by: Liu Long <long.liu@linux.intel.com>
Reviewed-by: Conghui <conghui.chen@intel.com>
Acked-by: Wang, Yu1 <yu1.wang@intel.com>
This commit is contained in:
Liu Long 2022-05-11 14:11:16 +08:00 committed by acrnsi-robot
parent 231f986bd9
commit 46e41390b5
4 changed files with 396 additions and 0 deletions

View File

@ -155,6 +155,7 @@ SRCS += hw/pci/virtio/virtio_hdcp.c
SRCS += hw/pci/virtio/virtio_rpmb.c
SRCS += hw/pci/virtio/virtio_gpio.c
SRCS += hw/pci/virtio/virtio_gpu.c
SRCS += hw/pci/virtio/vhost_vsock.c
SRCS += hw/pci/irq.c
SRCS += hw/pci/uart.c
SRCS += hw/pci/gvt.c

View File

@ -0,0 +1,340 @@
/*
* Copyright (C) OASIS Open 2018. All rights reserved.
* Copyright (C) 2022 Intel Corporation.
*
* SPDX-License-Identifier: BSD-3-Clause
*
* vhost-vsock device
*
*/
#include <sys/uio.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/errno.h>
#include <pthread.h>
#include <linux/vhost.h>
#include "dm.h"
#include "pci_core.h"
#include "virtio.h"
#include "vhost.h"
#include "dm_string.h"
#include "vhost_vsock.h"
static int
vhost_vsock_set_running(struct vhost_dev *vdev, int start)
{
return vhost_kernel_ioctl(vdev, VHOST_VSOCK_SET_RUNNING, &start);
}
static int
vhost_vsock_set_guest_cid(struct vhost_dev *vdev,
uint64_t guest_cid)
{
return vhost_kernel_ioctl(vdev,
VHOST_VSOCK_SET_GUEST_CID, &guest_cid);
}
static int
vhost_vsock_start(struct vhost_vsock *vhost_vsock)
{
int rc;
if (vhost_vsock->vhost_started) {
pr_err("vhost vsock is already started!\n");
return 0;
}
rc = vhost_dev_start(&vhost_vsock->vdev);
if (rc < 0) {
pr_err("vhost_dev_start is failed!\n");
return -1;
}
rc = vhost_vsock_set_running(&vhost_vsock->vdev, 1);
if (rc < 0) {
vhost_dev_stop(&vhost_vsock->vdev);
pr_err("vhost_vsock_set_running is failed %d!\n", rc);
return -1;
}
vhost_vsock->vhost_started = true;
return 0;
}
static int
vhost_vsock_stop(struct vhost_vsock *vhost_vsock)
{
int rc;
if (!vhost_vsock->vhost_started) {
pr_err("vhost vsock is not started!\n");
return 0;
}
rc = vhost_vsock_set_running(&vhost_vsock->vdev, 0);
if (rc < 0) {
pr_err("vhost_vsock_set_running is failed with %d\n", rc);
return -1;
}
rc = vhost_dev_stop(&vhost_vsock->vdev);
if (rc < 0) {
pr_err("vhost_dev_stop is failed!\n");
return -1;
}
vhost_vsock->vhost_started = false;
return 0;
}
static void
virtio_vsock_set_status(void *vdev, uint64_t status)
{
struct virtio_vsock *vsock = vdev;
bool should_start = status & VIRTIO_CONFIG_S_DRIVER_OK;
int rc;
if (!vsock->vhost_vsock) {
pr_err("virtio_vsock_set_status vhost is NULL!\n");
return;
}
if (should_start) {
rc = vhost_vsock_start(vsock->vhost_vsock);
if (rc < 0) {
pr_err("vhost_vsock_start is failed!\n");
return;
}
} else if (vsock->vhost_vsock->vhost_started &&
should_start == 0) {
rc = vhost_vsock_stop(vsock->vhost_vsock);
if (rc < 0) {
pr_err("vhost_vsock_stop is failed!\n");
return;
}
}
}
static void
virtio_vsock_apply_feature(void *vdev, uint64_t negotiated_features)
{
struct virtio_vsock *vsock = vdev;
vsock->features = negotiated_features;
}
static int
virtio_vsock_read_cfg(void *vdev, int offset, int size, uint32_t *retval)
{
struct virtio_vsock *vsock = vdev;
void *ptr;
ptr = (uint8_t *)&vsock->config + offset;
memcpy(retval, ptr, size);
return 0;
}
static void
virtio_vsock_reset(void *vdev)
{
struct virtio_vsock *vsock = vdev;
pr_dbg(("vsock: device reset requested!\n"));
/* now reset rings, MSI-X vectors, and negotiated capabilities */
virtio_reset_dev(&vsock->base);
}
static struct virtio_ops virtio_vsock_ops = {
"vhost-vsock", /* our name */
VHOST_VSOCK_MAXQ, /* we currently support 2 virtqueues */
sizeof(struct virtio_vsock_config), /* config reg size */
virtio_vsock_reset, /* reset */
NULL, /* device-wide qnotify -- not used */
virtio_vsock_read_cfg, /* read PCI config */
NULL, /* write PCI config */
virtio_vsock_apply_feature, /* apply negotiated features */
virtio_vsock_set_status, /* called on guest set status */
};
static struct vhost_vsock *
vhost_vsock_init(struct virtio_base *base, int vq_idx)
{
struct vhost_vsock *vhost_vsock = NULL;
uint64_t vhost_features = VHOST_VSOCK_FEATURES;
int rc;
vhost_vsock = calloc(1, sizeof(struct vhost_vsock));
if (!vhost_vsock) {
pr_err(("vhost init out of memory!\n"));
goto fail;
}
/* pre-init before calling vhost_dev_init */
vhost_vsock->vdev.nvqs = 2;
vhost_vsock->vdev.vqs = vhost_vsock->vqs;
vhost_vsock->vhost_fd = open("/dev/vhost-vsock", O_RDWR);;
if (vhost_vsock->vhost_fd < 0) {
pr_err(("Open vhost-vsock fail, pls open vsock kernel config!\n"));
goto fail;
}
rc = fcntl(vhost_vsock->vhost_fd, F_GETFL);
if (rc == -1) {
pr_err(("fcntl vhost node fail!\n"));
goto fail;
}
if (fcntl(vhost_vsock->vhost_fd, F_SETFL, rc | O_NONBLOCK) == -1) {
pr_err(("fcntl set NONBLOCK fail!\n"));
goto fail;
}
rc = vhost_dev_init(&vhost_vsock->vdev, base, vhost_vsock->vhost_fd, vq_idx,
vhost_features, 0, 0);
if (rc < 0) {
pr_err(("vhost_dev_init failed!\n"));
goto fail;
}
return vhost_vsock;
fail:
if (vhost_vsock)
free(vhost_vsock);
return NULL;
}
static int
vhost_vsock_deinit(struct vhost_vsock *vhost_vsock)
{
int rc;
rc = vhost_vsock_stop(vhost_vsock);
if (rc < 0) {
pr_err("vhost_dev_stop is failed!\n");
return -1;
}
rc = vhost_dev_deinit(&vhost_vsock->vdev);
if (rc < 0) {
pr_err("vhost_dev_deinit is failed!\n");
return -1;
}
close(vhost_vsock->vhost_fd);
free(vhost_vsock);
return 0;
}
static void
vhost_vsock_handle_output(void *vdev, struct virtio_vq_info *vq) {
/* do nothing */
}
static int
virtio_vhost_vsock_init(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
{
struct virtio_vsock *vsock;
int rc, cid;
pthread_mutexattr_t attr;
char *devopts = NULL;
char *tmp = NULL;
if (opts == NULL) {
pr_err(("vsock: must have a valid guest_cid!\n"));
return -1;
}
devopts = tmp = strdup(opts);
if (!devopts) {
pr_err(("vsock: The vsock parameter is NULL!\n"));
return -1;
}
if (!strncmp(tmp, "cid=", 4)) {
strsep(&tmp, "=");
dm_strtoi(tmp, NULL, 10, &cid);
}
free(devopts);
if (cid <= VMADDR_CID_HOST || cid >= U32_MAX) {
pr_err("vsock: guest_cid has to be 0x2~0xffffffff!\n");
return -1;
}
vsock = calloc(1, sizeof(struct virtio_vsock));
if (!vsock) {
pr_err("vosck: memory allocate failed!");
return -1;
}
vsock->config.guest_cid = cid;
rc = pthread_mutexattr_init(&attr);
if (rc)
pr_err("vsock: mutexattr init failed with erro %d\n", rc);
rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
if (rc)
pr_err("vsock: mutexattr_settype failed with error %d\n", rc);
rc = pthread_mutex_init(&vsock->mtx, &attr);
if (rc)
pr_err("vsock: pthread_mutexattr_init failed with error %d\n", rc);
virtio_linkup(&vsock->base, &virtio_vsock_ops, vsock, dev, vsock->queues, BACKEND_VHOST);
vsock->base.mtx = &vsock->mtx;
vsock->base.device_caps = (1UL << VIRTIO_F_VERSION_1) | VHOST_VSOCK_FEATURES;
vsock->queues[VHOST_VSOCK_RXQ].qsize = VHOST_VSOCK_QUEUE_SIZE;
vsock->queues[VHOST_VSOCK_RXQ].notify = vhost_vsock_handle_output;
vsock->queues[VHOST_VSOCK_TXQ].qsize = VHOST_VSOCK_QUEUE_SIZE;
vsock->queues[VHOST_VSOCK_TXQ].notify = vhost_vsock_handle_output;
vsock->queues[VHOST_VSOCK_CTLQ].qsize = VHOST_VSOCK_QUEUE_SIZE;
vsock->queues[VHOST_VSOCK_CTLQ].notify = vhost_vsock_handle_output;
/* initialize config space */
pci_set_cfgdata16(dev, PCIR_DEVICE, VIRTIO_DEV_VSOCK);
pci_set_cfgdata16(dev, PCIR_VENDOR, VIRTIO_VENDOR);
pci_set_cfgdata8(dev, PCIR_CLASS, PCIC_NETWORK);
pci_set_cfgdata16(dev, PCIR_REVID, 1);
virtio_set_modern_bar(&vsock->base, false);
vsock->vhost_vsock = vhost_vsock_init(&vsock->base, 0);
vhost_vsock_set_guest_cid(&vsock->vhost_vsock->vdev, vsock->config.guest_cid);
if (virtio_interrupt_init(&vsock->base, virtio_uses_msix())) {
if (vsock) {
if (vsock->vhost_vsock)
vhost_vsock_deinit(vsock->vhost_vsock);
free(vsock);
}
return -1;
}
return 0;
}
static void
virtio_vhost_vsock_deinit(struct vmctx *ctx, struct pci_vdev *dev, char *opts)
{
struct virtio_vsock *vsock;
if (dev->arg) {
vsock = (struct virtio_vsock *) dev->arg;
if (vsock->vhost_vsock) {
vhost_vsock_deinit(vsock->vhost_vsock);
}
pr_dbg("%s: done\n", __func__);
} else
pr_err("%s: NULL!\n", __func__);
}
struct pci_vdev_ops pci_ops_vhost_vsock = {
.class_name = "vhost-vsock",
.vdev_init = virtio_vhost_vsock_init,
.vdev_deinit = virtio_vhost_vsock_deinit,
.vdev_barwrite = virtio_pci_write,
.vdev_barread = virtio_pci_read
};
DEFINE_PCI_DEVTYPE(pci_ops_vhost_vsock);

View File

@ -0,0 +1,54 @@
/*
* Copyright (C) 2018 Intel Corporation.
* Copyright (C) 2022 Intel Corporation.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
/**
* @file vhost_vsock.h
*/
#ifndef __VHOST_VSOCK_H__
#define __VHOST_VSOCK_H__
#include "virtio.h"
#include "vhost.h"
#define VHOST_VSOCK_RXQ 0
#define VHOST_VSOCK_TXQ 1
#define VHOST_VSOCK_CTLQ 2 /* NB: not yet supported */
#define VHOST_VSOCK_MAXQ 3
#define VHOST_VSOCK_QUEUE_SIZE 128
#define VHOST_F_LOG_ALL 26
#define VHOST_VSOCK_FEATURES \
(1ULL << VIRTIO_F_NOTIFY_ON_EMPTY) | (1ULL << VIRTIO_RING_F_INDIRECT_DESC) | \
(1ULL << VIRTIO_RING_F_EVENT_IDX) | (1ULL << VHOST_F_LOG_ALL) | \
(1ULL << VIRTIO_F_ANY_LAYOUT) | (1ULL << VIRTIO_F_VERSION_1)
#define U32_MAX 65535
#define VMADDR_CID_HOST 2
struct virtio_vsock_config {
uint64_t guest_cid;
}__attribute__((packed));
struct virtio_vsock {
struct virtio_base base;
pthread_mutex_t mtx;
struct virtio_vq_info queues[VHOST_VSOCK_MAXQ];
struct virtio_vsock_config config;
struct vhost_vsock *vhost_vsock;
uint64_t features;
};
struct vhost_vsock {
struct vhost_dev vdev;
struct vhost_vq vqs[VHOST_VSOCK_MAXQ];
int vhost_fd;
bool vhost_started;
};
#endif

View File

@ -221,6 +221,7 @@ enum {
#define VIRTIO_DEV_CONSOLE 0x1003
#define VIRTIO_DEV_RANDOM 0x1005
#define VIRTIO_DEV_GPU 0x1050
#define VIRTIO_DEV_VSOCK 0x1053
/*
* ACRN virtio device IDs