vhost_scsi: implemented eventq hotremove

Implemented eventq. For now only hotremove is supported.

Change-Id: I1ec7aa2137173b5dfb30e9906dd41707e371b7a8
Signed-off-by: Dariusz Stojaczyk <dariuszx.stojaczyk@intel.com>
Reviewed-on: https://review.gerrithub.io/364285
Tested-by: SPDK Automated Test System <sys_sgsw@intel.com>
Reviewed-by: Daniel Verkamp <daniel.verkamp@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
This commit is contained in:
Dariusz Stojaczyk 2017-06-06 20:35:56 +02:00 committed by Jim Harris
parent f168e45283
commit 3c497395cc
2 changed files with 126 additions and 15 deletions

View File

@ -153,5 +153,4 @@ assigned to vm.
## Hot plug is not supported
Hot plug is not supported in vhost yet. Event queue path doesn't handle that
case. While hot plug will be just ignored, hot removal might cause segmentation
fault.
case yet.

View File

@ -40,6 +40,7 @@
#include "spdk/scsi_spec.h"
#include "spdk/conf.h"
#include "spdk/event.h"
#include "spdk/util.h"
#include "spdk/vhost.h"
#include "vhost_internal.h"
@ -57,12 +58,10 @@
/* Features that are specified in VIRTIO SCSI but currently not supported:
* - Live migration not supported yet
* - Hotplug/hotremove
* - LUN params change
* - T10 PI
*/
#define SPDK_VHOST_SCSI_DISABLED_FEATURES ((1ULL << VHOST_F_LOG_ALL) | \
(1ULL << VIRTIO_SCSI_F_HOTPLUG) | \
(1ULL << VIRTIO_SCSI_F_CHANGE ) | \
(1ULL << VIRTIO_SCSI_F_T10_PI ))
@ -78,6 +77,8 @@ struct spdk_vhost_scsi_dev {
struct spdk_scsi_dev *scsi_dev[SPDK_VHOST_SCSI_CTRLR_MAX_DEVS];
struct spdk_poller *requestq_poller;
struct spdk_poller *mgmt_poller;
struct spdk_ring *eventq_ring;
} __rte_cache_aligned;
static int new_device(int vid);
@ -92,9 +93,84 @@ const struct spdk_vhost_dev_backend spdk_vhost_scsi_device_backend = {
}
};
static void task_submit(struct spdk_vhost_task *task);
static int process_request(struct spdk_vhost_task *task);
static void invalid_request(struct spdk_vhost_task *task);
static void
process_eventq(struct spdk_vhost_scsi_dev *svdev)
{
struct rte_vhost_vring *vq;
struct vring_desc *desc;
struct virtio_scsi_event *ev, *desc_ev;
uint32_t req_size;
uint16_t req;
vq = &svdev->vdev.virtqueue[VIRTIO_SCSI_EVENTQ];
while (spdk_ring_dequeue(svdev->eventq_ring, (void **)&ev, 1) == 1) {
if (spdk_vhost_vq_avail_ring_get(vq, &req, 1) != 1) {
SPDK_ERRLOG("Controller %s: Failed to send virtio event (no avail ring entries?).\n",
svdev->vdev.name);
spdk_dma_free(ev);
break;
}
desc = spdk_vhost_vq_get_desc(vq, req);
desc_ev = spdk_vhost_gpa_to_vva(&svdev->vdev, desc->addr);
if (desc->len >= sizeof(*desc_ev) && desc_ev != NULL) {
req_size = sizeof(*desc_ev);
memcpy(desc_ev, ev, sizeof(*desc_ev));
} else {
SPDK_ERRLOG("Controller %s: Invalid eventq descriptor.\n", svdev->vdev.name);
req_size = 0;
}
spdk_vhost_vq_used_ring_enqueue(&svdev->vdev, vq, req, req_size);
spdk_dma_free(ev);
}
}
static void
eventq_enqueue(struct spdk_vhost_scsi_dev *svdev, const struct spdk_scsi_dev *dev,
const struct spdk_scsi_lun *lun, uint32_t event, uint32_t reason)
{
struct virtio_scsi_event *ev;
int dev_id, lun_id;
if (dev == NULL) {
SPDK_ERRLOG("%s: eventq device cannot be NULL.\n", svdev->vdev.name);
return;
}
for (dev_id = 0; dev_id < SPDK_VHOST_SCSI_CTRLR_MAX_DEVS; dev_id++) {
if (svdev->scsi_dev[dev_id] == dev) {
break;
}
}
if (dev_id == SPDK_VHOST_SCSI_CTRLR_MAX_DEVS) {
SPDK_ERRLOG("Dev %s is not a part of vhost scsi controller '%s'.\n", spdk_scsi_dev_get_name(dev),
svdev->vdev.name);
return;
}
/* some events may apply to the entire device via lun id set to 0 */
lun_id = lun == NULL ? 0 : spdk_scsi_lun_get_id(lun);
ev = spdk_dma_zmalloc(sizeof(*ev), SPDK_CACHE_LINE_SIZE, NULL);
assert(ev);
ev->event = event;
ev->lun[0] = 1;
ev->lun[1] = dev_id;
ev->lun[2] = lun_id >> 8; /* relies on linux kernel implementation */
ev->lun[3] = lun_id & 0xFF;
ev->reason = reason;
if (spdk_ring_enqueue(svdev->eventq_ring, (void **)&ev, 1) != 1) {
SPDK_ERRLOG("Controller %s: Failed to inform guest about LUN #%d removal (no room in ring?).\n",
svdev->vdev.name, lun_id);
spdk_dma_free(ev);
}
}
static void
submit_completion(struct spdk_vhost_task *task)
@ -155,6 +231,11 @@ mgmt_task_submit(struct spdk_vhost_task *task, enum spdk_scsi_task_func func)
static void
invalid_request(struct spdk_vhost_task *task)
{
/* Flush eventq so that guest is instantly notified about any hotremoved luns.
* This might prevent him from sending more invalid requests and trying to reset
* the device.
*/
process_eventq(task->svdev);
spdk_vhost_vq_used_ring_enqueue(&task->svdev->vdev, task->vq, task->req_idx, 0);
spdk_vhost_task_put(task);
@ -452,6 +533,7 @@ vdev_mgmt_worker(void *arg)
{
struct spdk_vhost_scsi_dev *svdev = arg;
process_eventq(svdev);
process_controlq(svdev, &svdev->vdev.virtqueue[VIRTIO_SCSI_CONTROLQ]);
}
@ -492,6 +574,7 @@ static void
remove_vdev_cb(void *arg)
{
struct spdk_vhost_scsi_dev *svdev = arg;
void *ev;
uint32_t i;
for (i = 0; i < SPDK_VHOST_SCSI_CTRLR_MAX_DEVS; i++) {
@ -503,6 +586,11 @@ remove_vdev_cb(void *arg)
SPDK_NOTICELOG("Stopping poller for vhost controller %s\n", svdev->vdev.name);
spdk_vhost_dev_mem_unregister(&svdev->vdev);
/* Cleanup not sent events */
while (spdk_ring_dequeue(svdev->eventq_ring, &ev, 1) == 1) {
spdk_dma_free(ev);
}
}
static struct spdk_vhost_scsi_dev *
@ -526,20 +614,28 @@ spdk_vhost_scsi_dev_construct(const char *name, uint64_t cpumask)
{
struct spdk_vhost_scsi_dev *svdev = spdk_dma_zmalloc(sizeof(struct spdk_vhost_scsi_dev),
SPDK_CACHE_LINE_SIZE, NULL);
int ret;
int rc;
if (svdev == NULL) {
return -ENOMEM;
}
ret = spdk_vhost_dev_construct(&svdev->vdev, name, cpumask, SPDK_VHOST_DEV_T_SCSI,
&spdk_vhost_scsi_device_backend);
if (ret) {
svdev->eventq_ring = spdk_ring_create(SPDK_RING_TYPE_MP_SC, 16, SOCKET_ID_ANY);
if (svdev->eventq_ring == NULL) {
spdk_dma_free(svdev);
return -ENOMEM;
}
return ret;
rc = spdk_vhost_dev_construct(&svdev->vdev, name, cpumask, SPDK_VHOST_DEV_T_SCSI,
&spdk_vhost_scsi_device_backend);
if (rc) {
spdk_ring_free(svdev->eventq_ring);
spdk_dma_free(svdev);
return rc;
}
return 0;
}
int
@ -563,6 +659,7 @@ spdk_vhost_scsi_dev_remove(struct spdk_vhost_dev *vdev)
return -EIO;
}
spdk_ring_free(svdev->eventq_ring);
spdk_dma_free(svdev);
return 0;
}
@ -578,6 +675,22 @@ spdk_vhost_scsi_dev_get_dev(struct spdk_vhost_dev *vdev, uint8_t num)
return svdev ? svdev->scsi_dev[num] : NULL;
}
static void
spdk_vhost_scsi_lun_hotremove(const struct spdk_scsi_lun *lun, void *arg)
{
struct spdk_vhost_scsi_dev *svdev = arg;
assert(lun != NULL);
assert(svdev != NULL);
if ((svdev->vdev.negotiated_features & (1ULL << VIRTIO_SCSI_F_HOTPLUG)) == 0) {
SPDK_WARNLOG("Controller %s: hotremove is not supported\n", svdev->vdev.name);
return;
}
eventq_enqueue(svdev, spdk_scsi_lun_get_dev(lun), lun, VIRTIO_SCSI_T_TRANSPORT_RESET,
VIRTIO_SCSI_EVT_RESET_REMOVED);
}
int
spdk_vhost_scsi_dev_add_dev(const char *ctrlr_name, unsigned scsi_dev_num, const char *lun_name)
{
@ -635,14 +748,13 @@ spdk_vhost_scsi_dev_add_dev(const char *ctrlr_name, unsigned scsi_dev_num, const
lun_names_list[0] = (char *)lun_name;
svdev->scsi_dev[scsi_dev_num] = spdk_scsi_dev_construct(dev_name, lun_names_list, lun_id_list, 1,
SPDK_SPC_PROTOCOL_IDENTIFIER_SAS, NULL, NULL);
SPDK_SPC_PROTOCOL_IDENTIFIER_SAS, spdk_vhost_scsi_lun_hotremove, svdev);
if (svdev->scsi_dev[scsi_dev_num] == NULL) {
SPDK_ERRLOG("Couldn't create spdk SCSI device '%s' using lun device '%s' in controller: %s\n",
dev_name, lun_name, vdev->name);
return -EINVAL;
}
spdk_scsi_dev_add_port(svdev->scsi_dev[scsi_dev_num], 0, "vhost");
SPDK_NOTICELOG("Controller %s: defined device '%s' using lun '%s'\n",
vdev->name, dev_name, lun_name);