virtio_blk: add hotplug support

It can divide to two parts:
1, UIO driver - sigbus error handling and uevent
process.
2, VFIO - request notify handling.

sigbus error process is in previous patch.

Change-Id: Idc09754b83ae9ddcaea1f2afcbc13e528ead9863
Signed-off-by: Jin Yu <jin.yu@intel.com>
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/5768
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Changpeng Liu <changpeng.liu@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
This commit is contained in:
Jin Yu 2020-12-08 23:40:27 +08:00 committed by Jim Harris
parent 5957f2c479
commit ebea4dd660
5 changed files with 181 additions and 1 deletions

View File

@ -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 */

View File

@ -28,6 +28,7 @@
virtio_pci_dev_attach;
virtio_user_dev_init;
virtio_pci_dev_init;
virtio_pci_dev_event_process;
local: *;
};

View File

@ -39,6 +39,7 @@
#include "spdk/env.h"
#include "spdk_internal/virtio.h"
#include <linux/virtio_ids.h>
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;
}

View File

@ -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 */

View File

@ -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)
{