Commit ebea4dd6 authored by Jin Yu's avatar Jin Yu Committed by Jim Harris
Browse files

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: default avatarJin Yu <jin.yu@intel.com>
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/5768


Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: default avatarChangpeng Liu <changpeng.liu@intel.com>
Reviewed-by: default avatarJim Harris <james.r.harris@intel.com>
Reviewed-by: default avatarBen Walker <benjamin.walker@intel.com>
parent 5957f2c4
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -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 */
+1 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@
	virtio_pci_dev_attach;
	virtio_user_dev_init;
	virtio_pci_dev_init;
	virtio_pci_dev_event_process;

	local: *;
};
+98 −1
Original line number Diff line number Diff line
@@ -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;
}
+12 −0
Original line number Diff line number Diff line
@@ -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 */
+58 −0
Original line number Diff line number Diff line
@@ -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)
{