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:
parent
5957f2c479
commit
ebea4dd660
@ -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 */
|
||||
|
@ -28,6 +28,7 @@
|
||||
virtio_pci_dev_attach;
|
||||
virtio_user_dev_init;
|
||||
virtio_pci_dev_init;
|
||||
virtio_pci_dev_event_process;
|
||||
|
||||
local: *;
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user