diff --git a/include/spdk_internal/virtio.h b/include/spdk_internal/virtio.h index 5f9f18f8c..e010cdaa4 100644 --- a/include/spdk_internal/virtio.h +++ b/include/spdk_internal/virtio.h @@ -483,4 +483,16 @@ int virtio_user_dev_init(struct virtio_dev *vdev, const char *name, const char * int virtio_pci_dev_init(struct virtio_dev *vdev, const char *name, struct virtio_pci_ctx *pci_ctx); +/** + * Process the uevent which is accepted from the kernel and the + * uevent descript the physical device hot add or remove action. + * + * \param fd the file descriptor of the kobject netlink socket + * \param device_id virtio device ID used to represent virtio-blk or other device. + * \return the name of the virtio device on success, NULL means it + * is not a suitable uevent. + */ +const char * +virtio_pci_dev_event_process(int fd, uint16_t device_id); + #endif /* SPDK_VIRTIO_H */ diff --git a/lib/virtio/spdk_virtio.map b/lib/virtio/spdk_virtio.map index 76e02cff8..2b41517b9 100644 --- a/lib/virtio/spdk_virtio.map +++ b/lib/virtio/spdk_virtio.map @@ -28,6 +28,7 @@ virtio_pci_dev_attach; virtio_user_dev_init; virtio_pci_dev_init; + virtio_pci_dev_event_process; local: *; }; diff --git a/lib/virtio/virtio_pci.c b/lib/virtio/virtio_pci.c index c5bc22fb8..8d06172ff 100644 --- a/lib/virtio/virtio_pci.c +++ b/lib/virtio/virtio_pci.c @@ -39,6 +39,7 @@ #include "spdk/env.h" #include "spdk_internal/virtio.h" +#include struct virtio_hw { uint8_t use_msix; @@ -60,7 +61,10 @@ struct virtio_hw { /** Device-specific PCI config space */ void *dev_cfg; + struct virtio_dev *vdev; bool is_remapped; + bool is_removing; + TAILQ_ENTRY(virtio_hw) tailq; }; struct virtio_pci_probe_ctx { @@ -69,6 +73,8 @@ struct virtio_pci_probe_ctx { uint16_t device_id; }; +static TAILQ_HEAD(, virtio_hw) g_virtio_hws = TAILQ_HEAD_INITIALIZER(g_virtio_hws); +static pthread_mutex_t g_hw_mutex = PTHREAD_MUTEX_INITIALIZER; __thread struct virtio_hw *g_thread_virtio_hw = NULL; static uint16_t g_signal_lock; static bool g_sigset = false; @@ -134,6 +140,87 @@ fail: __atomic_store_n(&g_signal_lock, 0, __ATOMIC_RELEASE); } +static struct virtio_hw * +virtio_pci_dev_get_by_addr(struct spdk_pci_addr *traddr) +{ + struct virtio_hw *hw; + struct spdk_pci_addr addr; + + pthread_mutex_lock(&g_hw_mutex); + TAILQ_FOREACH(hw, &g_virtio_hws, tailq) { + addr = spdk_pci_device_get_addr(hw->pci_dev); + if (!spdk_pci_addr_compare(&addr, traddr)) { + pthread_mutex_unlock(&g_hw_mutex); + return hw; + } + } + pthread_mutex_unlock(&g_hw_mutex); + + return NULL; +} + +static const char * +virtio_pci_dev_check(struct virtio_hw *hw, uint16_t device_id_match) +{ + uint16_t pci_device_id, device_id; + + pci_device_id = spdk_pci_device_get_device_id(hw->pci_dev); + if (pci_device_id < 0x1040) { + /* Transitional devices: use the PCI subsystem device id as + * virtio device id, same as legacy driver always did. + */ + device_id = spdk_pci_device_get_subdevice_id(hw->pci_dev); + } else { + /* Modern devices: simply use PCI device id, but start from 0x1040. */ + device_id = pci_device_id - 0x1040; + } + + if (device_id == device_id_match) { + hw->is_removing = true; + return hw->vdev->name; + } + + return NULL; +} + +const char * +virtio_pci_dev_event_process(int fd, uint16_t device_id) +{ + struct spdk_pci_event event; + struct virtio_hw *hw, *tmp; + const char *vdev_name; + + /* UIO remove handler */ + if (spdk_pci_get_event(fd, &event) > 0) { + if (event.action == SPDK_UEVENT_REMOVE) { + hw = virtio_pci_dev_get_by_addr(&event.traddr); + if (hw == NULL || hw->is_removing) { + return NULL; + } + + vdev_name = virtio_pci_dev_check(hw, device_id); + if (vdev_name != NULL) { + return vdev_name; + } + } + } + + /* VFIO remove handler */ + pthread_mutex_lock(&g_hw_mutex); + TAILQ_FOREACH_SAFE(hw, &g_virtio_hws, tailq, tmp) { + if (spdk_pci_device_is_removed(hw->pci_dev) && !hw->is_removing) { + vdev_name = virtio_pci_dev_check(hw, device_id); + if (vdev_name != NULL) { + pthread_mutex_unlock(&g_hw_mutex); + return vdev_name; + } + } + } + pthread_mutex_unlock(&g_hw_mutex); + + return NULL; +} + static inline int check_vq_phys_addr_ok(struct virtqueue *vq) { @@ -293,6 +380,9 @@ modern_destruct_dev(struct virtio_dev *vdev) struct spdk_pci_device *pci_dev; if (hw != NULL) { + pthread_mutex_lock(&g_hw_mutex); + TAILQ_REMOVE(&g_virtio_hws, hw, tailq); + pthread_mutex_unlock(&g_hw_mutex); pci_dev = hw->pci_dev; free_virtio_hw(hw); if (pci_dev) { @@ -612,6 +702,7 @@ virtio_pci_dev_probe(struct spdk_pci_device *pci_dev, struct virtio_pci_probe_ct rc = ctx->enum_cb((struct virtio_pci_ctx *)hw, ctx->enum_ctx); if (rc != 0) { free_virtio_hw(hw); + return rc; } if (g_sigset != true) { @@ -620,7 +711,11 @@ virtio_pci_dev_probe(struct spdk_pci_device *pci_dev, struct virtio_pci_probe_ct g_sigset = true; } - return rc; + pthread_mutex_lock(&g_hw_mutex); + TAILQ_INSERT_TAIL(&g_virtio_hws, hw, tailq); + pthread_mutex_unlock(&g_hw_mutex); + + return 0; } static int @@ -695,6 +790,7 @@ virtio_pci_dev_init(struct virtio_dev *vdev, const char *name, struct virtio_pci_ctx *pci_ctx) { int rc; + struct virtio_hw *hw = (struct virtio_hw *)pci_ctx; rc = virtio_dev_construct(vdev, name, &modern_ops, pci_ctx); if (rc != 0) { @@ -703,6 +799,7 @@ virtio_pci_dev_init(struct virtio_dev *vdev, const char *name, vdev->is_hw = 1; vdev->modern = 1; + hw->vdev = vdev; return 0; } diff --git a/module/bdev/virtio/bdev_virtio.h b/module/bdev/virtio/bdev_virtio.h index 538fab8f6..17c07c190 100644 --- a/module/bdev/virtio/bdev_virtio.h +++ b/module/bdev/virtio/bdev_virtio.h @@ -161,4 +161,16 @@ struct spdk_bdev *bdev_virtio_user_blk_dev_create(const char *name, const char * struct spdk_bdev *bdev_virtio_pci_blk_dev_create(const char *name, struct spdk_pci_addr *pci_addr); +/** + * Enable/Disable the virtio blk hotplug monitor or + * change the monitor period time + * + * \param enabled True means to enable the hotplug monitor and the monitor + * period time is period_us. False means to disable the hotplug monitor + * \param period_us The period time of the hotplug monitor in us + * \return 0 for success otherwise failure + */ +int +bdev_virtio_pci_blk_set_hotplug(bool enabled, uint64_t period_us); + #endif /* SPDK_BDEV_VIRTIO_H */ diff --git a/module/bdev/virtio/bdev_virtio_blk.c b/module/bdev/virtio/bdev_virtio_blk.c index c8a6abd93..e92b21cc9 100644 --- a/module/bdev/virtio/bdev_virtio_blk.c +++ b/module/bdev/virtio/bdev_virtio_blk.c @@ -90,6 +90,14 @@ struct bdev_virtio_blk_io_channel { 1ULL << VIRTIO_RING_F_EVENT_IDX | \ 1ULL << VHOST_USER_F_PROTOCOL_FEATURES) +/* 10 sec for max poll period */ +#define VIRTIO_BLK_HOTPLUG_POLL_PERIOD_MAX 10000000ULL +/* Default poll period is 100ms */ +#define VIRTIO_BLK_HOTPLUG_POLL_PERIOD_DEFAULT 100000ULL + +static struct spdk_poller *g_blk_hotplug_poller = NULL; +static int g_blk_hotplug_fd = -1; + static int bdev_virtio_initialize(void); static int bdev_virtio_blk_get_ctx_size(void); @@ -691,6 +699,56 @@ bdev_virtio_pci_blk_dev_create(const char *name, struct spdk_pci_addr *pci_addr) return &create_ctx.ret->bdev; } +static int +bdev_virtio_pci_blk_monitor(void *arg) +{ + const char *vdev_name; + struct bdev_virtio_pci_dev_create_ctx create_ctx; + + while ((vdev_name = virtio_pci_dev_event_process(g_blk_hotplug_fd, VIRTIO_ID_BLOCK)) != NULL) { + bdev_virtio_blk_dev_remove(vdev_name, NULL, NULL); + } + + /* Enumerate virtio pci_blk device */ + memset(&create_ctx, 0, sizeof(create_ctx)); + virtio_pci_dev_enumerate(bdev_virtio_pci_blk_dev_create_cb, &create_ctx, + VIRTIO_ID_BLOCK); + + return SPDK_POLLER_BUSY; +} + +int +bdev_virtio_pci_blk_set_hotplug(bool enabled, uint64_t period_us) +{ + if (enabled == true && !spdk_process_is_primary()) { + return -EPERM; + } + + if (g_blk_hotplug_poller) { + close(g_blk_hotplug_fd); + spdk_poller_unregister(&g_blk_hotplug_poller); + } + + if (!enabled) { + return 0; + } + + g_blk_hotplug_fd = spdk_pci_event_listen(); + if (g_blk_hotplug_fd < 0) { + return g_blk_hotplug_fd; + } + + period_us = period_us ? period_us : VIRTIO_BLK_HOTPLUG_POLL_PERIOD_DEFAULT; + period_us = spdk_min(period_us, VIRTIO_BLK_HOTPLUG_POLL_PERIOD_MAX); + g_blk_hotplug_poller = spdk_poller_register(bdev_virtio_pci_blk_monitor, NULL, period_us); + if (!g_blk_hotplug_poller) { + close(g_blk_hotplug_fd); + return -1; + } + + return 0; +} + static int bdev_virtio_initialize(void) {