diff options
author | Angelos Mouzakitis <a.mouzakitis@virtualopensystems.com> | 2023-11-29 12:44:40 +0100 |
---|---|---|
committer | Angelos Mouzakitis <a.mouzakitis@virtualopensystems.com> | 2023-11-29 14:15:20 +0000 |
commit | 274aa4832d16af5a6b10ffe6021b278bffca4eb8 (patch) | |
tree | 5b8bb165d7c593504188ba7ecd85bdeb86fcf1af | |
parent | 9ad83030ef18c04beae09a6acf9800378a8378bc (diff) |
Add vhost-user support for the virtio-can device
Enables support to probe, handle and connect virtio-can device
with vhost-user-can via the virtio-loopback-adapter
Depends: 29398
Bug-AGL: SPEC-4834
Change-Id: I31e328aee25b101f51911eab6fd15dc3dd78c771
Signed-off-by: Angelos Mouzakitis <a.mouzakitis@virtualopensystems.com>
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | adapter.c | 16 | ||||
-rw-r--r-- | vhost_loopback.c | 2 | ||||
-rw-r--r-- | vhost_user_can.c | 322 | ||||
-rw-r--r-- | vhost_user_can.h | 57 | ||||
-rw-r--r-- | vhost_user_loopback.c | 11 | ||||
-rw-r--r-- | virtio_loopback.c | 20 | ||||
-rw-r--r-- | virtio_loopback.h | 4 |
8 files changed, 410 insertions, 24 deletions
@@ -33,7 +33,7 @@ endif INCL += -I . DEPS = adapter.h vhost_user_loopback.h event_notifier.h virtio_loopback.h -SRC_C = event_notifier.c vhost_user_loopback.c virtio_loopback.c virtio_rng.c virtio_input.c vhost_user_input.c vhost_user_blk.c vhost_user_rng.c vhost_user_sound.c vhost_user_gpio.c vhost_loopback.c adapter.c +SRC_C = event_notifier.c vhost_user_loopback.c virtio_loopback.c virtio_rng.c virtio_input.c vhost_user_input.c vhost_user_blk.c vhost_user_rng.c vhost_user_sound.c vhost_user_gpio.c vhost_user_can.c vhost_loopback.c adapter.c OBJS = $(SRC_C:.c=.o) BINS = adapter @@ -49,6 +49,7 @@ #include "vhost_user_input.h" #include "vhost_user_gpio.h" #include "vhost_user_sound.h" +#include "vhost_user_can.h" #ifdef DEBUG @@ -158,9 +159,10 @@ int find_arg(int argc, char **argv, char *str) int val_device_arg(char *str) { char *adapter_devices[] = {"vrng", "vhurng", "vhublk", "vhuinput", - "vhusnd", "vhugpio"}; - char *vhu_devices[] = {"vhurng", "vhublk", "vhuinput", "vhusnd", "vhugpio"}; - int adapter_devices_num = 6, i; + "vhusnd", "vhugpio", "vhucan"}; + char *vhu_devices[] = {"vhurng", "vhublk", "vhuinput", "vhusnd", + "vhugpio", "vhucan"}; + int adapter_devices_num = 7, i; for (i = 0; i < adapter_devices_num; i++) { if (!strcmp(adapter_devices[i], str)) { @@ -173,8 +175,9 @@ int val_device_arg(char *str) bool check_vhu_device(char *str) { - char *vhu_devices[] = {"vhurng", "vhublk", "vhuinput", "vhusnd", "vhugpio"}; - int vhu_devices_num = 5, i; + char *vhu_devices[] = {"vhurng", "vhublk", "vhuinput", "vhusnd", + "vhugpio", "vhucan"}; + int vhu_devices_num = 6, i; for (i = 0; i < vhu_devices_num; i++) { if (!strcmp(vhu_devices[i], str)) { @@ -297,6 +300,9 @@ int main(int argc, char **argv) case 6: vu_gpio_device_realize(); break; + case 7: + vhost_user_can_realize(); + break; default: exit(1); } diff --git a/vhost_loopback.c b/vhost_loopback.c index af725e3..1855ee8 100644 --- a/vhost_loopback.c +++ b/vhost_loopback.c @@ -330,7 +330,7 @@ void update_mem_table(VirtIODevice *vdev) static int vhost_dev_set_vring_enable(struct vhost_dev *hdev, int enable) { - DBG("vhost_dev_set_vring_enable not yet implemented\n"); + DBG("vhost_dev_set_vring_enable:\n"); /* * For vhost-user devices, if VHOST_USER_F_PROTOCOL_FEATURES has not diff --git a/vhost_user_can.c b/vhost_user_can.c new file mode 100644 index 0000000..8a1b1ae --- /dev/null +++ b/vhost_user_can.c @@ -0,0 +1,322 @@ +/* + * Virtio CAN Device + * + * Based on virtio_can.h of OpenSynergy's virtio-can RFC + * https://github.com/OpenSynergy/qemu/tree/virtio-can-spec-rfc-v3 + * + * Copyright (C) 2021-2023 OpenSynergy GmbH + * Copyright (c) 2023 Virtual Open Systems SAS. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <stdbool.h> +#include <sys/param.h> + +/* Project header files */ +#include "vhost_user_can.h" + +#ifdef DEBUG +#define DBG(...) printf("vhost-user-can: " __VA_ARGS__) +#else +#define DBG(...) +#endif /* DEBUG */ + +/***************************** vhost-user-can ******************************/ + +static const int user_feature_bits[] = { + VIRTIO_F_VERSION_1, + VIRTIO_RING_F_INDIRECT_DESC, + VIRTIO_RING_F_EVENT_IDX, + VIRTIO_F_RING_RESET, + VIRTIO_F_NOTIFY_ON_EMPTY, + VHOST_INVALID_FEATURE_BIT +}; + +static void vhost_user_can_get_config(VirtIODevice *vdev, uint8_t *config) +{ + VHostUserCan *can = vdev->vhucan; + + DBG("vhost_user_can_get_config: Not yet implemented!\n"); + + /* + * TODO : Add this check depend on 'busoff' value + * if (vcan->busoff) { + * config->status = cpu_to_le32(VIRTIO_CAN_S_CTRL_BUSOFF); + * } else { + * config->status = cpu_to_le32(0); + * } + */ + + memcpy(config, &can->config, sizeof(struct virtio_can_config)); +} + + +static void vhost_user_can_start(VirtIODevice *vdev) +{ + VHostUserCan *vhucan = vdev->vhucan; + VirtioBus *k = vdev->vbus; + int ret; + int i; + + DBG("vhost_user_can_start(...)\n"); + + if (!k->set_guest_notifiers) { + DBG("binding does not support guest notifiers\n"); + return; + } + + ret = vhost_dev_enable_notifiers(vhucan->vhost_dev, vdev); + if (ret < 0) { + DBG("Error enabling host notifiers: %d\n", -ret); + return; + } + + ret = k->set_guest_notifiers(k->vdev, vhucan->vhost_dev->nvqs, true); + if (ret < 0) { + DBG("Error binding guest notifier: %d\n", -ret); + goto err_host_notifiers; + } + + vhucan->vhost_dev->acked_features = vdev->guest_features; + + ret = vhost_dev_start(vhucan->vhost_dev, vdev, true); + if (ret < 0) { + DBG("Error starting vhost: %d\n", -ret); + goto err_guest_notifiers; + } + + /* + * guest_notifier_mask/pending not used yet, so just unmask + * everything here. virtio-pci will do the right thing by + * enabling/disabling irqfd. + */ + for (i = 0; i < vhucan->vhost_dev->nvqs; i++) { + vhost_virtqueue_mask(vhucan->vhost_dev, vdev, i, false); + } + + /* Wait a bit for the vrings to be set in vhost-user-device */ + sleep(1); + + return; + +err_guest_notifiers: +err_host_notifiers: + DBG("vhu_start error\n"); + return; +} + +static void vhost_user_can_stop(VirtIODevice *vdev) +{ + DBG("vhost_user_can_stop: not yet implemented\n"); +} + +static void vhost_user_can_set_status(VirtIODevice *vdev, uint8_t status) +{ + VHostUserCan *vhucan = vdev->vhucan; + bool should_start = virtio_device_started(vdev, status); + DBG("vhost_user_can_set_status: %d\n", status); + + if (vhucan->vhost_dev->started == should_start) { + DBG("can->vhost_dev->started == should_start\n"); + return; + } + + if (should_start) { + vhost_user_can_start(vdev); + } else { + vhost_user_can_stop(vdev); + } +} + +static uint64_t vhost_user_can_get_features(VirtIODevice *vdev, + uint64_t features) +{ + VHostUserCan *s = vdev->vhucan; + + DBG("vhost_user_can_get_features()\n"); + + return vhost_get_features(s->vhost_dev, user_feature_bits, features); +} + +static void vhost_user_can_can_handle_output(VirtIODevice *vdev, VirtQueue *vq) +{ + /* + * Not normally called; it's the daemon that handles the queue; + * however virtio's cleanup path can call this. + */ + DBG("vhost_user_can_can_handle_output: Not yet implemented!\n"); +} + +/* + * TODO: Add it later + * static void vhost_sound_guest_notifier_mask(VirtIODevice *vdev, int idx, + * bool mask) + */ + +/* + * TODO: Add it later + * static bool vhost_sound_guest_notifier_pending(VirtIODevice *vdev, + * int idx) + */ + +static int vhost_user_can_can_config_change(struct vhost_dev *dev) +{ + VHostUserCan *vhucan = dev->vdev->vhucan; + DBG("vhost_user_can_can_config_change: Not yet implemented!\n"); + + int ret = vhost_dev_get_config(dev, (uint8_t *)&vhucan->config, + sizeof(struct virtio_can_config)); + if (ret < 0) { + DBG("vhost_user_can_sound_config_change error\n"); + return -1; + } + + virtio_notify_config(dev->vdev); + + return 0; +} + +const VhostDevConfigOps can_config_ops = { + .vhost_dev_config_notifier = vhost_user_can_can_config_change, +}; + +static void vhost_user_can_init(VirtIODevice *vdev); + +void vhost_user_can_realize() +{ + VirtIODevice *vdev = global_vdev; + int ret; + + DBG("vhost_user_can_device_realize\n"); + + /* This needs to be added */ + proxy = (VirtIOMMIOProxy *)malloc(sizeof(VirtIOMMIOProxy)); + *proxy = (VirtIOMMIOProxy) { + .legacy = 1, + }; + + /* VIRTIO_ID_CAN is 36, check virtio_ids.h in linux*/ + virtio_dev_init(vdev, "virtio-can", 36, sizeof(vdev->vhucan->config)); + vhost_user_can_init(global_vdev); + + /* add queues */ + vdev->vhucan->ctrl_vq = virtio_add_queue(vdev, 64, + vhost_user_can_can_handle_output); + vdev->vhucan->tx_vq = virtio_add_queue(vdev, 64, + vhost_user_can_can_handle_output); + vdev->vhucan->rx_vq = virtio_add_queue(vdev, 64, + vhost_user_can_can_handle_output); + vdev->vhucan->vhost_dev->nvqs = 3; + vdev->vhucan->num_queues = 3; + vdev->vhucan->queue_size = 64; + + /* NOTE: global_vdev->vqs == vhucan->virtqs */ + vdev->vqs = (VirtQueue **)malloc(sizeof(VirtQueue *) + * global_vdev->vhucan->num_queues); + vdev->vqs[0] = vdev->vhucan->tx_vq; + vdev->vqs[1] = vdev->vhucan->rx_vq; + vdev->vqs[2] = vdev->vhucan->ctrl_vq; + + vdev->vhucan->vhost_vqs = (struct vhost_virtqueue *)malloc( + sizeof(struct vhost_virtqueue) * + vdev->vhucan->num_queues); + + /* Set up vhost device */ + vdev->vhucan->vhost_dev->num_queues = vdev->vhucan->num_queues; + vdev->vhucan->vhost_dev->nvqs = vdev->vhucan->num_queues; + vdev->vhucan->vhost_dev->vqs = vdev->vhucan->vhost_vqs; + vdev->vhucan->vhost_dev->vq_index = 0; + vdev->vhucan->vhost_dev->backend_features = 0; + + vhost_dev_set_config_notifier(vdev->vhucan->vhost_dev, &can_config_ops); + + /* TODO: Add error handling */ + vhost_dev_init(vdev->vhucan->vhost_dev); + + /* Pass the new obtained features */ + global_vdev->host_features = vdev->vhucan->vhost_dev->features; + + ret = vhost_dev_get_config(vdev->vhucan->vhost_dev, + (uint8_t *)&vdev->vhucan->config, + sizeof(struct virtio_can_config)); + if (ret < 0) { + goto vhost_dev_init_failed; + } + + vdev->vdev_class->print_config((uint8_t *)&vdev->vhucan->config); + + return; + +vhost_dev_init_failed: + DBG("vhost_dev_init_failed\n"); + return; +} + +static void vhost_user_can_device_unrealize(VirtIODevice *vdev) +{ + DBG("vhost_user_blk_device_unrealize not yet implemented\n"); +} + +static struct vhost_dev *vhost_user_can_get_vhost(VirtIODevice *vdev) +{ + VHostUserCan *vhucan = vdev->vhucan; + return vhucan->vhost_dev; +} + +static void print_config_can(uint8_t *config_data) +{ + struct virtio_can_config *config_strct = + (struct virtio_can_config *)config_data; + + DBG("print_config_can:\n"); + + /* # of available physical jacks */ + DBG("\tuint16_t status: %u\n", config_strct->status); +} + +static void virtio_dev_class_init(VirtIODevice *vdev) +{ + DBG("virtio_dev_class_init\n"); + + vdev->vdev_class = (VirtioDeviceClass *)malloc(sizeof(VirtioDeviceClass)); + vdev->vdev_class->parent = vdev; + vdev->vdev_class->realize = vhost_user_can_realize; + vdev->vdev_class->unrealize = vhost_user_can_device_unrealize; + vdev->vdev_class->get_config = vhost_user_can_get_config; + vdev->vdev_class->get_features = vhost_user_can_get_features; + vdev->vdev_class->set_status = vhost_user_can_set_status; + vdev->vdev_class->update_mem_table = update_mem_table; + vdev->vdev_class->print_config = print_config_can; +} + +static void vhost_user_can_init(VirtIODevice *vdev) +{ + DBG("vhost_user_can_init\n"); + + VHostUserCan *vhucan = (VHostUserCan *)malloc(sizeof(VHostUserCan)); + vdev->vhucan = vhucan; + vdev->nvqs = &vdev->vhdev->nvqs; + vhucan->parent = vdev; + vhucan->virtqs = vdev->vqs; + vhucan->vhost_dev = vdev->vhdev; + + virtio_dev_class_init(vdev); + virtio_loopback_bus_init(vdev->vbus); +} diff --git a/vhost_user_can.h b/vhost_user_can.h new file mode 100644 index 0000000..6e2e5d5 --- /dev/null +++ b/vhost_user_can.h @@ -0,0 +1,57 @@ +/* + * Virtio CAN Device + * + * Based on virtio_can.h of OpenSynergy's virtio-can RFC + * https://github.com/OpenSynergy/qemu/tree/virtio-can-spec-rfc-v3 + * + * Copyright (C) 2021-2023 OpenSynergy GmbH + * Copyright (c) 2023 Virtual Open Systems SAS. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef VHOST_USER_CAN +#define VHOST_USER_CAN + +#include "virtio_loopback.h" +#include "vhost_loopback.h" +#include "vhost_user_loopback.h" + +/* The following are defined into virtio_can.h -- Delete them in the future */ +#define VIRTIO_CAN_S_CTRL_BUSOFF (1u << 0) /* Controller BusOff */ +struct virtio_can_config { + /* CAN controller status */ + __le16 status; +}; + +typedef struct VHostUserCan { + VirtIODevice *parent; + struct vhost_virtqueue *vhost_vqs; + VirtQueue **virtqs; + uint16_t num_queues; + uint32_t queue_size; + struct virtio_can_config config; + struct vhost_dev *vhost_dev; + VirtQueue *ctrl_vq; + VirtQueue *tx_vq; + VirtQueue *rx_vq; + /* Support classic CAN */ + bool support_can_classic; + /* Support CAN FD */ + bool support_can_fd; +} VHostUserCan; + +void vhost_user_can_realize(void); + +#endif /* VHOST_USER_CAN */ diff --git a/vhost_user_loopback.c b/vhost_user_loopback.c index 85b9405..31830ae 100644 --- a/vhost_user_loopback.c +++ b/vhost_user_loopback.c @@ -847,7 +847,8 @@ static int vhost_user_fill_set_mem_table_msg(struct vhost_user *u, u->region_rb_offset[i] = offset; u->region_rb[i] = mr->ram_block; } else if (*fd_num == VHOST_MEMORY_BASELINE_NREGIONS) { - DBG("Failed preparing vhost-user memory table msg: %d\n", *fd_num); + DBG("Failed preparing vhost-user memory table msg: %lu\n", + *fd_num); return -1; } vhost_user_fill_msg_region(®ion_buffer, reg, offset); @@ -1180,7 +1181,7 @@ int vhost_user_set_mem_table(struct vhost_dev *dev) } /* Update message parameters */ - DBG("\nfd_num: %d\n", fd_num); + DBG("\nfd_num: %lu\n", fd_num); msg.fd_num = fd_num; memcpy(msg.fds, fds, fd_num * sizeof(int)); @@ -1585,14 +1586,14 @@ void vhost_dev_init(struct vhost_dev *vhdev) */ vhdev->features = features; - DBG("vhdev->backend_features 0x%llx\n", vhdev->backend_features); - DBG("vhdev->features 0x%llx\n", vhdev->features); + DBG("vhdev->backend_features 0x%lx\n", vhdev->backend_features); + DBG("vhdev->features 0x%lx\n", vhdev->features); } int vhost_user_set_vring_enable(struct vhost_dev *dev, int enable) { int i; - DBG("vhost_user_set_vring_enable not yet implemented\n"); + DBG("vhost_user_set_vring_enable:\n"); if (!virtio_has_feature(dev->features, VHOST_USER_F_PROTOCOL_FEATURES)) { DBG("Does not have VHOST_USER_F_PROTOCOL_FEATURES\n"); diff --git a/virtio_loopback.c b/virtio_loopback.c index e95c648..8b57589 100644 --- a/virtio_loopback.c +++ b/virtio_loopback.c @@ -1032,20 +1032,19 @@ void *loopback_event_select(void *_e) } if (retval > 0) { + if (pthread_mutex_lock(&interrupt_lock) != 0) { + printf("[ERROR] Locking failed\n"); + exit(1); + } DBG("\n\nEvent has come from the vhost-user-device " "(eventfd: %d) -> event_count: %d (select value: %d)\n\n", rfd, eventfd_count, retval); if (event_notifier_test_and_clear(e)) { - if (pthread_mutex_lock(&interrupt_lock) == 0) { - eventfd_count++; - virtio_irq(vq); - pthread_mutex_unlock(&interrupt_lock); - } else { - printf("[ERROR] Locking failed\n"); - exit(1); - } + eventfd_count++; + virtio_irq(vq); } + pthread_mutex_unlock(&interrupt_lock); } } } @@ -1460,7 +1459,6 @@ static uint64_t virtio_loopback_read(VirtIODevice *vdev, uint64_t offset, case VIRTIO_MMIO_MAGIC_VALUE: return VIRT_MAGIC; case VIRTIO_MMIO_VERSION: - DBG("VIRTIO_MMIO_VERSION ->\n"); if (proxy->legacy) { DBG("VIRTIO_MMIO_VERSION -> legacy\n"); return VIRT_VERSION_LEGACY; @@ -1602,8 +1600,6 @@ void virtio_loopback_write(VirtIODevice *vdev, uint64_t offset, case VIRTIO_MMIO_DRIVER_FEATURES: if (proxy->legacy) { if (proxy->guest_features_sel) { - DBG("attempt to write guest features with " - "guest_features_sel > 0 in legacy mode\n"); DBG("Set driver features: 0x%lx\n", value << 32); virtio_set_features(vdev, value << 32); } else { @@ -1691,7 +1687,7 @@ void virtio_loopback_write(VirtIODevice *vdev, uint64_t offset, /* TODO: To be implemented */ break; case VIRTIO_MMIO_QUEUE_NOTIFY: - DBG("VIRTIO_MMIO_QUEUE_NOTIFY: vq_index -> %d, notify_cnt: %d\n", + DBG("VIRTIO_MMIO_QUEUE_NOTIFY: vq_index -> %lu, notify_cnt: %d\n", value, notify_cnt++); if (value < VIRTIO_QUEUE_MAX) { virtio_queue_notify(vdev, value); diff --git a/virtio_loopback.h b/virtio_loopback.h index 34bae2f..cd59a5a 100644 --- a/virtio_loopback.h +++ b/virtio_loopback.h @@ -364,6 +364,7 @@ typedef struct VHostUserBlk VHostUserBlk; typedef struct VhostUserInput VhostUserInput; typedef struct VHostUserGPIO VHostUserGPIO; typedef struct VHostUserSound VHostUserSound; +typedef struct VHostUserCan VHostUserCan; typedef struct VirtioBus VirtioBus; typedef struct VirtIODevice { @@ -405,6 +406,7 @@ typedef struct VirtIODevice { VhostUserInput *vhuinput; VHostUserSound *vhusnd; VHostUserGPIO *vhugpio; + VHostUserCan *vhucan; } VirtIODevice; typedef struct efd_data { @@ -659,7 +661,9 @@ uint32_t get_vqs_max_size(VirtIODevice *vdev); * Legacy name for VIRTIO_F_ACCESS_PLATFORM * (for compatibility with old userspace) */ +#ifndef VIRTIO_F_IOMMU_PLATFORM #define VIRTIO_F_IOMMU_PLATFORM 33 +#endif /* QEMU Aligned functions */ /* |