15004d7b8SChangpeng Liu /* SPDX-License-Identifier: BSD-3-Clause
2a6dbe372Spaul luse * Copyright (C) 2022 Intel Corporation.
35004d7b8SChangpeng Liu * All rights reserved.
45004d7b8SChangpeng Liu */
55004d7b8SChangpeng Liu
65004d7b8SChangpeng Liu /*
75004d7b8SChangpeng Liu * virtio-scsi over vfio-user transport
85004d7b8SChangpeng Liu */
95004d7b8SChangpeng Liu #include <linux/virtio_scsi.h>
105004d7b8SChangpeng Liu
115004d7b8SChangpeng Liu #include "spdk/stdinc.h"
125004d7b8SChangpeng Liu #include "spdk/env.h"
135004d7b8SChangpeng Liu #include "spdk/bdev.h"
145004d7b8SChangpeng Liu #include "spdk/bdev_module.h"
155004d7b8SChangpeng Liu #include "spdk/assert.h"
165004d7b8SChangpeng Liu #include "spdk/barrier.h"
175004d7b8SChangpeng Liu #include "spdk/thread.h"
185004d7b8SChangpeng Liu #include "spdk/memory.h"
195004d7b8SChangpeng Liu #include "spdk/util.h"
205004d7b8SChangpeng Liu #include "spdk/log.h"
215004d7b8SChangpeng Liu #include "spdk/string.h"
225004d7b8SChangpeng Liu #include "spdk/likely.h"
235004d7b8SChangpeng Liu #include "spdk/scsi.h"
245004d7b8SChangpeng Liu #include "spdk/scsi_spec.h"
255004d7b8SChangpeng Liu #include "spdk/pci_ids.h"
265004d7b8SChangpeng Liu
275004d7b8SChangpeng Liu #include "vfu_virtio_internal.h"
285004d7b8SChangpeng Liu
295004d7b8SChangpeng Liu #define VIRTIO_SCSI_SUPPORTED_FEATURES ((1ULL << VIRTIO_SCSI_F_INOUT) | \
305004d7b8SChangpeng Liu (1ULL << VIRTIO_SCSI_F_HOTPLUG) | \
315004d7b8SChangpeng Liu (1ULL << VIRTIO_SCSI_F_CHANGE))
325004d7b8SChangpeng Liu
335004d7b8SChangpeng Liu #define VIRTIO_SCSI_CTRLR_MAX_TARGETS (8)
345004d7b8SChangpeng Liu
355004d7b8SChangpeng Liu struct virtio_scsi_target {
365004d7b8SChangpeng Liu struct spdk_scsi_dev *dev;
375004d7b8SChangpeng Liu };
385004d7b8SChangpeng Liu
395004d7b8SChangpeng Liu struct virtio_scsi_endpoint {
405004d7b8SChangpeng Liu struct vfu_virtio_endpoint virtio;
415004d7b8SChangpeng Liu
425004d7b8SChangpeng Liu struct virtio_scsi_config scsi_cfg;
435004d7b8SChangpeng Liu /* virtio_scsi specific configurations */
445004d7b8SChangpeng Liu struct virtio_scsi_target targets[VIRTIO_SCSI_CTRLR_MAX_TARGETS];
455004d7b8SChangpeng Liu /* virtio_scsi SCSI task and IO ring process poller */
465004d7b8SChangpeng Liu struct spdk_poller *ring_poller;
475004d7b8SChangpeng Liu };
485004d7b8SChangpeng Liu
495004d7b8SChangpeng Liu struct virtio_scsi_req {
505004d7b8SChangpeng Liu struct spdk_scsi_task scsi;
515004d7b8SChangpeng Liu union {
525004d7b8SChangpeng Liu struct virtio_scsi_cmd_req *cmd_req;
535004d7b8SChangpeng Liu struct virtio_scsi_ctrl_tmf_req *tmf_req;
545004d7b8SChangpeng Liu };
555004d7b8SChangpeng Liu union {
565004d7b8SChangpeng Liu struct virtio_scsi_cmd_resp *cmd_resp;
575004d7b8SChangpeng Liu struct virtio_scsi_ctrl_tmf_resp *tmf_resp;
585004d7b8SChangpeng Liu };
595004d7b8SChangpeng Liu struct virtio_scsi_endpoint *endpoint;
605004d7b8SChangpeng Liu /* KEEP req at last */
615004d7b8SChangpeng Liu struct vfu_virtio_req req;
625004d7b8SChangpeng Liu };
635004d7b8SChangpeng Liu
645004d7b8SChangpeng Liu static inline struct virtio_scsi_endpoint *
to_scsi_endpoint(struct vfu_virtio_endpoint * virtio_endpoint)655004d7b8SChangpeng Liu to_scsi_endpoint(struct vfu_virtio_endpoint *virtio_endpoint)
665004d7b8SChangpeng Liu {
675004d7b8SChangpeng Liu return SPDK_CONTAINEROF(virtio_endpoint, struct virtio_scsi_endpoint, virtio);
685004d7b8SChangpeng Liu }
695004d7b8SChangpeng Liu
705004d7b8SChangpeng Liu static inline struct virtio_scsi_req *
to_scsi_request(struct vfu_virtio_req * request)715004d7b8SChangpeng Liu to_scsi_request(struct vfu_virtio_req *request)
725004d7b8SChangpeng Liu {
735004d7b8SChangpeng Liu return SPDK_CONTAINEROF(request, struct virtio_scsi_req, req);
745004d7b8SChangpeng Liu }
755004d7b8SChangpeng Liu
765004d7b8SChangpeng Liu static void
virtio_scsi_req_finish(struct virtio_scsi_req * scsi_req)775004d7b8SChangpeng Liu virtio_scsi_req_finish(struct virtio_scsi_req *scsi_req)
785004d7b8SChangpeng Liu {
795004d7b8SChangpeng Liu struct vfu_virtio_req *req = &scsi_req->req;
805004d7b8SChangpeng Liu
815004d7b8SChangpeng Liu vfu_virtio_finish_req(req);
825004d7b8SChangpeng Liu }
835004d7b8SChangpeng Liu
845004d7b8SChangpeng Liu static int
vfu_virtio_scsi_vring_poll(void * ctx)855004d7b8SChangpeng Liu vfu_virtio_scsi_vring_poll(void *ctx)
865004d7b8SChangpeng Liu {
875004d7b8SChangpeng Liu struct virtio_scsi_endpoint *scsi_endpoint = ctx;
885004d7b8SChangpeng Liu struct vfu_virtio_dev *dev = scsi_endpoint->virtio.dev;
895004d7b8SChangpeng Liu struct vfu_virtio_vq *vq;
905004d7b8SChangpeng Liu uint32_t i, count = 0;
915004d7b8SChangpeng Liu
925004d7b8SChangpeng Liu if (spdk_unlikely(!virtio_dev_is_started(dev))) {
935004d7b8SChangpeng Liu return SPDK_POLLER_IDLE;
945004d7b8SChangpeng Liu }
955004d7b8SChangpeng Liu
965004d7b8SChangpeng Liu if (spdk_unlikely(scsi_endpoint->virtio.quiesce_in_progress)) {
975004d7b8SChangpeng Liu return SPDK_POLLER_IDLE;
985004d7b8SChangpeng Liu }
995004d7b8SChangpeng Liu
1005004d7b8SChangpeng Liu /* We don't process event queue here */
1015004d7b8SChangpeng Liu for (i = 0; i < dev->num_queues; i++) {
1025004d7b8SChangpeng Liu if (i == 1) {
1035004d7b8SChangpeng Liu continue;
1045004d7b8SChangpeng Liu }
1055004d7b8SChangpeng Liu
1065004d7b8SChangpeng Liu vq = &dev->vqs[i];
1075004d7b8SChangpeng Liu if (!vq->enabled || vq->q_state != VFU_VQ_ACTIVE) {
1085004d7b8SChangpeng Liu continue;
1095004d7b8SChangpeng Liu }
1105004d7b8SChangpeng Liu
1115004d7b8SChangpeng Liu vfu_virtio_vq_flush_irq(dev, vq);
1125004d7b8SChangpeng Liu
1135004d7b8SChangpeng Liu if (vq->packed.packed_ring) {
1145004d7b8SChangpeng Liu /* packed vring */
115*e4e763a5SAnton Nayshtut count += vfu_virtio_dev_process_packed_ring(dev, vq);
1165004d7b8SChangpeng Liu } else {
1175004d7b8SChangpeng Liu /* split vring */
118*e4e763a5SAnton Nayshtut count += vfu_virtio_dev_process_split_ring(dev, vq);
1195004d7b8SChangpeng Liu }
1205004d7b8SChangpeng Liu }
1215004d7b8SChangpeng Liu
1225004d7b8SChangpeng Liu return count ? SPDK_POLLER_BUSY : SPDK_POLLER_IDLE;
1235004d7b8SChangpeng Liu }
1245004d7b8SChangpeng Liu
1255004d7b8SChangpeng Liu static void
vfu_virtio_scsi_eventq_enqueue(struct virtio_scsi_endpoint * scsi_endpoint,uint8_t scsi_target_num,uint32_t event,uint32_t reason)1265004d7b8SChangpeng Liu vfu_virtio_scsi_eventq_enqueue(struct virtio_scsi_endpoint *scsi_endpoint, uint8_t scsi_target_num,
1275004d7b8SChangpeng Liu uint32_t event, uint32_t reason)
1285004d7b8SChangpeng Liu {
1295004d7b8SChangpeng Liu struct vfu_virtio_dev *dev = scsi_endpoint->virtio.dev;
1305004d7b8SChangpeng Liu struct vfu_virtio_req *req = NULL;
1315004d7b8SChangpeng Liu struct virtio_scsi_req *scsi_req;
1325004d7b8SChangpeng Liu struct virtio_scsi_event *desc_ev;
1335004d7b8SChangpeng Liu struct vfu_virtio_vq *vq;
1345004d7b8SChangpeng Liu
1355004d7b8SChangpeng Liu assert(dev != NULL);
1365004d7b8SChangpeng Liu
1375004d7b8SChangpeng Liu if (scsi_target_num >= VIRTIO_SCSI_CTRLR_MAX_TARGETS) {
1385004d7b8SChangpeng Liu return;
1395004d7b8SChangpeng Liu }
1405004d7b8SChangpeng Liu
1415004d7b8SChangpeng Liu if (spdk_unlikely(scsi_endpoint->virtio.quiesce_in_progress)) {
1425004d7b8SChangpeng Liu return;
1435004d7b8SChangpeng Liu }
1445004d7b8SChangpeng Liu
1455004d7b8SChangpeng Liu /* event queue */
1465004d7b8SChangpeng Liu vq = &dev->vqs[1];
1475004d7b8SChangpeng Liu if (!vq->enabled || vq->q_state != VFU_VQ_ACTIVE) {
1485004d7b8SChangpeng Liu return;
1495004d7b8SChangpeng Liu }
1505004d7b8SChangpeng Liu
1515004d7b8SChangpeng Liu if (vq->packed.packed_ring) {
1525004d7b8SChangpeng Liu /* packed vring */
1535004d7b8SChangpeng Liu req = virito_dev_packed_ring_get_next_avail_req(dev, vq);
1545004d7b8SChangpeng Liu } else {
1555004d7b8SChangpeng Liu /* split vring */
1565004d7b8SChangpeng Liu req = virito_dev_split_ring_get_next_avail_req(dev, vq);
1575004d7b8SChangpeng Liu }
1585004d7b8SChangpeng Liu
1595004d7b8SChangpeng Liu if (!req) {
1605004d7b8SChangpeng Liu return;
1615004d7b8SChangpeng Liu }
1625004d7b8SChangpeng Liu scsi_req = to_scsi_request(req);
1635004d7b8SChangpeng Liu scsi_req->endpoint = scsi_endpoint;
1645004d7b8SChangpeng Liu /* add 1 for scsi event */
1655004d7b8SChangpeng Liu scsi_endpoint->virtio.io_outstanding++;
1665004d7b8SChangpeng Liu
1675004d7b8SChangpeng Liu assert(req->iovcnt == 1);
1685004d7b8SChangpeng Liu assert(req->iovs[0].iov_len == sizeof(struct virtio_scsi_event));
1695004d7b8SChangpeng Liu desc_ev = req->iovs[0].iov_base;
1705004d7b8SChangpeng Liu
1715004d7b8SChangpeng Liu desc_ev->event = event;
1725004d7b8SChangpeng Liu desc_ev->lun[0] = 1;
1735004d7b8SChangpeng Liu desc_ev->lun[1] = scsi_target_num;
1745004d7b8SChangpeng Liu /* virtio LUN id 0 can refer either to the entire device
1755004d7b8SChangpeng Liu * or actual LUN 0 (the only supported by vhost for now)
1765004d7b8SChangpeng Liu */
1775004d7b8SChangpeng Liu desc_ev->lun[2] = 0 >> 8;
1785004d7b8SChangpeng Liu desc_ev->lun[3] = 0 & 0xFF;
1795004d7b8SChangpeng Liu /* virtio doesn't specify any strict format for LUN id (bytes 2 and 3)
1805004d7b8SChangpeng Liu * current implementation relies on linux kernel sources
1815004d7b8SChangpeng Liu */
1825004d7b8SChangpeng Liu memset(&desc_ev->lun[4], 0, 4);
1835004d7b8SChangpeng Liu desc_ev->reason = reason;
1845004d7b8SChangpeng Liu
1855004d7b8SChangpeng Liu req->used_len = sizeof(*desc_ev);
1865004d7b8SChangpeng Liu
1875004d7b8SChangpeng Liu SPDK_DEBUGLOG(vfu_virtio_scsi, "%s: SCSI Target Num %u, Desc %p, Event %u, Reason %u\n",
1885004d7b8SChangpeng Liu spdk_vfu_get_endpoint_name(scsi_endpoint->virtio.endpoint), scsi_target_num, desc_ev, event,
1895004d7b8SChangpeng Liu reason);
1905004d7b8SChangpeng Liu
1915004d7b8SChangpeng Liu virtio_scsi_req_finish(scsi_req);
1925004d7b8SChangpeng Liu vfu_virtio_vq_flush_irq(dev, vq);
1935004d7b8SChangpeng Liu }
1945004d7b8SChangpeng Liu
1955004d7b8SChangpeng Liu static int
virtio_scsi_start(struct vfu_virtio_endpoint * virtio_endpoint)1965004d7b8SChangpeng Liu virtio_scsi_start(struct vfu_virtio_endpoint *virtio_endpoint)
1975004d7b8SChangpeng Liu {
1985004d7b8SChangpeng Liu struct virtio_scsi_endpoint *scsi_endpoint = to_scsi_endpoint(virtio_endpoint);
1995004d7b8SChangpeng Liu struct virtio_scsi_target *scsi_target;
2005004d7b8SChangpeng Liu uint8_t i;
2015004d7b8SChangpeng Liu int ret;
2025004d7b8SChangpeng Liu
2035004d7b8SChangpeng Liu if (scsi_endpoint->ring_poller) {
2045004d7b8SChangpeng Liu return 0;
2055004d7b8SChangpeng Liu }
2065004d7b8SChangpeng Liu
2075004d7b8SChangpeng Liu SPDK_DEBUGLOG(vfu_virtio_scsi, "starting %s\n",
2085004d7b8SChangpeng Liu spdk_vfu_get_endpoint_name(scsi_endpoint->virtio.endpoint));
2095004d7b8SChangpeng Liu
2105004d7b8SChangpeng Liu for (i = 0; i < VIRTIO_SCSI_CTRLR_MAX_TARGETS; i++) {
2115004d7b8SChangpeng Liu scsi_target = &scsi_endpoint->targets[i];
2125004d7b8SChangpeng Liu if (scsi_target->dev) {
2135004d7b8SChangpeng Liu ret = spdk_scsi_dev_allocate_io_channels(scsi_target->dev);
2145004d7b8SChangpeng Liu if (ret) {
2155004d7b8SChangpeng Liu SPDK_ERRLOG("%s: Couldn't allocate io channel for SCSI target %u.\n",
2165004d7b8SChangpeng Liu spdk_vfu_get_endpoint_name(scsi_endpoint->virtio.endpoint), i);
2175004d7b8SChangpeng Liu continue;
2185004d7b8SChangpeng Liu }
2195004d7b8SChangpeng Liu }
2205004d7b8SChangpeng Liu }
2215004d7b8SChangpeng Liu
2225004d7b8SChangpeng Liu scsi_endpoint->ring_poller = SPDK_POLLER_REGISTER(vfu_virtio_scsi_vring_poll, scsi_endpoint,
2235004d7b8SChangpeng Liu 0);
2245004d7b8SChangpeng Liu
2255004d7b8SChangpeng Liu return 0;
2265004d7b8SChangpeng Liu }
2275004d7b8SChangpeng Liu
2285004d7b8SChangpeng Liu static int
virtio_scsi_stop(struct vfu_virtio_endpoint * virtio_endpoint)2295004d7b8SChangpeng Liu virtio_scsi_stop(struct vfu_virtio_endpoint *virtio_endpoint)
2305004d7b8SChangpeng Liu {
2315004d7b8SChangpeng Liu struct virtio_scsi_endpoint *scsi_endpoint = to_scsi_endpoint(virtio_endpoint);
2325004d7b8SChangpeng Liu struct virtio_scsi_target *scsi_target;
2335004d7b8SChangpeng Liu uint8_t i;
2345004d7b8SChangpeng Liu
2355004d7b8SChangpeng Liu SPDK_DEBUGLOG(vfu_virtio_scsi, "stopping %s\n",
2365004d7b8SChangpeng Liu spdk_vfu_get_endpoint_name(scsi_endpoint->virtio.endpoint));
2375004d7b8SChangpeng Liu
2385004d7b8SChangpeng Liu spdk_poller_unregister(&scsi_endpoint->ring_poller);
2395004d7b8SChangpeng Liu
2405004d7b8SChangpeng Liu for (i = 0; i < VIRTIO_SCSI_CTRLR_MAX_TARGETS; i++) {
2415004d7b8SChangpeng Liu scsi_target = &scsi_endpoint->targets[i];
2425004d7b8SChangpeng Liu if (scsi_target->dev) {
2435004d7b8SChangpeng Liu spdk_scsi_dev_free_io_channels(scsi_target->dev);
2445004d7b8SChangpeng Liu }
2455004d7b8SChangpeng Liu }
2465004d7b8SChangpeng Liu
2475004d7b8SChangpeng Liu return 0;
2485004d7b8SChangpeng Liu }
2495004d7b8SChangpeng Liu
2505004d7b8SChangpeng Liu static void
virtio_scsi_task_cpl(struct spdk_scsi_task * scsi_task)2515004d7b8SChangpeng Liu virtio_scsi_task_cpl(struct spdk_scsi_task *scsi_task)
2525004d7b8SChangpeng Liu {
2535004d7b8SChangpeng Liu struct virtio_scsi_req *scsi_req = SPDK_CONTAINEROF(scsi_task, struct virtio_scsi_req, scsi);
2545004d7b8SChangpeng Liu
2555004d7b8SChangpeng Liu scsi_req->cmd_resp->status = scsi_task->status;
2565004d7b8SChangpeng Liu if (scsi_task->status != SPDK_SCSI_STATUS_GOOD) {
2575004d7b8SChangpeng Liu scsi_req->cmd_resp->sense_len = scsi_task->sense_data_len;
2585004d7b8SChangpeng Liu memcpy(scsi_req->cmd_resp->sense, scsi_task->sense_data, scsi_task->sense_data_len);
2595004d7b8SChangpeng Liu }
2605004d7b8SChangpeng Liu assert(scsi_task->transfer_len == scsi_task->length);
2615004d7b8SChangpeng Liu scsi_req->cmd_resp->resid = scsi_task->length - scsi_task->data_transferred;
2625004d7b8SChangpeng Liu
2635004d7b8SChangpeng Liu virtio_scsi_req_finish(scsi_req);
2645004d7b8SChangpeng Liu spdk_scsi_task_put(scsi_task);
2655004d7b8SChangpeng Liu }
2665004d7b8SChangpeng Liu
2675004d7b8SChangpeng Liu static void
virtio_scsi_task_mgmt_cpl(struct spdk_scsi_task * scsi_task)2685004d7b8SChangpeng Liu virtio_scsi_task_mgmt_cpl(struct spdk_scsi_task *scsi_task)
2695004d7b8SChangpeng Liu {
2705004d7b8SChangpeng Liu struct virtio_scsi_req *scsi_req = SPDK_CONTAINEROF(scsi_task, struct virtio_scsi_req, scsi);
2715004d7b8SChangpeng Liu
2725004d7b8SChangpeng Liu virtio_scsi_req_finish(scsi_req);
2735004d7b8SChangpeng Liu spdk_scsi_task_put(scsi_task);
2745004d7b8SChangpeng Liu }
2755004d7b8SChangpeng Liu
2765004d7b8SChangpeng Liu static void
virtio_scsi_task_free_cb(struct spdk_scsi_task * scsi_task)2775004d7b8SChangpeng Liu virtio_scsi_task_free_cb(struct spdk_scsi_task *scsi_task)
2785004d7b8SChangpeng Liu {
2795004d7b8SChangpeng Liu
2805004d7b8SChangpeng Liu }
2815004d7b8SChangpeng Liu
2825004d7b8SChangpeng Liu static struct virtio_scsi_target *
virtio_scsi_cmd_lun_setup(struct virtio_scsi_endpoint * scsi_endpoint,struct virtio_scsi_req * scsi_req,__u8 * lun)2835004d7b8SChangpeng Liu virtio_scsi_cmd_lun_setup(struct virtio_scsi_endpoint *scsi_endpoint,
2845004d7b8SChangpeng Liu struct virtio_scsi_req *scsi_req, __u8 *lun)
2855004d7b8SChangpeng Liu {
2865004d7b8SChangpeng Liu struct virtio_scsi_target *scsi_target;
2875004d7b8SChangpeng Liu uint16_t lun_id = (((uint16_t)lun[2] << 8) | lun[3]) & 0x3FFF;
2885004d7b8SChangpeng Liu
2895004d7b8SChangpeng Liu SPDK_LOGDUMP(vfu_virtio_scsi_data, "LUN", lun, 8);
2905004d7b8SChangpeng Liu
2915004d7b8SChangpeng Liu /* First byte must be 1 and second is target */
2925004d7b8SChangpeng Liu if (lun[0] != 1 || lun[1] >= VIRTIO_SCSI_CTRLR_MAX_TARGETS) {
2935004d7b8SChangpeng Liu SPDK_DEBUGLOG(vfu_virtio_scsi, "Invalid LUN %u:%u\n", lun[0], lun[1]);
2945004d7b8SChangpeng Liu return NULL;
2955004d7b8SChangpeng Liu }
2965004d7b8SChangpeng Liu
2975004d7b8SChangpeng Liu scsi_target = &scsi_endpoint->targets[lun[1]];
2985004d7b8SChangpeng Liu if (!scsi_target->dev) {
2995004d7b8SChangpeng Liu SPDK_DEBUGLOG(vfu_virtio_scsi, "SCSI Target num %u doesn't exist\n", lun[1]);
3005004d7b8SChangpeng Liu return NULL;
3015004d7b8SChangpeng Liu }
3025004d7b8SChangpeng Liu
3035004d7b8SChangpeng Liu scsi_req->scsi.target_port = spdk_scsi_dev_find_port_by_id(scsi_target->dev, 0);
3045004d7b8SChangpeng Liu scsi_req->scsi.lun = spdk_scsi_dev_get_lun(scsi_target->dev, lun_id);
3055004d7b8SChangpeng Liu if (scsi_req->scsi.lun == NULL) {
3065004d7b8SChangpeng Liu SPDK_DEBUGLOG(vfu_virtio_scsi, "LUN %u:%u doesn't exist\n", lun[0], lun[1]);
3075004d7b8SChangpeng Liu return NULL;
3085004d7b8SChangpeng Liu }
3095004d7b8SChangpeng Liu SPDK_DEBUGLOG(vfu_virtio_scsi, "Got valid SCSI Target num %u, bdev %s\n", lun[1],
3105004d7b8SChangpeng Liu spdk_scsi_lun_get_bdev_name(scsi_req->scsi.lun));
3115004d7b8SChangpeng Liu
3125004d7b8SChangpeng Liu return scsi_target;
3135004d7b8SChangpeng Liu }
3145004d7b8SChangpeng Liu
3155004d7b8SChangpeng Liu static int
virtio_scsi_cmd_data_setup(struct virtio_scsi_req * scsi_req)3165004d7b8SChangpeng Liu virtio_scsi_cmd_data_setup(struct virtio_scsi_req *scsi_req)
3175004d7b8SChangpeng Liu {
3185004d7b8SChangpeng Liu struct iovec *iov;
3195004d7b8SChangpeng Liu uint32_t iovcnt;
3205004d7b8SChangpeng Liu uint32_t payload_len;
3215004d7b8SChangpeng Liu
3225004d7b8SChangpeng Liu iov = &scsi_req->req.iovs[0];
3235004d7b8SChangpeng Liu iovcnt = scsi_req->req.iovcnt;
3245004d7b8SChangpeng Liu payload_len = scsi_req->req.payload_size;
3255004d7b8SChangpeng Liu
3265004d7b8SChangpeng Liu if (spdk_unlikely(iov->iov_len < sizeof(struct virtio_scsi_cmd_req))) {
3275004d7b8SChangpeng Liu SPDK_ERRLOG("Invalid virtio_scsi command header length");
3285004d7b8SChangpeng Liu return -EINVAL;
3295004d7b8SChangpeng Liu }
3305004d7b8SChangpeng Liu if (spdk_unlikely(iovcnt < 2)) {
3315004d7b8SChangpeng Liu SPDK_ERRLOG("Invalid iovcnt %u\n", iovcnt);
3325004d7b8SChangpeng Liu return -EINVAL;
3335004d7b8SChangpeng Liu }
3345004d7b8SChangpeng Liu
3355004d7b8SChangpeng Liu scsi_req->cmd_req = scsi_req->req.iovs[0].iov_base;
3365004d7b8SChangpeng Liu payload_len -= scsi_req->req.iovs[0].iov_len;
3375004d7b8SChangpeng Liu
3385004d7b8SChangpeng Liu /*
3395004d7b8SChangpeng Liu * FROM_DEV (READ): [RO_req][WR_resp][WR_buf0]...[WR_bufN]
3405004d7b8SChangpeng Liu * TO_DEV (WRITE): [RO_req][RO_buf0]...[RO_bufN][WR_resp]
3415004d7b8SChangpeng Liu */
3425004d7b8SChangpeng Liu if (virtio_req_iov_is_wr(&scsi_req->req, 1)) {
3435004d7b8SChangpeng Liu scsi_req->scsi.dxfer_dir = SPDK_SCSI_DIR_FROM_DEV;
3445004d7b8SChangpeng Liu } else {
3455004d7b8SChangpeng Liu scsi_req->scsi.dxfer_dir = SPDK_SCSI_DIR_TO_DEV;
3465004d7b8SChangpeng Liu }
3475004d7b8SChangpeng Liu
3485004d7b8SChangpeng Liu if (scsi_req->scsi.dxfer_dir == SPDK_SCSI_DIR_FROM_DEV) {
3495004d7b8SChangpeng Liu if (scsi_req->req.iovs[1].iov_len < sizeof(struct virtio_scsi_cmd_resp)) {
3505004d7b8SChangpeng Liu SPDK_ERRLOG("DIR_FROM_DEV: Invalid virtio_scsi command resp length");
3515004d7b8SChangpeng Liu return -EINVAL;
3525004d7b8SChangpeng Liu }
3535004d7b8SChangpeng Liu scsi_req->cmd_resp = scsi_req->req.iovs[1].iov_base;
3545004d7b8SChangpeng Liu scsi_req->req.used_len = payload_len;
3555004d7b8SChangpeng Liu scsi_req->scsi.iovs = &scsi_req->req.iovs[2];
3565004d7b8SChangpeng Liu } else {
3575004d7b8SChangpeng Liu if (scsi_req->req.iovs[iovcnt - 1].iov_len < sizeof(struct virtio_scsi_cmd_resp)) {
3585004d7b8SChangpeng Liu SPDK_ERRLOG("DIR_TO_DEV: Invalid virtio_scsi command resp length");
3595004d7b8SChangpeng Liu return -EINVAL;
3605004d7b8SChangpeng Liu }
3615004d7b8SChangpeng Liu scsi_req->req.used_len = sizeof(struct virtio_scsi_cmd_resp);
3625004d7b8SChangpeng Liu scsi_req->cmd_resp = scsi_req->req.iovs[iovcnt - 1].iov_base;
3635004d7b8SChangpeng Liu scsi_req->scsi.iovs = &scsi_req->req.iovs[1];
3645004d7b8SChangpeng Liu }
3655004d7b8SChangpeng Liu
3665004d7b8SChangpeng Liu /* -2 for REQ and RESP */
3675004d7b8SChangpeng Liu iovcnt -= 2;
3685004d7b8SChangpeng Liu if (!iovcnt) {
3695004d7b8SChangpeng Liu scsi_req->scsi.length = 0;
3705004d7b8SChangpeng Liu scsi_req->scsi.transfer_len = 0;
3715004d7b8SChangpeng Liu scsi_req->scsi.iovs[0].iov_len = 0;
3725004d7b8SChangpeng Liu } else {
3735004d7b8SChangpeng Liu assert(payload_len > sizeof(struct virtio_scsi_cmd_resp));
3745004d7b8SChangpeng Liu payload_len -= sizeof(struct virtio_scsi_cmd_resp);
3755004d7b8SChangpeng Liu scsi_req->scsi.length = payload_len;
3765004d7b8SChangpeng Liu scsi_req->scsi.transfer_len = payload_len;
3775004d7b8SChangpeng Liu }
3785004d7b8SChangpeng Liu scsi_req->scsi.iovcnt = iovcnt;
3795004d7b8SChangpeng Liu scsi_req->scsi.cdb = scsi_req->cmd_req->cdb;
3805004d7b8SChangpeng Liu scsi_req->cmd_resp->response = VIRTIO_SCSI_S_OK;
3815004d7b8SChangpeng Liu
3825004d7b8SChangpeng Liu SPDK_LOGDUMP(vfu_virtio_scsi_data, "CDB=", scsi_req->cmd_req->cdb, VIRTIO_SCSI_CDB_SIZE);
3835004d7b8SChangpeng Liu SPDK_DEBUGLOG(vfu_virtio_scsi, "%s, iovcnt %u, transfer_len %u, used len %u\n",
3845004d7b8SChangpeng Liu scsi_req->scsi.dxfer_dir == SPDK_SCSI_DIR_FROM_DEV ? "XFER_FROM_DEV" : "XFER_TO_DEV",
3855004d7b8SChangpeng Liu scsi_req->scsi.iovcnt, payload_len, scsi_req->req.used_len);
3865004d7b8SChangpeng Liu
3875004d7b8SChangpeng Liu return 0;
3885004d7b8SChangpeng Liu }
3895004d7b8SChangpeng Liu
3905004d7b8SChangpeng Liu static int
virtio_scsi_tmf_cmd_req(struct virtio_scsi_endpoint * scsi_endpoint,struct virtio_scsi_req * scsi_req)3915004d7b8SChangpeng Liu virtio_scsi_tmf_cmd_req(struct virtio_scsi_endpoint *scsi_endpoint,
3925004d7b8SChangpeng Liu struct virtio_scsi_req *scsi_req)
3935004d7b8SChangpeng Liu {
3945004d7b8SChangpeng Liu uint32_t iovcnt;
3955004d7b8SChangpeng Liu struct iovec *iov;
3965004d7b8SChangpeng Liu struct virtio_scsi_ctrl_tmf_req *tmf_req;
3975004d7b8SChangpeng Liu struct virtio_scsi_target *scsi_target;
3985004d7b8SChangpeng Liu
3995004d7b8SChangpeng Liu iov = &scsi_req->req.iovs[0];
4005004d7b8SChangpeng Liu iovcnt = scsi_req->req.iovcnt;
4015004d7b8SChangpeng Liu tmf_req = iov->iov_base;
4025004d7b8SChangpeng Liu if (spdk_unlikely(iovcnt < 2)) {
4035004d7b8SChangpeng Liu SPDK_ERRLOG("Invalid iovcnt %u\n", iovcnt);
4045004d7b8SChangpeng Liu goto invalid;
4055004d7b8SChangpeng Liu }
4065004d7b8SChangpeng Liu
4075004d7b8SChangpeng Liu memset(&scsi_req->scsi, 0, sizeof(struct spdk_scsi_task));
4085004d7b8SChangpeng Liu spdk_scsi_task_construct(&scsi_req->scsi, virtio_scsi_task_mgmt_cpl, virtio_scsi_task_free_cb);
4095004d7b8SChangpeng Liu
4105004d7b8SChangpeng Liu switch (tmf_req->type) {
4115004d7b8SChangpeng Liu case VIRTIO_SCSI_T_TMF:
4125004d7b8SChangpeng Liu if (scsi_req->req.iovs[0].iov_len < sizeof(struct virtio_scsi_ctrl_tmf_req) ||
4135004d7b8SChangpeng Liu scsi_req->req.iovs[1].iov_len < sizeof(struct virtio_scsi_ctrl_tmf_resp)) {
4145004d7b8SChangpeng Liu SPDK_ERRLOG("Invalid size of tmf_req or tmf_resp\n");
4155004d7b8SChangpeng Liu goto invalid;
4165004d7b8SChangpeng Liu }
4175004d7b8SChangpeng Liu scsi_req->tmf_req = tmf_req;
4185004d7b8SChangpeng Liu scsi_req->tmf_resp = scsi_req->req.iovs[1].iov_base;
4195004d7b8SChangpeng Liu switch (tmf_req->subtype) {
4205004d7b8SChangpeng Liu case VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET:
4215004d7b8SChangpeng Liu scsi_target = virtio_scsi_cmd_lun_setup(scsi_endpoint, scsi_req, scsi_req->tmf_req->lun);
4225004d7b8SChangpeng Liu if (!scsi_target) {
4235004d7b8SChangpeng Liu scsi_req->tmf_resp->response = VIRTIO_SCSI_S_BAD_TARGET;
4245004d7b8SChangpeng Liu break;
4255004d7b8SChangpeng Liu }
4265004d7b8SChangpeng Liu /* Management task submission */
4275004d7b8SChangpeng Liu scsi_req->tmf_resp->response = VIRTIO_SCSI_S_OK;
4285004d7b8SChangpeng Liu scsi_req->scsi.function = SPDK_SCSI_TASK_FUNC_LUN_RESET;
4295004d7b8SChangpeng Liu spdk_scsi_dev_queue_mgmt_task(scsi_target->dev, &scsi_req->scsi);
4305004d7b8SChangpeng Liu return 0;
4315004d7b8SChangpeng Liu break;
4325004d7b8SChangpeng Liu default:
4335004d7b8SChangpeng Liu scsi_req->tmf_resp->response = VIRTIO_SCSI_S_FUNCTION_REJECTED;
4345004d7b8SChangpeng Liu break;
4355004d7b8SChangpeng Liu }
4365004d7b8SChangpeng Liu break;
4375004d7b8SChangpeng Liu
4385004d7b8SChangpeng Liu case VIRTIO_SCSI_T_AN_QUERY:
4395004d7b8SChangpeng Liu case VIRTIO_SCSI_T_AN_SUBSCRIBE:
4405004d7b8SChangpeng Liu if (scsi_req->req.iovs[0].iov_len < sizeof(struct virtio_scsi_ctrl_an_req) ||
4415004d7b8SChangpeng Liu scsi_req->req.iovs[1].iov_len < sizeof(struct virtio_scsi_ctrl_an_resp)) {
4425004d7b8SChangpeng Liu SPDK_ERRLOG("Invalid size of tmf_req or tmf_resp\n");
4435004d7b8SChangpeng Liu goto invalid;
4445004d7b8SChangpeng Liu }
4455004d7b8SChangpeng Liu scsi_req->req.used_len = sizeof(struct virtio_scsi_ctrl_an_resp);
4465004d7b8SChangpeng Liu /* Do nothing to response byte of virtio_scsi_ctrl_an_resp */
4475004d7b8SChangpeng Liu goto invalid;
4485004d7b8SChangpeng Liu break;
4495004d7b8SChangpeng Liu default:
4505004d7b8SChangpeng Liu break;
4515004d7b8SChangpeng Liu }
4525004d7b8SChangpeng Liu
4535004d7b8SChangpeng Liu invalid:
4545004d7b8SChangpeng Liu /* invalid request */
4555004d7b8SChangpeng Liu virtio_scsi_req_finish(scsi_req);
4565004d7b8SChangpeng Liu return -1;
4575004d7b8SChangpeng Liu }
4585004d7b8SChangpeng Liu
4595004d7b8SChangpeng Liu static int
virtio_scsi_cmd_req(struct virtio_scsi_endpoint * scsi_endpoint,struct virtio_scsi_req * scsi_req)4605004d7b8SChangpeng Liu virtio_scsi_cmd_req(struct virtio_scsi_endpoint *scsi_endpoint, struct virtio_scsi_req *scsi_req)
4615004d7b8SChangpeng Liu {
4625004d7b8SChangpeng Liu int ret;
4635004d7b8SChangpeng Liu struct virtio_scsi_target *scsi_target;
4645004d7b8SChangpeng Liu
4655004d7b8SChangpeng Liu memset(&scsi_req->scsi, 0, sizeof(struct spdk_scsi_task));
4665004d7b8SChangpeng Liu spdk_scsi_task_construct(&scsi_req->scsi, virtio_scsi_task_cpl, virtio_scsi_task_free_cb);
4675004d7b8SChangpeng Liu
4685004d7b8SChangpeng Liu ret = virtio_scsi_cmd_data_setup(scsi_req);
4695004d7b8SChangpeng Liu if (ret) {
4705004d7b8SChangpeng Liu SPDK_ERRLOG("Error to setup SCSI command, ret %d\n", ret);
4715004d7b8SChangpeng Liu goto invalid;
4725004d7b8SChangpeng Liu }
4735004d7b8SChangpeng Liu
4745004d7b8SChangpeng Liu scsi_target = virtio_scsi_cmd_lun_setup(scsi_endpoint, scsi_req, scsi_req->cmd_req->lun);
4755004d7b8SChangpeng Liu if (!scsi_target) {
4765004d7b8SChangpeng Liu scsi_req->cmd_resp->response = VIRTIO_SCSI_S_BAD_TARGET;
4775004d7b8SChangpeng Liu goto invalid;
4785004d7b8SChangpeng Liu }
4795004d7b8SChangpeng Liu
4805004d7b8SChangpeng Liu spdk_scsi_dev_queue_task(scsi_target->dev, &scsi_req->scsi);
4815004d7b8SChangpeng Liu return 0;
4825004d7b8SChangpeng Liu
4835004d7b8SChangpeng Liu invalid:
4845004d7b8SChangpeng Liu /* invalid request */
4855004d7b8SChangpeng Liu virtio_scsi_req_finish(scsi_req);
4865004d7b8SChangpeng Liu return ret;
4875004d7b8SChangpeng Liu }
4885004d7b8SChangpeng Liu
4895004d7b8SChangpeng Liu static int
virtio_scsi_process_req(struct vfu_virtio_endpoint * virtio_endpoint,struct vfu_virtio_vq * vq,struct vfu_virtio_req * req)4905004d7b8SChangpeng Liu virtio_scsi_process_req(struct vfu_virtio_endpoint *virtio_endpoint, struct vfu_virtio_vq *vq,
4915004d7b8SChangpeng Liu struct vfu_virtio_req *req)
4925004d7b8SChangpeng Liu {
4935004d7b8SChangpeng Liu struct virtio_scsi_endpoint *scsi_endpoint = to_scsi_endpoint(virtio_endpoint);
4945004d7b8SChangpeng Liu struct virtio_scsi_req *scsi_req = to_scsi_request(req);
4955004d7b8SChangpeng Liu
4965004d7b8SChangpeng Liu scsi_req->endpoint = scsi_endpoint;
4975004d7b8SChangpeng Liu
4985004d7b8SChangpeng Liu /* SCSI task management command */
4995004d7b8SChangpeng Liu if (spdk_unlikely(vq->id == 0)) {
5005004d7b8SChangpeng Liu return virtio_scsi_tmf_cmd_req(scsi_endpoint, scsi_req);
5015004d7b8SChangpeng Liu }
5025004d7b8SChangpeng Liu
5035004d7b8SChangpeng Liu /* SCSI command */
5045004d7b8SChangpeng Liu return virtio_scsi_cmd_req(scsi_endpoint, scsi_req);;
5055004d7b8SChangpeng Liu }
5065004d7b8SChangpeng Liu
5075004d7b8SChangpeng Liu static void
virtio_scsi_update_config(struct virtio_scsi_endpoint * scsi_endpoint)5085004d7b8SChangpeng Liu virtio_scsi_update_config(struct virtio_scsi_endpoint *scsi_endpoint)
5095004d7b8SChangpeng Liu {
5105004d7b8SChangpeng Liu struct virtio_scsi_config *scsi_cfg;
5115004d7b8SChangpeng Liu
5125004d7b8SChangpeng Liu if (!scsi_endpoint) {
5135004d7b8SChangpeng Liu return;
5145004d7b8SChangpeng Liu }
5155004d7b8SChangpeng Liu
5165004d7b8SChangpeng Liu scsi_cfg = &scsi_endpoint->scsi_cfg;
5175004d7b8SChangpeng Liu
5185004d7b8SChangpeng Liu scsi_cfg->num_queues = scsi_endpoint->virtio.num_queues;
5195004d7b8SChangpeng Liu /* -2 for REQ and RESP and -1 for region boundary splitting */
520b45556e2SChangpeng Liu scsi_cfg->seg_max = spdk_min(VIRTIO_DEV_MAX_IOVS - 2 - 1, SPDK_BDEV_IO_NUM_CHILD_IOV - 2 - 1);
5215004d7b8SChangpeng Liu /* we can set `max_sectors` and `cmd_per_lun` based on bdevs */
5225004d7b8SChangpeng Liu scsi_cfg->max_sectors = 131072;
5235004d7b8SChangpeng Liu scsi_cfg->cmd_per_lun = scsi_endpoint->virtio.qsize;
5245004d7b8SChangpeng Liu scsi_cfg->event_info_size = sizeof(struct virtio_scsi_event);
5255004d7b8SChangpeng Liu scsi_cfg->sense_size = VIRTIO_SCSI_SENSE_DEFAULT_SIZE;
5265004d7b8SChangpeng Liu scsi_cfg->cdb_size = VIRTIO_SCSI_CDB_DEFAULT_SIZE;
5275004d7b8SChangpeng Liu scsi_cfg->max_channel = 0;
5285004d7b8SChangpeng Liu scsi_cfg->max_target = VIRTIO_SCSI_CTRLR_MAX_TARGETS;
5295004d7b8SChangpeng Liu scsi_cfg->max_lun = 16383;
5305004d7b8SChangpeng Liu }
5315004d7b8SChangpeng Liu
5325004d7b8SChangpeng Liu static uint64_t
virtio_scsi_get_supported_features(struct vfu_virtio_endpoint * virtio_endpoint)5335004d7b8SChangpeng Liu virtio_scsi_get_supported_features(struct vfu_virtio_endpoint *virtio_endpoint)
5345004d7b8SChangpeng Liu {
5355004d7b8SChangpeng Liu uint64_t features;
5365004d7b8SChangpeng Liu
5375004d7b8SChangpeng Liu features = VIRTIO_SCSI_SUPPORTED_FEATURES | VIRTIO_HOST_SUPPORTED_FEATURES;
5385004d7b8SChangpeng Liu
5395004d7b8SChangpeng Liu if (!virtio_endpoint->packed_ring) {
5405004d7b8SChangpeng Liu features &= ~(1ULL << VIRTIO_F_RING_PACKED);
5415004d7b8SChangpeng Liu }
5425004d7b8SChangpeng Liu
5435004d7b8SChangpeng Liu return features;
5445004d7b8SChangpeng Liu }
5455004d7b8SChangpeng Liu
5465004d7b8SChangpeng Liu static int
virtio_scsi_get_device_specific_config(struct vfu_virtio_endpoint * virtio_endpoint,char * buf,uint64_t offset,uint64_t count)5475004d7b8SChangpeng Liu virtio_scsi_get_device_specific_config(struct vfu_virtio_endpoint *virtio_endpoint, char *buf,
5485004d7b8SChangpeng Liu uint64_t offset, uint64_t count)
5495004d7b8SChangpeng Liu {
5505004d7b8SChangpeng Liu struct virtio_scsi_endpoint *scsi_endpoint = to_scsi_endpoint(virtio_endpoint);
5515004d7b8SChangpeng Liu uint8_t *scsi_cfg;
5525004d7b8SChangpeng Liu
5535004d7b8SChangpeng Liu if ((offset + count) > sizeof(struct virtio_scsi_config)) {
5545004d7b8SChangpeng Liu SPDK_ERRLOG("Invalid device specific configuration offset 0x%"PRIx64"\n", offset);
5555004d7b8SChangpeng Liu return -EINVAL;
5565004d7b8SChangpeng Liu }
5575004d7b8SChangpeng Liu
5585004d7b8SChangpeng Liu scsi_cfg = (uint8_t *)&scsi_endpoint->scsi_cfg;
5595004d7b8SChangpeng Liu memcpy(buf, scsi_cfg + offset, count);
5605004d7b8SChangpeng Liu
5615004d7b8SChangpeng Liu return 0;
5625004d7b8SChangpeng Liu }
5635004d7b8SChangpeng Liu
5645004d7b8SChangpeng Liu static int
virtio_scsi_set_device_specific_config(struct vfu_virtio_endpoint * virtio_endpoint,char * buf,uint64_t offset,uint64_t count)5655004d7b8SChangpeng Liu virtio_scsi_set_device_specific_config(struct vfu_virtio_endpoint *virtio_endpoint, char *buf,
5665004d7b8SChangpeng Liu uint64_t offset, uint64_t count)
5675004d7b8SChangpeng Liu {
5685004d7b8SChangpeng Liu struct virtio_scsi_endpoint *scsi_endpoint = to_scsi_endpoint(virtio_endpoint);
5695004d7b8SChangpeng Liu uint32_t value;
5705004d7b8SChangpeng Liu int ret = 0;
5715004d7b8SChangpeng Liu
5725004d7b8SChangpeng Liu if ((offset + count) > sizeof(struct virtio_scsi_config)) {
5735004d7b8SChangpeng Liu SPDK_ERRLOG("Invalid device specific configuration offset 0x%"PRIx64"\n", offset);
5745004d7b8SChangpeng Liu return -EINVAL;
5755004d7b8SChangpeng Liu }
5765004d7b8SChangpeng Liu
5775004d7b8SChangpeng Liu switch (offset) {
5785004d7b8SChangpeng Liu case offsetof(struct virtio_scsi_config, sense_size):
5795004d7b8SChangpeng Liu value = *(uint32_t *)buf;
5805004d7b8SChangpeng Liu if (scsi_endpoint->scsi_cfg.sense_size != value) {
5815004d7b8SChangpeng Liu SPDK_ERRLOG("Sense data size set to %u\n", value);
5825004d7b8SChangpeng Liu ret = -ENOTSUP;
5835004d7b8SChangpeng Liu }
5845004d7b8SChangpeng Liu break;
5855004d7b8SChangpeng Liu case offsetof(struct virtio_scsi_config, cdb_size):
5865004d7b8SChangpeng Liu value = *(uint32_t *)buf;
5875004d7b8SChangpeng Liu if (scsi_endpoint->scsi_cfg.cdb_size != value) {
5885004d7b8SChangpeng Liu SPDK_ERRLOG("CDB size set to %u\n", value);
5895004d7b8SChangpeng Liu ret = -ENOTSUP;
5905004d7b8SChangpeng Liu }
5915004d7b8SChangpeng Liu break;
5925004d7b8SChangpeng Liu default:
5935004d7b8SChangpeng Liu SPDK_ERRLOG("Error offset %"PRIu64"\n", offset);
5945004d7b8SChangpeng Liu ret = -EINVAL;
5955004d7b8SChangpeng Liu break;
5965004d7b8SChangpeng Liu }
5975004d7b8SChangpeng Liu
5985004d7b8SChangpeng Liu
5995004d7b8SChangpeng Liu return ret;
6005004d7b8SChangpeng Liu }
6015004d7b8SChangpeng Liu
6025004d7b8SChangpeng Liu static struct vfu_virtio_req *
virtio_scsi_alloc_req(struct vfu_virtio_endpoint * virtio_endpoint,struct vfu_virtio_vq * vq)6035004d7b8SChangpeng Liu virtio_scsi_alloc_req(struct vfu_virtio_endpoint *virtio_endpoint, struct vfu_virtio_vq *vq)
6045004d7b8SChangpeng Liu {
6055004d7b8SChangpeng Liu struct virtio_scsi_req *scsi_req;
6065004d7b8SChangpeng Liu
6075004d7b8SChangpeng Liu scsi_req = calloc(1, sizeof(*scsi_req) + dma_sg_size() * (VIRTIO_DEV_MAX_IOVS + 1));
6085004d7b8SChangpeng Liu if (!scsi_req) {
6095004d7b8SChangpeng Liu return NULL;
6105004d7b8SChangpeng Liu }
6115004d7b8SChangpeng Liu
6125004d7b8SChangpeng Liu return &scsi_req->req;
6135004d7b8SChangpeng Liu }
6145004d7b8SChangpeng Liu
6155004d7b8SChangpeng Liu static void
virtio_scsi_free_req(struct vfu_virtio_endpoint * virtio_endpoint,struct vfu_virtio_vq * vq,struct vfu_virtio_req * req)6165004d7b8SChangpeng Liu virtio_scsi_free_req(struct vfu_virtio_endpoint *virtio_endpoint, struct vfu_virtio_vq *vq,
6175004d7b8SChangpeng Liu struct vfu_virtio_req *req)
6185004d7b8SChangpeng Liu {
6195004d7b8SChangpeng Liu struct virtio_scsi_req *scsi_req = to_scsi_request(req);
6205004d7b8SChangpeng Liu
6215004d7b8SChangpeng Liu free(scsi_req);
6225004d7b8SChangpeng Liu }
6235004d7b8SChangpeng Liu
6245004d7b8SChangpeng Liu struct vfu_virtio_ops virtio_scsi_ops = {
6255004d7b8SChangpeng Liu .get_device_features = virtio_scsi_get_supported_features,
6265004d7b8SChangpeng Liu .alloc_req = virtio_scsi_alloc_req,
6275004d7b8SChangpeng Liu .free_req = virtio_scsi_free_req,
6285004d7b8SChangpeng Liu .exec_request = virtio_scsi_process_req,
6295004d7b8SChangpeng Liu .get_config = virtio_scsi_get_device_specific_config,
6305004d7b8SChangpeng Liu .set_config = virtio_scsi_set_device_specific_config,
6315004d7b8SChangpeng Liu .start_device = virtio_scsi_start,
6325004d7b8SChangpeng Liu .stop_device = virtio_scsi_stop,
6335004d7b8SChangpeng Liu };
6345004d7b8SChangpeng Liu
6355004d7b8SChangpeng Liu int
vfu_virtio_scsi_set_options(const char * name,uint16_t num_io_queues,uint16_t qsize,bool packed_ring)6365004d7b8SChangpeng Liu vfu_virtio_scsi_set_options(const char *name, uint16_t num_io_queues, uint16_t qsize,
6375004d7b8SChangpeng Liu bool packed_ring)
6385004d7b8SChangpeng Liu {
6395004d7b8SChangpeng Liu struct spdk_vfu_endpoint *endpoint;
6405004d7b8SChangpeng Liu uint32_t num_queues;
6415004d7b8SChangpeng Liu struct vfu_virtio_endpoint *virtio_endpoint;
6425004d7b8SChangpeng Liu struct virtio_scsi_endpoint *scsi_endpoint;
6435004d7b8SChangpeng Liu
6445004d7b8SChangpeng Liu num_queues = num_io_queues + 2;
6455004d7b8SChangpeng Liu
6465004d7b8SChangpeng Liu endpoint = spdk_vfu_get_endpoint_by_name(name);
6475004d7b8SChangpeng Liu if (!endpoint) {
6485004d7b8SChangpeng Liu SPDK_ERRLOG("Endpoint %s doesn't exist\n", name);
6495004d7b8SChangpeng Liu return -ENOENT;
6505004d7b8SChangpeng Liu }
6515004d7b8SChangpeng Liu
6525004d7b8SChangpeng Liu virtio_endpoint = spdk_vfu_get_endpoint_private(endpoint);
6535004d7b8SChangpeng Liu scsi_endpoint = to_scsi_endpoint(virtio_endpoint);
6545004d7b8SChangpeng Liu if (virtio_endpoint->dev) {
6555004d7b8SChangpeng Liu SPDK_ERRLOG("Options are not allowed to change in runtime\n");
6565004d7b8SChangpeng Liu return -EFAULT;
6575004d7b8SChangpeng Liu }
6585004d7b8SChangpeng Liu
6595004d7b8SChangpeng Liu if ((num_queues > 2) && (num_queues <= VIRTIO_DEV_MAX_VQS)) {
6605004d7b8SChangpeng Liu scsi_endpoint->virtio.num_queues = num_queues;
6615004d7b8SChangpeng Liu } else {
6625004d7b8SChangpeng Liu SPDK_NOTICELOG("Number of IO queue %u\n", VIRTIO_DEV_MAX_VQS - 2);
6635004d7b8SChangpeng Liu scsi_endpoint->virtio.num_queues = VIRTIO_DEV_MAX_VQS;
6645004d7b8SChangpeng Liu }
6655004d7b8SChangpeng Liu
6665004d7b8SChangpeng Liu if (qsize && qsize <= VIRTIO_VQ_MAX_SIZE) {
6675004d7b8SChangpeng Liu scsi_endpoint->virtio.qsize = qsize;
6685004d7b8SChangpeng Liu } else {
6695004d7b8SChangpeng Liu SPDK_NOTICELOG("Use queue size %u\n", VIRTIO_VQ_DEFAULT_SIZE);
6705004d7b8SChangpeng Liu scsi_endpoint->virtio.qsize = VIRTIO_VQ_DEFAULT_SIZE;
6715004d7b8SChangpeng Liu }
6725004d7b8SChangpeng Liu scsi_endpoint->virtio.packed_ring = packed_ring;
6735004d7b8SChangpeng Liu
6745004d7b8SChangpeng Liu SPDK_DEBUGLOG(vfu_virtio_scsi, "%s: num_queues %u, qsize %u, packed ring %s\n",
6755004d7b8SChangpeng Liu spdk_vfu_get_endpoint_id(endpoint),
6765004d7b8SChangpeng Liu scsi_endpoint->virtio.num_queues, scsi_endpoint->virtio.qsize,
6775004d7b8SChangpeng Liu packed_ring ? "enabled" : "disabled");
6785004d7b8SChangpeng Liu
6795004d7b8SChangpeng Liu virtio_scsi_update_config(scsi_endpoint);
6805004d7b8SChangpeng Liu
6815004d7b8SChangpeng Liu return 0;
6825004d7b8SChangpeng Liu }
6835004d7b8SChangpeng Liu
6845004d7b8SChangpeng Liu struct virtio_scsi_event_ctx {
6855004d7b8SChangpeng Liu struct virtio_scsi_endpoint *scsi_endpoint;
6865004d7b8SChangpeng Liu struct virtio_scsi_target *scsi_target;
6875004d7b8SChangpeng Liu uint8_t scsi_target_num;
6885004d7b8SChangpeng Liu };
6895004d7b8SChangpeng Liu
6905004d7b8SChangpeng Liu static uint8_t
get_scsi_target_num_by_lun(struct virtio_scsi_endpoint * scsi_endpoint,const struct spdk_scsi_lun * lun)6915004d7b8SChangpeng Liu get_scsi_target_num_by_lun(struct virtio_scsi_endpoint *scsi_endpoint,
6925004d7b8SChangpeng Liu const struct spdk_scsi_lun *lun)
6935004d7b8SChangpeng Liu {
6945004d7b8SChangpeng Liu const struct spdk_scsi_dev *scsi_dev;
6955004d7b8SChangpeng Liu struct virtio_scsi_target *scsi_target;
6965004d7b8SChangpeng Liu uint8_t i;
6975004d7b8SChangpeng Liu
6985004d7b8SChangpeng Liu scsi_dev = spdk_scsi_lun_get_dev(lun);
6995004d7b8SChangpeng Liu for (i = 0; i < VIRTIO_SCSI_CTRLR_MAX_TARGETS; i++) {
7005004d7b8SChangpeng Liu scsi_target = &scsi_endpoint->targets[i];
7015004d7b8SChangpeng Liu if (scsi_target->dev == scsi_dev) {
7025004d7b8SChangpeng Liu return i;
7035004d7b8SChangpeng Liu }
7045004d7b8SChangpeng Liu }
7055004d7b8SChangpeng Liu
7065004d7b8SChangpeng Liu return VIRTIO_SCSI_CTRLR_MAX_TARGETS;
7075004d7b8SChangpeng Liu }
7085004d7b8SChangpeng Liu
7095004d7b8SChangpeng Liu static void
vfu_virtio_scsi_lun_resize_msg(void * ctx)7105004d7b8SChangpeng Liu vfu_virtio_scsi_lun_resize_msg(void *ctx)
7115004d7b8SChangpeng Liu {
7125004d7b8SChangpeng Liu struct virtio_scsi_event_ctx *resize_ctx = ctx;
7135004d7b8SChangpeng Liu struct virtio_scsi_endpoint *scsi_endpoint = resize_ctx->scsi_endpoint;
7145004d7b8SChangpeng Liu uint8_t scsi_target_num = resize_ctx->scsi_target_num;
7155004d7b8SChangpeng Liu
7165004d7b8SChangpeng Liu free(resize_ctx);
7175004d7b8SChangpeng Liu
7185004d7b8SChangpeng Liu if (virtio_guest_has_feature(scsi_endpoint->virtio.dev, VIRTIO_SCSI_F_CHANGE)) {
7195004d7b8SChangpeng Liu vfu_virtio_scsi_eventq_enqueue(scsi_endpoint, scsi_target_num,
7205004d7b8SChangpeng Liu VIRTIO_SCSI_T_PARAM_CHANGE, 0x2a | (0x09 << 8));
7215004d7b8SChangpeng Liu }
7225004d7b8SChangpeng Liu }
7235004d7b8SChangpeng Liu
7245004d7b8SChangpeng Liu static void
vfu_virtio_scsi_lun_resize(const struct spdk_scsi_lun * lun,void * arg)7255004d7b8SChangpeng Liu vfu_virtio_scsi_lun_resize(const struct spdk_scsi_lun *lun, void *arg)
7265004d7b8SChangpeng Liu {
7275004d7b8SChangpeng Liu struct virtio_scsi_endpoint *scsi_endpoint = arg;
7285004d7b8SChangpeng Liu uint8_t scsi_target_num;
7295004d7b8SChangpeng Liu struct virtio_scsi_event_ctx *ctx;
7305004d7b8SChangpeng Liu
7315004d7b8SChangpeng Liu scsi_target_num = get_scsi_target_num_by_lun(scsi_endpoint, lun);
7325004d7b8SChangpeng Liu if (scsi_target_num == VIRTIO_SCSI_CTRLR_MAX_TARGETS) {
7335004d7b8SChangpeng Liu return;
7345004d7b8SChangpeng Liu }
7355004d7b8SChangpeng Liu
7365004d7b8SChangpeng Liu ctx = calloc(1, sizeof(*ctx));
7375004d7b8SChangpeng Liu if (!ctx) {
7385004d7b8SChangpeng Liu SPDK_ERRLOG("Error to allocate hotplug ctx\n");
7395004d7b8SChangpeng Liu return;
7405004d7b8SChangpeng Liu }
7415004d7b8SChangpeng Liu ctx->scsi_endpoint = scsi_endpoint;
7425004d7b8SChangpeng Liu ctx->scsi_target_num = scsi_target_num;
7435004d7b8SChangpeng Liu
7445004d7b8SChangpeng Liu spdk_thread_send_msg(scsi_endpoint->virtio.thread, vfu_virtio_scsi_lun_resize_msg, ctx);
7455004d7b8SChangpeng Liu }
7465004d7b8SChangpeng Liu
7475004d7b8SChangpeng Liu static void
vfu_virtio_scsi_lun_hotremove_msg(void * ctx)7485004d7b8SChangpeng Liu vfu_virtio_scsi_lun_hotremove_msg(void *ctx)
7495004d7b8SChangpeng Liu {
7505004d7b8SChangpeng Liu struct virtio_scsi_event_ctx *hotplug = ctx;
7515004d7b8SChangpeng Liu struct virtio_scsi_endpoint *scsi_endpoint = hotplug->scsi_endpoint;
7525004d7b8SChangpeng Liu struct virtio_scsi_target *scsi_target = hotplug->scsi_target;
7535004d7b8SChangpeng Liu struct spdk_scsi_dev *scsi_dev = scsi_target->dev;
7545004d7b8SChangpeng Liu uint8_t scsi_target_num = hotplug->scsi_target_num;
7555004d7b8SChangpeng Liu
7565004d7b8SChangpeng Liu free(hotplug);
7575004d7b8SChangpeng Liu
7585004d7b8SChangpeng Liu if (!scsi_dev) {
7595004d7b8SChangpeng Liu return;
7605004d7b8SChangpeng Liu }
7615004d7b8SChangpeng Liu scsi_target->dev = NULL;
7625004d7b8SChangpeng Liu spdk_scsi_dev_free_io_channels(scsi_dev);
7635004d7b8SChangpeng Liu spdk_scsi_dev_destruct(scsi_dev, NULL, NULL);
7645004d7b8SChangpeng Liu
7655004d7b8SChangpeng Liu assert(scsi_endpoint->virtio.dev);
7665004d7b8SChangpeng Liu if (!virtio_dev_is_started(scsi_endpoint->virtio.dev)) {
7675004d7b8SChangpeng Liu return;
7685004d7b8SChangpeng Liu }
7695004d7b8SChangpeng Liu
7705004d7b8SChangpeng Liu if (virtio_guest_has_feature(scsi_endpoint->virtio.dev, VIRTIO_SCSI_F_HOTPLUG)) {
7715004d7b8SChangpeng Liu SPDK_DEBUGLOG(vfu_virtio_scsi, "Target num %u, sending event\n", scsi_target_num);
7725004d7b8SChangpeng Liu vfu_virtio_scsi_eventq_enqueue(scsi_endpoint, scsi_target_num,
7735004d7b8SChangpeng Liu VIRTIO_SCSI_T_TRANSPORT_RESET, VIRTIO_SCSI_EVT_RESET_REMOVED);
7745004d7b8SChangpeng Liu }
7755004d7b8SChangpeng Liu }
7765004d7b8SChangpeng Liu
7775004d7b8SChangpeng Liu static void
vfu_virtio_scsi_lun_hotremove(const struct spdk_scsi_lun * lun,void * arg)7785004d7b8SChangpeng Liu vfu_virtio_scsi_lun_hotremove(const struct spdk_scsi_lun *lun, void *arg)
7795004d7b8SChangpeng Liu {
7805004d7b8SChangpeng Liu struct virtio_scsi_endpoint *scsi_endpoint = arg;
7815004d7b8SChangpeng Liu struct virtio_scsi_target *scsi_target;
7825004d7b8SChangpeng Liu struct virtio_scsi_event_ctx *ctx;
7835004d7b8SChangpeng Liu uint8_t scsi_target_num;
7845004d7b8SChangpeng Liu
7855004d7b8SChangpeng Liu if (!scsi_endpoint->virtio.dev) {
7865004d7b8SChangpeng Liu return;
7875004d7b8SChangpeng Liu }
7885004d7b8SChangpeng Liu
7895004d7b8SChangpeng Liu scsi_target_num = get_scsi_target_num_by_lun(scsi_endpoint, lun);
7905004d7b8SChangpeng Liu if (scsi_target_num == VIRTIO_SCSI_CTRLR_MAX_TARGETS) {
7915004d7b8SChangpeng Liu return;
7925004d7b8SChangpeng Liu }
7935004d7b8SChangpeng Liu scsi_target = &scsi_endpoint->targets[scsi_target_num];
7945004d7b8SChangpeng Liu if (!scsi_target->dev) {
7955004d7b8SChangpeng Liu return;
7965004d7b8SChangpeng Liu }
7975004d7b8SChangpeng Liu
7985004d7b8SChangpeng Liu SPDK_DEBUGLOG(vfu_virtio_scsi, "Removing bdev %s, Target num %u\n",
7995004d7b8SChangpeng Liu spdk_scsi_lun_get_bdev_name(lun), scsi_target_num);
8005004d7b8SChangpeng Liu
8015004d7b8SChangpeng Liu ctx = calloc(1, sizeof(*ctx));
8025004d7b8SChangpeng Liu if (!ctx) {
8035004d7b8SChangpeng Liu SPDK_ERRLOG("Error to allocate hotplug ctx\n");
8045004d7b8SChangpeng Liu return;
8055004d7b8SChangpeng Liu }
8065004d7b8SChangpeng Liu ctx->scsi_endpoint = scsi_endpoint;
8075004d7b8SChangpeng Liu ctx->scsi_target = scsi_target;
8085004d7b8SChangpeng Liu ctx->scsi_target_num = scsi_target_num;
8095004d7b8SChangpeng Liu
8105004d7b8SChangpeng Liu spdk_thread_send_msg(scsi_endpoint->virtio.thread, vfu_virtio_scsi_lun_hotremove_msg, ctx);
8115004d7b8SChangpeng Liu }
8125004d7b8SChangpeng Liu
8135004d7b8SChangpeng Liu static void
vfu_virtio_scsi_lun_hotplug_msg(void * ctx)8145004d7b8SChangpeng Liu vfu_virtio_scsi_lun_hotplug_msg(void *ctx)
8155004d7b8SChangpeng Liu {
8165004d7b8SChangpeng Liu struct virtio_scsi_event_ctx *hotplug = ctx;
8175004d7b8SChangpeng Liu struct virtio_scsi_endpoint *scsi_endpoint = hotplug->scsi_endpoint;
8185004d7b8SChangpeng Liu struct virtio_scsi_target *scsi_target = hotplug->scsi_target;
8195004d7b8SChangpeng Liu uint8_t scsi_target_num = hotplug->scsi_target_num;
8205004d7b8SChangpeng Liu int ret;
8215004d7b8SChangpeng Liu
8225004d7b8SChangpeng Liu free(hotplug);
8235004d7b8SChangpeng Liu
8245004d7b8SChangpeng Liu assert(scsi_endpoint->virtio.dev);
8255004d7b8SChangpeng Liu if (!virtio_dev_is_started(scsi_endpoint->virtio.dev)) {
8265004d7b8SChangpeng Liu return;
8275004d7b8SChangpeng Liu }
8285004d7b8SChangpeng Liu
8295004d7b8SChangpeng Liu ret = spdk_scsi_dev_allocate_io_channels(scsi_target->dev);
8305004d7b8SChangpeng Liu if (ret) {
8315004d7b8SChangpeng Liu SPDK_ERRLOG("%s: Couldn't allocate io channel for SCSI target %u.\n",
8325004d7b8SChangpeng Liu spdk_vfu_get_endpoint_name(scsi_endpoint->virtio.endpoint), scsi_target_num);
8335004d7b8SChangpeng Liu return;
8345004d7b8SChangpeng Liu }
8355004d7b8SChangpeng Liu
8365004d7b8SChangpeng Liu if (virtio_guest_has_feature(scsi_endpoint->virtio.dev, VIRTIO_SCSI_F_HOTPLUG)) {
8375004d7b8SChangpeng Liu vfu_virtio_scsi_eventq_enqueue(scsi_endpoint, scsi_target_num,
8385004d7b8SChangpeng Liu VIRTIO_SCSI_T_TRANSPORT_RESET, VIRTIO_SCSI_EVT_RESET_RESCAN);
8395004d7b8SChangpeng Liu }
8405004d7b8SChangpeng Liu }
8415004d7b8SChangpeng Liu
8425004d7b8SChangpeng Liu int
vfu_virtio_scsi_add_target(const char * name,uint8_t scsi_target_num,const char * bdev_name)8435004d7b8SChangpeng Liu vfu_virtio_scsi_add_target(const char *name, uint8_t scsi_target_num, const char *bdev_name)
8445004d7b8SChangpeng Liu {
8455004d7b8SChangpeng Liu struct spdk_vfu_endpoint *endpoint;
8465004d7b8SChangpeng Liu struct vfu_virtio_endpoint *virtio_endpoint;
8475004d7b8SChangpeng Liu struct virtio_scsi_endpoint *scsi_endpoint;
8485004d7b8SChangpeng Liu struct virtio_scsi_target *scsi_target;
8495004d7b8SChangpeng Liu char target_name[SPDK_SCSI_DEV_MAX_NAME];
8505004d7b8SChangpeng Liu int lun_id_list[1];
8515004d7b8SChangpeng Liu const char *bdev_names_list[1];
8525004d7b8SChangpeng Liu
8535004d7b8SChangpeng Liu endpoint = spdk_vfu_get_endpoint_by_name(name);
8545004d7b8SChangpeng Liu if (!endpoint) {
8555004d7b8SChangpeng Liu SPDK_ERRLOG("Endpoint %s doesn't exist\n", name);
8565004d7b8SChangpeng Liu return -ENOENT;
8575004d7b8SChangpeng Liu }
8585004d7b8SChangpeng Liu virtio_endpoint = spdk_vfu_get_endpoint_private(endpoint);
8595004d7b8SChangpeng Liu scsi_endpoint = to_scsi_endpoint(virtio_endpoint);
8605004d7b8SChangpeng Liu
8615004d7b8SChangpeng Liu if (scsi_target_num >= VIRTIO_SCSI_CTRLR_MAX_TARGETS) {
8625004d7b8SChangpeng Liu SPDK_ERRLOG("Invalid SCSI target number, maximum SCSI target number is %u\n",
8635004d7b8SChangpeng Liu VIRTIO_SCSI_CTRLR_MAX_TARGETS - 1);
8645004d7b8SChangpeng Liu return -EINVAL;
8655004d7b8SChangpeng Liu }
8665004d7b8SChangpeng Liu scsi_target = &scsi_endpoint->targets[scsi_target_num];
8675004d7b8SChangpeng Liu if (scsi_target->dev) {
8685004d7b8SChangpeng Liu SPDK_ERRLOG("SCSI Target %u is already occupied\n", scsi_target_num);
8695004d7b8SChangpeng Liu return -EEXIST;
8705004d7b8SChangpeng Liu }
8715004d7b8SChangpeng Liu
8725004d7b8SChangpeng Liu snprintf(target_name, sizeof(target_name), "Target %u", scsi_target_num);
8735004d7b8SChangpeng Liu lun_id_list[0] = 0;
8745004d7b8SChangpeng Liu bdev_names_list[0] = (char *)bdev_name;
8755004d7b8SChangpeng Liu
8765004d7b8SChangpeng Liu scsi_target->dev = spdk_scsi_dev_construct_ext(target_name, bdev_names_list, lun_id_list, 1,
8775004d7b8SChangpeng Liu SPDK_SPC_PROTOCOL_IDENTIFIER_SAS,
8785004d7b8SChangpeng Liu vfu_virtio_scsi_lun_resize, scsi_endpoint,
8795004d7b8SChangpeng Liu vfu_virtio_scsi_lun_hotremove, scsi_endpoint);
8805004d7b8SChangpeng Liu if (!scsi_target->dev) {
8815004d7b8SChangpeng Liu SPDK_ERRLOG("%s: couldn't create SCSI target %u via bdev %s\n", name, scsi_target_num, bdev_name);
8825004d7b8SChangpeng Liu return -EFAULT;
8835004d7b8SChangpeng Liu }
8845004d7b8SChangpeng Liu spdk_scsi_dev_add_port(scsi_target->dev, 0, "vfu-virtio-scsi");
8855004d7b8SChangpeng Liu
8865004d7b8SChangpeng Liu SPDK_NOTICELOG("%s: added SCSI target %u using bdev '%s'\n", name, scsi_target_num, bdev_name);
8875004d7b8SChangpeng Liu virtio_scsi_update_config(scsi_endpoint);
8885004d7b8SChangpeng Liu
8895004d7b8SChangpeng Liu if (virtio_endpoint->dev) {
8905004d7b8SChangpeng Liu struct virtio_scsi_event_ctx *ctx;
8915004d7b8SChangpeng Liu
8925004d7b8SChangpeng Liu ctx = calloc(1, sizeof(*ctx));
8935004d7b8SChangpeng Liu if (!ctx) {
8945004d7b8SChangpeng Liu SPDK_ERRLOG("Error to allocate hotplug ctx\n");
8955004d7b8SChangpeng Liu /* This isn't fatal, just skip hotplug notification */
8965004d7b8SChangpeng Liu } else {
8975004d7b8SChangpeng Liu ctx->scsi_endpoint = scsi_endpoint;
8985004d7b8SChangpeng Liu ctx->scsi_target = scsi_target;
8995004d7b8SChangpeng Liu ctx->scsi_target_num = scsi_target_num;
9005004d7b8SChangpeng Liu spdk_thread_send_msg(virtio_endpoint->thread, vfu_virtio_scsi_lun_hotplug_msg, ctx);
9015004d7b8SChangpeng Liu }
9025004d7b8SChangpeng Liu }
9035004d7b8SChangpeng Liu
9045004d7b8SChangpeng Liu return 0;
9055004d7b8SChangpeng Liu }
9065004d7b8SChangpeng Liu
9075004d7b8SChangpeng Liu int
vfu_virtio_scsi_remove_target(const char * name,uint8_t scsi_target_num)9085004d7b8SChangpeng Liu vfu_virtio_scsi_remove_target(const char *name, uint8_t scsi_target_num)
9095004d7b8SChangpeng Liu {
9105004d7b8SChangpeng Liu struct spdk_vfu_endpoint *endpoint;
9115004d7b8SChangpeng Liu struct vfu_virtio_endpoint *virtio_endpoint;
9125004d7b8SChangpeng Liu struct virtio_scsi_endpoint *scsi_endpoint;
9135004d7b8SChangpeng Liu struct virtio_scsi_target *scsi_target;
9145004d7b8SChangpeng Liu
9155004d7b8SChangpeng Liu endpoint = spdk_vfu_get_endpoint_by_name(name);
9165004d7b8SChangpeng Liu if (!endpoint) {
9175004d7b8SChangpeng Liu SPDK_ERRLOG("Endpoint %s doesn't exist\n", name);
9185004d7b8SChangpeng Liu return -ENOENT;
9195004d7b8SChangpeng Liu }
9205004d7b8SChangpeng Liu virtio_endpoint = spdk_vfu_get_endpoint_private(endpoint);
9215004d7b8SChangpeng Liu scsi_endpoint = to_scsi_endpoint(virtio_endpoint);
9225004d7b8SChangpeng Liu
9235004d7b8SChangpeng Liu if (scsi_target_num >= VIRTIO_SCSI_CTRLR_MAX_TARGETS) {
9245004d7b8SChangpeng Liu SPDK_ERRLOG("Invalid SCSI target number, maximum SCSI target number is %u\n",
9255004d7b8SChangpeng Liu VIRTIO_SCSI_CTRLR_MAX_TARGETS - 1);
9265004d7b8SChangpeng Liu return -EINVAL;
9275004d7b8SChangpeng Liu }
9285004d7b8SChangpeng Liu scsi_target = &scsi_endpoint->targets[scsi_target_num];
9295004d7b8SChangpeng Liu if (!scsi_target->dev) {
9305004d7b8SChangpeng Liu SPDK_ERRLOG("SCSI Target %u doesn't exist\n", scsi_target_num);
9315004d7b8SChangpeng Liu return -ENOENT;
9325004d7b8SChangpeng Liu }
9335004d7b8SChangpeng Liu
9345004d7b8SChangpeng Liu SPDK_NOTICELOG("%s: Remove SCSI target num %u\n", name, scsi_target_num);
9355004d7b8SChangpeng Liu
9365004d7b8SChangpeng Liu if (virtio_endpoint->dev) {
9375004d7b8SChangpeng Liu struct virtio_scsi_event_ctx *ctx;
9385004d7b8SChangpeng Liu
9395004d7b8SChangpeng Liu ctx = calloc(1, sizeof(*ctx));
9405004d7b8SChangpeng Liu if (!ctx) {
9415004d7b8SChangpeng Liu SPDK_ERRLOG("Error to allocate hotplug ctx\n");
9425004d7b8SChangpeng Liu /* This isn't fatal, just skip hotplug notification */
9435004d7b8SChangpeng Liu } else {
9445004d7b8SChangpeng Liu ctx->scsi_endpoint = scsi_endpoint;
9455004d7b8SChangpeng Liu ctx->scsi_target = scsi_target;
9465004d7b8SChangpeng Liu ctx->scsi_target_num = scsi_target_num;
9475004d7b8SChangpeng Liu spdk_thread_send_msg(scsi_endpoint->virtio.thread, vfu_virtio_scsi_lun_hotremove_msg, ctx);
9485004d7b8SChangpeng Liu }
9495004d7b8SChangpeng Liu } else {
9505004d7b8SChangpeng Liu spdk_scsi_dev_destruct(scsi_target->dev, NULL, NULL);
9515004d7b8SChangpeng Liu scsi_target->dev = NULL;
9525004d7b8SChangpeng Liu }
9535004d7b8SChangpeng Liu
9545004d7b8SChangpeng Liu return 0;
9555004d7b8SChangpeng Liu }
9565004d7b8SChangpeng Liu
9575004d7b8SChangpeng Liu static int
vfu_virtio_scsi_endpoint_destruct(struct spdk_vfu_endpoint * endpoint)9585004d7b8SChangpeng Liu vfu_virtio_scsi_endpoint_destruct(struct spdk_vfu_endpoint *endpoint)
9595004d7b8SChangpeng Liu {
9605004d7b8SChangpeng Liu struct vfu_virtio_endpoint *virtio_endpoint = spdk_vfu_get_endpoint_private(endpoint);
9615004d7b8SChangpeng Liu struct virtio_scsi_endpoint *scsi_endpoint = to_scsi_endpoint(virtio_endpoint);
9625004d7b8SChangpeng Liu struct virtio_scsi_target *scsi_target;
9635004d7b8SChangpeng Liu uint8_t i;
9645004d7b8SChangpeng Liu
9655004d7b8SChangpeng Liu for (i = 0; i < VIRTIO_SCSI_CTRLR_MAX_TARGETS; i++) {
9665004d7b8SChangpeng Liu scsi_target = &scsi_endpoint->targets[i];
9675004d7b8SChangpeng Liu if (scsi_target->dev) {
9685004d7b8SChangpeng Liu spdk_scsi_dev_destruct(scsi_target->dev, NULL, NULL);
9695004d7b8SChangpeng Liu }
9705004d7b8SChangpeng Liu }
9715004d7b8SChangpeng Liu
9725004d7b8SChangpeng Liu vfu_virtio_endpoint_destruct(&scsi_endpoint->virtio);
9735004d7b8SChangpeng Liu free(scsi_endpoint);
9745004d7b8SChangpeng Liu
9755004d7b8SChangpeng Liu return 0;
9765004d7b8SChangpeng Liu }
9775004d7b8SChangpeng Liu
9785004d7b8SChangpeng Liu static void *
vfu_virtio_scsi_endpoint_init(struct spdk_vfu_endpoint * endpoint,char * basename,const char * endpoint_name)9795004d7b8SChangpeng Liu vfu_virtio_scsi_endpoint_init(struct spdk_vfu_endpoint *endpoint,
9805004d7b8SChangpeng Liu char *basename, const char *endpoint_name)
9815004d7b8SChangpeng Liu {
9825004d7b8SChangpeng Liu struct virtio_scsi_endpoint *scsi_endpoint;
9835004d7b8SChangpeng Liu int ret;
9845004d7b8SChangpeng Liu
9855004d7b8SChangpeng Liu scsi_endpoint = calloc(1, sizeof(*scsi_endpoint));
9865004d7b8SChangpeng Liu if (!scsi_endpoint) {
9875004d7b8SChangpeng Liu return NULL;
9885004d7b8SChangpeng Liu }
9895004d7b8SChangpeng Liu
9905004d7b8SChangpeng Liu ret = vfu_virtio_endpoint_setup(&scsi_endpoint->virtio, endpoint, basename, endpoint_name,
9915004d7b8SChangpeng Liu &virtio_scsi_ops);
9925004d7b8SChangpeng Liu if (ret) {
9935004d7b8SChangpeng Liu SPDK_ERRLOG("Error to setup endpoint %s\n", endpoint_name);
9945004d7b8SChangpeng Liu free(scsi_endpoint);
9955004d7b8SChangpeng Liu return NULL;
9965004d7b8SChangpeng Liu }
9975004d7b8SChangpeng Liu
9985004d7b8SChangpeng Liu virtio_scsi_update_config(scsi_endpoint);
9995004d7b8SChangpeng Liu return (void *)&scsi_endpoint->virtio;
10005004d7b8SChangpeng Liu }
10015004d7b8SChangpeng Liu
10025004d7b8SChangpeng Liu static int
vfu_virtio_scsi_get_device_info(struct spdk_vfu_endpoint * endpoint,struct spdk_vfu_pci_device * device_info)10035004d7b8SChangpeng Liu vfu_virtio_scsi_get_device_info(struct spdk_vfu_endpoint *endpoint,
10045004d7b8SChangpeng Liu struct spdk_vfu_pci_device *device_info)
10055004d7b8SChangpeng Liu {
10065004d7b8SChangpeng Liu struct vfu_virtio_endpoint *virtio_endpoint = spdk_vfu_get_endpoint_private(endpoint);
10075004d7b8SChangpeng Liu struct virtio_scsi_endpoint *scsi_endpoint = to_scsi_endpoint(virtio_endpoint);
10085004d7b8SChangpeng Liu
10095004d7b8SChangpeng Liu vfu_virtio_get_device_info(&scsi_endpoint->virtio, device_info);
10105004d7b8SChangpeng Liu /* Fill Device ID */
10115004d7b8SChangpeng Liu device_info->id.did = PCI_DEVICE_ID_VIRTIO_SCSI_MODERN;
10125004d7b8SChangpeng Liu
10135004d7b8SChangpeng Liu return 0;
10145004d7b8SChangpeng Liu }
10155004d7b8SChangpeng Liu
10165004d7b8SChangpeng Liu struct spdk_vfu_endpoint_ops vfu_virtio_scsi_ops = {
10175004d7b8SChangpeng Liu .name = "virtio_scsi",
10185004d7b8SChangpeng Liu .init = vfu_virtio_scsi_endpoint_init,
10195004d7b8SChangpeng Liu .get_device_info = vfu_virtio_scsi_get_device_info,
10205004d7b8SChangpeng Liu .get_vendor_capability = vfu_virtio_get_vendor_capability,
10215004d7b8SChangpeng Liu .post_memory_add = vfu_virtio_post_memory_add,
10225004d7b8SChangpeng Liu .pre_memory_remove = vfu_virtio_pre_memory_remove,
10235004d7b8SChangpeng Liu .reset_device = vfu_virtio_pci_reset_cb,
10245004d7b8SChangpeng Liu .quiesce_device = vfu_virtio_quiesce_cb,
10255004d7b8SChangpeng Liu .destruct = vfu_virtio_scsi_endpoint_destruct,
10265004d7b8SChangpeng Liu .attach_device = vfu_virtio_attach_device,
10275004d7b8SChangpeng Liu .detach_device = vfu_virtio_detach_device,
10285004d7b8SChangpeng Liu };
10295004d7b8SChangpeng Liu
10305004d7b8SChangpeng Liu static void
_vfu_virtio_scsi_pci_model_register(void)10315004d7b8SChangpeng Liu __attribute__((constructor)) _vfu_virtio_scsi_pci_model_register(void)
10325004d7b8SChangpeng Liu {
10335004d7b8SChangpeng Liu spdk_vfu_register_endpoint_ops(&vfu_virtio_scsi_ops);
10345004d7b8SChangpeng Liu }
10355004d7b8SChangpeng Liu
10365004d7b8SChangpeng Liu SPDK_LOG_REGISTER_COMPONENT(vfu_virtio_scsi)
10375004d7b8SChangpeng Liu SPDK_LOG_REGISTER_COMPONENT(vfu_virtio_scsi_data)
1038