2021-08-21 20:33:20 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0+
|
2012-07-18 14:31:32 -07:00
|
|
|
/*******************************************************************************
|
|
|
|
* Vhost kernel TCM fabric driver for virtio SCSI initiators
|
|
|
|
*
|
2013-09-05 15:29:12 -07:00
|
|
|
* (C) Copyright 2010-2013 Datera, Inc.
|
2012-07-18 14:31:32 -07:00
|
|
|
* (C) Copyright 2010-2012 IBM Corp.
|
|
|
|
*
|
2013-09-05 15:29:12 -07:00
|
|
|
* Authors: Nicholas A. Bellinger <nab@daterainc.com>
|
2012-07-18 14:31:32 -07:00
|
|
|
* Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/moduleparam.h>
|
|
|
|
#include <generated/utsrelease.h>
|
|
|
|
#include <linux/utsname.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/kthread.h>
|
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/string.h>
|
|
|
|
#include <linux/configfs.h>
|
|
|
|
#include <linux/ctype.h>
|
|
|
|
#include <linux/compat.h>
|
|
|
|
#include <linux/eventfd.h>
|
|
|
|
#include <linux/fs.h>
|
2015-05-28 11:35:41 -07:00
|
|
|
#include <linux/vmalloc.h>
|
2012-07-18 14:31:32 -07:00
|
|
|
#include <linux/miscdevice.h>
|
2023-07-09 15:28:58 -05:00
|
|
|
#include <linux/blk_types.h>
|
|
|
|
#include <linux/bio.h>
|
2024-10-01 15:35:57 -04:00
|
|
|
#include <linux/unaligned.h>
|
2015-05-08 10:11:12 +02:00
|
|
|
#include <scsi/scsi_common.h>
|
|
|
|
#include <scsi/scsi_proto.h>
|
2012-07-18 14:31:32 -07:00
|
|
|
#include <target/target_core_base.h>
|
|
|
|
#include <target/target_core_fabric.h>
|
|
|
|
#include <linux/vhost.h>
|
|
|
|
#include <linux/virtio_scsi.h>
|
2013-01-06 14:36:13 +08:00
|
|
|
#include <linux/llist.h>
|
tcm_vhost: Multi-queue support
This adds virtio-scsi multi-queue support to tcm_vhost. In order to use
multi-queue, guest side multi-queue support is need. It can
be found here:
https://lkml.org/lkml/2012/12/18/166
Currently, only one thread is created by vhost core code for each
vhost_scsi instance. Even if there are multi-queues, all the handling of
guest kick (vhost_scsi_handle_kick) are processed in one thread. This is
not optimal. Luckily, most of the work is offloaded to the tcm_vhost
workqueue.
Some initial perf numbers:
1 queue, 4 targets, 1 lun per target
4K request size, 50% randread + 50% randwrite: 127K/127k IOPS
4 queues, 4 targets, 1 lun per target
4K request size, 50% randread + 50% randwrite: 181K/181k IOPS
Signed-off-by: Asias He <asias@redhat.com>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
2013-02-06 13:20:59 +08:00
|
|
|
#include <linux/bitmap.h>
|
2012-07-18 14:31:32 -07:00
|
|
|
|
|
|
|
#include "vhost.h"
|
2013-05-02 03:50:34 +03:00
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
#define VHOST_SCSI_VERSION "v0.1"
|
|
|
|
#define VHOST_SCSI_NAMELEN 256
|
|
|
|
#define VHOST_SCSI_MAX_CDB_SIZE 32
|
|
|
|
#define VHOST_SCSI_PREALLOC_SGLS 2048
|
|
|
|
#define VHOST_SCSI_PREALLOC_UPAGES 2048
|
2018-08-08 13:29:55 -06:00
|
|
|
#define VHOST_SCSI_PREALLOC_PROT_SGLS 2048
|
2024-12-03 13:15:15 -06:00
|
|
|
/*
|
|
|
|
* For the legacy descriptor case we allocate an iov per byte in the
|
|
|
|
* virtio_scsi_cmd_resp struct.
|
|
|
|
*/
|
|
|
|
#define VHOST_SCSI_MAX_RESP_IOVS sizeof(struct virtio_scsi_cmd_resp)
|
2013-05-02 03:50:34 +03:00
|
|
|
|
2024-12-03 13:15:12 -06:00
|
|
|
static unsigned int vhost_scsi_inline_sg_cnt = VHOST_SCSI_PREALLOC_SGLS;
|
|
|
|
|
|
|
|
#ifdef CONFIG_ARCH_NO_SG_CHAIN
|
|
|
|
static int vhost_scsi_set_inline_sg_cnt(const char *buf,
|
|
|
|
const struct kernel_param *kp)
|
|
|
|
{
|
|
|
|
pr_err("Setting inline_sg_cnt is not supported.\n");
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
static int vhost_scsi_set_inline_sg_cnt(const char *buf,
|
|
|
|
const struct kernel_param *kp)
|
|
|
|
{
|
|
|
|
unsigned int cnt;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = kstrtouint(buf, 10, &cnt);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2025-06-28 11:33:53 -07:00
|
|
|
if (cnt > VHOST_SCSI_PREALLOC_SGLS) {
|
2024-12-03 13:15:12 -06:00
|
|
|
pr_err("Max inline_sg_cnt is %u\n", VHOST_SCSI_PREALLOC_SGLS);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
vhost_scsi_inline_sg_cnt = cnt;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static int vhost_scsi_get_inline_sg_cnt(char *buf,
|
|
|
|
const struct kernel_param *kp)
|
|
|
|
{
|
|
|
|
return sprintf(buf, "%u\n", vhost_scsi_inline_sg_cnt);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct kernel_param_ops vhost_scsi_inline_sg_cnt_op = {
|
|
|
|
.get = vhost_scsi_get_inline_sg_cnt,
|
|
|
|
.set = vhost_scsi_set_inline_sg_cnt,
|
|
|
|
};
|
|
|
|
|
|
|
|
module_param_cb(inline_sg_cnt, &vhost_scsi_inline_sg_cnt_op, NULL, 0644);
|
|
|
|
MODULE_PARM_DESC(inline_sg_cnt, "Set the number of scatterlist entries to pre-allocate. The default is 2048.");
|
|
|
|
|
2019-05-17 00:29:49 -04:00
|
|
|
/* Max number of requests before requeueing the job.
|
|
|
|
* Using this limit prevents one virtqueue from starving others with
|
|
|
|
* request.
|
|
|
|
*/
|
|
|
|
#define VHOST_SCSI_WEIGHT 256
|
|
|
|
|
2013-05-02 03:50:34 +03:00
|
|
|
struct vhost_scsi_inflight {
|
|
|
|
/* Wait for the flush operation to finish */
|
|
|
|
struct completion comp;
|
|
|
|
/* Refcount for the inflight reqs */
|
|
|
|
struct kref kref;
|
|
|
|
};
|
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
struct vhost_scsi_cmd {
|
2013-05-02 03:50:34 +03:00
|
|
|
/* Descriptor from vhost_get_vq_desc() for virt_queue segment */
|
|
|
|
int tvc_vq_desc;
|
|
|
|
/* The number of scatterlists associated with this cmd */
|
|
|
|
u32 tvc_sgl_count;
|
2014-02-22 18:34:43 -08:00
|
|
|
u32 tvc_prot_sgl_count;
|
2023-07-09 15:28:58 -05:00
|
|
|
u32 copied_iov:1;
|
2024-12-03 13:15:14 -06:00
|
|
|
const void *read_iov;
|
|
|
|
struct iov_iter *read_iter;
|
2024-12-03 13:15:12 -06:00
|
|
|
struct scatterlist *sgl;
|
|
|
|
struct sg_table table;
|
|
|
|
struct scatterlist *prot_sgl;
|
|
|
|
struct sg_table prot_table;
|
2024-12-03 13:15:15 -06:00
|
|
|
/* Fast path response header iovec used when only one vec is needed */
|
|
|
|
struct iovec tvc_resp_iov;
|
|
|
|
/* Number of iovs for response */
|
|
|
|
unsigned int tvc_resp_iovs_cnt;
|
|
|
|
/* Pointer to response header iovecs if more than one is needed */
|
|
|
|
struct iovec *tvc_resp_iovs;
|
2013-05-02 03:50:34 +03:00
|
|
|
/* Pointer to vhost_virtqueue for the cmd */
|
|
|
|
struct vhost_virtqueue *tvc_vq;
|
|
|
|
/* The TCM I/O descriptor that is accessed via container_of() */
|
|
|
|
struct se_cmd tvc_se_cmd;
|
|
|
|
/* Sense buffer that will be mapped into outgoing status */
|
|
|
|
unsigned char tvc_sense_buf[TRANSPORT_SENSE_BUFFER];
|
2025-04-02 23:29:51 -07:00
|
|
|
/*
|
|
|
|
* Dirty write descriptors of this command.
|
|
|
|
*/
|
|
|
|
struct vhost_log *tvc_log;
|
|
|
|
unsigned int tvc_log_num;
|
2013-05-02 03:50:34 +03:00
|
|
|
/* Completed commands list, serviced from vhost worker thread */
|
|
|
|
struct llist_node tvc_completion_list;
|
|
|
|
/* Used to track inflight cmd */
|
|
|
|
struct vhost_scsi_inflight *inflight;
|
|
|
|
};
|
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
struct vhost_scsi_nexus {
|
2013-05-02 03:50:34 +03:00
|
|
|
/* Pointer to TCM session for I_T Nexus */
|
|
|
|
struct se_session *tvn_se_sess;
|
|
|
|
};
|
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
struct vhost_scsi_tpg {
|
2013-05-02 03:50:34 +03:00
|
|
|
/* Vhost port target portal group tag for TCM */
|
|
|
|
u16 tport_tpgt;
|
2025-06-11 07:39:21 -07:00
|
|
|
/* Used to track number of TPG Port/Lun Links wrt to explicit I_T Nexus shutdown */
|
2013-05-02 03:50:34 +03:00
|
|
|
int tv_tpg_port_count;
|
|
|
|
/* Used for vhost_scsi device reference to tpg_nexus, protected by tv_tpg_mutex */
|
|
|
|
int tv_tpg_vhost_count;
|
2015-03-28 00:03:51 -07:00
|
|
|
/* Used for enabling T10-PI with legacy devices */
|
|
|
|
int tv_fabric_prot_type;
|
2015-01-31 23:56:53 -08:00
|
|
|
/* list for vhost_scsi_list */
|
2013-05-02 03:50:34 +03:00
|
|
|
struct list_head tv_tpg_list;
|
|
|
|
/* Used to protect access for tpg_nexus */
|
|
|
|
struct mutex tv_tpg_mutex;
|
|
|
|
/* Pointer to the TCM VHost I_T Nexus for this TPG endpoint */
|
2015-01-31 23:56:53 -08:00
|
|
|
struct vhost_scsi_nexus *tpg_nexus;
|
|
|
|
/* Pointer back to vhost_scsi_tport */
|
|
|
|
struct vhost_scsi_tport *tport;
|
|
|
|
/* Returned by vhost_scsi_make_tpg() */
|
2013-05-02 03:50:34 +03:00
|
|
|
struct se_portal_group se_tpg;
|
|
|
|
/* Pointer back to vhost_scsi, protected by tv_tpg_mutex */
|
|
|
|
struct vhost_scsi *vhost_scsi;
|
|
|
|
};
|
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
struct vhost_scsi_tport {
|
2013-05-02 03:50:34 +03:00
|
|
|
/* SCSI protocol the tport is providing */
|
|
|
|
u8 tport_proto_id;
|
|
|
|
/* Binary World Wide unique Port Name for Vhost Target port */
|
|
|
|
u64 tport_wwpn;
|
|
|
|
/* ASCII formatted WWPN for Vhost Target port */
|
2015-01-31 23:56:53 -08:00
|
|
|
char tport_name[VHOST_SCSI_NAMELEN];
|
|
|
|
/* Returned by vhost_scsi_make_tport() */
|
2013-05-02 03:50:34 +03:00
|
|
|
struct se_wwn tport_wwn;
|
|
|
|
};
|
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
struct vhost_scsi_evt {
|
2013-05-02 03:50:34 +03:00
|
|
|
/* event to be sent to guest */
|
|
|
|
struct virtio_scsi_event event;
|
|
|
|
/* event list, serviced from vhost worker thread */
|
|
|
|
struct llist_node list;
|
|
|
|
};
|
2012-07-18 14:31:32 -07:00
|
|
|
|
2012-07-30 13:30:00 -07:00
|
|
|
enum {
|
|
|
|
VHOST_SCSI_VQ_CTL = 0,
|
|
|
|
VHOST_SCSI_VQ_EVT = 1,
|
|
|
|
VHOST_SCSI_VQ_IO = 2,
|
|
|
|
};
|
|
|
|
|
2014-11-23 18:01:34 +02:00
|
|
|
/* Note: can't set VIRTIO_F_VERSION_1 yet, since that implies ANY_LAYOUT. */
|
2013-03-27 17:23:41 -07:00
|
|
|
enum {
|
2014-02-22 18:22:31 -08:00
|
|
|
VHOST_SCSI_FEATURES = VHOST_FEATURES | (1ULL << VIRTIO_SCSI_F_HOTPLUG) |
|
2015-09-09 22:24:56 +03:00
|
|
|
(1ULL << VIRTIO_SCSI_F_T10_PI)
|
2013-03-27 17:23:41 -07:00
|
|
|
};
|
|
|
|
|
tcm_vhost: Multi-queue support
This adds virtio-scsi multi-queue support to tcm_vhost. In order to use
multi-queue, guest side multi-queue support is need. It can
be found here:
https://lkml.org/lkml/2012/12/18/166
Currently, only one thread is created by vhost core code for each
vhost_scsi instance. Even if there are multi-queues, all the handling of
guest kick (vhost_scsi_handle_kick) are processed in one thread. This is
not optimal. Luckily, most of the work is offloaded to the tcm_vhost
workqueue.
Some initial perf numbers:
1 queue, 4 targets, 1 lun per target
4K request size, 50% randread + 50% randwrite: 127K/127k IOPS
4 queues, 4 targets, 1 lun per target
4K request size, 50% randread + 50% randwrite: 181K/181k IOPS
Signed-off-by: Asias He <asias@redhat.com>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
2013-02-06 13:20:59 +08:00
|
|
|
#define VHOST_SCSI_MAX_TARGET 256
|
2022-07-07 22:05:25 -05:00
|
|
|
#define VHOST_SCSI_MAX_IO_VQ 1024
|
2013-04-25 15:35:21 +08:00
|
|
|
#define VHOST_SCSI_MAX_EVENT 128
|
2013-02-05 12:31:57 +08:00
|
|
|
|
2022-07-07 22:05:25 -05:00
|
|
|
static unsigned vhost_scsi_max_io_vqs = 128;
|
|
|
|
module_param_named(max_io_vqs, vhost_scsi_max_io_vqs, uint, 0644);
|
|
|
|
MODULE_PARM_DESC(max_io_vqs, "Set the max number of IO virtqueues a vhost scsi device can support. The default is 128. The max is 1024.");
|
|
|
|
|
2013-04-27 11:16:48 +08:00
|
|
|
struct vhost_scsi_virtqueue {
|
|
|
|
struct vhost_virtqueue vq;
|
2023-06-26 18:22:59 -05:00
|
|
|
struct vhost_scsi *vs;
|
2013-04-28 15:38:52 +03:00
|
|
|
/*
|
|
|
|
* Reference counting for inflight reqs, used for flush operation. At
|
|
|
|
* each time, one reference tracks new commands submitted, while we
|
|
|
|
* wait for another one to reach 0.
|
|
|
|
*/
|
2013-04-27 11:16:49 +08:00
|
|
|
struct vhost_scsi_inflight inflights[2];
|
2013-04-28 15:38:52 +03:00
|
|
|
/*
|
|
|
|
* Indicate current inflight in use, protected by vq->mutex.
|
|
|
|
* Writers must also take dev mutex and flush under it.
|
|
|
|
*/
|
2013-04-27 11:16:49 +08:00
|
|
|
int inflight_idx;
|
2020-11-09 23:33:20 -06:00
|
|
|
struct vhost_scsi_cmd *scsi_cmds;
|
|
|
|
struct sbitmap scsi_tags;
|
|
|
|
int max_cmds;
|
2024-12-03 13:15:08 -06:00
|
|
|
struct page **upages;
|
2023-06-26 18:22:59 -05:00
|
|
|
|
|
|
|
struct vhost_work completion_work;
|
|
|
|
struct llist_head completion_list;
|
2013-04-27 11:16:48 +08:00
|
|
|
};
|
|
|
|
|
2012-07-18 14:31:32 -07:00
|
|
|
struct vhost_scsi {
|
2013-02-05 12:31:57 +08:00
|
|
|
/* Protected by vhost_scsi->dev.mutex */
|
2015-01-31 23:56:53 -08:00
|
|
|
struct vhost_scsi_tpg **vs_tpg;
|
2013-02-05 12:31:57 +08:00
|
|
|
char vs_vhost_wwpn[TRANSPORT_IQN_LEN];
|
|
|
|
|
2012-07-18 14:31:32 -07:00
|
|
|
struct vhost_dev dev;
|
2022-07-07 22:05:25 -05:00
|
|
|
struct vhost_scsi_virtqueue *vqs;
|
|
|
|
struct vhost_scsi_inflight **old_inflight;
|
2012-07-18 14:31:32 -07:00
|
|
|
|
2013-04-25 15:35:21 +08:00
|
|
|
struct vhost_work vs_event_work; /* evt injection work item */
|
|
|
|
struct llist_head vs_event_list; /* evt injection queue */
|
|
|
|
|
|
|
|
bool vs_events_missed; /* any missed events, protected by vq->mutex */
|
|
|
|
int vs_events_nr; /* num of pending events, protected by vq->mutex */
|
2024-12-03 13:15:12 -06:00
|
|
|
|
|
|
|
unsigned int inline_sg_cnt;
|
2012-07-18 14:31:32 -07:00
|
|
|
};
|
|
|
|
|
2020-11-09 23:33:23 -06:00
|
|
|
struct vhost_scsi_tmf {
|
|
|
|
struct vhost_work vwork;
|
2024-03-15 19:47:01 -05:00
|
|
|
struct work_struct flush_work;
|
2020-11-09 23:33:23 -06:00
|
|
|
struct vhost_scsi *vhost;
|
|
|
|
struct vhost_scsi_virtqueue *svq;
|
|
|
|
|
|
|
|
struct se_cmd se_cmd;
|
2020-11-20 09:50:59 -06:00
|
|
|
u8 scsi_resp;
|
2020-11-09 23:33:23 -06:00
|
|
|
struct vhost_scsi_inflight *inflight;
|
|
|
|
struct iovec resp_iov;
|
|
|
|
int in_iovs;
|
|
|
|
int vq_desc;
|
2025-04-02 23:29:52 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Dirty write descriptors of this command.
|
|
|
|
*/
|
|
|
|
struct vhost_log *tmf_log;
|
|
|
|
unsigned int tmf_log_num;
|
2020-11-09 23:33:23 -06:00
|
|
|
};
|
|
|
|
|
2018-09-17 17:09:48 -07:00
|
|
|
/*
|
|
|
|
* Context for processing request and control queue operations.
|
|
|
|
*/
|
|
|
|
struct vhost_scsi_ctx {
|
|
|
|
int head;
|
|
|
|
unsigned int out, in;
|
|
|
|
size_t req_size, rsp_size;
|
|
|
|
size_t out_size, in_size;
|
|
|
|
u8 *target, *lunp;
|
|
|
|
void *req;
|
|
|
|
struct iov_iter out_iter;
|
|
|
|
};
|
|
|
|
|
2023-03-20 21:06:24 -05:00
|
|
|
/*
|
|
|
|
* Global mutex to protect vhost_scsi TPG list for vhost IOCTLs and LIO
|
|
|
|
* configfs management operations.
|
|
|
|
*/
|
2015-01-31 23:56:53 -08:00
|
|
|
static DEFINE_MUTEX(vhost_scsi_mutex);
|
|
|
|
static LIST_HEAD(vhost_scsi_list);
|
2012-07-18 14:31:32 -07:00
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
static void vhost_scsi_done_inflight(struct kref *kref)
|
2013-04-27 11:16:49 +08:00
|
|
|
{
|
|
|
|
struct vhost_scsi_inflight *inflight;
|
|
|
|
|
|
|
|
inflight = container_of(kref, struct vhost_scsi_inflight, kref);
|
|
|
|
complete(&inflight->comp);
|
|
|
|
}
|
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
static void vhost_scsi_init_inflight(struct vhost_scsi *vs,
|
2013-04-27 11:16:49 +08:00
|
|
|
struct vhost_scsi_inflight *old_inflight[])
|
|
|
|
{
|
|
|
|
struct vhost_scsi_inflight *new_inflight;
|
|
|
|
struct vhost_virtqueue *vq;
|
|
|
|
int idx, i;
|
|
|
|
|
2022-07-07 22:05:25 -05:00
|
|
|
for (i = 0; i < vs->dev.nvqs; i++) {
|
2013-04-27 11:16:49 +08:00
|
|
|
vq = &vs->vqs[i].vq;
|
|
|
|
|
|
|
|
mutex_lock(&vq->mutex);
|
|
|
|
|
2025-06-11 07:39:21 -07:00
|
|
|
/* store old inflight */
|
2013-04-27 11:16:49 +08:00
|
|
|
idx = vs->vqs[i].inflight_idx;
|
|
|
|
if (old_inflight)
|
|
|
|
old_inflight[i] = &vs->vqs[i].inflights[idx];
|
|
|
|
|
2025-06-11 07:39:21 -07:00
|
|
|
/* setup new inflight */
|
2013-04-27 11:16:49 +08:00
|
|
|
vs->vqs[i].inflight_idx = idx ^ 1;
|
|
|
|
new_inflight = &vs->vqs[i].inflights[idx ^ 1];
|
|
|
|
kref_init(&new_inflight->kref);
|
|
|
|
init_completion(&new_inflight->comp);
|
|
|
|
|
|
|
|
mutex_unlock(&vq->mutex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct vhost_scsi_inflight *
|
2015-01-31 23:56:53 -08:00
|
|
|
vhost_scsi_get_inflight(struct vhost_virtqueue *vq)
|
2013-04-27 11:16:49 +08:00
|
|
|
{
|
|
|
|
struct vhost_scsi_inflight *inflight;
|
|
|
|
struct vhost_scsi_virtqueue *svq;
|
|
|
|
|
|
|
|
svq = container_of(vq, struct vhost_scsi_virtqueue, vq);
|
|
|
|
inflight = &svq->inflights[svq->inflight_idx];
|
|
|
|
kref_get(&inflight->kref);
|
|
|
|
|
|
|
|
return inflight;
|
|
|
|
}
|
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
static void vhost_scsi_put_inflight(struct vhost_scsi_inflight *inflight)
|
2013-04-27 11:16:49 +08:00
|
|
|
{
|
2015-01-31 23:56:53 -08:00
|
|
|
kref_put(&inflight->kref, vhost_scsi_done_inflight);
|
2013-04-27 11:16:49 +08:00
|
|
|
}
|
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
static int vhost_scsi_check_true(struct se_portal_group *se_tpg)
|
2012-07-18 14:31:32 -07:00
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
static char *vhost_scsi_get_fabric_wwn(struct se_portal_group *se_tpg)
|
2012-07-18 14:31:32 -07:00
|
|
|
{
|
2015-01-31 23:56:53 -08:00
|
|
|
struct vhost_scsi_tpg *tpg = container_of(se_tpg,
|
|
|
|
struct vhost_scsi_tpg, se_tpg);
|
|
|
|
struct vhost_scsi_tport *tport = tpg->tport;
|
2012-07-18 14:31:32 -07:00
|
|
|
|
|
|
|
return &tport->tport_name[0];
|
|
|
|
}
|
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
static u16 vhost_scsi_get_tpgt(struct se_portal_group *se_tpg)
|
2012-07-18 14:31:32 -07:00
|
|
|
{
|
2015-01-31 23:56:53 -08:00
|
|
|
struct vhost_scsi_tpg *tpg = container_of(se_tpg,
|
|
|
|
struct vhost_scsi_tpg, se_tpg);
|
2012-07-18 14:31:32 -07:00
|
|
|
return tpg->tport_tpgt;
|
|
|
|
}
|
|
|
|
|
2015-03-28 00:03:51 -07:00
|
|
|
static int vhost_scsi_check_prot_fabric_only(struct se_portal_group *se_tpg)
|
|
|
|
{
|
|
|
|
struct vhost_scsi_tpg *tpg = container_of(se_tpg,
|
|
|
|
struct vhost_scsi_tpg, se_tpg);
|
|
|
|
|
|
|
|
return tpg->tv_fabric_prot_type;
|
|
|
|
}
|
|
|
|
|
2025-04-02 23:29:51 -07:00
|
|
|
static int vhost_scsi_copy_cmd_log(struct vhost_virtqueue *vq,
|
|
|
|
struct vhost_scsi_cmd *cmd,
|
|
|
|
struct vhost_log *log,
|
|
|
|
unsigned int log_num)
|
|
|
|
{
|
|
|
|
if (!cmd->tvc_log)
|
|
|
|
cmd->tvc_log = kmalloc_array(vq->dev->iov_limit,
|
|
|
|
sizeof(*cmd->tvc_log),
|
|
|
|
GFP_KERNEL);
|
|
|
|
|
|
|
|
if (unlikely(!cmd->tvc_log)) {
|
|
|
|
vq_err(vq, "Failed to alloc tvc_log\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(cmd->tvc_log, log, sizeof(*cmd->tvc_log) * log_num);
|
|
|
|
cmd->tvc_log_num = log_num;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void vhost_scsi_log_write(struct vhost_virtqueue *vq,
|
|
|
|
struct vhost_log *log,
|
|
|
|
unsigned int log_num)
|
|
|
|
{
|
|
|
|
if (likely(!vhost_has_feature(vq, VHOST_F_LOG_ALL)))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (likely(!log_num || !log))
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* vhost-scsi doesn't support VIRTIO_F_ACCESS_PLATFORM.
|
|
|
|
* No requirement for vq->iotlb case.
|
|
|
|
*/
|
|
|
|
WARN_ON_ONCE(unlikely(vq->iotlb));
|
|
|
|
vhost_log_write(vq, log, log_num, U64_MAX, NULL, 0);
|
|
|
|
}
|
|
|
|
|
2020-11-09 23:33:21 -06:00
|
|
|
static void vhost_scsi_release_cmd_res(struct se_cmd *se_cmd)
|
2012-07-18 14:31:32 -07:00
|
|
|
{
|
2015-01-31 23:56:53 -08:00
|
|
|
struct vhost_scsi_cmd *tv_cmd = container_of(se_cmd,
|
|
|
|
struct vhost_scsi_cmd, tvc_se_cmd);
|
2020-11-09 23:33:20 -06:00
|
|
|
struct vhost_scsi_virtqueue *svq = container_of(tv_cmd->tvc_vq,
|
|
|
|
struct vhost_scsi_virtqueue, vq);
|
2024-12-03 13:15:13 -06:00
|
|
|
struct vhost_scsi *vs = svq->vs;
|
2020-11-09 23:33:20 -06:00
|
|
|
struct vhost_scsi_inflight *inflight = tv_cmd->inflight;
|
2024-12-03 13:15:12 -06:00
|
|
|
struct scatterlist *sg;
|
|
|
|
struct page *page;
|
2014-02-22 18:34:43 -08:00
|
|
|
int i;
|
2013-06-06 02:20:41 -07:00
|
|
|
|
|
|
|
if (tv_cmd->tvc_sgl_count) {
|
2024-12-03 13:15:12 -06:00
|
|
|
for_each_sgtable_sg(&tv_cmd->table, sg, i) {
|
|
|
|
page = sg_page(sg);
|
|
|
|
if (!page)
|
|
|
|
continue;
|
|
|
|
|
2023-07-09 15:28:58 -05:00
|
|
|
if (tv_cmd->copied_iov)
|
2024-12-03 13:15:12 -06:00
|
|
|
__free_page(page);
|
2023-07-09 15:28:58 -05:00
|
|
|
else
|
2024-12-03 13:15:12 -06:00
|
|
|
put_page(page);
|
2023-07-09 15:28:58 -05:00
|
|
|
}
|
2024-12-03 13:15:14 -06:00
|
|
|
kfree(tv_cmd->read_iter);
|
|
|
|
kfree(tv_cmd->read_iov);
|
2024-12-03 13:15:12 -06:00
|
|
|
sg_free_table_chained(&tv_cmd->table, vs->inline_sg_cnt);
|
2013-09-17 22:54:31 +03:00
|
|
|
}
|
2014-02-22 18:34:43 -08:00
|
|
|
if (tv_cmd->tvc_prot_sgl_count) {
|
2024-12-03 13:15:12 -06:00
|
|
|
for_each_sgtable_sg(&tv_cmd->prot_table, sg, i) {
|
|
|
|
page = sg_page(sg);
|
|
|
|
if (page)
|
|
|
|
put_page(page);
|
|
|
|
}
|
|
|
|
sg_free_table_chained(&tv_cmd->prot_table, vs->inline_sg_cnt);
|
2014-02-22 18:34:43 -08:00
|
|
|
}
|
2013-06-06 02:20:41 -07:00
|
|
|
|
2024-12-03 13:15:15 -06:00
|
|
|
if (tv_cmd->tvc_resp_iovs != &tv_cmd->tvc_resp_iov)
|
|
|
|
kfree(tv_cmd->tvc_resp_iovs);
|
2020-11-09 23:33:20 -06:00
|
|
|
sbitmap_clear_bit(&svq->scsi_tags, se_cmd->map_tag);
|
|
|
|
vhost_scsi_put_inflight(inflight);
|
2012-07-18 14:31:32 -07:00
|
|
|
}
|
|
|
|
|
2020-11-09 23:33:23 -06:00
|
|
|
static void vhost_scsi_release_tmf_res(struct vhost_scsi_tmf *tmf)
|
|
|
|
{
|
|
|
|
struct vhost_scsi_inflight *inflight = tmf->inflight;
|
|
|
|
|
2025-04-02 23:29:52 -07:00
|
|
|
/*
|
|
|
|
* tmf->tmf_log is default NULL unless VHOST_F_LOG_ALL is set.
|
|
|
|
*/
|
|
|
|
kfree(tmf->tmf_log);
|
2023-03-20 21:06:19 -05:00
|
|
|
kfree(tmf);
|
2020-11-09 23:33:23 -06:00
|
|
|
vhost_scsi_put_inflight(inflight);
|
|
|
|
}
|
|
|
|
|
2024-03-15 19:47:00 -05:00
|
|
|
static void vhost_scsi_drop_cmds(struct vhost_scsi_virtqueue *svq)
|
|
|
|
{
|
|
|
|
struct vhost_scsi_cmd *cmd, *t;
|
|
|
|
struct llist_node *llnode;
|
|
|
|
|
|
|
|
llnode = llist_del_all(&svq->completion_list);
|
|
|
|
llist_for_each_entry_safe(cmd, t, llnode, tvc_completion_list)
|
|
|
|
vhost_scsi_release_cmd_res(&cmd->tvc_se_cmd);
|
|
|
|
}
|
|
|
|
|
2020-11-09 23:33:21 -06:00
|
|
|
static void vhost_scsi_release_cmd(struct se_cmd *se_cmd)
|
|
|
|
{
|
2020-11-09 23:33:23 -06:00
|
|
|
if (se_cmd->se_cmd_flags & SCF_SCSI_TMR_CDB) {
|
|
|
|
struct vhost_scsi_tmf *tmf = container_of(se_cmd,
|
|
|
|
struct vhost_scsi_tmf, se_cmd);
|
|
|
|
|
2024-03-15 19:47:01 -05:00
|
|
|
schedule_work(&tmf->flush_work);
|
2020-11-09 23:33:23 -06:00
|
|
|
} else {
|
|
|
|
struct vhost_scsi_cmd *cmd = container_of(se_cmd,
|
2020-11-09 23:33:21 -06:00
|
|
|
struct vhost_scsi_cmd, tvc_se_cmd);
|
2023-06-26 18:22:59 -05:00
|
|
|
struct vhost_scsi_virtqueue *svq = container_of(cmd->tvc_vq,
|
|
|
|
struct vhost_scsi_virtqueue, vq);
|
2020-11-09 23:33:21 -06:00
|
|
|
|
2023-06-26 18:22:59 -05:00
|
|
|
llist_add(&cmd->tvc_completion_list, &svq->completion_list);
|
2024-03-15 19:47:00 -05:00
|
|
|
if (!vhost_vq_work_queue(&svq->vq, &svq->completion_work))
|
|
|
|
vhost_scsi_drop_cmds(svq);
|
2020-11-09 23:33:23 -06:00
|
|
|
}
|
2020-11-09 23:33:21 -06:00
|
|
|
}
|
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
static int vhost_scsi_write_pending(struct se_cmd *se_cmd)
|
2012-07-18 14:31:32 -07:00
|
|
|
{
|
|
|
|
/* Go ahead and process the write immediately */
|
|
|
|
target_execute_cmd(se_cmd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
static int vhost_scsi_queue_data_in(struct se_cmd *se_cmd)
|
2012-07-18 14:31:32 -07:00
|
|
|
{
|
2020-11-09 23:33:21 -06:00
|
|
|
transport_generic_free_cmd(se_cmd, 0);
|
2012-07-18 14:31:32 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
static int vhost_scsi_queue_status(struct se_cmd *se_cmd)
|
2012-07-18 14:31:32 -07:00
|
|
|
{
|
2020-11-09 23:33:21 -06:00
|
|
|
transport_generic_free_cmd(se_cmd, 0);
|
2012-07-18 14:31:32 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
static void vhost_scsi_queue_tm_rsp(struct se_cmd *se_cmd)
|
2012-07-18 14:31:32 -07:00
|
|
|
{
|
2020-11-09 23:33:23 -06:00
|
|
|
struct vhost_scsi_tmf *tmf = container_of(se_cmd, struct vhost_scsi_tmf,
|
|
|
|
se_cmd);
|
|
|
|
|
2020-11-20 09:50:59 -06:00
|
|
|
tmf->scsi_resp = se_cmd->se_tmr_req->response;
|
2020-11-09 23:33:23 -06:00
|
|
|
transport_generic_free_cmd(&tmf->se_cmd, 0);
|
2012-07-18 14:31:32 -07:00
|
|
|
}
|
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
static void vhost_scsi_aborted_task(struct se_cmd *se_cmd)
|
2014-03-22 14:55:56 -07:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
static void vhost_scsi_free_evt(struct vhost_scsi *vs, struct vhost_scsi_evt *evt)
|
2013-04-25 15:35:21 +08:00
|
|
|
{
|
|
|
|
vs->vs_events_nr--;
|
|
|
|
kfree(evt);
|
|
|
|
}
|
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
static struct vhost_scsi_evt *
|
|
|
|
vhost_scsi_allocate_evt(struct vhost_scsi *vs,
|
2013-05-06 16:38:27 +08:00
|
|
|
u32 event, u32 reason)
|
2013-04-25 15:35:21 +08:00
|
|
|
{
|
2013-04-27 11:16:48 +08:00
|
|
|
struct vhost_virtqueue *vq = &vs->vqs[VHOST_SCSI_VQ_EVT].vq;
|
2015-01-31 23:56:53 -08:00
|
|
|
struct vhost_scsi_evt *evt;
|
2013-04-25 15:35:21 +08:00
|
|
|
|
|
|
|
if (vs->vs_events_nr > VHOST_SCSI_MAX_EVENT) {
|
|
|
|
vs->vs_events_missed = true;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
evt = kzalloc(sizeof(*evt), GFP_KERNEL);
|
|
|
|
if (!evt) {
|
2015-01-31 23:56:53 -08:00
|
|
|
vq_err(vq, "Failed to allocate vhost_scsi_evt\n");
|
2013-04-25 15:35:21 +08:00
|
|
|
vs->vs_events_missed = true;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-11-23 18:01:34 +02:00
|
|
|
evt->event.event = cpu_to_vhost32(vq, event);
|
|
|
|
evt->event.reason = cpu_to_vhost32(vq, reason);
|
2013-04-25 15:35:21 +08:00
|
|
|
vs->vs_events_nr++;
|
|
|
|
|
|
|
|
return evt;
|
|
|
|
}
|
|
|
|
|
2013-06-06 02:20:41 -07:00
|
|
|
static int vhost_scsi_check_stop_free(struct se_cmd *se_cmd)
|
|
|
|
{
|
2015-04-27 13:52:36 +02:00
|
|
|
return target_put_sess_cmd(se_cmd);
|
2012-07-18 14:31:32 -07:00
|
|
|
}
|
|
|
|
|
2013-05-06 16:38:27 +08:00
|
|
|
static void
|
2015-01-31 23:56:53 -08:00
|
|
|
vhost_scsi_do_evt_work(struct vhost_scsi *vs, struct vhost_scsi_evt *evt)
|
2013-04-25 15:35:21 +08:00
|
|
|
{
|
2013-04-27 11:16:48 +08:00
|
|
|
struct vhost_virtqueue *vq = &vs->vqs[VHOST_SCSI_VQ_EVT].vq;
|
2013-04-25 15:35:21 +08:00
|
|
|
struct virtio_scsi_event *event = &evt->event;
|
|
|
|
struct virtio_scsi_event __user *eventp;
|
2025-04-02 23:29:53 -07:00
|
|
|
struct vhost_log *vq_log;
|
|
|
|
unsigned int log_num;
|
2013-04-25 15:35:21 +08:00
|
|
|
unsigned out, in;
|
|
|
|
int head, ret;
|
|
|
|
|
2020-03-31 21:27:57 +02:00
|
|
|
if (!vhost_vq_get_backend(vq)) {
|
2013-04-25 15:35:21 +08:00
|
|
|
vs->vs_events_missed = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
again:
|
|
|
|
vhost_disable_notify(&vs->dev, vq);
|
2025-04-02 23:29:53 -07:00
|
|
|
|
|
|
|
vq_log = unlikely(vhost_has_feature(vq, VHOST_F_LOG_ALL)) ?
|
|
|
|
vq->log : NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reset 'log_num' since vhost_get_vq_desc() may reset it only
|
|
|
|
* after certain condition checks.
|
|
|
|
*/
|
|
|
|
log_num = 0;
|
|
|
|
|
2014-06-05 15:20:27 +03:00
|
|
|
head = vhost_get_vq_desc(vq, vq->iov,
|
2013-04-25 15:35:21 +08:00
|
|
|
ARRAY_SIZE(vq->iov), &out, &in,
|
2025-04-02 23:29:53 -07:00
|
|
|
vq_log, &log_num);
|
2013-04-25 15:35:21 +08:00
|
|
|
if (head < 0) {
|
|
|
|
vs->vs_events_missed = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (head == vq->num) {
|
|
|
|
if (vhost_enable_notify(&vs->dev, vq))
|
|
|
|
goto again;
|
|
|
|
vs->vs_events_missed = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((vq->iov[out].iov_len != sizeof(struct virtio_scsi_event))) {
|
|
|
|
vq_err(vq, "Expecting virtio_scsi_event, got %zu bytes\n",
|
|
|
|
vq->iov[out].iov_len);
|
|
|
|
vs->vs_events_missed = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vs->vs_events_missed) {
|
2014-11-23 18:01:34 +02:00
|
|
|
event->event |= cpu_to_vhost32(vq, VIRTIO_SCSI_T_EVENTS_MISSED);
|
2013-04-25 15:35:21 +08:00
|
|
|
vs->vs_events_missed = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
eventp = vq->iov[out].iov_base;
|
|
|
|
ret = __copy_to_user(eventp, event, sizeof(*event));
|
|
|
|
if (!ret)
|
|
|
|
vhost_add_used_and_signal(&vs->dev, vq, head, 0);
|
|
|
|
else
|
2015-01-31 23:56:53 -08:00
|
|
|
vq_err(vq, "Faulted on vhost_scsi_send_event\n");
|
2025-04-02 23:29:53 -07:00
|
|
|
|
|
|
|
vhost_scsi_log_write(vq, vq_log, log_num);
|
2013-04-25 15:35:21 +08:00
|
|
|
}
|
|
|
|
|
2024-03-15 19:46:59 -05:00
|
|
|
static void vhost_scsi_complete_events(struct vhost_scsi *vs, bool drop)
|
2013-04-25 15:35:21 +08:00
|
|
|
{
|
2013-04-27 11:16:48 +08:00
|
|
|
struct vhost_virtqueue *vq = &vs->vqs[VHOST_SCSI_VQ_EVT].vq;
|
2017-05-12 09:42:56 +09:00
|
|
|
struct vhost_scsi_evt *evt, *t;
|
2013-04-25 15:35:21 +08:00
|
|
|
struct llist_node *llnode;
|
|
|
|
|
|
|
|
mutex_lock(&vq->mutex);
|
|
|
|
llnode = llist_del_all(&vs->vs_event_list);
|
2017-05-12 09:42:56 +09:00
|
|
|
llist_for_each_entry_safe(evt, t, llnode, list) {
|
2024-03-15 19:46:59 -05:00
|
|
|
if (!drop)
|
|
|
|
vhost_scsi_do_evt_work(vs, evt);
|
2015-01-31 23:56:53 -08:00
|
|
|
vhost_scsi_free_evt(vs, evt);
|
2013-04-25 15:35:21 +08:00
|
|
|
}
|
|
|
|
mutex_unlock(&vq->mutex);
|
|
|
|
}
|
|
|
|
|
2024-03-15 19:46:59 -05:00
|
|
|
static void vhost_scsi_evt_work(struct vhost_work *work)
|
|
|
|
{
|
|
|
|
struct vhost_scsi *vs = container_of(work, struct vhost_scsi,
|
|
|
|
vs_event_work);
|
|
|
|
vhost_scsi_complete_events(vs, false);
|
|
|
|
}
|
|
|
|
|
2023-07-09 15:28:58 -05:00
|
|
|
static int vhost_scsi_copy_sgl_to_iov(struct vhost_scsi_cmd *cmd)
|
|
|
|
{
|
2024-12-03 13:15:14 -06:00
|
|
|
struct iov_iter *iter = cmd->read_iter;
|
2024-12-03 13:15:12 -06:00
|
|
|
struct scatterlist *sg;
|
2023-07-09 15:28:58 -05:00
|
|
|
struct page *page;
|
|
|
|
size_t len;
|
|
|
|
int i;
|
|
|
|
|
2024-12-03 13:15:12 -06:00
|
|
|
for_each_sgtable_sg(&cmd->table, sg, i) {
|
|
|
|
page = sg_page(sg);
|
|
|
|
if (!page)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
len = sg->length;
|
2023-07-09 15:28:58 -05:00
|
|
|
|
|
|
|
if (copy_page_to_iter(page, 0, len, iter) != len) {
|
|
|
|
pr_err("Could not copy data while handling misaligned cmd. Error %zu\n",
|
|
|
|
len);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-07-18 14:31:32 -07:00
|
|
|
/* Fill in status and signal that we are done processing this command
|
|
|
|
*
|
|
|
|
* This is scheduled in the vhost work queue so we are called with the owner
|
|
|
|
* process mm and can access the vring.
|
|
|
|
*/
|
|
|
|
static void vhost_scsi_complete_cmd_work(struct vhost_work *work)
|
|
|
|
{
|
2023-06-26 18:22:59 -05:00
|
|
|
struct vhost_scsi_virtqueue *svq = container_of(work,
|
|
|
|
struct vhost_scsi_virtqueue, completion_work);
|
2013-01-06 14:36:13 +08:00
|
|
|
struct virtio_scsi_cmd_resp v_rsp;
|
2017-11-09 09:00:21 +09:00
|
|
|
struct vhost_scsi_cmd *cmd, *t;
|
2013-01-06 14:36:13 +08:00
|
|
|
struct llist_node *llnode;
|
|
|
|
struct se_cmd *se_cmd;
|
2015-01-27 13:13:12 -08:00
|
|
|
struct iov_iter iov_iter;
|
2023-06-26 18:22:59 -05:00
|
|
|
bool signal = false;
|
|
|
|
int ret;
|
2012-07-18 14:31:32 -07:00
|
|
|
|
2023-06-26 18:22:59 -05:00
|
|
|
llnode = llist_del_all(&svq->completion_list);
|
2025-04-02 23:29:46 -07:00
|
|
|
|
|
|
|
mutex_lock(&svq->vq.mutex);
|
|
|
|
|
2017-11-09 09:00:21 +09:00
|
|
|
llist_for_each_entry_safe(cmd, t, llnode, tvc_completion_list) {
|
2013-05-06 16:38:29 +08:00
|
|
|
se_cmd = &cmd->tvc_se_cmd;
|
2012-07-18 14:31:32 -07:00
|
|
|
|
|
|
|
pr_debug("%s tv_cmd %p resid %u status %#02x\n", __func__,
|
2013-05-06 16:38:29 +08:00
|
|
|
cmd, se_cmd->residual_count, se_cmd->scsi_status);
|
2012-07-18 14:31:32 -07:00
|
|
|
memset(&v_rsp, 0, sizeof(v_rsp));
|
2023-07-09 15:28:58 -05:00
|
|
|
|
2024-12-03 13:15:14 -06:00
|
|
|
if (cmd->read_iter && vhost_scsi_copy_sgl_to_iov(cmd)) {
|
2023-07-09 15:28:58 -05:00
|
|
|
v_rsp.response = VIRTIO_SCSI_S_BAD_TARGET;
|
|
|
|
} else {
|
|
|
|
v_rsp.resid = cpu_to_vhost32(cmd->tvc_vq,
|
|
|
|
se_cmd->residual_count);
|
|
|
|
/* TODO is status_qualifier field needed? */
|
|
|
|
v_rsp.status = se_cmd->scsi_status;
|
|
|
|
v_rsp.sense_len = cpu_to_vhost32(cmd->tvc_vq,
|
|
|
|
se_cmd->scsi_sense_length);
|
|
|
|
memcpy(v_rsp.sense, cmd->tvc_sense_buf,
|
|
|
|
se_cmd->scsi_sense_length);
|
|
|
|
}
|
2015-01-27 13:13:12 -08:00
|
|
|
|
2024-12-03 13:15:15 -06:00
|
|
|
iov_iter_init(&iov_iter, ITER_DEST, cmd->tvc_resp_iovs,
|
|
|
|
cmd->tvc_resp_iovs_cnt, sizeof(v_rsp));
|
2015-01-27 13:13:12 -08:00
|
|
|
ret = copy_to_iter(&v_rsp, sizeof(v_rsp), &iov_iter);
|
|
|
|
if (likely(ret == sizeof(v_rsp))) {
|
2023-06-26 18:22:59 -05:00
|
|
|
signal = true;
|
|
|
|
|
2013-05-06 16:38:29 +08:00
|
|
|
vhost_add_used(cmd->tvc_vq, cmd->tvc_vq_desc, 0);
|
tcm_vhost: Multi-queue support
This adds virtio-scsi multi-queue support to tcm_vhost. In order to use
multi-queue, guest side multi-queue support is need. It can
be found here:
https://lkml.org/lkml/2012/12/18/166
Currently, only one thread is created by vhost core code for each
vhost_scsi instance. Even if there are multi-queues, all the handling of
guest kick (vhost_scsi_handle_kick) are processed in one thread. This is
not optimal. Luckily, most of the work is offloaded to the tcm_vhost
workqueue.
Some initial perf numbers:
1 queue, 4 targets, 1 lun per target
4K request size, 50% randread + 50% randwrite: 127K/127k IOPS
4 queues, 4 targets, 1 lun per target
4K request size, 50% randread + 50% randwrite: 181K/181k IOPS
Signed-off-by: Asias He <asias@redhat.com>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
2013-02-06 13:20:59 +08:00
|
|
|
} else
|
2012-07-18 14:31:32 -07:00
|
|
|
pr_err("Faulted on virtio_scsi_cmd_resp\n");
|
|
|
|
|
2025-04-02 23:29:51 -07:00
|
|
|
vhost_scsi_log_write(cmd->tvc_vq, cmd->tvc_log,
|
|
|
|
cmd->tvc_log_num);
|
|
|
|
|
2020-11-09 23:33:21 -06:00
|
|
|
vhost_scsi_release_cmd_res(se_cmd);
|
2012-07-18 14:31:32 -07:00
|
|
|
}
|
|
|
|
|
2025-04-02 23:29:46 -07:00
|
|
|
mutex_unlock(&svq->vq.mutex);
|
|
|
|
|
2023-06-26 18:22:59 -05:00
|
|
|
if (signal)
|
|
|
|
vhost_signal(&svq->vs->dev, &svq->vq);
|
2012-07-18 14:31:32 -07:00
|
|
|
}
|
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
static struct vhost_scsi_cmd *
|
2024-12-03 13:15:13 -06:00
|
|
|
vhost_scsi_get_cmd(struct vhost_virtqueue *vq, u64 scsi_tag)
|
2012-07-18 14:31:32 -07:00
|
|
|
{
|
2020-11-09 23:33:20 -06:00
|
|
|
struct vhost_scsi_virtqueue *svq = container_of(vq,
|
|
|
|
struct vhost_scsi_virtqueue, vq);
|
2015-01-31 23:56:53 -08:00
|
|
|
struct vhost_scsi_cmd *cmd;
|
2024-12-03 13:15:12 -06:00
|
|
|
struct scatterlist *sgl, *prot_sgl;
|
2025-04-02 23:29:51 -07:00
|
|
|
struct vhost_log *log;
|
2020-11-09 23:33:20 -06:00
|
|
|
int tag;
|
2012-07-18 14:31:32 -07:00
|
|
|
|
2021-01-22 10:33:08 +08:00
|
|
|
tag = sbitmap_get(&svq->scsi_tags);
|
2013-09-23 11:42:28 -07:00
|
|
|
if (tag < 0) {
|
2024-12-03 13:15:10 -06:00
|
|
|
pr_warn_once("Guest sent too many cmds. Returning TASK_SET_FULL.\n");
|
2013-09-23 11:42:28 -07:00
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
}
|
|
|
|
|
2020-11-09 23:33:20 -06:00
|
|
|
cmd = &svq->scsi_cmds[tag];
|
2024-12-03 13:15:12 -06:00
|
|
|
sgl = cmd->sgl;
|
|
|
|
prot_sgl = cmd->prot_sgl;
|
2025-04-02 23:29:51 -07:00
|
|
|
log = cmd->tvc_log;
|
2017-05-20 13:48:44 +02:00
|
|
|
memset(cmd, 0, sizeof(*cmd));
|
2024-12-03 13:15:12 -06:00
|
|
|
cmd->sgl = sgl;
|
|
|
|
cmd->prot_sgl = prot_sgl;
|
2025-04-02 23:29:51 -07:00
|
|
|
cmd->tvc_log = log;
|
2013-06-07 17:47:46 -07:00
|
|
|
cmd->tvc_se_cmd.map_tag = tag;
|
2015-01-31 23:56:53 -08:00
|
|
|
cmd->inflight = vhost_scsi_get_inflight(vq);
|
2013-05-06 16:38:29 +08:00
|
|
|
|
|
|
|
return cmd;
|
2012-07-18 14:31:32 -07:00
|
|
|
}
|
|
|
|
|
2024-12-03 13:15:12 -06:00
|
|
|
static void vhost_scsi_revert_map_iov_to_sgl(struct iov_iter *iter,
|
|
|
|
struct scatterlist *curr,
|
|
|
|
struct scatterlist *end)
|
|
|
|
{
|
|
|
|
size_t revert_bytes = 0;
|
|
|
|
struct page *page;
|
|
|
|
|
|
|
|
while (curr != end) {
|
|
|
|
page = sg_page(curr);
|
|
|
|
|
|
|
|
if (page) {
|
|
|
|
put_page(page);
|
|
|
|
revert_bytes += curr->length;
|
|
|
|
}
|
|
|
|
/* Clear so we can re-use it for the copy path */
|
|
|
|
sg_set_page(curr, NULL, 0, 0);
|
|
|
|
curr = sg_next(curr);
|
|
|
|
}
|
|
|
|
iov_iter_revert(iter, revert_bytes);
|
|
|
|
}
|
|
|
|
|
2012-07-18 14:31:32 -07:00
|
|
|
/*
|
|
|
|
* Map a user memory range into a scatterlist
|
|
|
|
*
|
|
|
|
* Returns the number of scatterlist entries used or -errno on error.
|
|
|
|
*/
|
2013-05-06 16:38:27 +08:00
|
|
|
static int
|
2015-01-31 23:56:53 -08:00
|
|
|
vhost_scsi_map_to_sgl(struct vhost_scsi_cmd *cmd,
|
2017-09-24 22:07:59 -04:00
|
|
|
struct iov_iter *iter,
|
2024-12-03 13:15:12 -06:00
|
|
|
struct sg_table *sg_table,
|
|
|
|
struct scatterlist **sgl,
|
2023-07-09 15:28:58 -05:00
|
|
|
bool is_prot)
|
2012-07-18 14:31:32 -07:00
|
|
|
{
|
2024-12-03 13:15:08 -06:00
|
|
|
struct vhost_scsi_virtqueue *svq = container_of(cmd->tvc_vq,
|
|
|
|
struct vhost_scsi_virtqueue, vq);
|
|
|
|
struct page **pages = svq->upages;
|
2024-12-03 13:15:12 -06:00
|
|
|
struct scatterlist *sg = *sgl;
|
|
|
|
ssize_t bytes;
|
|
|
|
size_t offset;
|
|
|
|
unsigned int n, npages = 0;
|
2013-06-21 14:32:04 -07:00
|
|
|
|
2022-06-09 10:28:36 -04:00
|
|
|
bytes = iov_iter_get_pages2(iter, pages, LONG_MAX,
|
2017-09-24 22:07:59 -04:00
|
|
|
VHOST_SCSI_PREALLOC_UPAGES, &offset);
|
2013-01-22 11:20:27 +08:00
|
|
|
/* No pages were pinned */
|
2017-09-24 22:07:59 -04:00
|
|
|
if (bytes <= 0)
|
|
|
|
return bytes < 0 ? bytes : -EFAULT;
|
2013-01-22 11:20:27 +08:00
|
|
|
|
2017-09-24 22:07:59 -04:00
|
|
|
while (bytes) {
|
2024-12-03 13:15:12 -06:00
|
|
|
n = min_t(unsigned int, PAGE_SIZE - offset, bytes);
|
2023-07-09 15:28:58 -05:00
|
|
|
/*
|
|
|
|
* The block layer requires bios/requests to be a multiple of
|
|
|
|
* 512 bytes, but Windows can send us vecs that are misaligned.
|
|
|
|
* This can result in bios and later requests with misaligned
|
|
|
|
* sizes if we have to break up a cmd/scatterlist into multiple
|
|
|
|
* bios.
|
|
|
|
*
|
|
|
|
* We currently only break up a command into multiple bios if
|
|
|
|
* we hit the vec/seg limit, so check if our sgl_count is
|
|
|
|
* greater than the max and if a vec in the cmd has a
|
|
|
|
* misaligned offset/size.
|
|
|
|
*/
|
|
|
|
if (!is_prot &&
|
|
|
|
(offset & (SECTOR_SIZE - 1) || n & (SECTOR_SIZE - 1)) &&
|
|
|
|
cmd->tvc_sgl_count > BIO_MAX_VECS) {
|
|
|
|
WARN_ONCE(true,
|
|
|
|
"vhost-scsi detected misaligned IO. Performance may be degraded.");
|
|
|
|
goto revert_iter_get_pages;
|
|
|
|
}
|
|
|
|
|
2024-12-03 13:15:12 -06:00
|
|
|
sg_set_page(sg, pages[npages++], n, offset);
|
|
|
|
sg = sg_next(sg);
|
2017-09-24 22:07:59 -04:00
|
|
|
bytes -= n;
|
|
|
|
offset = 0;
|
|
|
|
}
|
2023-07-09 15:28:58 -05:00
|
|
|
|
2024-12-03 13:15:12 -06:00
|
|
|
*sgl = sg;
|
2017-09-24 22:07:59 -04:00
|
|
|
return npages;
|
2023-07-09 15:28:58 -05:00
|
|
|
|
|
|
|
revert_iter_get_pages:
|
2024-12-03 13:15:12 -06:00
|
|
|
vhost_scsi_revert_map_iov_to_sgl(iter, *sgl, sg);
|
2023-07-09 15:28:58 -05:00
|
|
|
|
2024-12-03 13:15:12 -06:00
|
|
|
iov_iter_revert(iter, bytes);
|
|
|
|
while (bytes) {
|
|
|
|
n = min_t(unsigned int, PAGE_SIZE, bytes);
|
2023-07-09 15:28:58 -05:00
|
|
|
|
|
|
|
put_page(pages[npages++]);
|
2024-12-03 13:15:12 -06:00
|
|
|
bytes -= n;
|
2023-07-09 15:28:58 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return -EINVAL;
|
2012-07-18 14:31:32 -07:00
|
|
|
}
|
|
|
|
|
2013-05-06 16:38:27 +08:00
|
|
|
static int
|
2015-01-28 00:15:00 -08:00
|
|
|
vhost_scsi_calc_sgls(struct iov_iter *iter, size_t bytes, int max_sgls)
|
2012-07-18 14:31:32 -07:00
|
|
|
{
|
2015-01-28 00:15:00 -08:00
|
|
|
int sgl_count = 0;
|
2012-07-18 14:31:32 -07:00
|
|
|
|
2023-03-29 08:52:15 -06:00
|
|
|
if (!iter || !iter_iov(iter)) {
|
2015-01-28 00:15:00 -08:00
|
|
|
pr_err("%s: iter->iov is NULL, but expected bytes: %zu"
|
|
|
|
" present\n", __func__, bytes);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2013-01-22 11:20:26 +08:00
|
|
|
|
2015-01-28 00:15:00 -08:00
|
|
|
sgl_count = iov_iter_npages(iter, 0xffff);
|
|
|
|
if (sgl_count > max_sgls) {
|
|
|
|
pr_err("%s: requested sgl_count: %d exceeds pre-allocated"
|
|
|
|
" max_sgls: %d\n", __func__, sgl_count, max_sgls);
|
|
|
|
return -EINVAL;
|
2014-02-22 18:34:08 -08:00
|
|
|
}
|
2015-01-28 00:15:00 -08:00
|
|
|
return sgl_count;
|
|
|
|
}
|
2012-07-18 14:31:32 -07:00
|
|
|
|
2015-01-28 00:15:00 -08:00
|
|
|
static int
|
2023-07-09 15:28:58 -05:00
|
|
|
vhost_scsi_copy_iov_to_sgl(struct vhost_scsi_cmd *cmd, struct iov_iter *iter,
|
2024-12-03 13:15:13 -06:00
|
|
|
struct sg_table *sg_table, int sg_count,
|
|
|
|
int data_dir)
|
2023-07-09 15:28:58 -05:00
|
|
|
{
|
|
|
|
size_t len = iov_iter_count(iter);
|
|
|
|
unsigned int nbytes = 0;
|
2024-12-03 13:15:12 -06:00
|
|
|
struct scatterlist *sg;
|
2023-07-09 15:28:58 -05:00
|
|
|
struct page *page;
|
2024-12-03 13:15:11 -06:00
|
|
|
int i, ret;
|
2023-07-09 15:28:58 -05:00
|
|
|
|
2024-12-03 13:15:13 -06:00
|
|
|
if (data_dir == DMA_FROM_DEVICE) {
|
2024-12-03 13:15:14 -06:00
|
|
|
cmd->read_iter = kzalloc(sizeof(*cmd->read_iter), GFP_KERNEL);
|
|
|
|
if (!cmd->read_iter)
|
2023-07-09 15:28:58 -05:00
|
|
|
return -ENOMEM;
|
2024-12-03 13:15:14 -06:00
|
|
|
|
|
|
|
cmd->read_iov = dup_iter(cmd->read_iter, iter, GFP_KERNEL);
|
|
|
|
if (!cmd->read_iov) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto free_iter;
|
|
|
|
}
|
2023-07-09 15:28:58 -05:00
|
|
|
}
|
|
|
|
|
2024-12-03 13:15:12 -06:00
|
|
|
for_each_sgtable_sg(sg_table, sg, i) {
|
2023-07-09 15:28:58 -05:00
|
|
|
page = alloc_page(GFP_KERNEL);
|
|
|
|
if (!page) {
|
2024-12-03 13:15:11 -06:00
|
|
|
ret = -ENOMEM;
|
2023-07-09 15:28:58 -05:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
nbytes = min_t(unsigned int, PAGE_SIZE, len);
|
2024-12-03 13:15:12 -06:00
|
|
|
sg_set_page(sg, page, nbytes, 0);
|
2023-07-09 15:28:58 -05:00
|
|
|
|
2024-12-03 13:15:13 -06:00
|
|
|
if (data_dir == DMA_TO_DEVICE &&
|
2024-12-03 13:15:11 -06:00
|
|
|
copy_page_from_iter(page, 0, nbytes, iter) != nbytes) {
|
|
|
|
ret = -EFAULT;
|
2023-07-09 15:28:58 -05:00
|
|
|
goto err;
|
2024-12-03 13:15:11 -06:00
|
|
|
}
|
2023-07-09 15:28:58 -05:00
|
|
|
|
|
|
|
len -= nbytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd->copied_iov = 1;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err:
|
|
|
|
pr_err("Could not read %u bytes while handling misaligned cmd\n",
|
|
|
|
nbytes);
|
|
|
|
|
2024-12-03 13:15:12 -06:00
|
|
|
for_each_sgtable_sg(sg_table, sg, i) {
|
|
|
|
page = sg_page(sg);
|
|
|
|
if (page)
|
|
|
|
__free_page(page);
|
|
|
|
}
|
2024-12-03 13:15:14 -06:00
|
|
|
kfree(cmd->read_iov);
|
|
|
|
free_iter:
|
|
|
|
kfree(cmd->read_iter);
|
2024-12-03 13:15:11 -06:00
|
|
|
return ret;
|
2023-07-09 15:28:58 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2023-07-09 15:28:59 -05:00
|
|
|
vhost_scsi_map_iov_to_sgl(struct vhost_scsi_cmd *cmd, struct iov_iter *iter,
|
2024-12-03 13:15:12 -06:00
|
|
|
struct sg_table *sg_table, int sg_count, bool is_prot)
|
2015-01-28 00:15:00 -08:00
|
|
|
{
|
2024-12-03 13:15:12 -06:00
|
|
|
struct scatterlist *sg = sg_table->sgl;
|
2017-09-24 22:07:59 -04:00
|
|
|
int ret;
|
2014-02-22 18:34:08 -08:00
|
|
|
|
2017-09-24 22:07:59 -04:00
|
|
|
while (iov_iter_count(iter)) {
|
2024-12-03 13:15:12 -06:00
|
|
|
ret = vhost_scsi_map_to_sgl(cmd, iter, sg_table, &sg, is_prot);
|
2012-07-18 14:31:32 -07:00
|
|
|
if (ret < 0) {
|
2024-12-03 13:15:12 -06:00
|
|
|
vhost_scsi_revert_map_iov_to_sgl(iter, sg_table->sgl,
|
|
|
|
sg);
|
2012-07-18 14:31:32 -07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
2023-07-09 15:28:58 -05:00
|
|
|
|
2012-07-18 14:31:32 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-02-22 18:34:43 -08:00
|
|
|
static int
|
2024-12-03 13:15:13 -06:00
|
|
|
vhost_scsi_mapal(struct vhost_scsi *vs, struct vhost_scsi_cmd *cmd,
|
2015-01-28 00:15:00 -08:00
|
|
|
size_t prot_bytes, struct iov_iter *prot_iter,
|
2024-12-03 13:15:13 -06:00
|
|
|
size_t data_bytes, struct iov_iter *data_iter, int data_dir)
|
2015-01-28 00:15:00 -08:00
|
|
|
{
|
|
|
|
int sgl_count, ret;
|
|
|
|
|
|
|
|
if (prot_bytes) {
|
|
|
|
sgl_count = vhost_scsi_calc_sgls(prot_iter, prot_bytes,
|
2015-01-31 23:56:53 -08:00
|
|
|
VHOST_SCSI_PREALLOC_PROT_SGLS);
|
2024-12-03 13:15:12 -06:00
|
|
|
cmd->prot_table.sgl = cmd->prot_sgl;
|
|
|
|
ret = sg_alloc_table_chained(&cmd->prot_table, sgl_count,
|
|
|
|
cmd->prot_table.sgl,
|
|
|
|
vs->inline_sg_cnt);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2015-01-28 00:15:00 -08:00
|
|
|
|
|
|
|
cmd->tvc_prot_sgl_count = sgl_count;
|
|
|
|
pr_debug("%s prot_sg %p prot_sgl_count %u\n", __func__,
|
2024-12-03 13:15:12 -06:00
|
|
|
cmd->prot_table.sgl, cmd->tvc_prot_sgl_count);
|
2015-01-28 00:15:00 -08:00
|
|
|
|
2023-07-09 15:28:59 -05:00
|
|
|
ret = vhost_scsi_map_iov_to_sgl(cmd, prot_iter,
|
2024-12-03 13:15:12 -06:00
|
|
|
&cmd->prot_table,
|
2023-07-09 15:28:59 -05:00
|
|
|
cmd->tvc_prot_sgl_count, true);
|
2014-02-22 18:34:43 -08:00
|
|
|
if (ret < 0) {
|
2024-12-03 13:15:12 -06:00
|
|
|
sg_free_table_chained(&cmd->prot_table,
|
|
|
|
vs->inline_sg_cnt);
|
2014-02-22 18:34:43 -08:00
|
|
|
cmd->tvc_prot_sgl_count = 0;
|
|
|
|
return ret;
|
|
|
|
}
|
2015-01-28 00:15:00 -08:00
|
|
|
}
|
|
|
|
sgl_count = vhost_scsi_calc_sgls(data_iter, data_bytes,
|
2015-01-31 23:56:53 -08:00
|
|
|
VHOST_SCSI_PREALLOC_SGLS);
|
2015-01-28 00:15:00 -08:00
|
|
|
if (sgl_count < 0)
|
|
|
|
return sgl_count;
|
|
|
|
|
2024-12-03 13:15:12 -06:00
|
|
|
cmd->table.sgl = cmd->sgl;
|
|
|
|
ret = sg_alloc_table_chained(&cmd->table, sgl_count, cmd->table.sgl,
|
|
|
|
vs->inline_sg_cnt);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2015-01-28 00:15:00 -08:00
|
|
|
cmd->tvc_sgl_count = sgl_count;
|
|
|
|
pr_debug("%s data_sg %p data_sgl_count %u\n", __func__,
|
2024-12-03 13:15:12 -06:00
|
|
|
cmd->table.sgl, cmd->tvc_sgl_count);
|
2015-01-28 00:15:00 -08:00
|
|
|
|
2024-12-03 13:15:12 -06:00
|
|
|
ret = vhost_scsi_map_iov_to_sgl(cmd, data_iter, &cmd->table,
|
2023-07-09 15:28:59 -05:00
|
|
|
cmd->tvc_sgl_count, false);
|
2024-12-03 13:15:12 -06:00
|
|
|
if (ret == -EINVAL)
|
|
|
|
ret = vhost_scsi_copy_iov_to_sgl(cmd, data_iter, &cmd->table,
|
2024-12-03 13:15:13 -06:00
|
|
|
cmd->tvc_sgl_count, data_dir);
|
2015-01-28 00:15:00 -08:00
|
|
|
if (ret < 0) {
|
2024-12-03 13:15:12 -06:00
|
|
|
sg_free_table_chained(&cmd->table, vs->inline_sg_cnt);
|
2015-01-28 00:15:00 -08:00
|
|
|
cmd->tvc_sgl_count = 0;
|
|
|
|
return ret;
|
2014-02-22 18:34:43 -08:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-12-21 10:42:08 -08:00
|
|
|
static int vhost_scsi_to_tcm_attr(int attr)
|
|
|
|
{
|
|
|
|
switch (attr) {
|
|
|
|
case VIRTIO_SCSI_S_SIMPLE:
|
|
|
|
return TCM_SIMPLE_TAG;
|
|
|
|
case VIRTIO_SCSI_S_ORDERED:
|
|
|
|
return TCM_ORDERED_TAG;
|
|
|
|
case VIRTIO_SCSI_S_HEAD:
|
|
|
|
return TCM_HEAD_TAG;
|
|
|
|
case VIRTIO_SCSI_S_ACA:
|
|
|
|
return TCM_ACA_TAG;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return TCM_SIMPLE_TAG;
|
|
|
|
}
|
|
|
|
|
2024-12-03 13:15:13 -06:00
|
|
|
static void vhost_scsi_target_queue_cmd(struct vhost_scsi_nexus *nexus,
|
|
|
|
struct vhost_scsi_cmd *cmd,
|
|
|
|
unsigned char *cdb, u16 lun,
|
|
|
|
int task_attr, int data_dir,
|
|
|
|
u32 exp_data_len)
|
2012-07-18 14:31:32 -07:00
|
|
|
{
|
2013-05-06 16:38:29 +08:00
|
|
|
struct se_cmd *se_cmd = &cmd->tvc_se_cmd;
|
2014-02-22 18:22:31 -08:00
|
|
|
struct scatterlist *sg_ptr, *sg_prot_ptr = NULL;
|
2012-07-18 14:31:32 -07:00
|
|
|
|
2014-02-22 18:22:31 -08:00
|
|
|
/* FIXME: BIDI operation */
|
2013-05-06 16:38:29 +08:00
|
|
|
if (cmd->tvc_sgl_count) {
|
2024-12-03 13:15:12 -06:00
|
|
|
sg_ptr = cmd->table.sgl;
|
2014-02-22 18:22:31 -08:00
|
|
|
|
|
|
|
if (cmd->tvc_prot_sgl_count)
|
2024-12-03 13:15:12 -06:00
|
|
|
sg_prot_ptr = cmd->prot_table.sgl;
|
2014-02-22 18:22:31 -08:00
|
|
|
else
|
|
|
|
se_cmd->prot_pto = true;
|
2012-07-18 14:31:32 -07:00
|
|
|
} else {
|
|
|
|
sg_ptr = NULL;
|
|
|
|
}
|
2012-10-01 18:40:55 -07:00
|
|
|
|
2015-04-14 13:26:44 +02:00
|
|
|
se_cmd->tag = 0;
|
2024-12-03 13:15:13 -06:00
|
|
|
target_init_cmd(se_cmd, nexus->tvn_se_sess, &cmd->tvc_sense_buf[0],
|
|
|
|
lun, exp_data_len, vhost_scsi_to_tcm_attr(task_attr),
|
|
|
|
data_dir, TARGET_SCF_ACK_KREF);
|
scsi: target: vhost-scsi: Convert to new submission API
target_submit_cmd_map_sgls() is being removed, so convert vhost-scsi to the
new submission API. This has it use target_init_cmd(),
target_submit_prep(), target_submit() because we need to have LIO core map
sgls which is now done in target_submit_prep(), and in the next patches we
will do the target_submit step from the LIO workqueue.
Note: vhost-scsi never calls target_stop_session() so
target_submit_cmd_map_sgls() never failed (in the new API target_init_cmd()
handles target_stop_session() being called when cmds are being
submitted). If it were to have used target_stop_session() and got an error,
we would have hit a refcount bug like xen and usb, because it does:
if (rc < 0) {
transport_send_check_condition_and_sense(se_cmd,
TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE, 0);
transport_generic_free_cmd(se_cmd, 0);
}
transport_send_check_condition_and_sense() calls queue_status which does
transport_generic_free_cmd(), and then we do an extra
transport_generic_free_cmd() call above which would have dropped the
refcount to -1 and the refcount code would spit out errors.
Link: https://lore.kernel.org/r/20210227170006.5077-12-michael.christie@oracle.com
Cc: "Michael S. Tsirkin" <mst@redhat.com>
Cc: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Mike Christie <michael.christie@oracle.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2021-02-27 10:59:52 -06:00
|
|
|
|
2024-12-03 13:15:13 -06:00
|
|
|
if (target_submit_prep(se_cmd, cdb, sg_ptr,
|
scsi: target: vhost-scsi: Convert to new submission API
target_submit_cmd_map_sgls() is being removed, so convert vhost-scsi to the
new submission API. This has it use target_init_cmd(),
target_submit_prep(), target_submit() because we need to have LIO core map
sgls which is now done in target_submit_prep(), and in the next patches we
will do the target_submit step from the LIO workqueue.
Note: vhost-scsi never calls target_stop_session() so
target_submit_cmd_map_sgls() never failed (in the new API target_init_cmd()
handles target_stop_session() being called when cmds are being
submitted). If it were to have used target_stop_session() and got an error,
we would have hit a refcount bug like xen and usb, because it does:
if (rc < 0) {
transport_send_check_condition_and_sense(se_cmd,
TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE, 0);
transport_generic_free_cmd(se_cmd, 0);
}
transport_send_check_condition_and_sense() calls queue_status which does
transport_generic_free_cmd(), and then we do an extra
transport_generic_free_cmd() call above which would have dropped the
refcount to -1 and the refcount code would spit out errors.
Link: https://lore.kernel.org/r/20210227170006.5077-12-michael.christie@oracle.com
Cc: "Michael S. Tsirkin" <mst@redhat.com>
Cc: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Mike Christie <michael.christie@oracle.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2021-02-27 10:59:52 -06:00
|
|
|
cmd->tvc_sgl_count, NULL, 0, sg_prot_ptr,
|
2021-02-27 10:59:56 -06:00
|
|
|
cmd->tvc_prot_sgl_count, GFP_KERNEL))
|
scsi: target: vhost-scsi: Convert to new submission API
target_submit_cmd_map_sgls() is being removed, so convert vhost-scsi to the
new submission API. This has it use target_init_cmd(),
target_submit_prep(), target_submit() because we need to have LIO core map
sgls which is now done in target_submit_prep(), and in the next patches we
will do the target_submit step from the LIO workqueue.
Note: vhost-scsi never calls target_stop_session() so
target_submit_cmd_map_sgls() never failed (in the new API target_init_cmd()
handles target_stop_session() being called when cmds are being
submitted). If it were to have used target_stop_session() and got an error,
we would have hit a refcount bug like xen and usb, because it does:
if (rc < 0) {
transport_send_check_condition_and_sense(se_cmd,
TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE, 0);
transport_generic_free_cmd(se_cmd, 0);
}
transport_send_check_condition_and_sense() calls queue_status which does
transport_generic_free_cmd(), and then we do an extra
transport_generic_free_cmd() call above which would have dropped the
refcount to -1 and the refcount code would spit out errors.
Link: https://lore.kernel.org/r/20210227170006.5077-12-michael.christie@oracle.com
Cc: "Michael S. Tsirkin" <mst@redhat.com>
Cc: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Mike Christie <michael.christie@oracle.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2021-02-27 10:59:52 -06:00
|
|
|
return;
|
|
|
|
|
2023-09-27 21:09:05 -05:00
|
|
|
target_submit(se_cmd);
|
2012-07-18 14:31:32 -07:00
|
|
|
}
|
|
|
|
|
2024-12-03 13:15:10 -06:00
|
|
|
static void
|
|
|
|
vhost_scsi_send_status(struct vhost_scsi *vs, struct vhost_virtqueue *vq,
|
2025-04-02 23:29:48 -07:00
|
|
|
struct vhost_scsi_ctx *vc, u8 status)
|
2024-12-03 13:15:10 -06:00
|
|
|
{
|
|
|
|
struct virtio_scsi_cmd_resp rsp;
|
2025-04-02 23:29:48 -07:00
|
|
|
struct iov_iter iov_iter;
|
2024-12-03 13:15:10 -06:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
memset(&rsp, 0, sizeof(rsp));
|
|
|
|
rsp.status = status;
|
2025-04-02 23:29:48 -07:00
|
|
|
|
|
|
|
iov_iter_init(&iov_iter, ITER_DEST, &vq->iov[vc->out], vc->in,
|
|
|
|
sizeof(rsp));
|
|
|
|
|
|
|
|
ret = copy_to_iter(&rsp, sizeof(rsp), &iov_iter);
|
|
|
|
|
|
|
|
if (likely(ret == sizeof(rsp)))
|
|
|
|
vhost_add_used_and_signal(&vs->dev, vq, vc->head, 0);
|
2024-12-03 13:15:10 -06:00
|
|
|
else
|
|
|
|
pr_err("Faulted on virtio_scsi_cmd_resp\n");
|
|
|
|
}
|
|
|
|
|
2025-04-02 23:29:47 -07:00
|
|
|
#define TYPE_IO_CMD 0
|
|
|
|
#define TYPE_CTRL_TMF 1
|
|
|
|
#define TYPE_CTRL_AN 2
|
|
|
|
|
2013-05-06 16:38:27 +08:00
|
|
|
static void
|
|
|
|
vhost_scsi_send_bad_target(struct vhost_scsi *vs,
|
|
|
|
struct vhost_virtqueue *vq,
|
2025-04-02 23:29:47 -07:00
|
|
|
struct vhost_scsi_ctx *vc, int type)
|
2013-04-10 15:06:15 +08:00
|
|
|
{
|
2025-04-02 23:29:47 -07:00
|
|
|
union {
|
|
|
|
struct virtio_scsi_cmd_resp cmd;
|
|
|
|
struct virtio_scsi_ctrl_tmf_resp tmf;
|
|
|
|
struct virtio_scsi_ctrl_an_resp an;
|
|
|
|
} rsp;
|
|
|
|
struct iov_iter iov_iter;
|
|
|
|
size_t rsp_size;
|
2013-04-10 15:06:15 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
memset(&rsp, 0, sizeof(rsp));
|
2025-04-02 23:29:47 -07:00
|
|
|
|
|
|
|
if (type == TYPE_IO_CMD) {
|
|
|
|
rsp_size = sizeof(struct virtio_scsi_cmd_resp);
|
|
|
|
rsp.cmd.response = VIRTIO_SCSI_S_BAD_TARGET;
|
|
|
|
} else if (type == TYPE_CTRL_TMF) {
|
|
|
|
rsp_size = sizeof(struct virtio_scsi_ctrl_tmf_resp);
|
|
|
|
rsp.tmf.response = VIRTIO_SCSI_S_BAD_TARGET;
|
|
|
|
} else {
|
|
|
|
rsp_size = sizeof(struct virtio_scsi_ctrl_an_resp);
|
|
|
|
rsp.an.response = VIRTIO_SCSI_S_BAD_TARGET;
|
|
|
|
}
|
|
|
|
|
|
|
|
iov_iter_init(&iov_iter, ITER_DEST, &vq->iov[vc->out], vc->in,
|
|
|
|
rsp_size);
|
|
|
|
|
|
|
|
ret = copy_to_iter(&rsp, rsp_size, &iov_iter);
|
|
|
|
|
|
|
|
if (likely(ret == rsp_size))
|
|
|
|
vhost_add_used_and_signal(&vs->dev, vq, vc->head, 0);
|
2013-04-10 15:06:15 +08:00
|
|
|
else
|
2025-04-02 23:29:47 -07:00
|
|
|
pr_err("Faulted on virtio scsi type=%d\n", type);
|
2013-04-10 15:06:15 +08:00
|
|
|
}
|
|
|
|
|
2018-09-17 17:09:49 -07:00
|
|
|
static int
|
|
|
|
vhost_scsi_get_desc(struct vhost_scsi *vs, struct vhost_virtqueue *vq,
|
2025-04-02 23:29:50 -07:00
|
|
|
struct vhost_scsi_ctx *vc,
|
|
|
|
struct vhost_log *log, unsigned int *log_num)
|
2018-09-17 17:09:49 -07:00
|
|
|
{
|
|
|
|
int ret = -ENXIO;
|
|
|
|
|
2025-04-02 23:29:50 -07:00
|
|
|
if (likely(log_num))
|
|
|
|
*log_num = 0;
|
|
|
|
|
2018-09-17 17:09:49 -07:00
|
|
|
vc->head = vhost_get_vq_desc(vq, vq->iov,
|
|
|
|
ARRAY_SIZE(vq->iov), &vc->out, &vc->in,
|
2025-04-02 23:29:50 -07:00
|
|
|
log, log_num);
|
2018-09-17 17:09:49 -07:00
|
|
|
|
|
|
|
pr_debug("vhost_get_vq_desc: head: %d, out: %u in: %u\n",
|
|
|
|
vc->head, vc->out, vc->in);
|
|
|
|
|
|
|
|
/* On error, stop handling until the next kick. */
|
|
|
|
if (unlikely(vc->head < 0))
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
/* Nothing new? Wait for eventfd to tell us they refilled. */
|
|
|
|
if (vc->head == vq->num) {
|
|
|
|
if (unlikely(vhost_enable_notify(&vs->dev, vq))) {
|
|
|
|
vhost_disable_notify(&vs->dev, vq);
|
|
|
|
ret = -EAGAIN;
|
|
|
|
}
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the size of request and response buffers.
|
|
|
|
* FIXME: Not correct for BIDI operation
|
|
|
|
*/
|
|
|
|
vc->out_size = iov_length(vq->iov, vc->out);
|
|
|
|
vc->in_size = iov_length(&vq->iov[vc->out], vc->in);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copy over the virtio-scsi request header, which for a
|
|
|
|
* ANY_LAYOUT enabled guest may span multiple iovecs, or a
|
|
|
|
* single iovec may contain both the header + outgoing
|
|
|
|
* WRITE payloads.
|
|
|
|
*
|
|
|
|
* copy_from_iter() will advance out_iter, so that it will
|
|
|
|
* point at the start of the outgoing WRITE payload, if
|
|
|
|
* DMA_TO_DEVICE is set.
|
|
|
|
*/
|
2022-09-15 20:25:47 -04:00
|
|
|
iov_iter_init(&vc->out_iter, ITER_SOURCE, vq->iov, vc->out, vc->out_size);
|
2018-09-17 17:09:49 -07:00
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
done:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
vhost_scsi_chk_size(struct vhost_virtqueue *vq, struct vhost_scsi_ctx *vc)
|
|
|
|
{
|
|
|
|
if (unlikely(vc->in_size < vc->rsp_size)) {
|
|
|
|
vq_err(vq,
|
|
|
|
"Response buf too small, need min %zu bytes got %zu",
|
|
|
|
vc->rsp_size, vc->in_size);
|
|
|
|
return -EINVAL;
|
|
|
|
} else if (unlikely(vc->out_size < vc->req_size)) {
|
|
|
|
vq_err(vq,
|
|
|
|
"Request buf too small, need min %zu bytes got %zu",
|
|
|
|
vc->req_size, vc->out_size);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
vhost_scsi_get_req(struct vhost_virtqueue *vq, struct vhost_scsi_ctx *vc,
|
|
|
|
struct vhost_scsi_tpg **tpgp)
|
|
|
|
{
|
|
|
|
int ret = -EIO;
|
|
|
|
|
|
|
|
if (unlikely(!copy_from_iter_full(vc->req, vc->req_size,
|
|
|
|
&vc->out_iter))) {
|
2018-12-13 09:10:14 +08:00
|
|
|
vq_err(vq, "Faulted on copy_from_iter_full\n");
|
2018-09-17 17:09:49 -07:00
|
|
|
} else if (unlikely(*vc->lunp != 1)) {
|
|
|
|
/* virtio-scsi spec requires byte 0 of the lun to be 1 */
|
|
|
|
vq_err(vq, "Illegal virtio-scsi lun: %u\n", *vc->lunp);
|
|
|
|
} else {
|
vhost/scsi: null-ptr-dereference in vhost_scsi_get_req()
Since commit 3f8ca2e115e5 ("vhost/scsi: Extract common handling code
from control queue handler") a null pointer dereference bug can be
triggered when guest sends an SCSI AN request.
In vhost_scsi_ctl_handle_vq(), `vc.target` is assigned with
`&v_req.tmf.lun[1]` within a switch-case block and is then passed to
vhost_scsi_get_req() which extracts `vc->req` and `tpg`. However, for
a `VIRTIO_SCSI_T_AN_*` request, tpg is not required, so `vc.target` is
set to NULL in this branch. Later, in vhost_scsi_get_req(),
`vc->target` is dereferenced without being checked, leading to a null
pointer dereference bug. This bug can be triggered from guest.
When this bug occurs, the vhost_worker process is killed while holding
`vq->mutex` and the corresponding tpg will remain occupied
indefinitely.
Below is the KASAN report:
Oops: general protection fault, probably for non-canonical address
0xdffffc0000000000: 0000 [#1] PREEMPT SMP KASAN NOPTI
KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007]
CPU: 1 PID: 840 Comm: poc Not tainted 6.10.0+ #1
Hardware name: QEMU Ubuntu 24.04 PC (i440FX + PIIX, 1996), BIOS
1.16.3-debian-1.16.3-2 04/01/2014
RIP: 0010:vhost_scsi_get_req+0x165/0x3a0
Code: 00 fc ff df 48 89 fa 48 c1 ea 03 80 3c 02 00 0f 85 2b 02 00 00
48 b8 00 00 00 00 00 fc ff df 4d 8b 65 30 4c 89 e2 48 c1 ea 03 <0f> b6
04 02 4c 89 e2 83 e2 07 38 d0 7f 08 84 c0 0f 85 be 01 00 00
RSP: 0018:ffff888017affb50 EFLAGS: 00010246
RAX: dffffc0000000000 RBX: ffff88801b000000 RCX: 0000000000000000
RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff888017affcb8
RBP: ffff888017affb80 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000000
R13: ffff888017affc88 R14: ffff888017affd1c R15: ffff888017993000
FS: 000055556e076500(0000) GS:ffff88806b100000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00000000200027c0 CR3: 0000000010ed0004 CR4: 0000000000370ef0
Call Trace:
<TASK>
? show_regs+0x86/0xa0
? die_addr+0x4b/0xd0
? exc_general_protection+0x163/0x260
? asm_exc_general_protection+0x27/0x30
? vhost_scsi_get_req+0x165/0x3a0
vhost_scsi_ctl_handle_vq+0x2a4/0xca0
? __pfx_vhost_scsi_ctl_handle_vq+0x10/0x10
? __switch_to+0x721/0xeb0
? __schedule+0xda5/0x5710
? __kasan_check_write+0x14/0x30
? _raw_spin_lock+0x82/0xf0
vhost_scsi_ctl_handle_kick+0x52/0x90
vhost_run_work_list+0x134/0x1b0
vhost_task_fn+0x121/0x350
...
</TASK>
---[ end trace 0000000000000000 ]---
Let's add a check in vhost_scsi_get_req.
Fixes: 3f8ca2e115e5 ("vhost/scsi: Extract common handling code from control queue handler")
Signed-off-by: Haoran Zhang <wh1sper@zju.edu.cn>
[whitespace fixes]
Signed-off-by: Mike Christie <michael.christie@oracle.com>
Message-Id: <b26d7ddd-b098-4361-88f8-17ca7f90adf7@oracle.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2024-10-01 15:14:15 -05:00
|
|
|
struct vhost_scsi_tpg **vs_tpg, *tpg = NULL;
|
|
|
|
|
|
|
|
if (vc->target) {
|
|
|
|
/* validated at handler entry */
|
|
|
|
vs_tpg = vhost_vq_get_backend(vq);
|
|
|
|
tpg = READ_ONCE(vs_tpg[*vc->target]);
|
2025-06-11 16:01:13 -05:00
|
|
|
if (unlikely(!tpg))
|
vhost/scsi: null-ptr-dereference in vhost_scsi_get_req()
Since commit 3f8ca2e115e5 ("vhost/scsi: Extract common handling code
from control queue handler") a null pointer dereference bug can be
triggered when guest sends an SCSI AN request.
In vhost_scsi_ctl_handle_vq(), `vc.target` is assigned with
`&v_req.tmf.lun[1]` within a switch-case block and is then passed to
vhost_scsi_get_req() which extracts `vc->req` and `tpg`. However, for
a `VIRTIO_SCSI_T_AN_*` request, tpg is not required, so `vc.target` is
set to NULL in this branch. Later, in vhost_scsi_get_req(),
`vc->target` is dereferenced without being checked, leading to a null
pointer dereference bug. This bug can be triggered from guest.
When this bug occurs, the vhost_worker process is killed while holding
`vq->mutex` and the corresponding tpg will remain occupied
indefinitely.
Below is the KASAN report:
Oops: general protection fault, probably for non-canonical address
0xdffffc0000000000: 0000 [#1] PREEMPT SMP KASAN NOPTI
KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007]
CPU: 1 PID: 840 Comm: poc Not tainted 6.10.0+ #1
Hardware name: QEMU Ubuntu 24.04 PC (i440FX + PIIX, 1996), BIOS
1.16.3-debian-1.16.3-2 04/01/2014
RIP: 0010:vhost_scsi_get_req+0x165/0x3a0
Code: 00 fc ff df 48 89 fa 48 c1 ea 03 80 3c 02 00 0f 85 2b 02 00 00
48 b8 00 00 00 00 00 fc ff df 4d 8b 65 30 4c 89 e2 48 c1 ea 03 <0f> b6
04 02 4c 89 e2 83 e2 07 38 d0 7f 08 84 c0 0f 85 be 01 00 00
RSP: 0018:ffff888017affb50 EFLAGS: 00010246
RAX: dffffc0000000000 RBX: ffff88801b000000 RCX: 0000000000000000
RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff888017affcb8
RBP: ffff888017affb80 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000000
R13: ffff888017affc88 R14: ffff888017affd1c R15: ffff888017993000
FS: 000055556e076500(0000) GS:ffff88806b100000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00000000200027c0 CR3: 0000000010ed0004 CR4: 0000000000370ef0
Call Trace:
<TASK>
? show_regs+0x86/0xa0
? die_addr+0x4b/0xd0
? exc_general_protection+0x163/0x260
? asm_exc_general_protection+0x27/0x30
? vhost_scsi_get_req+0x165/0x3a0
vhost_scsi_ctl_handle_vq+0x2a4/0xca0
? __pfx_vhost_scsi_ctl_handle_vq+0x10/0x10
? __switch_to+0x721/0xeb0
? __schedule+0xda5/0x5710
? __kasan_check_write+0x14/0x30
? _raw_spin_lock+0x82/0xf0
vhost_scsi_ctl_handle_kick+0x52/0x90
vhost_run_work_list+0x134/0x1b0
vhost_task_fn+0x121/0x350
...
</TASK>
---[ end trace 0000000000000000 ]---
Let's add a check in vhost_scsi_get_req.
Fixes: 3f8ca2e115e5 ("vhost/scsi: Extract common handling code from control queue handler")
Signed-off-by: Haoran Zhang <wh1sper@zju.edu.cn>
[whitespace fixes]
Signed-off-by: Mike Christie <michael.christie@oracle.com>
Message-Id: <b26d7ddd-b098-4361-88f8-17ca7f90adf7@oracle.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2024-10-01 15:14:15 -05:00
|
|
|
goto out;
|
2018-09-17 17:09:49 -07:00
|
|
|
}
|
|
|
|
|
vhost/scsi: null-ptr-dereference in vhost_scsi_get_req()
Since commit 3f8ca2e115e5 ("vhost/scsi: Extract common handling code
from control queue handler") a null pointer dereference bug can be
triggered when guest sends an SCSI AN request.
In vhost_scsi_ctl_handle_vq(), `vc.target` is assigned with
`&v_req.tmf.lun[1]` within a switch-case block and is then passed to
vhost_scsi_get_req() which extracts `vc->req` and `tpg`. However, for
a `VIRTIO_SCSI_T_AN_*` request, tpg is not required, so `vc.target` is
set to NULL in this branch. Later, in vhost_scsi_get_req(),
`vc->target` is dereferenced without being checked, leading to a null
pointer dereference bug. This bug can be triggered from guest.
When this bug occurs, the vhost_worker process is killed while holding
`vq->mutex` and the corresponding tpg will remain occupied
indefinitely.
Below is the KASAN report:
Oops: general protection fault, probably for non-canonical address
0xdffffc0000000000: 0000 [#1] PREEMPT SMP KASAN NOPTI
KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007]
CPU: 1 PID: 840 Comm: poc Not tainted 6.10.0+ #1
Hardware name: QEMU Ubuntu 24.04 PC (i440FX + PIIX, 1996), BIOS
1.16.3-debian-1.16.3-2 04/01/2014
RIP: 0010:vhost_scsi_get_req+0x165/0x3a0
Code: 00 fc ff df 48 89 fa 48 c1 ea 03 80 3c 02 00 0f 85 2b 02 00 00
48 b8 00 00 00 00 00 fc ff df 4d 8b 65 30 4c 89 e2 48 c1 ea 03 <0f> b6
04 02 4c 89 e2 83 e2 07 38 d0 7f 08 84 c0 0f 85 be 01 00 00
RSP: 0018:ffff888017affb50 EFLAGS: 00010246
RAX: dffffc0000000000 RBX: ffff88801b000000 RCX: 0000000000000000
RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff888017affcb8
RBP: ffff888017affb80 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000000
R13: ffff888017affc88 R14: ffff888017affd1c R15: ffff888017993000
FS: 000055556e076500(0000) GS:ffff88806b100000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00000000200027c0 CR3: 0000000010ed0004 CR4: 0000000000370ef0
Call Trace:
<TASK>
? show_regs+0x86/0xa0
? die_addr+0x4b/0xd0
? exc_general_protection+0x163/0x260
? asm_exc_general_protection+0x27/0x30
? vhost_scsi_get_req+0x165/0x3a0
vhost_scsi_ctl_handle_vq+0x2a4/0xca0
? __pfx_vhost_scsi_ctl_handle_vq+0x10/0x10
? __switch_to+0x721/0xeb0
? __schedule+0xda5/0x5710
? __kasan_check_write+0x14/0x30
? _raw_spin_lock+0x82/0xf0
vhost_scsi_ctl_handle_kick+0x52/0x90
vhost_run_work_list+0x134/0x1b0
vhost_task_fn+0x121/0x350
...
</TASK>
---[ end trace 0000000000000000 ]---
Let's add a check in vhost_scsi_get_req.
Fixes: 3f8ca2e115e5 ("vhost/scsi: Extract common handling code from control queue handler")
Signed-off-by: Haoran Zhang <wh1sper@zju.edu.cn>
[whitespace fixes]
Signed-off-by: Mike Christie <michael.christie@oracle.com>
Message-Id: <b26d7ddd-b098-4361-88f8-17ca7f90adf7@oracle.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2024-10-01 15:14:15 -05:00
|
|
|
if (tpgp)
|
|
|
|
*tpgp = tpg;
|
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
out:
|
2018-09-17 17:09:49 -07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2024-12-03 13:15:15 -06:00
|
|
|
static int
|
|
|
|
vhost_scsi_setup_resp_iovs(struct vhost_scsi_cmd *cmd, struct iovec *in_iovs,
|
|
|
|
unsigned int in_iovs_cnt)
|
|
|
|
{
|
|
|
|
int i, cnt;
|
|
|
|
|
|
|
|
if (!in_iovs_cnt)
|
|
|
|
return 0;
|
|
|
|
/*
|
2025-06-11 07:39:21 -07:00
|
|
|
* Initiators normally just put the virtio_scsi_cmd_resp in the first
|
2024-12-03 13:15:15 -06:00
|
|
|
* iov, but just in case they wedged in some data with it we check for
|
|
|
|
* greater than or equal to the response struct.
|
|
|
|
*/
|
|
|
|
if (in_iovs[0].iov_len >= sizeof(struct virtio_scsi_cmd_resp)) {
|
|
|
|
cmd->tvc_resp_iovs = &cmd->tvc_resp_iov;
|
|
|
|
cmd->tvc_resp_iovs_cnt = 1;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Legacy descriptor layouts didn't specify that we must put
|
|
|
|
* the entire response in one iov. Worst case we have a
|
|
|
|
* iov per byte.
|
|
|
|
*/
|
|
|
|
cnt = min(VHOST_SCSI_MAX_RESP_IOVS, in_iovs_cnt);
|
|
|
|
cmd->tvc_resp_iovs = kcalloc(cnt, sizeof(struct iovec),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!cmd->tvc_resp_iovs)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
cmd->tvc_resp_iovs_cnt = cnt;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < cmd->tvc_resp_iovs_cnt; i++)
|
|
|
|
cmd->tvc_resp_iovs[i] = in_iovs[i];
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-11-09 23:33:22 -06:00
|
|
|
static u16 vhost_buf_to_lun(u8 *lun_buf)
|
|
|
|
{
|
|
|
|
return ((lun_buf[2] << 8) | lun_buf[3]) & 0x3FFF;
|
|
|
|
}
|
|
|
|
|
2013-05-06 16:38:27 +08:00
|
|
|
static void
|
|
|
|
vhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq)
|
2012-07-18 14:31:32 -07:00
|
|
|
{
|
2015-01-31 23:56:53 -08:00
|
|
|
struct vhost_scsi_tpg **vs_tpg, *tpg;
|
2012-07-18 14:31:32 -07:00
|
|
|
struct virtio_scsi_cmd_req v_req;
|
2014-02-22 18:22:31 -08:00
|
|
|
struct virtio_scsi_cmd_req_pi v_req_pi;
|
2024-12-03 13:15:13 -06:00
|
|
|
struct vhost_scsi_nexus *nexus;
|
2018-09-17 17:09:49 -07:00
|
|
|
struct vhost_scsi_ctx vc;
|
2015-01-31 23:56:53 -08:00
|
|
|
struct vhost_scsi_cmd *cmd;
|
2018-09-17 17:09:49 -07:00
|
|
|
struct iov_iter in_iter, prot_iter, data_iter;
|
2014-02-22 18:22:31 -08:00
|
|
|
u64 tag;
|
2015-01-25 21:14:58 -08:00
|
|
|
u32 exp_data_len, data_direction;
|
2024-12-03 13:15:15 -06:00
|
|
|
int ret, prot_bytes, c = 0;
|
2014-02-22 18:22:31 -08:00
|
|
|
u16 lun;
|
2018-09-17 17:09:49 -07:00
|
|
|
u8 task_attr;
|
2015-01-25 21:14:58 -08:00
|
|
|
bool t10_pi = vhost_has_feature(vq, VIRTIO_SCSI_F_T10_PI);
|
2024-12-03 13:15:13 -06:00
|
|
|
u8 *cdb;
|
2025-04-02 23:29:51 -07:00
|
|
|
struct vhost_log *vq_log;
|
|
|
|
unsigned int log_num;
|
2012-07-18 14:31:32 -07:00
|
|
|
|
2013-05-07 14:54:35 +08:00
|
|
|
mutex_lock(&vq->mutex);
|
2013-04-03 14:17:37 +08:00
|
|
|
/*
|
|
|
|
* We can handle the vq only after the endpoint is setup by calling the
|
|
|
|
* VHOST_SCSI_SET_ENDPOINT ioctl.
|
|
|
|
*/
|
2020-03-31 21:27:57 +02:00
|
|
|
vs_tpg = vhost_vq_get_backend(vq);
|
2013-04-03 14:17:37 +08:00
|
|
|
if (!vs_tpg)
|
2013-05-07 14:54:35 +08:00
|
|
|
goto out;
|
2012-07-18 14:31:32 -07:00
|
|
|
|
2018-09-17 17:09:49 -07:00
|
|
|
memset(&vc, 0, sizeof(vc));
|
|
|
|
vc.rsp_size = sizeof(struct virtio_scsi_cmd_resp);
|
|
|
|
|
2012-07-18 14:31:32 -07:00
|
|
|
vhost_disable_notify(&vs->dev, vq);
|
|
|
|
|
2025-04-02 23:29:51 -07:00
|
|
|
vq_log = unlikely(vhost_has_feature(vq, VHOST_F_LOG_ALL)) ?
|
|
|
|
vq->log : NULL;
|
|
|
|
|
2019-05-17 00:29:52 -04:00
|
|
|
do {
|
2025-04-02 23:29:51 -07:00
|
|
|
ret = vhost_scsi_get_desc(vs, vq, &vc, vq_log, &log_num);
|
2018-09-17 17:09:49 -07:00
|
|
|
if (ret)
|
|
|
|
goto err;
|
|
|
|
|
2015-01-25 21:14:58 -08:00
|
|
|
/*
|
|
|
|
* Setup pointers and values based upon different virtio-scsi
|
|
|
|
* request header if T10_PI is enabled in KVM guest.
|
|
|
|
*/
|
|
|
|
if (t10_pi) {
|
2018-09-17 17:09:49 -07:00
|
|
|
vc.req = &v_req_pi;
|
|
|
|
vc.req_size = sizeof(v_req_pi);
|
|
|
|
vc.lunp = &v_req_pi.lun[0];
|
|
|
|
vc.target = &v_req_pi.lun[1];
|
2014-02-22 18:22:31 -08:00
|
|
|
} else {
|
2018-09-17 17:09:49 -07:00
|
|
|
vc.req = &v_req;
|
|
|
|
vc.req_size = sizeof(v_req);
|
|
|
|
vc.lunp = &v_req.lun[0];
|
|
|
|
vc.target = &v_req.lun[1];
|
2014-02-22 18:22:31 -08:00
|
|
|
}
|
|
|
|
|
2015-01-25 21:14:58 -08:00
|
|
|
/*
|
2018-09-17 17:09:49 -07:00
|
|
|
* Validate the size of request and response buffers.
|
|
|
|
* Check for a sane response buffer so we can report
|
|
|
|
* early errors back to the guest.
|
2015-01-25 21:14:58 -08:00
|
|
|
*/
|
2018-09-17 17:09:49 -07:00
|
|
|
ret = vhost_scsi_chk_size(vq, &vc);
|
|
|
|
if (ret)
|
|
|
|
goto err;
|
2012-07-18 14:31:32 -07:00
|
|
|
|
2018-09-17 17:09:49 -07:00
|
|
|
ret = vhost_scsi_get_req(vq, &vc, &tpg);
|
|
|
|
if (ret)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
ret = -EIO; /* bad target on any error from here on */
|
2014-02-24 14:13:32 -08:00
|
|
|
|
2014-02-22 18:22:31 -08:00
|
|
|
/*
|
2015-01-25 21:14:58 -08:00
|
|
|
* Determine data_direction by calculating the total outgoing
|
|
|
|
* iovec sizes + incoming iovec sizes vs. virtio-scsi request +
|
|
|
|
* response headers respectively.
|
2014-02-22 18:22:31 -08:00
|
|
|
*
|
2015-01-25 21:14:58 -08:00
|
|
|
* For DMA_TO_DEVICE this is out_iter, which is already pointing
|
|
|
|
* to the right place.
|
|
|
|
*
|
|
|
|
* For DMA_FROM_DEVICE, the iovec will be just past the end
|
|
|
|
* of the virtio-scsi response header in either the same
|
|
|
|
* or immediately following iovec.
|
2014-02-22 18:22:31 -08:00
|
|
|
*
|
2015-01-25 21:14:58 -08:00
|
|
|
* Any associated T10_PI bytes for the outgoing / incoming
|
|
|
|
* payloads are included in calculation of exp_data_len here.
|
2014-02-22 18:22:31 -08:00
|
|
|
*/
|
2015-01-25 21:14:58 -08:00
|
|
|
prot_bytes = 0;
|
|
|
|
|
2018-09-17 17:09:49 -07:00
|
|
|
if (vc.out_size > vc.req_size) {
|
2015-01-25 21:14:58 -08:00
|
|
|
data_direction = DMA_TO_DEVICE;
|
2018-09-17 17:09:49 -07:00
|
|
|
exp_data_len = vc.out_size - vc.req_size;
|
|
|
|
data_iter = vc.out_iter;
|
|
|
|
} else if (vc.in_size > vc.rsp_size) {
|
2015-01-25 21:14:58 -08:00
|
|
|
data_direction = DMA_FROM_DEVICE;
|
2018-09-17 17:09:49 -07:00
|
|
|
exp_data_len = vc.in_size - vc.rsp_size;
|
2015-01-25 21:14:58 -08:00
|
|
|
|
2022-09-15 20:25:47 -04:00
|
|
|
iov_iter_init(&in_iter, ITER_DEST, &vq->iov[vc.out], vc.in,
|
2018-09-17 17:09:49 -07:00
|
|
|
vc.rsp_size + exp_data_len);
|
|
|
|
iov_iter_advance(&in_iter, vc.rsp_size);
|
2015-01-25 21:14:58 -08:00
|
|
|
data_iter = in_iter;
|
|
|
|
} else {
|
|
|
|
data_direction = DMA_NONE;
|
|
|
|
exp_data_len = 0;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* If T10_PI header + payload is present, setup prot_iter values
|
|
|
|
* and recalculate data_iter for vhost_scsi_mapal() mapping to
|
|
|
|
* host scatterlists via get_user_pages_fast().
|
2014-02-22 18:22:31 -08:00
|
|
|
*/
|
2015-01-25 21:14:58 -08:00
|
|
|
if (t10_pi) {
|
2014-02-22 18:22:31 -08:00
|
|
|
if (v_req_pi.pi_bytesout) {
|
|
|
|
if (data_direction != DMA_TO_DEVICE) {
|
2015-01-25 21:14:58 -08:00
|
|
|
vq_err(vq, "Received non zero pi_bytesout,"
|
|
|
|
" but wrong data_direction\n");
|
2018-09-17 17:09:49 -07:00
|
|
|
goto err;
|
2014-02-22 18:22:31 -08:00
|
|
|
}
|
2014-11-23 18:01:34 +02:00
|
|
|
prot_bytes = vhost32_to_cpu(vq, v_req_pi.pi_bytesout);
|
2014-02-22 18:22:31 -08:00
|
|
|
} else if (v_req_pi.pi_bytesin) {
|
|
|
|
if (data_direction != DMA_FROM_DEVICE) {
|
2015-01-25 21:14:58 -08:00
|
|
|
vq_err(vq, "Received non zero pi_bytesin,"
|
|
|
|
" but wrong data_direction\n");
|
2018-09-17 17:09:49 -07:00
|
|
|
goto err;
|
2014-02-22 18:22:31 -08:00
|
|
|
}
|
2014-11-23 18:01:34 +02:00
|
|
|
prot_bytes = vhost32_to_cpu(vq, v_req_pi.pi_bytesin);
|
2014-02-22 18:22:31 -08:00
|
|
|
}
|
2015-01-25 21:14:58 -08:00
|
|
|
/*
|
2018-08-22 13:21:53 -06:00
|
|
|
* Set prot_iter to data_iter and truncate it to
|
|
|
|
* prot_bytes, and advance data_iter past any
|
2023-09-28 14:18:33 +02:00
|
|
|
* preceding prot_bytes that may be present.
|
2015-01-25 21:14:58 -08:00
|
|
|
*
|
|
|
|
* Also fix up the exp_data_len to reflect only the
|
|
|
|
* actual data payload length.
|
|
|
|
*/
|
2014-02-22 18:22:31 -08:00
|
|
|
if (prot_bytes) {
|
2015-01-25 21:14:58 -08:00
|
|
|
exp_data_len -= prot_bytes;
|
|
|
|
prot_iter = data_iter;
|
2018-08-22 13:21:53 -06:00
|
|
|
iov_iter_truncate(&prot_iter, prot_bytes);
|
2015-01-25 21:14:58 -08:00
|
|
|
iov_iter_advance(&data_iter, prot_bytes);
|
2014-02-22 18:22:31 -08:00
|
|
|
}
|
2014-11-23 18:01:34 +02:00
|
|
|
tag = vhost64_to_cpu(vq, v_req_pi.tag);
|
2014-02-22 18:22:31 -08:00
|
|
|
task_attr = v_req_pi.task_attr;
|
|
|
|
cdb = &v_req_pi.cdb[0];
|
2020-11-09 23:33:22 -06:00
|
|
|
lun = vhost_buf_to_lun(v_req_pi.lun);
|
2014-02-22 18:22:31 -08:00
|
|
|
} else {
|
2014-11-23 18:01:34 +02:00
|
|
|
tag = vhost64_to_cpu(vq, v_req.tag);
|
2014-02-22 18:22:31 -08:00
|
|
|
task_attr = v_req.task_attr;
|
|
|
|
cdb = &v_req.cdb[0];
|
2020-11-09 23:33:22 -06:00
|
|
|
lun = vhost_buf_to_lun(v_req.lun);
|
2014-02-22 18:22:31 -08:00
|
|
|
}
|
|
|
|
/*
|
2015-01-25 21:14:58 -08:00
|
|
|
* Check that the received CDB size does not exceeded our
|
|
|
|
* hardcoded max for vhost-scsi, then get a pre-allocated
|
|
|
|
* cmd descriptor for the new virtio-scsi tag.
|
2014-02-22 18:22:31 -08:00
|
|
|
*
|
|
|
|
* TODO what if cdb was too small for varlen cdb header?
|
|
|
|
*/
|
2015-01-31 23:56:53 -08:00
|
|
|
if (unlikely(scsi_command_size(cdb) > VHOST_SCSI_MAX_CDB_SIZE)) {
|
2014-02-22 18:22:31 -08:00
|
|
|
vq_err(vq, "Received SCSI CDB with command_size: %d that"
|
|
|
|
" exceeds SCSI_MAX_VARLEN_CDB_SIZE: %d\n",
|
2015-01-31 23:56:53 -08:00
|
|
|
scsi_command_size(cdb), VHOST_SCSI_MAX_CDB_SIZE);
|
2018-09-17 17:09:49 -07:00
|
|
|
goto err;
|
2014-02-22 18:22:31 -08:00
|
|
|
}
|
2024-12-03 13:15:13 -06:00
|
|
|
|
|
|
|
nexus = tpg->tpg_nexus;
|
|
|
|
if (!nexus) {
|
|
|
|
vq_err(vq, "Unable to locate active struct vhost_scsi_nexus\n");
|
|
|
|
ret = -EIO;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd = vhost_scsi_get_cmd(vq, tag);
|
2013-05-06 16:38:29 +08:00
|
|
|
if (IS_ERR(cmd)) {
|
2024-12-03 13:15:10 -06:00
|
|
|
ret = PTR_ERR(cmd);
|
2025-06-11 07:39:21 -07:00
|
|
|
vq_err(vq, "vhost_scsi_get_tag failed %d\n", ret);
|
2018-09-17 17:09:49 -07:00
|
|
|
goto err;
|
2012-07-18 14:31:32 -07:00
|
|
|
}
|
2013-05-06 16:38:29 +08:00
|
|
|
cmd->tvc_vq = vq;
|
2024-12-03 13:15:15 -06:00
|
|
|
|
|
|
|
ret = vhost_scsi_setup_resp_iovs(cmd, &vq->iov[vc.out], vc.in);
|
|
|
|
if (ret) {
|
|
|
|
vq_err(vq, "Failed to alloc recv iovs\n");
|
|
|
|
vhost_scsi_release_cmd_res(&cmd->tvc_se_cmd);
|
|
|
|
goto err;
|
|
|
|
}
|
2012-07-18 14:31:32 -07:00
|
|
|
|
2025-04-02 23:29:51 -07:00
|
|
|
if (unlikely(vq_log && log_num)) {
|
|
|
|
ret = vhost_scsi_copy_cmd_log(vq, cmd, vq_log, log_num);
|
|
|
|
if (unlikely(ret)) {
|
|
|
|
vhost_scsi_release_cmd_res(&cmd->tvc_se_cmd);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-18 14:31:32 -07:00
|
|
|
pr_debug("vhost_scsi got command opcode: %#02x, lun: %d\n",
|
2024-12-03 13:15:13 -06:00
|
|
|
cdb[0], lun);
|
2015-01-25 21:14:58 -08:00
|
|
|
pr_debug("cmd: %p exp_data_len: %d, prot_bytes: %d data_direction:"
|
|
|
|
" %d\n", cmd, exp_data_len, prot_bytes, data_direction);
|
2012-07-18 14:31:32 -07:00
|
|
|
|
|
|
|
if (data_direction != DMA_NONE) {
|
2024-12-03 13:15:13 -06:00
|
|
|
ret = vhost_scsi_mapal(vs, cmd, prot_bytes, &prot_iter,
|
|
|
|
exp_data_len, &data_iter,
|
|
|
|
data_direction);
|
2024-12-03 13:15:11 -06:00
|
|
|
if (unlikely(ret)) {
|
2012-07-18 14:31:32 -07:00
|
|
|
vq_err(vq, "Failed to map iov to sgl\n");
|
2020-11-09 23:33:21 -06:00
|
|
|
vhost_scsi_release_cmd_res(&cmd->tvc_se_cmd);
|
2018-09-17 17:09:49 -07:00
|
|
|
goto err;
|
2012-07-18 14:31:32 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Save the descriptor from vhost_get_vq_desc() to be used to
|
|
|
|
* complete the virtio-scsi request in TCM callback context via
|
2015-01-25 21:14:58 -08:00
|
|
|
* vhost_scsi_queue_data_in() and vhost_scsi_queue_status()
|
2012-07-18 14:31:32 -07:00
|
|
|
*/
|
2018-09-17 17:09:49 -07:00
|
|
|
cmd->tvc_vq_desc = vc.head;
|
2024-12-03 13:15:13 -06:00
|
|
|
vhost_scsi_target_queue_cmd(nexus, cmd, cdb, lun, task_attr,
|
|
|
|
data_direction,
|
|
|
|
exp_data_len + prot_bytes);
|
2018-09-17 17:09:49 -07:00
|
|
|
ret = 0;
|
|
|
|
err:
|
|
|
|
/*
|
|
|
|
* ENXIO: No more requests, or read error, wait for next kick
|
|
|
|
* EINVAL: Invalid response buffer, drop the request
|
|
|
|
* EIO: Respond with bad target
|
|
|
|
* EAGAIN: Pending request
|
2024-12-03 13:15:10 -06:00
|
|
|
* ENOMEM: Could not allocate resources for request
|
2018-09-17 17:09:49 -07:00
|
|
|
*/
|
|
|
|
if (ret == -ENXIO)
|
|
|
|
break;
|
2025-04-02 23:29:51 -07:00
|
|
|
else if (ret == -EIO) {
|
2025-04-02 23:29:47 -07:00
|
|
|
vhost_scsi_send_bad_target(vs, vq, &vc, TYPE_IO_CMD);
|
2025-04-02 23:29:51 -07:00
|
|
|
vhost_scsi_log_write(vq, vq_log, log_num);
|
|
|
|
} else if (ret == -ENOMEM) {
|
2025-04-02 23:29:48 -07:00
|
|
|
vhost_scsi_send_status(vs, vq, &vc,
|
2024-12-03 13:15:10 -06:00
|
|
|
SAM_STAT_TASK_SET_FULL);
|
2025-04-02 23:29:51 -07:00
|
|
|
vhost_scsi_log_write(vq, vq_log, log_num);
|
|
|
|
}
|
2019-05-17 00:29:52 -04:00
|
|
|
} while (likely(!vhost_exceeds_weight(vq, ++c, 0)));
|
2013-05-07 14:54:35 +08:00
|
|
|
out:
|
2013-04-10 15:06:14 +08:00
|
|
|
mutex_unlock(&vq->mutex);
|
2012-07-18 14:31:32 -07:00
|
|
|
}
|
|
|
|
|
2018-09-17 17:09:47 -07:00
|
|
|
static void
|
2020-11-09 23:33:23 -06:00
|
|
|
vhost_scsi_send_tmf_resp(struct vhost_scsi *vs, struct vhost_virtqueue *vq,
|
|
|
|
int in_iovs, int vq_desc, struct iovec *resp_iov,
|
|
|
|
int tmf_resp_code)
|
2018-09-17 17:09:47 -07:00
|
|
|
{
|
|
|
|
struct virtio_scsi_ctrl_tmf_resp rsp;
|
2018-12-03 16:48:23 -08:00
|
|
|
struct iov_iter iov_iter;
|
2018-09-17 17:09:47 -07:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
pr_debug("%s\n", __func__);
|
|
|
|
memset(&rsp, 0, sizeof(rsp));
|
2020-11-09 23:33:23 -06:00
|
|
|
rsp.response = tmf_resp_code;
|
2018-12-03 16:48:23 -08:00
|
|
|
|
2022-09-15 20:25:47 -04:00
|
|
|
iov_iter_init(&iov_iter, ITER_DEST, resp_iov, in_iovs, sizeof(rsp));
|
2018-12-03 16:48:23 -08:00
|
|
|
|
|
|
|
ret = copy_to_iter(&rsp, sizeof(rsp), &iov_iter);
|
|
|
|
if (likely(ret == sizeof(rsp)))
|
2020-11-09 23:33:23 -06:00
|
|
|
vhost_add_used_and_signal(&vs->dev, vq, vq_desc, 0);
|
2018-09-17 17:09:47 -07:00
|
|
|
else
|
|
|
|
pr_err("Faulted on virtio_scsi_ctrl_tmf_resp\n");
|
|
|
|
}
|
|
|
|
|
2020-11-09 23:33:23 -06:00
|
|
|
static void vhost_scsi_tmf_resp_work(struct vhost_work *work)
|
|
|
|
{
|
|
|
|
struct vhost_scsi_tmf *tmf = container_of(work, struct vhost_scsi_tmf,
|
|
|
|
vwork);
|
2024-03-15 19:47:01 -05:00
|
|
|
int resp_code;
|
2020-11-09 23:33:23 -06:00
|
|
|
|
2024-03-15 19:47:01 -05:00
|
|
|
if (tmf->scsi_resp == TMR_FUNCTION_COMPLETE)
|
2020-11-09 23:33:23 -06:00
|
|
|
resp_code = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED;
|
2024-03-15 19:47:01 -05:00
|
|
|
else
|
2020-11-09 23:33:23 -06:00
|
|
|
resp_code = VIRTIO_SCSI_S_FUNCTION_REJECTED;
|
|
|
|
|
2025-04-02 23:29:46 -07:00
|
|
|
mutex_lock(&tmf->svq->vq.mutex);
|
2020-11-09 23:33:23 -06:00
|
|
|
vhost_scsi_send_tmf_resp(tmf->vhost, &tmf->svq->vq, tmf->in_iovs,
|
|
|
|
tmf->vq_desc, &tmf->resp_iov, resp_code);
|
2025-04-02 23:29:52 -07:00
|
|
|
vhost_scsi_log_write(&tmf->svq->vq, tmf->tmf_log,
|
|
|
|
tmf->tmf_log_num);
|
2025-04-02 23:29:46 -07:00
|
|
|
mutex_unlock(&tmf->svq->vq.mutex);
|
|
|
|
|
2020-11-09 23:33:23 -06:00
|
|
|
vhost_scsi_release_tmf_res(tmf);
|
|
|
|
}
|
|
|
|
|
2024-03-15 19:47:01 -05:00
|
|
|
static void vhost_scsi_tmf_flush_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct vhost_scsi_tmf *tmf = container_of(work, struct vhost_scsi_tmf,
|
|
|
|
flush_work);
|
|
|
|
struct vhost_virtqueue *vq = &tmf->svq->vq;
|
|
|
|
/*
|
|
|
|
* Make sure we have sent responses for other commands before we
|
|
|
|
* send our response.
|
|
|
|
*/
|
|
|
|
vhost_dev_flush(vq->dev);
|
2024-03-15 19:47:03 -05:00
|
|
|
if (!vhost_vq_work_queue(vq, &tmf->vwork))
|
|
|
|
vhost_scsi_release_tmf_res(tmf);
|
2024-03-15 19:47:01 -05:00
|
|
|
}
|
|
|
|
|
2020-11-09 23:33:23 -06:00
|
|
|
static void
|
|
|
|
vhost_scsi_handle_tmf(struct vhost_scsi *vs, struct vhost_scsi_tpg *tpg,
|
|
|
|
struct vhost_virtqueue *vq,
|
|
|
|
struct virtio_scsi_ctrl_tmf_req *vtmf,
|
2025-04-02 23:29:52 -07:00
|
|
|
struct vhost_scsi_ctx *vc,
|
|
|
|
struct vhost_log *log, unsigned int log_num)
|
2020-11-09 23:33:23 -06:00
|
|
|
{
|
|
|
|
struct vhost_scsi_virtqueue *svq = container_of(vq,
|
|
|
|
struct vhost_scsi_virtqueue, vq);
|
|
|
|
struct vhost_scsi_tmf *tmf;
|
|
|
|
|
|
|
|
if (vhost32_to_cpu(vq, vtmf->subtype) !=
|
|
|
|
VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET)
|
|
|
|
goto send_reject;
|
|
|
|
|
|
|
|
if (!tpg->tpg_nexus || !tpg->tpg_nexus->tvn_se_sess) {
|
|
|
|
pr_err("Unable to locate active struct vhost_scsi_nexus for LUN RESET.\n");
|
|
|
|
goto send_reject;
|
|
|
|
}
|
|
|
|
|
2023-03-20 21:06:19 -05:00
|
|
|
tmf = kzalloc(sizeof(*tmf), GFP_KERNEL);
|
|
|
|
if (!tmf)
|
2020-11-09 23:33:23 -06:00
|
|
|
goto send_reject;
|
|
|
|
|
2024-03-15 19:47:01 -05:00
|
|
|
INIT_WORK(&tmf->flush_work, vhost_scsi_tmf_flush_work);
|
2023-03-20 21:06:19 -05:00
|
|
|
vhost_work_init(&tmf->vwork, vhost_scsi_tmf_resp_work);
|
2020-11-09 23:33:23 -06:00
|
|
|
tmf->vhost = vs;
|
|
|
|
tmf->svq = svq;
|
|
|
|
tmf->resp_iov = vq->iov[vc->out];
|
|
|
|
tmf->vq_desc = vc->head;
|
|
|
|
tmf->in_iovs = vc->in;
|
|
|
|
tmf->inflight = vhost_scsi_get_inflight(vq);
|
|
|
|
|
2025-04-02 23:29:52 -07:00
|
|
|
if (unlikely(log && log_num)) {
|
|
|
|
tmf->tmf_log = kmalloc_array(log_num, sizeof(*tmf->tmf_log),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (tmf->tmf_log) {
|
|
|
|
memcpy(tmf->tmf_log, log, sizeof(*tmf->tmf_log) * log_num);
|
|
|
|
tmf->tmf_log_num = log_num;
|
|
|
|
} else {
|
|
|
|
pr_err("vhost_scsi tmf log allocation error\n");
|
|
|
|
vhost_scsi_release_tmf_res(tmf);
|
|
|
|
goto send_reject;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-09 23:33:23 -06:00
|
|
|
if (target_submit_tmr(&tmf->se_cmd, tpg->tpg_nexus->tvn_se_sess, NULL,
|
|
|
|
vhost_buf_to_lun(vtmf->lun), NULL,
|
|
|
|
TMR_LUN_RESET, GFP_KERNEL, 0,
|
|
|
|
TARGET_SCF_ACK_KREF) < 0) {
|
|
|
|
vhost_scsi_release_tmf_res(tmf);
|
|
|
|
goto send_reject;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
send_reject:
|
|
|
|
vhost_scsi_send_tmf_resp(vs, vq, vc->in, vc->head, &vq->iov[vc->out],
|
|
|
|
VIRTIO_SCSI_S_FUNCTION_REJECTED);
|
2025-04-02 23:29:52 -07:00
|
|
|
vhost_scsi_log_write(vq, log, log_num);
|
2020-11-09 23:33:23 -06:00
|
|
|
}
|
|
|
|
|
2018-09-17 17:09:47 -07:00
|
|
|
static void
|
|
|
|
vhost_scsi_send_an_resp(struct vhost_scsi *vs,
|
2018-09-17 17:09:48 -07:00
|
|
|
struct vhost_virtqueue *vq,
|
|
|
|
struct vhost_scsi_ctx *vc)
|
2018-09-17 17:09:47 -07:00
|
|
|
{
|
|
|
|
struct virtio_scsi_ctrl_an_resp rsp;
|
2018-12-03 16:48:23 -08:00
|
|
|
struct iov_iter iov_iter;
|
2018-09-17 17:09:47 -07:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
pr_debug("%s\n", __func__);
|
|
|
|
memset(&rsp, 0, sizeof(rsp)); /* event_actual = 0 */
|
|
|
|
rsp.response = VIRTIO_SCSI_S_OK;
|
2018-12-03 16:48:23 -08:00
|
|
|
|
2022-09-15 20:25:47 -04:00
|
|
|
iov_iter_init(&iov_iter, ITER_DEST, &vq->iov[vc->out], vc->in, sizeof(rsp));
|
2018-12-03 16:48:23 -08:00
|
|
|
|
|
|
|
ret = copy_to_iter(&rsp, sizeof(rsp), &iov_iter);
|
|
|
|
if (likely(ret == sizeof(rsp)))
|
2018-09-17 17:09:48 -07:00
|
|
|
vhost_add_used_and_signal(&vs->dev, vq, vc->head, 0);
|
2018-09-17 17:09:47 -07:00
|
|
|
else
|
|
|
|
pr_err("Faulted on virtio_scsi_ctrl_an_resp\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
vhost_scsi_ctl_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq)
|
|
|
|
{
|
2020-11-09 23:33:23 -06:00
|
|
|
struct vhost_scsi_tpg *tpg;
|
2018-09-17 17:09:47 -07:00
|
|
|
union {
|
|
|
|
__virtio32 type;
|
|
|
|
struct virtio_scsi_ctrl_an_req an;
|
|
|
|
struct virtio_scsi_ctrl_tmf_req tmf;
|
|
|
|
} v_req;
|
2018-09-17 17:09:48 -07:00
|
|
|
struct vhost_scsi_ctx vc;
|
|
|
|
size_t typ_size;
|
2019-05-17 00:29:52 -04:00
|
|
|
int ret, c = 0;
|
2025-04-02 23:29:52 -07:00
|
|
|
struct vhost_log *vq_log;
|
|
|
|
unsigned int log_num;
|
2018-09-17 17:09:47 -07:00
|
|
|
|
|
|
|
mutex_lock(&vq->mutex);
|
|
|
|
/*
|
|
|
|
* We can handle the vq only after the endpoint is setup by calling the
|
|
|
|
* VHOST_SCSI_SET_ENDPOINT ioctl.
|
|
|
|
*/
|
2020-03-31 21:27:57 +02:00
|
|
|
if (!vhost_vq_get_backend(vq))
|
2018-09-17 17:09:47 -07:00
|
|
|
goto out;
|
|
|
|
|
2018-09-17 17:09:48 -07:00
|
|
|
memset(&vc, 0, sizeof(vc));
|
|
|
|
|
2018-09-17 17:09:47 -07:00
|
|
|
vhost_disable_notify(&vs->dev, vq);
|
|
|
|
|
2025-04-02 23:29:52 -07:00
|
|
|
vq_log = unlikely(vhost_has_feature(vq, VHOST_F_LOG_ALL)) ?
|
|
|
|
vq->log : NULL;
|
|
|
|
|
2019-05-17 00:29:52 -04:00
|
|
|
do {
|
2025-04-02 23:29:52 -07:00
|
|
|
ret = vhost_scsi_get_desc(vs, vq, &vc, vq_log, &log_num);
|
2018-09-17 17:09:48 -07:00
|
|
|
if (ret)
|
|
|
|
goto err;
|
2018-09-17 17:09:47 -07:00
|
|
|
|
|
|
|
/*
|
2018-09-17 17:09:48 -07:00
|
|
|
* Get the request type first in order to setup
|
|
|
|
* other parameters dependent on the type.
|
2018-09-17 17:09:47 -07:00
|
|
|
*/
|
2018-09-17 17:09:48 -07:00
|
|
|
vc.req = &v_req.type;
|
2018-09-17 17:09:47 -07:00
|
|
|
typ_size = sizeof(v_req.type);
|
|
|
|
|
2018-09-17 17:09:48 -07:00
|
|
|
if (unlikely(!copy_from_iter_full(vc.req, typ_size,
|
|
|
|
&vc.out_iter))) {
|
2018-09-17 17:09:47 -07:00
|
|
|
vq_err(vq, "Faulted on copy_from_iter tmf type\n");
|
|
|
|
/*
|
2018-09-17 17:09:48 -07:00
|
|
|
* The size of the response buffer depends on the
|
|
|
|
* request type and must be validated against it.
|
2018-09-17 17:09:47 -07:00
|
|
|
* Since the request type is not known, don't send
|
|
|
|
* a response.
|
|
|
|
*/
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-07-10 06:36:16 -04:00
|
|
|
switch (vhost32_to_cpu(vq, v_req.type)) {
|
2018-09-17 17:09:47 -07:00
|
|
|
case VIRTIO_SCSI_T_TMF:
|
2018-09-17 17:09:48 -07:00
|
|
|
vc.req = &v_req.tmf;
|
|
|
|
vc.req_size = sizeof(struct virtio_scsi_ctrl_tmf_req);
|
|
|
|
vc.rsp_size = sizeof(struct virtio_scsi_ctrl_tmf_resp);
|
|
|
|
vc.lunp = &v_req.tmf.lun[0];
|
|
|
|
vc.target = &v_req.tmf.lun[1];
|
2018-09-17 17:09:47 -07:00
|
|
|
break;
|
|
|
|
case VIRTIO_SCSI_T_AN_QUERY:
|
|
|
|
case VIRTIO_SCSI_T_AN_SUBSCRIBE:
|
2018-09-17 17:09:48 -07:00
|
|
|
vc.req = &v_req.an;
|
|
|
|
vc.req_size = sizeof(struct virtio_scsi_ctrl_an_req);
|
|
|
|
vc.rsp_size = sizeof(struct virtio_scsi_ctrl_an_resp);
|
|
|
|
vc.lunp = &v_req.an.lun[0];
|
|
|
|
vc.target = NULL;
|
2018-09-17 17:09:47 -07:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
vq_err(vq, "Unknown control request %d", v_req.type);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2018-09-17 17:09:48 -07:00
|
|
|
* Validate the size of request and response buffers.
|
|
|
|
* Check for a sane response buffer so we can report
|
|
|
|
* early errors back to the guest.
|
2018-09-17 17:09:47 -07:00
|
|
|
*/
|
2018-09-17 17:09:48 -07:00
|
|
|
ret = vhost_scsi_chk_size(vq, &vc);
|
|
|
|
if (ret)
|
|
|
|
goto err;
|
2018-09-17 17:09:47 -07:00
|
|
|
|
2018-09-17 17:09:48 -07:00
|
|
|
/*
|
|
|
|
* Get the rest of the request now that its size is known.
|
|
|
|
*/
|
|
|
|
vc.req += typ_size;
|
|
|
|
vc.req_size -= typ_size;
|
2018-09-17 17:09:47 -07:00
|
|
|
|
2020-11-09 23:33:23 -06:00
|
|
|
ret = vhost_scsi_get_req(vq, &vc, &tpg);
|
2018-09-17 17:09:48 -07:00
|
|
|
if (ret)
|
|
|
|
goto err;
|
2018-09-17 17:09:47 -07:00
|
|
|
|
2018-09-17 17:09:48 -07:00
|
|
|
if (v_req.type == VIRTIO_SCSI_T_TMF)
|
2025-04-02 23:29:52 -07:00
|
|
|
vhost_scsi_handle_tmf(vs, tpg, vq, &v_req.tmf, &vc,
|
|
|
|
vq_log, log_num);
|
|
|
|
else {
|
2018-09-17 17:09:48 -07:00
|
|
|
vhost_scsi_send_an_resp(vs, vq, &vc);
|
2025-04-02 23:29:52 -07:00
|
|
|
vhost_scsi_log_write(vq, vq_log, log_num);
|
|
|
|
}
|
2018-09-17 17:09:48 -07:00
|
|
|
err:
|
|
|
|
/*
|
|
|
|
* ENXIO: No more requests, or read error, wait for next kick
|
|
|
|
* EINVAL: Invalid response buffer, drop the request
|
|
|
|
* EIO: Respond with bad target
|
|
|
|
* EAGAIN: Pending request
|
|
|
|
*/
|
|
|
|
if (ret == -ENXIO)
|
|
|
|
break;
|
2025-04-02 23:29:52 -07:00
|
|
|
else if (ret == -EIO) {
|
2025-04-02 23:29:47 -07:00
|
|
|
vhost_scsi_send_bad_target(vs, vq, &vc,
|
|
|
|
v_req.type == VIRTIO_SCSI_T_TMF ?
|
|
|
|
TYPE_CTRL_TMF :
|
|
|
|
TYPE_CTRL_AN);
|
2025-04-02 23:29:52 -07:00
|
|
|
vhost_scsi_log_write(vq, vq_log, log_num);
|
|
|
|
}
|
2019-05-17 00:29:52 -04:00
|
|
|
} while (likely(!vhost_exceeds_weight(vq, ++c, 0)));
|
2018-09-17 17:09:47 -07:00
|
|
|
out:
|
|
|
|
mutex_unlock(&vq->mutex);
|
|
|
|
}
|
|
|
|
|
2012-07-18 14:31:32 -07:00
|
|
|
static void vhost_scsi_ctl_handle_kick(struct vhost_work *work)
|
|
|
|
{
|
2018-09-17 17:09:47 -07:00
|
|
|
struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue,
|
|
|
|
poll.work);
|
|
|
|
struct vhost_scsi *vs = container_of(vq->dev, struct vhost_scsi, dev);
|
|
|
|
|
2012-07-30 13:30:00 -07:00
|
|
|
pr_debug("%s: The handling func for control queue.\n", __func__);
|
2018-09-17 17:09:47 -07:00
|
|
|
vhost_scsi_ctl_handle_vq(vs, vq);
|
2012-07-18 14:31:32 -07:00
|
|
|
}
|
|
|
|
|
2013-05-06 16:38:27 +08:00
|
|
|
static void
|
2023-06-26 18:23:00 -05:00
|
|
|
vhost_scsi_send_evt(struct vhost_scsi *vs, struct vhost_virtqueue *vq,
|
|
|
|
struct vhost_scsi_tpg *tpg, struct se_lun *lun,
|
|
|
|
u32 event, u32 reason)
|
2013-04-25 15:35:21 +08:00
|
|
|
{
|
2015-01-31 23:56:53 -08:00
|
|
|
struct vhost_scsi_evt *evt;
|
2013-04-25 15:35:21 +08:00
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
evt = vhost_scsi_allocate_evt(vs, event, reason);
|
2013-04-25 15:35:21 +08:00
|
|
|
if (!evt)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (tpg && lun) {
|
|
|
|
/* TODO: share lun setup code with virtio-scsi.ko */
|
|
|
|
/*
|
|
|
|
* Note: evt->event is zeroed when we allocate it and
|
|
|
|
* lun[4-7] need to be zero according to virtio-scsi spec.
|
|
|
|
*/
|
|
|
|
evt->event.lun[0] = 0x01;
|
2015-02-05 10:37:33 +03:00
|
|
|
evt->event.lun[1] = tpg->tport_tpgt;
|
2013-04-25 15:35:21 +08:00
|
|
|
if (lun->unpacked_lun >= 256)
|
|
|
|
evt->event.lun[2] = lun->unpacked_lun >> 8 | 0x40 ;
|
|
|
|
evt->event.lun[3] = lun->unpacked_lun & 0xFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
llist_add(&evt->list, &vs->vs_event_list);
|
2024-03-15 19:46:59 -05:00
|
|
|
if (!vhost_vq_work_queue(vq, &vs->vs_event_work))
|
|
|
|
vhost_scsi_complete_events(vs, true);
|
2013-04-25 15:35:21 +08:00
|
|
|
}
|
|
|
|
|
2012-07-18 14:31:32 -07:00
|
|
|
static void vhost_scsi_evt_handle_kick(struct vhost_work *work)
|
|
|
|
{
|
2013-04-25 15:35:21 +08:00
|
|
|
struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue,
|
|
|
|
poll.work);
|
|
|
|
struct vhost_scsi *vs = container_of(vq->dev, struct vhost_scsi, dev);
|
|
|
|
|
|
|
|
mutex_lock(&vq->mutex);
|
2020-03-31 21:27:57 +02:00
|
|
|
if (!vhost_vq_get_backend(vq))
|
2013-04-25 15:35:21 +08:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (vs->vs_events_missed)
|
2023-06-26 18:23:00 -05:00
|
|
|
vhost_scsi_send_evt(vs, vq, NULL, NULL, VIRTIO_SCSI_T_NO_EVENT,
|
|
|
|
0);
|
2013-04-25 15:35:21 +08:00
|
|
|
out:
|
|
|
|
mutex_unlock(&vq->mutex);
|
2012-07-18 14:31:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void vhost_scsi_handle_kick(struct vhost_work *work)
|
|
|
|
{
|
|
|
|
struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue,
|
|
|
|
poll.work);
|
|
|
|
struct vhost_scsi *vs = container_of(vq->dev, struct vhost_scsi, dev);
|
|
|
|
|
tcm_vhost: Multi-queue support
This adds virtio-scsi multi-queue support to tcm_vhost. In order to use
multi-queue, guest side multi-queue support is need. It can
be found here:
https://lkml.org/lkml/2012/12/18/166
Currently, only one thread is created by vhost core code for each
vhost_scsi instance. Even if there are multi-queues, all the handling of
guest kick (vhost_scsi_handle_kick) are processed in one thread. This is
not optimal. Luckily, most of the work is offloaded to the tcm_vhost
workqueue.
Some initial perf numbers:
1 queue, 4 targets, 1 lun per target
4K request size, 50% randread + 50% randwrite: 127K/127k IOPS
4 queues, 4 targets, 1 lun per target
4K request size, 50% randread + 50% randwrite: 181K/181k IOPS
Signed-off-by: Asias He <asias@redhat.com>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
2013-02-06 13:20:59 +08:00
|
|
|
vhost_scsi_handle_vq(vs, vq);
|
2012-07-18 14:31:32 -07:00
|
|
|
}
|
|
|
|
|
2013-04-28 15:38:52 +03:00
|
|
|
/* Callers must hold dev mutex */
|
2013-04-03 14:17:37 +08:00
|
|
|
static void vhost_scsi_flush(struct vhost_scsi *vs)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2013-04-27 11:16:49 +08:00
|
|
|
/* Init new inflight and remember the old inflight */
|
2022-07-07 22:05:25 -05:00
|
|
|
vhost_scsi_init_inflight(vs, vs->old_inflight);
|
2013-04-27 11:16:49 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The inflight->kref was initialized to 1. We decrement it here to
|
|
|
|
* indicate the start of the flush operation so that it will reach 0
|
|
|
|
* when all the reqs are finished.
|
|
|
|
*/
|
2022-07-07 22:05:25 -05:00
|
|
|
for (i = 0; i < vs->dev.nvqs; i++)
|
|
|
|
kref_put(&vs->old_inflight[i]->kref, vhost_scsi_done_inflight);
|
2013-04-27 11:16:49 +08:00
|
|
|
|
|
|
|
/* Flush both the vhost poll and vhost work */
|
2022-05-17 13:08:50 -05:00
|
|
|
vhost_dev_flush(&vs->dev);
|
2013-04-27 11:16:49 +08:00
|
|
|
|
|
|
|
/* Wait for all reqs issued before the flush to be finished */
|
2022-07-07 22:05:25 -05:00
|
|
|
for (i = 0; i < vs->dev.nvqs; i++)
|
|
|
|
wait_for_completion(&vs->old_inflight[i]->comp);
|
2013-04-03 14:17:37 +08:00
|
|
|
}
|
|
|
|
|
2025-04-02 23:29:51 -07:00
|
|
|
static void vhost_scsi_destroy_vq_log(struct vhost_virtqueue *vq)
|
|
|
|
{
|
|
|
|
struct vhost_scsi_virtqueue *svq = container_of(vq,
|
|
|
|
struct vhost_scsi_virtqueue, vq);
|
|
|
|
struct vhost_scsi_cmd *tv_cmd;
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
if (!svq->scsi_cmds)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < svq->max_cmds; i++) {
|
|
|
|
tv_cmd = &svq->scsi_cmds[i];
|
|
|
|
kfree(tv_cmd->tvc_log);
|
|
|
|
tv_cmd->tvc_log = NULL;
|
|
|
|
tv_cmd->tvc_log_num = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-09 23:33:20 -06:00
|
|
|
static void vhost_scsi_destroy_vq_cmds(struct vhost_virtqueue *vq)
|
|
|
|
{
|
|
|
|
struct vhost_scsi_virtqueue *svq = container_of(vq,
|
|
|
|
struct vhost_scsi_virtqueue, vq);
|
|
|
|
struct vhost_scsi_cmd *tv_cmd;
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
if (!svq->scsi_cmds)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < svq->max_cmds; i++) {
|
|
|
|
tv_cmd = &svq->scsi_cmds[i];
|
|
|
|
|
2024-12-03 13:15:12 -06:00
|
|
|
kfree(tv_cmd->sgl);
|
|
|
|
kfree(tv_cmd->prot_sgl);
|
2020-11-09 23:33:20 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
sbitmap_free(&svq->scsi_tags);
|
2024-12-03 13:15:08 -06:00
|
|
|
kfree(svq->upages);
|
2025-04-02 23:29:51 -07:00
|
|
|
vhost_scsi_destroy_vq_log(vq);
|
2020-11-09 23:33:20 -06:00
|
|
|
kfree(svq->scsi_cmds);
|
|
|
|
svq->scsi_cmds = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vhost_scsi_setup_vq_cmds(struct vhost_virtqueue *vq, int max_cmds)
|
|
|
|
{
|
|
|
|
struct vhost_scsi_virtqueue *svq = container_of(vq,
|
|
|
|
struct vhost_scsi_virtqueue, vq);
|
2024-12-03 13:15:12 -06:00
|
|
|
struct vhost_scsi *vs = svq->vs;
|
2020-11-09 23:33:20 -06:00
|
|
|
struct vhost_scsi_cmd *tv_cmd;
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
if (svq->scsi_cmds)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (sbitmap_init_node(&svq->scsi_tags, max_cmds, -1, GFP_KERNEL,
|
2021-01-22 10:33:08 +08:00
|
|
|
NUMA_NO_NODE, false, true))
|
2020-11-09 23:33:20 -06:00
|
|
|
return -ENOMEM;
|
|
|
|
svq->max_cmds = max_cmds;
|
|
|
|
|
|
|
|
svq->scsi_cmds = kcalloc(max_cmds, sizeof(*tv_cmd), GFP_KERNEL);
|
|
|
|
if (!svq->scsi_cmds) {
|
|
|
|
sbitmap_free(&svq->scsi_tags);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2024-12-03 13:15:08 -06:00
|
|
|
svq->upages = kcalloc(VHOST_SCSI_PREALLOC_UPAGES, sizeof(struct page *),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!svq->upages)
|
|
|
|
goto out;
|
|
|
|
|
2020-11-09 23:33:20 -06:00
|
|
|
for (i = 0; i < max_cmds; i++) {
|
|
|
|
tv_cmd = &svq->scsi_cmds[i];
|
|
|
|
|
2024-12-03 13:15:12 -06:00
|
|
|
if (vs->inline_sg_cnt) {
|
|
|
|
tv_cmd->sgl = kcalloc(vs->inline_sg_cnt,
|
|
|
|
sizeof(struct scatterlist),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!tv_cmd->sgl) {
|
|
|
|
pr_err("Unable to allocate tv_cmd->sgl\n");
|
|
|
|
goto out;
|
|
|
|
}
|
2020-11-09 23:33:20 -06:00
|
|
|
}
|
|
|
|
|
2024-12-03 13:15:12 -06:00
|
|
|
if (vhost_has_feature(vq, VIRTIO_SCSI_F_T10_PI) &&
|
|
|
|
vs->inline_sg_cnt) {
|
|
|
|
tv_cmd->prot_sgl = kcalloc(vs->inline_sg_cnt,
|
|
|
|
sizeof(struct scatterlist),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!tv_cmd->prot_sgl) {
|
|
|
|
pr_err("Unable to allocate tv_cmd->prot_sgl\n");
|
2024-12-03 13:15:09 -06:00
|
|
|
goto out;
|
|
|
|
}
|
2020-11-09 23:33:20 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
out:
|
|
|
|
vhost_scsi_destroy_vq_cmds(vq);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2012-07-18 14:31:32 -07:00
|
|
|
/*
|
|
|
|
* Called from vhost_scsi_ioctl() context to walk the list of available
|
2015-01-31 23:56:53 -08:00
|
|
|
* vhost_scsi_tpg with an active struct vhost_scsi_nexus
|
2013-04-25 15:35:20 +08:00
|
|
|
*
|
|
|
|
* The lock nesting rule is:
|
2023-03-20 21:06:24 -05:00
|
|
|
* vs->dev.mutex -> vhost_scsi_mutex -> tpg->tv_tpg_mutex -> vq->mutex
|
2012-07-18 14:31:32 -07:00
|
|
|
*/
|
2013-05-06 16:38:27 +08:00
|
|
|
static int
|
|
|
|
vhost_scsi_set_endpoint(struct vhost_scsi *vs,
|
|
|
|
struct vhost_scsi_target *t)
|
2012-07-18 14:31:32 -07:00
|
|
|
{
|
2014-10-08 06:19:20 +00:00
|
|
|
struct se_portal_group *se_tpg;
|
2015-01-31 23:56:53 -08:00
|
|
|
struct vhost_scsi_tport *tv_tport;
|
|
|
|
struct vhost_scsi_tpg *tpg;
|
|
|
|
struct vhost_scsi_tpg **vs_tpg;
|
2013-04-03 14:17:37 +08:00
|
|
|
struct vhost_virtqueue *vq;
|
|
|
|
int index, ret, i, len;
|
2013-02-05 12:31:57 +08:00
|
|
|
bool match = false;
|
2012-07-18 14:31:32 -07:00
|
|
|
|
|
|
|
mutex_lock(&vs->dev.mutex);
|
2013-04-25 15:35:20 +08:00
|
|
|
|
2012-07-18 14:31:32 -07:00
|
|
|
/* Verify that ring has been setup correctly. */
|
|
|
|
for (index = 0; index < vs->dev.nvqs; ++index) {
|
|
|
|
/* Verify that ring has been setup correctly. */
|
2013-04-27 11:16:48 +08:00
|
|
|
if (!vhost_vq_access_ok(&vs->vqs[index].vq)) {
|
2013-04-25 15:35:20 +08:00
|
|
|
ret = -EFAULT;
|
|
|
|
goto out;
|
2012-07-18 14:31:32 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
vhost-scsi: Fix handling of multiple calls to vhost_scsi_set_endpoint
If vhost_scsi_set_endpoint is called multiple times without a
vhost_scsi_clear_endpoint between them, we can hit multiple bugs
found by Haoran Zhang:
1. Use-after-free when no tpgs are found:
This fixes a use after free that occurs when vhost_scsi_set_endpoint is
called more than once and calls after the first call do not find any
tpgs to add to the vs_tpg. When vhost_scsi_set_endpoint first finds
tpgs to add to the vs_tpg array match=true, so we will do:
vhost_vq_set_backend(vq, vs_tpg);
...
kfree(vs->vs_tpg);
vs->vs_tpg = vs_tpg;
If vhost_scsi_set_endpoint is called again and no tpgs are found
match=false so we skip the vhost_vq_set_backend call leaving the
pointer to the vs_tpg we then free via:
kfree(vs->vs_tpg);
vs->vs_tpg = vs_tpg;
If a scsi request is then sent we do:
vhost_scsi_handle_vq -> vhost_scsi_get_req -> vhost_vq_get_backend
which sees the vs_tpg we just did a kfree on.
2. Tpg dir removal hang:
This patch fixes an issue where we cannot remove a LIO/target layer
tpg (and structs above it like the target) dir due to the refcount
dropping to -1.
The problem is that if vhost_scsi_set_endpoint detects a tpg is already
in the vs->vs_tpg array or if the tpg has been removed so
target_depend_item fails, the undepend goto handler will do
target_undepend_item on all tpgs in the vs_tpg array dropping their
refcount to 0. At this time vs_tpg contains both the tpgs we have added
in the current vhost_scsi_set_endpoint call as well as tpgs we added in
previous calls which are also in vs->vs_tpg.
Later, when vhost_scsi_clear_endpoint runs it will do
target_undepend_item on all the tpgs in the vs->vs_tpg which will drop
their refcount to -1. Userspace will then not be able to remove the tpg
and will hang when it tries to do rmdir on the tpg dir.
3. Tpg leak:
This fixes a bug where we can leak tpgs and cause them to be
un-removable because the target name is overwritten when
vhost_scsi_set_endpoint is called multiple times but with different
target names.
The bug occurs if a user has called VHOST_SCSI_SET_ENDPOINT and setup
a vhost-scsi device to target/tpg mapping, then calls
VHOST_SCSI_SET_ENDPOINT again with a new target name that has tpgs we
haven't seen before (target1 has tpg1 but target2 has tpg2). When this
happens we don't teardown the old target tpg mapping and just overwrite
the target name and the vs->vs_tpg array. Later when we do
vhost_scsi_clear_endpoint, we are passed in either target1 or target2's
name and we will only match that target's tpgs when we loop over the
vs->vs_tpg. We will then return from the function without doing
target_undepend_item on the tpgs.
Because of all these bugs, it looks like being able to call
vhost_scsi_set_endpoint multiple times was never supported. The major
user, QEMU, already has checks to prevent this use case. So to fix the
issues, this patch prevents vhost_scsi_set_endpoint from being called
if it's already successfully added tpgs. To add, remove or change the
tpg config or target name, you must do a vhost_scsi_clear_endpoint
first.
Fixes: 25b98b64e284 ("vhost scsi: alloc cmds per vq instead of session")
Fixes: 4f7f46d32c98 ("tcm_vhost: Use vq->private_data to indicate if the endpoint is setup")
Reported-by: Haoran Zhang <wh1sper@zju.edu.cn>
Closes: https://lore.kernel.org/virtualization/e418a5ee-45ca-4d18-9b5d-6f8b6b1add8e@oracle.com/T/#me6c0041ce376677419b9b2563494172a01487ecb
Signed-off-by: Mike Christie <michael.christie@oracle.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Message-Id: <20250129210922.121533-1-michael.christie@oracle.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Acked-by: Stefano Garzarella <sgarzare@redhat.com>
2025-01-29 15:09:22 -06:00
|
|
|
if (vs->vs_tpg) {
|
|
|
|
pr_err("vhost-scsi endpoint already set for %s.\n",
|
|
|
|
vs->vs_vhost_wwpn);
|
|
|
|
ret = -EEXIST;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2013-04-03 14:17:37 +08:00
|
|
|
len = sizeof(vs_tpg[0]) * VHOST_SCSI_MAX_TARGET;
|
|
|
|
vs_tpg = kzalloc(len, GFP_KERNEL);
|
|
|
|
if (!vs_tpg) {
|
2013-04-25 15:35:20 +08:00
|
|
|
ret = -ENOMEM;
|
|
|
|
goto out;
|
2013-04-03 14:17:37 +08:00
|
|
|
}
|
|
|
|
|
2023-03-20 21:06:24 -05:00
|
|
|
mutex_lock(&vhost_scsi_mutex);
|
2015-01-31 23:56:53 -08:00
|
|
|
list_for_each_entry(tpg, &vhost_scsi_list, tv_tpg_list) {
|
2013-05-06 16:38:28 +08:00
|
|
|
mutex_lock(&tpg->tv_tpg_mutex);
|
|
|
|
if (!tpg->tpg_nexus) {
|
|
|
|
mutex_unlock(&tpg->tv_tpg_mutex);
|
2012-07-18 14:31:32 -07:00
|
|
|
continue;
|
|
|
|
}
|
2013-05-06 16:38:28 +08:00
|
|
|
if (tpg->tv_tpg_vhost_count != 0) {
|
|
|
|
mutex_unlock(&tpg->tv_tpg_mutex);
|
2012-07-18 14:31:32 -07:00
|
|
|
continue;
|
|
|
|
}
|
2013-05-06 16:38:28 +08:00
|
|
|
tv_tport = tpg->tport;
|
2012-07-18 14:31:32 -07:00
|
|
|
|
2013-02-05 12:31:57 +08:00
|
|
|
if (!strcmp(tv_tport->tport_name, t->vhost_wwpn)) {
|
2014-10-08 06:19:20 +00:00
|
|
|
/*
|
|
|
|
* In order to ensure individual vhost-scsi configfs
|
|
|
|
* groups cannot be removed while in use by vhost ioctl,
|
|
|
|
* go ahead and take an explicit se_tpg->tpg_group.cg_item
|
|
|
|
* dependency now.
|
|
|
|
*/
|
|
|
|
se_tpg = &tpg->se_tpg;
|
2015-05-03 08:50:52 +02:00
|
|
|
ret = target_depend_item(&se_tpg->tpg_group.cg_item);
|
2014-10-08 06:19:20 +00:00
|
|
|
if (ret) {
|
2018-12-13 09:10:14 +08:00
|
|
|
pr_warn("target_depend_item() failed: %d\n", ret);
|
2014-10-08 06:19:20 +00:00
|
|
|
mutex_unlock(&tpg->tv_tpg_mutex);
|
2023-03-20 21:06:24 -05:00
|
|
|
mutex_unlock(&vhost_scsi_mutex);
|
2020-11-09 23:33:20 -06:00
|
|
|
goto undepend;
|
2014-10-08 06:19:20 +00:00
|
|
|
}
|
2013-05-06 16:38:28 +08:00
|
|
|
tpg->tv_tpg_vhost_count++;
|
|
|
|
tpg->vhost_scsi = vs;
|
|
|
|
vs_tpg[tpg->tport_tpgt] = tpg;
|
2013-02-05 12:31:57 +08:00
|
|
|
match = true;
|
2012-07-18 14:31:32 -07:00
|
|
|
}
|
2013-05-06 16:38:28 +08:00
|
|
|
mutex_unlock(&tpg->tv_tpg_mutex);
|
2012-07-18 14:31:32 -07:00
|
|
|
}
|
2023-03-20 21:06:24 -05:00
|
|
|
mutex_unlock(&vhost_scsi_mutex);
|
2013-02-05 12:31:57 +08:00
|
|
|
|
|
|
|
if (match) {
|
|
|
|
memcpy(vs->vs_vhost_wwpn, t->vhost_wwpn,
|
|
|
|
sizeof(vs->vs_vhost_wwpn));
|
2020-11-09 23:33:20 -06:00
|
|
|
|
2022-07-07 22:05:25 -05:00
|
|
|
for (i = VHOST_SCSI_VQ_IO; i < vs->dev.nvqs; i++) {
|
2020-11-09 23:33:20 -06:00
|
|
|
vq = &vs->vqs[i].vq;
|
|
|
|
if (!vhost_vq_is_setup(vq))
|
|
|
|
continue;
|
|
|
|
|
2020-12-04 16:43:30 +08:00
|
|
|
ret = vhost_scsi_setup_vq_cmds(vq, vq->num);
|
|
|
|
if (ret)
|
2020-11-09 23:33:20 -06:00
|
|
|
goto destroy_vq_cmds;
|
|
|
|
}
|
|
|
|
|
2022-07-07 22:05:25 -05:00
|
|
|
for (i = 0; i < vs->dev.nvqs; i++) {
|
2013-04-27 11:16:48 +08:00
|
|
|
vq = &vs->vqs[i].vq;
|
2013-04-03 14:17:37 +08:00
|
|
|
mutex_lock(&vq->mutex);
|
2020-03-31 21:27:57 +02:00
|
|
|
vhost_vq_set_backend(vq, vs_tpg);
|
2016-02-16 15:59:44 +01:00
|
|
|
vhost_vq_init_access(vq);
|
2013-04-03 14:17:37 +08:00
|
|
|
mutex_unlock(&vq->mutex);
|
|
|
|
}
|
2013-02-05 12:31:57 +08:00
|
|
|
ret = 0;
|
|
|
|
} else {
|
vhost-scsi: Fix handling of multiple calls to vhost_scsi_set_endpoint
If vhost_scsi_set_endpoint is called multiple times without a
vhost_scsi_clear_endpoint between them, we can hit multiple bugs
found by Haoran Zhang:
1. Use-after-free when no tpgs are found:
This fixes a use after free that occurs when vhost_scsi_set_endpoint is
called more than once and calls after the first call do not find any
tpgs to add to the vs_tpg. When vhost_scsi_set_endpoint first finds
tpgs to add to the vs_tpg array match=true, so we will do:
vhost_vq_set_backend(vq, vs_tpg);
...
kfree(vs->vs_tpg);
vs->vs_tpg = vs_tpg;
If vhost_scsi_set_endpoint is called again and no tpgs are found
match=false so we skip the vhost_vq_set_backend call leaving the
pointer to the vs_tpg we then free via:
kfree(vs->vs_tpg);
vs->vs_tpg = vs_tpg;
If a scsi request is then sent we do:
vhost_scsi_handle_vq -> vhost_scsi_get_req -> vhost_vq_get_backend
which sees the vs_tpg we just did a kfree on.
2. Tpg dir removal hang:
This patch fixes an issue where we cannot remove a LIO/target layer
tpg (and structs above it like the target) dir due to the refcount
dropping to -1.
The problem is that if vhost_scsi_set_endpoint detects a tpg is already
in the vs->vs_tpg array or if the tpg has been removed so
target_depend_item fails, the undepend goto handler will do
target_undepend_item on all tpgs in the vs_tpg array dropping their
refcount to 0. At this time vs_tpg contains both the tpgs we have added
in the current vhost_scsi_set_endpoint call as well as tpgs we added in
previous calls which are also in vs->vs_tpg.
Later, when vhost_scsi_clear_endpoint runs it will do
target_undepend_item on all the tpgs in the vs->vs_tpg which will drop
their refcount to -1. Userspace will then not be able to remove the tpg
and will hang when it tries to do rmdir on the tpg dir.
3. Tpg leak:
This fixes a bug where we can leak tpgs and cause them to be
un-removable because the target name is overwritten when
vhost_scsi_set_endpoint is called multiple times but with different
target names.
The bug occurs if a user has called VHOST_SCSI_SET_ENDPOINT and setup
a vhost-scsi device to target/tpg mapping, then calls
VHOST_SCSI_SET_ENDPOINT again with a new target name that has tpgs we
haven't seen before (target1 has tpg1 but target2 has tpg2). When this
happens we don't teardown the old target tpg mapping and just overwrite
the target name and the vs->vs_tpg array. Later when we do
vhost_scsi_clear_endpoint, we are passed in either target1 or target2's
name and we will only match that target's tpgs when we loop over the
vs->vs_tpg. We will then return from the function without doing
target_undepend_item on the tpgs.
Because of all these bugs, it looks like being able to call
vhost_scsi_set_endpoint multiple times was never supported. The major
user, QEMU, already has checks to prevent this use case. So to fix the
issues, this patch prevents vhost_scsi_set_endpoint from being called
if it's already successfully added tpgs. To add, remove or change the
tpg config or target name, you must do a vhost_scsi_clear_endpoint
first.
Fixes: 25b98b64e284 ("vhost scsi: alloc cmds per vq instead of session")
Fixes: 4f7f46d32c98 ("tcm_vhost: Use vq->private_data to indicate if the endpoint is setup")
Reported-by: Haoran Zhang <wh1sper@zju.edu.cn>
Closes: https://lore.kernel.org/virtualization/e418a5ee-45ca-4d18-9b5d-6f8b6b1add8e@oracle.com/T/#me6c0041ce376677419b9b2563494172a01487ecb
Signed-off-by: Mike Christie <michael.christie@oracle.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Message-Id: <20250129210922.121533-1-michael.christie@oracle.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Acked-by: Stefano Garzarella <sgarzare@redhat.com>
2025-01-29 15:09:22 -06:00
|
|
|
ret = -ENODEV;
|
|
|
|
goto free_tpg;
|
2013-02-05 12:31:57 +08:00
|
|
|
}
|
|
|
|
|
2013-04-03 14:17:37 +08:00
|
|
|
/*
|
vhost-scsi: Fix handling of multiple calls to vhost_scsi_set_endpoint
If vhost_scsi_set_endpoint is called multiple times without a
vhost_scsi_clear_endpoint between them, we can hit multiple bugs
found by Haoran Zhang:
1. Use-after-free when no tpgs are found:
This fixes a use after free that occurs when vhost_scsi_set_endpoint is
called more than once and calls after the first call do not find any
tpgs to add to the vs_tpg. When vhost_scsi_set_endpoint first finds
tpgs to add to the vs_tpg array match=true, so we will do:
vhost_vq_set_backend(vq, vs_tpg);
...
kfree(vs->vs_tpg);
vs->vs_tpg = vs_tpg;
If vhost_scsi_set_endpoint is called again and no tpgs are found
match=false so we skip the vhost_vq_set_backend call leaving the
pointer to the vs_tpg we then free via:
kfree(vs->vs_tpg);
vs->vs_tpg = vs_tpg;
If a scsi request is then sent we do:
vhost_scsi_handle_vq -> vhost_scsi_get_req -> vhost_vq_get_backend
which sees the vs_tpg we just did a kfree on.
2. Tpg dir removal hang:
This patch fixes an issue where we cannot remove a LIO/target layer
tpg (and structs above it like the target) dir due to the refcount
dropping to -1.
The problem is that if vhost_scsi_set_endpoint detects a tpg is already
in the vs->vs_tpg array or if the tpg has been removed so
target_depend_item fails, the undepend goto handler will do
target_undepend_item on all tpgs in the vs_tpg array dropping their
refcount to 0. At this time vs_tpg contains both the tpgs we have added
in the current vhost_scsi_set_endpoint call as well as tpgs we added in
previous calls which are also in vs->vs_tpg.
Later, when vhost_scsi_clear_endpoint runs it will do
target_undepend_item on all the tpgs in the vs->vs_tpg which will drop
their refcount to -1. Userspace will then not be able to remove the tpg
and will hang when it tries to do rmdir on the tpg dir.
3. Tpg leak:
This fixes a bug where we can leak tpgs and cause them to be
un-removable because the target name is overwritten when
vhost_scsi_set_endpoint is called multiple times but with different
target names.
The bug occurs if a user has called VHOST_SCSI_SET_ENDPOINT and setup
a vhost-scsi device to target/tpg mapping, then calls
VHOST_SCSI_SET_ENDPOINT again with a new target name that has tpgs we
haven't seen before (target1 has tpg1 but target2 has tpg2). When this
happens we don't teardown the old target tpg mapping and just overwrite
the target name and the vs->vs_tpg array. Later when we do
vhost_scsi_clear_endpoint, we are passed in either target1 or target2's
name and we will only match that target's tpgs when we loop over the
vs->vs_tpg. We will then return from the function without doing
target_undepend_item on the tpgs.
Because of all these bugs, it looks like being able to call
vhost_scsi_set_endpoint multiple times was never supported. The major
user, QEMU, already has checks to prevent this use case. So to fix the
issues, this patch prevents vhost_scsi_set_endpoint from being called
if it's already successfully added tpgs. To add, remove or change the
tpg config or target name, you must do a vhost_scsi_clear_endpoint
first.
Fixes: 25b98b64e284 ("vhost scsi: alloc cmds per vq instead of session")
Fixes: 4f7f46d32c98 ("tcm_vhost: Use vq->private_data to indicate if the endpoint is setup")
Reported-by: Haoran Zhang <wh1sper@zju.edu.cn>
Closes: https://lore.kernel.org/virtualization/e418a5ee-45ca-4d18-9b5d-6f8b6b1add8e@oracle.com/T/#me6c0041ce376677419b9b2563494172a01487ecb
Signed-off-by: Mike Christie <michael.christie@oracle.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Message-Id: <20250129210922.121533-1-michael.christie@oracle.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Acked-by: Stefano Garzarella <sgarzare@redhat.com>
2025-01-29 15:09:22 -06:00
|
|
|
* Act as synchronize_rcu to make sure requests after this point
|
|
|
|
* see a fully setup device.
|
2013-04-03 14:17:37 +08:00
|
|
|
*/
|
|
|
|
vhost_scsi_flush(vs);
|
|
|
|
vs->vs_tpg = vs_tpg;
|
2020-11-09 23:33:20 -06:00
|
|
|
goto out;
|
2013-04-03 14:17:37 +08:00
|
|
|
|
2020-11-09 23:33:20 -06:00
|
|
|
destroy_vq_cmds:
|
|
|
|
for (i--; i >= VHOST_SCSI_VQ_IO; i--) {
|
|
|
|
if (!vhost_vq_get_backend(&vs->vqs[i].vq))
|
|
|
|
vhost_scsi_destroy_vq_cmds(&vs->vqs[i].vq);
|
|
|
|
}
|
|
|
|
undepend:
|
|
|
|
for (i = 0; i < VHOST_SCSI_MAX_TARGET; i++) {
|
|
|
|
tpg = vs_tpg[i];
|
|
|
|
if (tpg) {
|
2023-03-20 21:06:18 -05:00
|
|
|
mutex_lock(&tpg->tv_tpg_mutex);
|
|
|
|
tpg->vhost_scsi = NULL;
|
2020-11-09 23:33:20 -06:00
|
|
|
tpg->tv_tpg_vhost_count--;
|
2023-03-20 21:06:18 -05:00
|
|
|
mutex_unlock(&tpg->tv_tpg_mutex);
|
2020-11-09 23:33:20 -06:00
|
|
|
target_undepend_item(&tpg->se_tpg.tpg_group.cg_item);
|
|
|
|
}
|
|
|
|
}
|
vhost-scsi: Fix handling of multiple calls to vhost_scsi_set_endpoint
If vhost_scsi_set_endpoint is called multiple times without a
vhost_scsi_clear_endpoint between them, we can hit multiple bugs
found by Haoran Zhang:
1. Use-after-free when no tpgs are found:
This fixes a use after free that occurs when vhost_scsi_set_endpoint is
called more than once and calls after the first call do not find any
tpgs to add to the vs_tpg. When vhost_scsi_set_endpoint first finds
tpgs to add to the vs_tpg array match=true, so we will do:
vhost_vq_set_backend(vq, vs_tpg);
...
kfree(vs->vs_tpg);
vs->vs_tpg = vs_tpg;
If vhost_scsi_set_endpoint is called again and no tpgs are found
match=false so we skip the vhost_vq_set_backend call leaving the
pointer to the vs_tpg we then free via:
kfree(vs->vs_tpg);
vs->vs_tpg = vs_tpg;
If a scsi request is then sent we do:
vhost_scsi_handle_vq -> vhost_scsi_get_req -> vhost_vq_get_backend
which sees the vs_tpg we just did a kfree on.
2. Tpg dir removal hang:
This patch fixes an issue where we cannot remove a LIO/target layer
tpg (and structs above it like the target) dir due to the refcount
dropping to -1.
The problem is that if vhost_scsi_set_endpoint detects a tpg is already
in the vs->vs_tpg array or if the tpg has been removed so
target_depend_item fails, the undepend goto handler will do
target_undepend_item on all tpgs in the vs_tpg array dropping their
refcount to 0. At this time vs_tpg contains both the tpgs we have added
in the current vhost_scsi_set_endpoint call as well as tpgs we added in
previous calls which are also in vs->vs_tpg.
Later, when vhost_scsi_clear_endpoint runs it will do
target_undepend_item on all the tpgs in the vs->vs_tpg which will drop
their refcount to -1. Userspace will then not be able to remove the tpg
and will hang when it tries to do rmdir on the tpg dir.
3. Tpg leak:
This fixes a bug where we can leak tpgs and cause them to be
un-removable because the target name is overwritten when
vhost_scsi_set_endpoint is called multiple times but with different
target names.
The bug occurs if a user has called VHOST_SCSI_SET_ENDPOINT and setup
a vhost-scsi device to target/tpg mapping, then calls
VHOST_SCSI_SET_ENDPOINT again with a new target name that has tpgs we
haven't seen before (target1 has tpg1 but target2 has tpg2). When this
happens we don't teardown the old target tpg mapping and just overwrite
the target name and the vs->vs_tpg array. Later when we do
vhost_scsi_clear_endpoint, we are passed in either target1 or target2's
name and we will only match that target's tpgs when we loop over the
vs->vs_tpg. We will then return from the function without doing
target_undepend_item on the tpgs.
Because of all these bugs, it looks like being able to call
vhost_scsi_set_endpoint multiple times was never supported. The major
user, QEMU, already has checks to prevent this use case. So to fix the
issues, this patch prevents vhost_scsi_set_endpoint from being called
if it's already successfully added tpgs. To add, remove or change the
tpg config or target name, you must do a vhost_scsi_clear_endpoint
first.
Fixes: 25b98b64e284 ("vhost scsi: alloc cmds per vq instead of session")
Fixes: 4f7f46d32c98 ("tcm_vhost: Use vq->private_data to indicate if the endpoint is setup")
Reported-by: Haoran Zhang <wh1sper@zju.edu.cn>
Closes: https://lore.kernel.org/virtualization/e418a5ee-45ca-4d18-9b5d-6f8b6b1add8e@oracle.com/T/#me6c0041ce376677419b9b2563494172a01487ecb
Signed-off-by: Mike Christie <michael.christie@oracle.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Message-Id: <20250129210922.121533-1-michael.christie@oracle.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Acked-by: Stefano Garzarella <sgarzare@redhat.com>
2025-01-29 15:09:22 -06:00
|
|
|
free_tpg:
|
2020-11-09 23:33:20 -06:00
|
|
|
kfree(vs_tpg);
|
2013-04-25 15:35:20 +08:00
|
|
|
out:
|
2013-02-05 12:31:57 +08:00
|
|
|
mutex_unlock(&vs->dev.mutex);
|
|
|
|
return ret;
|
2012-07-18 14:31:32 -07:00
|
|
|
}
|
|
|
|
|
2013-05-06 16:38:27 +08:00
|
|
|
static int
|
|
|
|
vhost_scsi_clear_endpoint(struct vhost_scsi *vs,
|
|
|
|
struct vhost_scsi_target *t)
|
2012-07-18 14:31:32 -07:00
|
|
|
{
|
2014-10-08 06:19:20 +00:00
|
|
|
struct se_portal_group *se_tpg;
|
2015-01-31 23:56:53 -08:00
|
|
|
struct vhost_scsi_tport *tv_tport;
|
|
|
|
struct vhost_scsi_tpg *tpg;
|
2013-04-03 14:17:37 +08:00
|
|
|
struct vhost_virtqueue *vq;
|
|
|
|
bool match = false;
|
2013-02-05 12:31:57 +08:00
|
|
|
int index, ret, i;
|
|
|
|
u8 target;
|
2012-07-18 14:31:32 -07:00
|
|
|
|
|
|
|
mutex_lock(&vs->dev.mutex);
|
|
|
|
/* Verify that ring has been setup correctly. */
|
|
|
|
for (index = 0; index < vs->dev.nvqs; ++index) {
|
2013-04-27 11:16:48 +08:00
|
|
|
if (!vhost_vq_access_ok(&vs->vqs[index].vq)) {
|
2012-07-30 13:30:00 -07:00
|
|
|
ret = -EFAULT;
|
2013-03-15 09:14:05 +08:00
|
|
|
goto err_dev;
|
2012-07-18 14:31:32 -07:00
|
|
|
}
|
|
|
|
}
|
2013-04-03 14:17:37 +08:00
|
|
|
|
|
|
|
if (!vs->vs_tpg) {
|
2013-04-25 15:35:20 +08:00
|
|
|
ret = 0;
|
|
|
|
goto err_dev;
|
2013-04-03 14:17:37 +08:00
|
|
|
}
|
|
|
|
|
2013-02-05 12:31:57 +08:00
|
|
|
for (i = 0; i < VHOST_SCSI_MAX_TARGET; i++) {
|
|
|
|
target = i;
|
2013-05-06 16:38:28 +08:00
|
|
|
tpg = vs->vs_tpg[target];
|
|
|
|
if (!tpg)
|
2013-02-05 12:31:57 +08:00
|
|
|
continue;
|
|
|
|
|
2013-05-06 16:38:28 +08:00
|
|
|
tv_tport = tpg->tport;
|
2013-02-05 12:31:57 +08:00
|
|
|
if (!tv_tport) {
|
|
|
|
ret = -ENODEV;
|
2023-03-20 21:06:20 -05:00
|
|
|
goto err_dev;
|
2013-02-05 12:31:57 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (strcmp(tv_tport->tport_name, t->vhost_wwpn)) {
|
2013-05-06 16:38:28 +08:00
|
|
|
pr_warn("tv_tport->tport_name: %s, tpg->tport_tpgt: %hu"
|
2013-02-05 12:31:57 +08:00
|
|
|
" does not match t->vhost_wwpn: %s, t->vhost_tpgt: %hu\n",
|
2013-05-06 16:38:28 +08:00
|
|
|
tv_tport->tport_name, tpg->tport_tpgt,
|
2013-02-05 12:31:57 +08:00
|
|
|
t->vhost_wwpn, t->vhost_tpgt);
|
|
|
|
ret = -EINVAL;
|
2023-03-20 21:06:20 -05:00
|
|
|
goto err_dev;
|
2013-02-05 12:31:57 +08:00
|
|
|
}
|
2023-03-20 21:06:20 -05:00
|
|
|
match = true;
|
|
|
|
}
|
|
|
|
if (!match)
|
|
|
|
goto free_vs_tpg;
|
|
|
|
|
|
|
|
/* Prevent new cmds from starting and accessing the tpgs/sessions */
|
|
|
|
for (i = 0; i < vs->dev.nvqs; i++) {
|
|
|
|
vq = &vs->vqs[i].vq;
|
|
|
|
mutex_lock(&vq->mutex);
|
|
|
|
vhost_vq_set_backend(vq, NULL);
|
|
|
|
mutex_unlock(&vq->mutex);
|
|
|
|
}
|
|
|
|
/* Make sure cmds are not running before tearing them down. */
|
|
|
|
vhost_scsi_flush(vs);
|
|
|
|
|
|
|
|
for (i = 0; i < vs->dev.nvqs; i++) {
|
|
|
|
vq = &vs->vqs[i].vq;
|
|
|
|
vhost_scsi_destroy_vq_cmds(vq);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We can now release our hold on the tpg and sessions and userspace
|
|
|
|
* can free them after this point.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < VHOST_SCSI_MAX_TARGET; i++) {
|
|
|
|
target = i;
|
|
|
|
tpg = vs->vs_tpg[target];
|
|
|
|
if (!tpg)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
mutex_lock(&tpg->tv_tpg_mutex);
|
|
|
|
|
2013-05-06 16:38:28 +08:00
|
|
|
tpg->tv_tpg_vhost_count--;
|
|
|
|
tpg->vhost_scsi = NULL;
|
2013-02-05 12:31:57 +08:00
|
|
|
vs->vs_tpg[target] = NULL;
|
2023-03-20 21:06:20 -05:00
|
|
|
|
2013-05-06 16:38:28 +08:00
|
|
|
mutex_unlock(&tpg->tv_tpg_mutex);
|
2023-03-20 21:06:20 -05:00
|
|
|
|
2014-10-08 06:19:20 +00:00
|
|
|
se_tpg = &tpg->se_tpg;
|
2015-05-03 08:50:52 +02:00
|
|
|
target_undepend_item(&se_tpg->tpg_group.cg_item);
|
2012-07-18 14:31:32 -07:00
|
|
|
}
|
2021-05-25 12:47:31 -05:00
|
|
|
|
2023-03-20 21:06:20 -05:00
|
|
|
free_vs_tpg:
|
2013-04-03 14:17:37 +08:00
|
|
|
/*
|
|
|
|
* Act as synchronize_rcu to make sure access to
|
|
|
|
* old vs->vs_tpg is finished.
|
|
|
|
*/
|
|
|
|
vhost_scsi_flush(vs);
|
|
|
|
kfree(vs->vs_tpg);
|
|
|
|
vs->vs_tpg = NULL;
|
vhost-scsi: Fix handling of multiple calls to vhost_scsi_set_endpoint
If vhost_scsi_set_endpoint is called multiple times without a
vhost_scsi_clear_endpoint between them, we can hit multiple bugs
found by Haoran Zhang:
1. Use-after-free when no tpgs are found:
This fixes a use after free that occurs when vhost_scsi_set_endpoint is
called more than once and calls after the first call do not find any
tpgs to add to the vs_tpg. When vhost_scsi_set_endpoint first finds
tpgs to add to the vs_tpg array match=true, so we will do:
vhost_vq_set_backend(vq, vs_tpg);
...
kfree(vs->vs_tpg);
vs->vs_tpg = vs_tpg;
If vhost_scsi_set_endpoint is called again and no tpgs are found
match=false so we skip the vhost_vq_set_backend call leaving the
pointer to the vs_tpg we then free via:
kfree(vs->vs_tpg);
vs->vs_tpg = vs_tpg;
If a scsi request is then sent we do:
vhost_scsi_handle_vq -> vhost_scsi_get_req -> vhost_vq_get_backend
which sees the vs_tpg we just did a kfree on.
2. Tpg dir removal hang:
This patch fixes an issue where we cannot remove a LIO/target layer
tpg (and structs above it like the target) dir due to the refcount
dropping to -1.
The problem is that if vhost_scsi_set_endpoint detects a tpg is already
in the vs->vs_tpg array or if the tpg has been removed so
target_depend_item fails, the undepend goto handler will do
target_undepend_item on all tpgs in the vs_tpg array dropping their
refcount to 0. At this time vs_tpg contains both the tpgs we have added
in the current vhost_scsi_set_endpoint call as well as tpgs we added in
previous calls which are also in vs->vs_tpg.
Later, when vhost_scsi_clear_endpoint runs it will do
target_undepend_item on all the tpgs in the vs->vs_tpg which will drop
their refcount to -1. Userspace will then not be able to remove the tpg
and will hang when it tries to do rmdir on the tpg dir.
3. Tpg leak:
This fixes a bug where we can leak tpgs and cause them to be
un-removable because the target name is overwritten when
vhost_scsi_set_endpoint is called multiple times but with different
target names.
The bug occurs if a user has called VHOST_SCSI_SET_ENDPOINT and setup
a vhost-scsi device to target/tpg mapping, then calls
VHOST_SCSI_SET_ENDPOINT again with a new target name that has tpgs we
haven't seen before (target1 has tpg1 but target2 has tpg2). When this
happens we don't teardown the old target tpg mapping and just overwrite
the target name and the vs->vs_tpg array. Later when we do
vhost_scsi_clear_endpoint, we are passed in either target1 or target2's
name and we will only match that target's tpgs when we loop over the
vs->vs_tpg. We will then return from the function without doing
target_undepend_item on the tpgs.
Because of all these bugs, it looks like being able to call
vhost_scsi_set_endpoint multiple times was never supported. The major
user, QEMU, already has checks to prevent this use case. So to fix the
issues, this patch prevents vhost_scsi_set_endpoint from being called
if it's already successfully added tpgs. To add, remove or change the
tpg config or target name, you must do a vhost_scsi_clear_endpoint
first.
Fixes: 25b98b64e284 ("vhost scsi: alloc cmds per vq instead of session")
Fixes: 4f7f46d32c98 ("tcm_vhost: Use vq->private_data to indicate if the endpoint is setup")
Reported-by: Haoran Zhang <wh1sper@zju.edu.cn>
Closes: https://lore.kernel.org/virtualization/e418a5ee-45ca-4d18-9b5d-6f8b6b1add8e@oracle.com/T/#me6c0041ce376677419b9b2563494172a01487ecb
Signed-off-by: Mike Christie <michael.christie@oracle.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Message-Id: <20250129210922.121533-1-michael.christie@oracle.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Acked-by: Stefano Garzarella <sgarzare@redhat.com>
2025-01-29 15:09:22 -06:00
|
|
|
memset(vs->vs_vhost_wwpn, 0, sizeof(vs->vs_vhost_wwpn));
|
2013-04-25 15:35:21 +08:00
|
|
|
WARN_ON(vs->vs_events_nr);
|
2012-07-18 14:31:32 -07:00
|
|
|
mutex_unlock(&vs->dev.mutex);
|
|
|
|
return 0;
|
2012-07-30 13:30:00 -07:00
|
|
|
|
2013-03-15 09:14:05 +08:00
|
|
|
err_dev:
|
2012-07-30 13:30:00 -07:00
|
|
|
mutex_unlock(&vs->dev.mutex);
|
|
|
|
return ret;
|
2012-07-18 14:31:32 -07:00
|
|
|
}
|
|
|
|
|
2013-04-03 14:17:37 +08:00
|
|
|
static int vhost_scsi_set_features(struct vhost_scsi *vs, u64 features)
|
|
|
|
{
|
2014-06-05 15:20:23 +03:00
|
|
|
struct vhost_virtqueue *vq;
|
2025-04-02 23:29:51 -07:00
|
|
|
bool is_log, was_log;
|
2014-06-05 15:20:23 +03:00
|
|
|
int i;
|
|
|
|
|
2013-04-03 14:17:37 +08:00
|
|
|
if (features & ~VHOST_SCSI_FEATURES)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
mutex_lock(&vs->dev.mutex);
|
|
|
|
if ((features & (1 << VHOST_F_LOG_ALL)) &&
|
|
|
|
!vhost_log_access_ok(&vs->dev)) {
|
|
|
|
mutex_unlock(&vs->dev.mutex);
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
2014-06-05 15:20:23 +03:00
|
|
|
|
2025-04-02 23:29:51 -07:00
|
|
|
if (!vs->dev.nvqs)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
is_log = features & (1 << VHOST_F_LOG_ALL);
|
|
|
|
/*
|
|
|
|
* All VQs should have same feature.
|
|
|
|
*/
|
|
|
|
was_log = vhost_has_feature(&vs->vqs[0].vq, VHOST_F_LOG_ALL);
|
|
|
|
|
2022-07-07 22:05:25 -05:00
|
|
|
for (i = 0; i < vs->dev.nvqs; i++) {
|
2014-06-05 15:20:23 +03:00
|
|
|
vq = &vs->vqs[i].vq;
|
|
|
|
mutex_lock(&vq->mutex);
|
|
|
|
vq->acked_features = features;
|
|
|
|
mutex_unlock(&vq->mutex);
|
|
|
|
}
|
2025-04-02 23:29:51 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If VHOST_F_LOG_ALL is removed, free tvc_log after
|
|
|
|
* vq->acked_features is committed.
|
|
|
|
*/
|
|
|
|
if (!is_log && was_log) {
|
|
|
|
for (i = VHOST_SCSI_VQ_IO; i < vs->dev.nvqs; i++) {
|
|
|
|
if (!vs->vqs[i].scsi_cmds)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
vq = &vs->vqs[i].vq;
|
|
|
|
mutex_lock(&vq->mutex);
|
|
|
|
vhost_scsi_destroy_vq_log(vq);
|
|
|
|
mutex_unlock(&vq->mutex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
2013-04-03 14:17:37 +08:00
|
|
|
mutex_unlock(&vs->dev.mutex);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-07-18 14:31:32 -07:00
|
|
|
static int vhost_scsi_open(struct inode *inode, struct file *f)
|
|
|
|
{
|
2023-06-26 18:22:59 -05:00
|
|
|
struct vhost_scsi_virtqueue *svq;
|
2013-05-06 16:38:26 +08:00
|
|
|
struct vhost_scsi *vs;
|
2013-04-27 11:16:48 +08:00
|
|
|
struct vhost_virtqueue **vqs;
|
2022-07-07 22:05:25 -05:00
|
|
|
int r = -ENOMEM, i, nvqs = vhost_scsi_max_io_vqs;
|
2012-07-18 14:31:32 -07:00
|
|
|
|
2021-01-23 00:08:53 -08:00
|
|
|
vs = kvzalloc(sizeof(*vs), GFP_KERNEL);
|
|
|
|
if (!vs)
|
|
|
|
goto err_vs;
|
2024-12-03 13:15:12 -06:00
|
|
|
vs->inline_sg_cnt = vhost_scsi_inline_sg_cnt;
|
2012-07-18 14:31:32 -07:00
|
|
|
|
2022-07-07 22:05:25 -05:00
|
|
|
if (nvqs > VHOST_SCSI_MAX_IO_VQ) {
|
|
|
|
pr_err("Invalid max_io_vqs of %d. Using %d.\n", nvqs,
|
|
|
|
VHOST_SCSI_MAX_IO_VQ);
|
|
|
|
nvqs = VHOST_SCSI_MAX_IO_VQ;
|
|
|
|
} else if (nvqs == 0) {
|
|
|
|
pr_err("Invalid max_io_vqs of %d. Using 1.\n", nvqs);
|
|
|
|
nvqs = 1;
|
|
|
|
}
|
|
|
|
nvqs += VHOST_SCSI_VQ_IO;
|
|
|
|
|
|
|
|
vs->old_inflight = kmalloc_array(nvqs, sizeof(*vs->old_inflight),
|
|
|
|
GFP_KERNEL | __GFP_ZERO);
|
|
|
|
if (!vs->old_inflight)
|
|
|
|
goto err_inflight;
|
|
|
|
|
|
|
|
vs->vqs = kmalloc_array(nvqs, sizeof(*vs->vqs),
|
|
|
|
GFP_KERNEL | __GFP_ZERO);
|
|
|
|
if (!vs->vqs)
|
2013-09-17 09:30:34 +03:00
|
|
|
goto err_vqs;
|
2013-04-27 11:16:48 +08:00
|
|
|
|
2022-07-07 22:05:25 -05:00
|
|
|
vqs = kmalloc_array(nvqs, sizeof(*vqs), GFP_KERNEL);
|
|
|
|
if (!vqs)
|
|
|
|
goto err_local_vqs;
|
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
vhost_work_init(&vs->vs_event_work, vhost_scsi_evt_work);
|
2013-04-25 15:35:21 +08:00
|
|
|
|
2013-05-06 16:38:26 +08:00
|
|
|
vs->vs_events_nr = 0;
|
|
|
|
vs->vs_events_missed = false;
|
2012-07-18 14:31:32 -07:00
|
|
|
|
2013-05-06 16:38:26 +08:00
|
|
|
vqs[VHOST_SCSI_VQ_CTL] = &vs->vqs[VHOST_SCSI_VQ_CTL].vq;
|
|
|
|
vqs[VHOST_SCSI_VQ_EVT] = &vs->vqs[VHOST_SCSI_VQ_EVT].vq;
|
|
|
|
vs->vqs[VHOST_SCSI_VQ_CTL].vq.handle_kick = vhost_scsi_ctl_handle_kick;
|
|
|
|
vs->vqs[VHOST_SCSI_VQ_EVT].vq.handle_kick = vhost_scsi_evt_handle_kick;
|
2022-07-07 22:05:25 -05:00
|
|
|
for (i = VHOST_SCSI_VQ_IO; i < nvqs; i++) {
|
2023-06-26 18:22:59 -05:00
|
|
|
svq = &vs->vqs[i];
|
|
|
|
|
|
|
|
vqs[i] = &svq->vq;
|
|
|
|
svq->vs = vs;
|
|
|
|
init_llist_head(&svq->completion_list);
|
|
|
|
vhost_work_init(&svq->completion_work,
|
|
|
|
vhost_scsi_complete_cmd_work);
|
|
|
|
svq->vq.handle_kick = vhost_scsi_handle_kick;
|
2013-04-27 11:16:48 +08:00
|
|
|
}
|
2022-07-07 22:05:25 -05:00
|
|
|
vhost_dev_init(&vs->dev, vqs, nvqs, UIO_MAXIOV,
|
2020-05-29 16:02:58 +08:00
|
|
|
VHOST_SCSI_WEIGHT, 0, true, NULL);
|
2013-04-27 11:16:49 +08:00
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
vhost_scsi_init_inflight(vs, NULL);
|
2013-04-27 11:16:49 +08:00
|
|
|
|
2013-05-06 16:38:26 +08:00
|
|
|
f->private_data = vs;
|
2012-07-18 14:31:32 -07:00
|
|
|
return 0;
|
2013-09-17 09:30:34 +03:00
|
|
|
|
2022-07-07 22:05:25 -05:00
|
|
|
err_local_vqs:
|
|
|
|
kfree(vs->vqs);
|
2013-09-17 09:30:34 +03:00
|
|
|
err_vqs:
|
2022-07-07 22:05:25 -05:00
|
|
|
kfree(vs->old_inflight);
|
|
|
|
err_inflight:
|
2014-06-12 19:00:01 +03:00
|
|
|
kvfree(vs);
|
2013-09-17 09:30:34 +03:00
|
|
|
err_vs:
|
|
|
|
return r;
|
2012-07-18 14:31:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int vhost_scsi_release(struct inode *inode, struct file *f)
|
|
|
|
{
|
2013-05-06 16:38:26 +08:00
|
|
|
struct vhost_scsi *vs = f->private_data;
|
2013-02-05 12:31:57 +08:00
|
|
|
struct vhost_scsi_target t;
|
2012-07-18 14:31:32 -07:00
|
|
|
|
2013-05-06 16:38:26 +08:00
|
|
|
mutex_lock(&vs->dev.mutex);
|
|
|
|
memcpy(t.vhost_wwpn, vs->vs_vhost_wwpn, sizeof(t.vhost_wwpn));
|
|
|
|
mutex_unlock(&vs->dev.mutex);
|
|
|
|
vhost_scsi_clear_endpoint(vs, &t);
|
|
|
|
vhost_dev_stop(&vs->dev);
|
2017-12-25 00:08:58 +08:00
|
|
|
vhost_dev_cleanup(&vs->dev);
|
2013-05-06 16:38:26 +08:00
|
|
|
kfree(vs->dev.vqs);
|
2022-07-07 22:05:25 -05:00
|
|
|
kfree(vs->vqs);
|
|
|
|
kfree(vs->old_inflight);
|
2014-06-12 19:00:01 +03:00
|
|
|
kvfree(vs);
|
2012-07-18 14:31:32 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-05-06 16:38:27 +08:00
|
|
|
static long
|
|
|
|
vhost_scsi_ioctl(struct file *f,
|
|
|
|
unsigned int ioctl,
|
|
|
|
unsigned long arg)
|
2012-07-18 14:31:32 -07:00
|
|
|
{
|
|
|
|
struct vhost_scsi *vs = f->private_data;
|
|
|
|
struct vhost_scsi_target backend;
|
|
|
|
void __user *argp = (void __user *)arg;
|
|
|
|
u64 __user *featurep = argp;
|
2013-04-25 15:35:22 +08:00
|
|
|
u32 __user *eventsp = argp;
|
|
|
|
u32 events_missed;
|
2012-07-18 14:31:32 -07:00
|
|
|
u64 features;
|
2012-07-30 13:30:00 -07:00
|
|
|
int r, abi_version = VHOST_SCSI_ABI_VERSION;
|
2013-04-27 11:16:48 +08:00
|
|
|
struct vhost_virtqueue *vq = &vs->vqs[VHOST_SCSI_VQ_EVT].vq;
|
2012-07-18 14:31:32 -07:00
|
|
|
|
|
|
|
switch (ioctl) {
|
|
|
|
case VHOST_SCSI_SET_ENDPOINT:
|
|
|
|
if (copy_from_user(&backend, argp, sizeof backend))
|
|
|
|
return -EFAULT;
|
2012-08-18 15:44:09 -07:00
|
|
|
if (backend.reserved != 0)
|
|
|
|
return -EOPNOTSUPP;
|
2012-07-18 14:31:32 -07:00
|
|
|
|
|
|
|
return vhost_scsi_set_endpoint(vs, &backend);
|
|
|
|
case VHOST_SCSI_CLEAR_ENDPOINT:
|
|
|
|
if (copy_from_user(&backend, argp, sizeof backend))
|
|
|
|
return -EFAULT;
|
2012-08-18 15:44:09 -07:00
|
|
|
if (backend.reserved != 0)
|
|
|
|
return -EOPNOTSUPP;
|
2012-07-18 14:31:32 -07:00
|
|
|
|
|
|
|
return vhost_scsi_clear_endpoint(vs, &backend);
|
|
|
|
case VHOST_SCSI_GET_ABI_VERSION:
|
2012-07-30 13:30:00 -07:00
|
|
|
if (copy_to_user(argp, &abi_version, sizeof abi_version))
|
2012-07-18 14:31:32 -07:00
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
2013-04-25 15:35:22 +08:00
|
|
|
case VHOST_SCSI_SET_EVENTS_MISSED:
|
|
|
|
if (get_user(events_missed, eventsp))
|
|
|
|
return -EFAULT;
|
|
|
|
mutex_lock(&vq->mutex);
|
|
|
|
vs->vs_events_missed = events_missed;
|
|
|
|
mutex_unlock(&vq->mutex);
|
|
|
|
return 0;
|
|
|
|
case VHOST_SCSI_GET_EVENTS_MISSED:
|
|
|
|
mutex_lock(&vq->mutex);
|
|
|
|
events_missed = vs->vs_events_missed;
|
|
|
|
mutex_unlock(&vq->mutex);
|
|
|
|
if (put_user(events_missed, eventsp))
|
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
2012-07-18 14:31:32 -07:00
|
|
|
case VHOST_GET_FEATURES:
|
2013-03-27 17:23:41 -07:00
|
|
|
features = VHOST_SCSI_FEATURES;
|
2012-07-18 14:31:32 -07:00
|
|
|
if (copy_to_user(featurep, &features, sizeof features))
|
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
case VHOST_SET_FEATURES:
|
|
|
|
if (copy_from_user(&features, featurep, sizeof features))
|
|
|
|
return -EFAULT;
|
|
|
|
return vhost_scsi_set_features(vs, features);
|
2023-06-26 18:23:06 -05:00
|
|
|
case VHOST_NEW_WORKER:
|
|
|
|
case VHOST_FREE_WORKER:
|
|
|
|
case VHOST_ATTACH_VRING_WORKER:
|
|
|
|
case VHOST_GET_VRING_WORKER:
|
|
|
|
mutex_lock(&vs->dev.mutex);
|
|
|
|
r = vhost_worker_ioctl(&vs->dev, ioctl, argp);
|
|
|
|
mutex_unlock(&vs->dev.mutex);
|
|
|
|
return r;
|
2012-07-18 14:31:32 -07:00
|
|
|
default:
|
|
|
|
mutex_lock(&vs->dev.mutex);
|
2012-12-06 14:03:34 +02:00
|
|
|
r = vhost_dev_ioctl(&vs->dev, ioctl, argp);
|
|
|
|
/* TODO: flush backend after dev ioctl. */
|
|
|
|
if (r == -ENOIOCTLCMD)
|
|
|
|
r = vhost_vring_ioctl(&vs->dev, ioctl, argp);
|
2012-07-18 14:31:32 -07:00
|
|
|
mutex_unlock(&vs->dev.mutex);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct file_operations vhost_scsi_fops = {
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.release = vhost_scsi_release,
|
|
|
|
.unlocked_ioctl = vhost_scsi_ioctl,
|
2018-09-11 17:23:00 +02:00
|
|
|
.compat_ioctl = compat_ptr_ioctl,
|
2012-07-18 14:31:32 -07:00
|
|
|
.open = vhost_scsi_open,
|
|
|
|
.llseek = noop_llseek,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct miscdevice vhost_scsi_misc = {
|
|
|
|
MISC_DYNAMIC_MINOR,
|
|
|
|
"vhost-scsi",
|
|
|
|
&vhost_scsi_fops,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int __init vhost_scsi_register(void)
|
|
|
|
{
|
|
|
|
return misc_register(&vhost_scsi_misc);
|
|
|
|
}
|
|
|
|
|
2015-07-30 15:59:57 -07:00
|
|
|
static void vhost_scsi_deregister(void)
|
2012-07-18 14:31:32 -07:00
|
|
|
{
|
2015-07-30 15:59:57 -07:00
|
|
|
misc_deregister(&vhost_scsi_misc);
|
2012-07-18 14:31:32 -07:00
|
|
|
}
|
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
static char *vhost_scsi_dump_proto_id(struct vhost_scsi_tport *tport)
|
2012-07-18 14:31:32 -07:00
|
|
|
{
|
|
|
|
switch (tport->tport_proto_id) {
|
|
|
|
case SCSI_PROTOCOL_SAS:
|
|
|
|
return "SAS";
|
|
|
|
case SCSI_PROTOCOL_FCP:
|
|
|
|
return "FCP";
|
|
|
|
case SCSI_PROTOCOL_ISCSI:
|
|
|
|
return "iSCSI";
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return "Unknown";
|
|
|
|
}
|
|
|
|
|
2013-05-06 16:38:27 +08:00
|
|
|
static void
|
2015-01-31 23:56:53 -08:00
|
|
|
vhost_scsi_do_plug(struct vhost_scsi_tpg *tpg,
|
2013-05-06 16:38:27 +08:00
|
|
|
struct se_lun *lun, bool plug)
|
2013-04-25 15:35:21 +08:00
|
|
|
{
|
|
|
|
|
|
|
|
struct vhost_scsi *vs = tpg->vhost_scsi;
|
|
|
|
struct vhost_virtqueue *vq;
|
|
|
|
u32 reason;
|
|
|
|
|
|
|
|
if (!vs)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (plug)
|
|
|
|
reason = VIRTIO_SCSI_EVT_RESET_RESCAN;
|
|
|
|
else
|
|
|
|
reason = VIRTIO_SCSI_EVT_RESET_REMOVED;
|
|
|
|
|
2013-04-27 11:16:48 +08:00
|
|
|
vq = &vs->vqs[VHOST_SCSI_VQ_EVT].vq;
|
2013-04-25 15:35:21 +08:00
|
|
|
mutex_lock(&vq->mutex);
|
2023-03-20 21:06:22 -05:00
|
|
|
/*
|
|
|
|
* We can't queue events if the backend has been cleared, because
|
|
|
|
* we could end up queueing an event after the flush.
|
|
|
|
*/
|
|
|
|
if (!vhost_vq_get_backend(vq))
|
|
|
|
goto unlock;
|
|
|
|
|
2014-06-05 15:20:23 +03:00
|
|
|
if (vhost_has_feature(vq, VIRTIO_SCSI_F_HOTPLUG))
|
2023-06-26 18:23:00 -05:00
|
|
|
vhost_scsi_send_evt(vs, vq, tpg, lun,
|
2014-06-05 15:20:23 +03:00
|
|
|
VIRTIO_SCSI_T_TRANSPORT_RESET, reason);
|
2023-03-20 21:06:22 -05:00
|
|
|
unlock:
|
2013-04-25 15:35:21 +08:00
|
|
|
mutex_unlock(&vq->mutex);
|
|
|
|
}
|
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
static void vhost_scsi_hotplug(struct vhost_scsi_tpg *tpg, struct se_lun *lun)
|
2013-04-25 15:35:21 +08:00
|
|
|
{
|
2015-01-31 23:56:53 -08:00
|
|
|
vhost_scsi_do_plug(tpg, lun, true);
|
2013-04-25 15:35:21 +08:00
|
|
|
}
|
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
static void vhost_scsi_hotunplug(struct vhost_scsi_tpg *tpg, struct se_lun *lun)
|
2013-04-25 15:35:21 +08:00
|
|
|
{
|
2015-01-31 23:56:53 -08:00
|
|
|
vhost_scsi_do_plug(tpg, lun, false);
|
2013-04-25 15:35:21 +08:00
|
|
|
}
|
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
static int vhost_scsi_port_link(struct se_portal_group *se_tpg,
|
2013-05-06 16:38:27 +08:00
|
|
|
struct se_lun *lun)
|
2012-07-18 14:31:32 -07:00
|
|
|
{
|
2015-01-31 23:56:53 -08:00
|
|
|
struct vhost_scsi_tpg *tpg = container_of(se_tpg,
|
|
|
|
struct vhost_scsi_tpg, se_tpg);
|
2012-07-18 14:31:32 -07:00
|
|
|
|
2013-05-06 16:38:28 +08:00
|
|
|
mutex_lock(&tpg->tv_tpg_mutex);
|
|
|
|
tpg->tv_tpg_port_count++;
|
2015-01-31 23:56:53 -08:00
|
|
|
vhost_scsi_hotplug(tpg, lun);
|
2023-03-20 21:06:23 -05:00
|
|
|
mutex_unlock(&tpg->tv_tpg_mutex);
|
2013-04-25 15:35:21 +08:00
|
|
|
|
2012-07-18 14:31:32 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
static void vhost_scsi_port_unlink(struct se_portal_group *se_tpg,
|
2013-05-06 16:38:27 +08:00
|
|
|
struct se_lun *lun)
|
2012-07-18 14:31:32 -07:00
|
|
|
{
|
2015-01-31 23:56:53 -08:00
|
|
|
struct vhost_scsi_tpg *tpg = container_of(se_tpg,
|
|
|
|
struct vhost_scsi_tpg, se_tpg);
|
2012-07-18 14:31:32 -07:00
|
|
|
|
2013-05-06 16:38:28 +08:00
|
|
|
mutex_lock(&tpg->tv_tpg_mutex);
|
|
|
|
tpg->tv_tpg_port_count--;
|
2015-01-31 23:56:53 -08:00
|
|
|
vhost_scsi_hotunplug(tpg, lun);
|
2023-03-20 21:06:23 -05:00
|
|
|
mutex_unlock(&tpg->tv_tpg_mutex);
|
2012-07-18 14:31:32 -07:00
|
|
|
}
|
|
|
|
|
2015-10-03 15:32:55 +02:00
|
|
|
static ssize_t vhost_scsi_tpg_attrib_fabric_prot_type_store(
|
|
|
|
struct config_item *item, const char *page, size_t count)
|
2015-03-28 00:03:51 -07:00
|
|
|
{
|
2015-10-03 15:32:55 +02:00
|
|
|
struct se_portal_group *se_tpg = attrib_to_tpg(item);
|
2015-03-28 00:03:51 -07:00
|
|
|
struct vhost_scsi_tpg *tpg = container_of(se_tpg,
|
|
|
|
struct vhost_scsi_tpg, se_tpg);
|
|
|
|
unsigned long val;
|
|
|
|
int ret = kstrtoul(page, 0, &val);
|
|
|
|
|
|
|
|
if (ret) {
|
|
|
|
pr_err("kstrtoul() returned %d for fabric_prot_type\n", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
if (val != 0 && val != 1 && val != 3) {
|
|
|
|
pr_err("Invalid vhost_scsi fabric_prot_type: %lu\n", val);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
tpg->tv_fabric_prot_type = val;
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2015-10-03 15:32:55 +02:00
|
|
|
static ssize_t vhost_scsi_tpg_attrib_fabric_prot_type_show(
|
|
|
|
struct config_item *item, char *page)
|
2015-03-28 00:03:51 -07:00
|
|
|
{
|
2015-10-03 15:32:55 +02:00
|
|
|
struct se_portal_group *se_tpg = attrib_to_tpg(item);
|
2015-03-28 00:03:51 -07:00
|
|
|
struct vhost_scsi_tpg *tpg = container_of(se_tpg,
|
|
|
|
struct vhost_scsi_tpg, se_tpg);
|
|
|
|
|
2023-01-29 04:11:45 -05:00
|
|
|
return sysfs_emit(page, "%d\n", tpg->tv_fabric_prot_type);
|
2015-03-28 00:03:51 -07:00
|
|
|
}
|
2015-10-03 15:32:55 +02:00
|
|
|
|
|
|
|
CONFIGFS_ATTR(vhost_scsi_tpg_attrib_, fabric_prot_type);
|
2015-03-28 00:03:51 -07:00
|
|
|
|
|
|
|
static struct configfs_attribute *vhost_scsi_tpg_attrib_attrs[] = {
|
2015-10-03 15:32:55 +02:00
|
|
|
&vhost_scsi_tpg_attrib_attr_fabric_prot_type,
|
2015-03-28 00:03:51 -07:00
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
2016-01-09 05:47:55 -08:00
|
|
|
static int vhost_scsi_make_nexus(struct vhost_scsi_tpg *tpg,
|
|
|
|
const char *name)
|
|
|
|
{
|
|
|
|
struct vhost_scsi_nexus *tv_nexus;
|
|
|
|
|
|
|
|
mutex_lock(&tpg->tv_tpg_mutex);
|
|
|
|
if (tpg->tpg_nexus) {
|
|
|
|
mutex_unlock(&tpg->tv_tpg_mutex);
|
|
|
|
pr_debug("tpg->tpg_nexus already exists\n");
|
|
|
|
return -EEXIST;
|
|
|
|
}
|
|
|
|
|
2017-05-20 13:48:44 +02:00
|
|
|
tv_nexus = kzalloc(sizeof(*tv_nexus), GFP_KERNEL);
|
2016-01-09 05:47:55 -08:00
|
|
|
if (!tv_nexus) {
|
|
|
|
mutex_unlock(&tpg->tv_tpg_mutex);
|
|
|
|
pr_err("Unable to allocate struct vhost_scsi_nexus\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2012-07-18 14:31:32 -07:00
|
|
|
/*
|
2025-06-11 07:39:21 -07:00
|
|
|
* Since we are running in 'demo mode' this call will generate a
|
2015-01-31 23:56:53 -08:00
|
|
|
* struct se_node_acl for the vhost_scsi struct se_portal_group with
|
2012-07-18 14:31:32 -07:00
|
|
|
* the SCSI Initiator port name of the passed configfs group 'name'.
|
|
|
|
*/
|
2020-11-09 23:33:20 -06:00
|
|
|
tv_nexus->tvn_se_sess = target_setup_session(&tpg->se_tpg, 0, 0,
|
2016-01-09 05:47:55 -08:00
|
|
|
TARGET_PROT_DIN_PASS | TARGET_PROT_DOUT_PASS,
|
2020-11-09 23:33:20 -06:00
|
|
|
(unsigned char *)name, tv_nexus, NULL);
|
2016-01-09 05:47:55 -08:00
|
|
|
if (IS_ERR(tv_nexus->tvn_se_sess)) {
|
2013-05-06 16:38:28 +08:00
|
|
|
mutex_unlock(&tpg->tv_tpg_mutex);
|
2016-01-09 05:47:55 -08:00
|
|
|
kfree(tv_nexus);
|
|
|
|
return -ENOMEM;
|
2012-07-18 14:31:32 -07:00
|
|
|
}
|
2013-05-06 16:38:28 +08:00
|
|
|
tpg->tpg_nexus = tv_nexus;
|
2012-07-18 14:31:32 -07:00
|
|
|
|
2013-05-06 16:38:28 +08:00
|
|
|
mutex_unlock(&tpg->tv_tpg_mutex);
|
2012-07-18 14:31:32 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
static int vhost_scsi_drop_nexus(struct vhost_scsi_tpg *tpg)
|
2012-07-18 14:31:32 -07:00
|
|
|
{
|
|
|
|
struct se_session *se_sess;
|
2015-01-31 23:56:53 -08:00
|
|
|
struct vhost_scsi_nexus *tv_nexus;
|
2012-07-18 14:31:32 -07:00
|
|
|
|
|
|
|
mutex_lock(&tpg->tv_tpg_mutex);
|
|
|
|
tv_nexus = tpg->tpg_nexus;
|
|
|
|
if (!tv_nexus) {
|
|
|
|
mutex_unlock(&tpg->tv_tpg_mutex);
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
se_sess = tv_nexus->tvn_se_sess;
|
|
|
|
if (!se_sess) {
|
|
|
|
mutex_unlock(&tpg->tv_tpg_mutex);
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
2012-07-30 13:30:00 -07:00
|
|
|
if (tpg->tv_tpg_port_count != 0) {
|
2012-07-18 14:31:32 -07:00
|
|
|
mutex_unlock(&tpg->tv_tpg_mutex);
|
2012-07-30 13:30:00 -07:00
|
|
|
pr_err("Unable to remove TCM_vhost I_T Nexus with"
|
2012-07-18 14:31:32 -07:00
|
|
|
" active TPG port count: %d\n",
|
2012-07-30 13:30:00 -07:00
|
|
|
tpg->tv_tpg_port_count);
|
|
|
|
return -EBUSY;
|
2012-07-18 14:31:32 -07:00
|
|
|
}
|
|
|
|
|
2012-07-30 13:30:00 -07:00
|
|
|
if (tpg->tv_tpg_vhost_count != 0) {
|
2012-07-18 14:31:32 -07:00
|
|
|
mutex_unlock(&tpg->tv_tpg_mutex);
|
2012-07-30 13:30:00 -07:00
|
|
|
pr_err("Unable to remove TCM_vhost I_T Nexus with"
|
2012-07-18 14:31:32 -07:00
|
|
|
" active TPG vhost count: %d\n",
|
2012-07-30 13:30:00 -07:00
|
|
|
tpg->tv_tpg_vhost_count);
|
|
|
|
return -EBUSY;
|
2012-07-18 14:31:32 -07:00
|
|
|
}
|
|
|
|
|
2012-07-30 13:30:00 -07:00
|
|
|
pr_debug("TCM_vhost_ConfigFS: Removing I_T Nexus to emulated"
|
2015-01-31 23:56:53 -08:00
|
|
|
" %s Initiator Port: %s\n", vhost_scsi_dump_proto_id(tpg->tport),
|
2012-07-18 14:31:32 -07:00
|
|
|
tv_nexus->tvn_se_sess->se_node_acl->initiatorname);
|
2013-06-21 14:32:04 -07:00
|
|
|
|
2012-07-18 14:31:32 -07:00
|
|
|
/*
|
2012-07-30 13:30:00 -07:00
|
|
|
* Release the SCSI I_T Nexus to the emulated vhost Target Port
|
2012-07-18 14:31:32 -07:00
|
|
|
*/
|
2018-08-02 12:12:27 -05:00
|
|
|
target_remove_session(se_sess);
|
2012-07-18 14:31:32 -07:00
|
|
|
tpg->tpg_nexus = NULL;
|
|
|
|
mutex_unlock(&tpg->tv_tpg_mutex);
|
|
|
|
|
|
|
|
kfree(tv_nexus);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-03 15:32:55 +02:00
|
|
|
static ssize_t vhost_scsi_tpg_nexus_show(struct config_item *item, char *page)
|
2012-07-18 14:31:32 -07:00
|
|
|
{
|
2015-10-03 15:32:55 +02:00
|
|
|
struct se_portal_group *se_tpg = to_tpg(item);
|
2015-01-31 23:56:53 -08:00
|
|
|
struct vhost_scsi_tpg *tpg = container_of(se_tpg,
|
|
|
|
struct vhost_scsi_tpg, se_tpg);
|
|
|
|
struct vhost_scsi_nexus *tv_nexus;
|
2012-07-18 14:31:32 -07:00
|
|
|
ssize_t ret;
|
|
|
|
|
2013-05-06 16:38:28 +08:00
|
|
|
mutex_lock(&tpg->tv_tpg_mutex);
|
|
|
|
tv_nexus = tpg->tpg_nexus;
|
2012-07-18 14:31:32 -07:00
|
|
|
if (!tv_nexus) {
|
2013-05-06 16:38:28 +08:00
|
|
|
mutex_unlock(&tpg->tv_tpg_mutex);
|
2012-07-18 14:31:32 -07:00
|
|
|
return -ENODEV;
|
|
|
|
}
|
2023-01-29 04:11:45 -05:00
|
|
|
ret = sysfs_emit(page, "%s\n",
|
2012-07-18 14:31:32 -07:00
|
|
|
tv_nexus->tvn_se_sess->se_node_acl->initiatorname);
|
2013-05-06 16:38:28 +08:00
|
|
|
mutex_unlock(&tpg->tv_tpg_mutex);
|
2012-07-18 14:31:32 -07:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-10-03 15:32:55 +02:00
|
|
|
static ssize_t vhost_scsi_tpg_nexus_store(struct config_item *item,
|
|
|
|
const char *page, size_t count)
|
2012-07-18 14:31:32 -07:00
|
|
|
{
|
2015-10-03 15:32:55 +02:00
|
|
|
struct se_portal_group *se_tpg = to_tpg(item);
|
2015-01-31 23:56:53 -08:00
|
|
|
struct vhost_scsi_tpg *tpg = container_of(se_tpg,
|
|
|
|
struct vhost_scsi_tpg, se_tpg);
|
|
|
|
struct vhost_scsi_tport *tport_wwn = tpg->tport;
|
|
|
|
unsigned char i_port[VHOST_SCSI_NAMELEN], *ptr, *port_ptr;
|
2012-07-18 14:31:32 -07:00
|
|
|
int ret;
|
|
|
|
/*
|
|
|
|
* Shutdown the active I_T nexus if 'NULL' is passed..
|
|
|
|
*/
|
|
|
|
if (!strncmp(page, "NULL", 4)) {
|
2015-01-31 23:56:53 -08:00
|
|
|
ret = vhost_scsi_drop_nexus(tpg);
|
2012-07-18 14:31:32 -07:00
|
|
|
return (!ret) ? count : ret;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Otherwise make sure the passed virtual Initiator port WWN matches
|
2015-01-31 23:56:53 -08:00
|
|
|
* the fabric protocol_id set in vhost_scsi_make_tport(), and call
|
|
|
|
* vhost_scsi_make_nexus().
|
2012-07-18 14:31:32 -07:00
|
|
|
*/
|
2015-01-31 23:56:53 -08:00
|
|
|
if (strlen(page) >= VHOST_SCSI_NAMELEN) {
|
2012-07-18 14:31:32 -07:00
|
|
|
pr_err("Emulated NAA Sas Address: %s, exceeds"
|
2015-01-31 23:56:53 -08:00
|
|
|
" max: %d\n", page, VHOST_SCSI_NAMELEN);
|
2012-07-18 14:31:32 -07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2015-01-31 23:56:53 -08:00
|
|
|
snprintf(&i_port[0], VHOST_SCSI_NAMELEN, "%s", page);
|
2012-07-18 14:31:32 -07:00
|
|
|
|
|
|
|
ptr = strstr(i_port, "naa.");
|
|
|
|
if (ptr) {
|
|
|
|
if (tport_wwn->tport_proto_id != SCSI_PROTOCOL_SAS) {
|
|
|
|
pr_err("Passed SAS Initiator Port %s does not"
|
|
|
|
" match target port protoid: %s\n", i_port,
|
2015-01-31 23:56:53 -08:00
|
|
|
vhost_scsi_dump_proto_id(tport_wwn));
|
2012-07-18 14:31:32 -07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
port_ptr = &i_port[0];
|
|
|
|
goto check_newline;
|
|
|
|
}
|
|
|
|
ptr = strstr(i_port, "fc.");
|
|
|
|
if (ptr) {
|
|
|
|
if (tport_wwn->tport_proto_id != SCSI_PROTOCOL_FCP) {
|
|
|
|
pr_err("Passed FCP Initiator Port %s does not"
|
|
|
|
" match target port protoid: %s\n", i_port,
|
2015-01-31 23:56:53 -08:00
|
|
|
vhost_scsi_dump_proto_id(tport_wwn));
|
2012-07-18 14:31:32 -07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
port_ptr = &i_port[3]; /* Skip over "fc." */
|
|
|
|
goto check_newline;
|
|
|
|
}
|
|
|
|
ptr = strstr(i_port, "iqn.");
|
|
|
|
if (ptr) {
|
|
|
|
if (tport_wwn->tport_proto_id != SCSI_PROTOCOL_ISCSI) {
|
|
|
|
pr_err("Passed iSCSI Initiator Port %s does not"
|
|
|
|
" match target port protoid: %s\n", i_port,
|
2015-01-31 23:56:53 -08:00
|
|
|
vhost_scsi_dump_proto_id(tport_wwn));
|
2012-07-18 14:31:32 -07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
port_ptr = &i_port[0];
|
|
|
|
goto check_newline;
|
|
|
|
}
|
|
|
|
pr_err("Unable to locate prefix for emulated Initiator Port:"
|
|
|
|
" %s\n", i_port);
|
|
|
|
return -EINVAL;
|
|
|
|
/*
|
|
|
|
* Clear any trailing newline for the NAA WWN
|
|
|
|
*/
|
|
|
|
check_newline:
|
|
|
|
if (i_port[strlen(i_port)-1] == '\n')
|
|
|
|
i_port[strlen(i_port)-1] = '\0';
|
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
ret = vhost_scsi_make_nexus(tpg, port_ptr);
|
2012-07-18 14:31:32 -07:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2015-10-03 15:32:55 +02:00
|
|
|
CONFIGFS_ATTR(vhost_scsi_tpg_, nexus);
|
2012-07-18 14:31:32 -07:00
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
static struct configfs_attribute *vhost_scsi_tpg_attrs[] = {
|
2015-10-03 15:32:55 +02:00
|
|
|
&vhost_scsi_tpg_attr_nexus,
|
2012-07-18 14:31:32 -07:00
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
2013-05-06 16:38:27 +08:00
|
|
|
static struct se_portal_group *
|
2018-06-22 14:53:02 -07:00
|
|
|
vhost_scsi_make_tpg(struct se_wwn *wwn, const char *name)
|
2012-07-18 14:31:32 -07:00
|
|
|
{
|
2015-01-31 23:56:53 -08:00
|
|
|
struct vhost_scsi_tport *tport = container_of(wwn,
|
|
|
|
struct vhost_scsi_tport, tport_wwn);
|
2012-07-18 14:31:32 -07:00
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
struct vhost_scsi_tpg *tpg;
|
2015-02-05 10:37:33 +03:00
|
|
|
u16 tpgt;
|
2012-07-18 14:31:32 -07:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (strstr(name, "tpgt_") != name)
|
|
|
|
return ERR_PTR(-EINVAL);
|
2015-02-05 10:37:33 +03:00
|
|
|
if (kstrtou16(name + 5, 10, &tpgt) || tpgt >= VHOST_SCSI_MAX_TARGET)
|
2012-07-18 14:31:32 -07:00
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
|
2017-05-20 13:48:44 +02:00
|
|
|
tpg = kzalloc(sizeof(*tpg), GFP_KERNEL);
|
2012-07-18 14:31:32 -07:00
|
|
|
if (!tpg) {
|
2015-01-31 23:56:53 -08:00
|
|
|
pr_err("Unable to allocate struct vhost_scsi_tpg");
|
2012-07-18 14:31:32 -07:00
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
}
|
|
|
|
mutex_init(&tpg->tv_tpg_mutex);
|
|
|
|
INIT_LIST_HEAD(&tpg->tv_tpg_list);
|
|
|
|
tpg->tport = tport;
|
|
|
|
tpg->tport_tpgt = tpgt;
|
|
|
|
|
2015-05-20 21:48:03 -07:00
|
|
|
ret = core_tpg_register(wwn, &tpg->se_tpg, tport->tport_proto_id);
|
2012-07-18 14:31:32 -07:00
|
|
|
if (ret < 0) {
|
|
|
|
kfree(tpg);
|
|
|
|
return NULL;
|
|
|
|
}
|
2015-01-31 23:56:53 -08:00
|
|
|
mutex_lock(&vhost_scsi_mutex);
|
|
|
|
list_add_tail(&tpg->tv_tpg_list, &vhost_scsi_list);
|
|
|
|
mutex_unlock(&vhost_scsi_mutex);
|
2012-07-18 14:31:32 -07:00
|
|
|
|
|
|
|
return &tpg->se_tpg;
|
|
|
|
}
|
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
static void vhost_scsi_drop_tpg(struct se_portal_group *se_tpg)
|
2012-07-18 14:31:32 -07:00
|
|
|
{
|
2015-01-31 23:56:53 -08:00
|
|
|
struct vhost_scsi_tpg *tpg = container_of(se_tpg,
|
|
|
|
struct vhost_scsi_tpg, se_tpg);
|
2012-07-18 14:31:32 -07:00
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
mutex_lock(&vhost_scsi_mutex);
|
2012-07-18 14:31:32 -07:00
|
|
|
list_del(&tpg->tv_tpg_list);
|
2015-01-31 23:56:53 -08:00
|
|
|
mutex_unlock(&vhost_scsi_mutex);
|
2012-07-18 14:31:32 -07:00
|
|
|
/*
|
2012-07-30 13:30:00 -07:00
|
|
|
* Release the virtual I_T Nexus for this vhost TPG
|
2012-07-18 14:31:32 -07:00
|
|
|
*/
|
2015-01-31 23:56:53 -08:00
|
|
|
vhost_scsi_drop_nexus(tpg);
|
2012-07-18 14:31:32 -07:00
|
|
|
/*
|
|
|
|
* Deregister the se_tpg from TCM..
|
|
|
|
*/
|
|
|
|
core_tpg_deregister(se_tpg);
|
|
|
|
kfree(tpg);
|
|
|
|
}
|
|
|
|
|
2013-05-06 16:38:27 +08:00
|
|
|
static struct se_wwn *
|
2015-01-31 23:56:53 -08:00
|
|
|
vhost_scsi_make_tport(struct target_fabric_configfs *tf,
|
2013-05-06 16:38:27 +08:00
|
|
|
struct config_group *group,
|
|
|
|
const char *name)
|
2012-07-18 14:31:32 -07:00
|
|
|
{
|
2015-01-31 23:56:53 -08:00
|
|
|
struct vhost_scsi_tport *tport;
|
2012-07-18 14:31:32 -07:00
|
|
|
char *ptr;
|
|
|
|
u64 wwpn = 0;
|
|
|
|
int off = 0;
|
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
/* if (vhost_scsi_parse_wwn(name, &wwpn, 1) < 0)
|
2012-07-18 14:31:32 -07:00
|
|
|
return ERR_PTR(-EINVAL); */
|
|
|
|
|
2017-05-20 13:48:44 +02:00
|
|
|
tport = kzalloc(sizeof(*tport), GFP_KERNEL);
|
2012-07-18 14:31:32 -07:00
|
|
|
if (!tport) {
|
2015-01-31 23:56:53 -08:00
|
|
|
pr_err("Unable to allocate struct vhost_scsi_tport");
|
2012-07-18 14:31:32 -07:00
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
}
|
|
|
|
tport->tport_wwpn = wwpn;
|
|
|
|
/*
|
|
|
|
* Determine the emulated Protocol Identifier and Target Port Name
|
|
|
|
* based on the incoming configfs directory name.
|
|
|
|
*/
|
|
|
|
ptr = strstr(name, "naa.");
|
|
|
|
if (ptr) {
|
|
|
|
tport->tport_proto_id = SCSI_PROTOCOL_SAS;
|
|
|
|
goto check_len;
|
|
|
|
}
|
|
|
|
ptr = strstr(name, "fc.");
|
|
|
|
if (ptr) {
|
|
|
|
tport->tport_proto_id = SCSI_PROTOCOL_FCP;
|
|
|
|
off = 3; /* Skip over "fc." */
|
|
|
|
goto check_len;
|
|
|
|
}
|
|
|
|
ptr = strstr(name, "iqn.");
|
|
|
|
if (ptr) {
|
|
|
|
tport->tport_proto_id = SCSI_PROTOCOL_ISCSI;
|
|
|
|
goto check_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
pr_err("Unable to locate prefix for emulated Target Port:"
|
|
|
|
" %s\n", name);
|
|
|
|
kfree(tport);
|
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
|
|
|
|
check_len:
|
2015-01-31 23:56:53 -08:00
|
|
|
if (strlen(name) >= VHOST_SCSI_NAMELEN) {
|
2012-07-18 14:31:32 -07:00
|
|
|
pr_err("Emulated %s Address: %s, exceeds"
|
2015-01-31 23:56:53 -08:00
|
|
|
" max: %d\n", name, vhost_scsi_dump_proto_id(tport),
|
|
|
|
VHOST_SCSI_NAMELEN);
|
2012-07-18 14:31:32 -07:00
|
|
|
kfree(tport);
|
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
}
|
2015-01-31 23:56:53 -08:00
|
|
|
snprintf(&tport->tport_name[0], VHOST_SCSI_NAMELEN, "%s", &name[off]);
|
2012-07-18 14:31:32 -07:00
|
|
|
|
|
|
|
pr_debug("TCM_VHost_ConfigFS: Allocated emulated Target"
|
2015-01-31 23:56:53 -08:00
|
|
|
" %s Address: %s\n", vhost_scsi_dump_proto_id(tport), name);
|
2012-07-18 14:31:32 -07:00
|
|
|
|
|
|
|
return &tport->tport_wwn;
|
|
|
|
}
|
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
static void vhost_scsi_drop_tport(struct se_wwn *wwn)
|
2012-07-18 14:31:32 -07:00
|
|
|
{
|
2015-01-31 23:56:53 -08:00
|
|
|
struct vhost_scsi_tport *tport = container_of(wwn,
|
|
|
|
struct vhost_scsi_tport, tport_wwn);
|
2012-07-18 14:31:32 -07:00
|
|
|
|
|
|
|
pr_debug("TCM_VHost_ConfigFS: Deallocating emulated Target"
|
2015-01-31 23:56:53 -08:00
|
|
|
" %s Address: %s\n", vhost_scsi_dump_proto_id(tport),
|
2012-07-18 14:31:32 -07:00
|
|
|
tport->tport_name);
|
|
|
|
|
|
|
|
kfree(tport);
|
|
|
|
}
|
|
|
|
|
2013-05-06 16:38:27 +08:00
|
|
|
static ssize_t
|
2015-10-03 15:32:55 +02:00
|
|
|
vhost_scsi_wwn_version_show(struct config_item *item, char *page)
|
2012-07-18 14:31:32 -07:00
|
|
|
{
|
2023-01-29 04:11:45 -05:00
|
|
|
return sysfs_emit(page, "TCM_VHOST fabric module %s on %s/%s"
|
2025-06-11 07:39:21 -07:00
|
|
|
" on "UTS_RELEASE"\n", VHOST_SCSI_VERSION, utsname()->sysname,
|
2012-07-18 14:31:32 -07:00
|
|
|
utsname()->machine);
|
|
|
|
}
|
|
|
|
|
2015-10-03 15:32:55 +02:00
|
|
|
CONFIGFS_ATTR_RO(vhost_scsi_wwn_, version);
|
2012-07-18 14:31:32 -07:00
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
static struct configfs_attribute *vhost_scsi_wwn_attrs[] = {
|
2015-10-03 15:32:55 +02:00
|
|
|
&vhost_scsi_wwn_attr_version,
|
2012-07-18 14:31:32 -07:00
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
2017-01-09 20:51:02 +05:30
|
|
|
static const struct target_core_fabric_ops vhost_scsi_ops = {
|
2015-04-08 20:01:35 +02:00
|
|
|
.module = THIS_MODULE,
|
2018-11-23 18:36:12 +01:00
|
|
|
.fabric_name = "vhost",
|
2020-05-22 16:51:57 +00:00
|
|
|
.max_data_sg_nents = VHOST_SCSI_PREALLOC_SGLS,
|
2015-01-31 23:56:53 -08:00
|
|
|
.tpg_get_wwn = vhost_scsi_get_fabric_wwn,
|
|
|
|
.tpg_get_tag = vhost_scsi_get_tpgt,
|
|
|
|
.tpg_check_demo_mode = vhost_scsi_check_true,
|
|
|
|
.tpg_check_demo_mode_cache = vhost_scsi_check_true,
|
2015-03-28 00:03:51 -07:00
|
|
|
.tpg_check_prot_fabric_only = vhost_scsi_check_prot_fabric_only,
|
2015-01-31 23:56:53 -08:00
|
|
|
.release_cmd = vhost_scsi_release_cmd,
|
2013-06-06 02:20:41 -07:00
|
|
|
.check_stop_free = vhost_scsi_check_stop_free,
|
2012-07-18 14:31:32 -07:00
|
|
|
.sess_get_initiator_sid = NULL,
|
2015-01-31 23:56:53 -08:00
|
|
|
.write_pending = vhost_scsi_write_pending,
|
|
|
|
.queue_data_in = vhost_scsi_queue_data_in,
|
|
|
|
.queue_status = vhost_scsi_queue_status,
|
|
|
|
.queue_tm_rsp = vhost_scsi_queue_tm_rsp,
|
|
|
|
.aborted_task = vhost_scsi_aborted_task,
|
2012-07-18 14:31:32 -07:00
|
|
|
/*
|
|
|
|
* Setup callers for generic logic in target_core_fabric_configfs.c
|
|
|
|
*/
|
2015-01-31 23:56:53 -08:00
|
|
|
.fabric_make_wwn = vhost_scsi_make_tport,
|
|
|
|
.fabric_drop_wwn = vhost_scsi_drop_tport,
|
|
|
|
.fabric_make_tpg = vhost_scsi_make_tpg,
|
|
|
|
.fabric_drop_tpg = vhost_scsi_drop_tpg,
|
|
|
|
.fabric_post_link = vhost_scsi_port_link,
|
|
|
|
.fabric_pre_unlink = vhost_scsi_port_unlink,
|
2015-04-08 20:01:35 +02:00
|
|
|
|
|
|
|
.tfc_wwn_attrs = vhost_scsi_wwn_attrs,
|
|
|
|
.tfc_tpg_base_attrs = vhost_scsi_tpg_attrs,
|
|
|
|
.tfc_tpg_attrib_attrs = vhost_scsi_tpg_attrib_attrs,
|
2023-09-27 21:09:01 -05:00
|
|
|
|
|
|
|
.default_submit_type = TARGET_QUEUE_SUBMIT,
|
|
|
|
.direct_submit_supp = 1,
|
2012-07-18 14:31:32 -07:00
|
|
|
};
|
|
|
|
|
2015-04-08 20:01:35 +02:00
|
|
|
static int __init vhost_scsi_init(void)
|
2012-07-18 14:31:32 -07:00
|
|
|
{
|
2015-04-08 20:01:35 +02:00
|
|
|
int ret = -ENOMEM;
|
2012-07-18 14:31:32 -07:00
|
|
|
|
2015-04-08 20:01:35 +02:00
|
|
|
pr_debug("TCM_VHOST fabric module %s on %s/%s"
|
2015-01-31 23:56:53 -08:00
|
|
|
" on "UTS_RELEASE"\n", VHOST_SCSI_VERSION, utsname()->sysname,
|
2012-07-18 14:31:32 -07:00
|
|
|
utsname()->machine);
|
|
|
|
|
|
|
|
ret = vhost_scsi_register();
|
|
|
|
if (ret < 0)
|
2021-02-27 10:59:58 -06:00
|
|
|
goto out;
|
2012-07-18 14:31:32 -07:00
|
|
|
|
2015-04-08 20:01:35 +02:00
|
|
|
ret = target_register_template(&vhost_scsi_ops);
|
2012-07-18 14:31:32 -07:00
|
|
|
if (ret < 0)
|
|
|
|
goto out_vhost_scsi_deregister;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
out_vhost_scsi_deregister:
|
|
|
|
vhost_scsi_deregister();
|
|
|
|
out:
|
|
|
|
return ret;
|
2025-06-11 07:39:21 -07:00
|
|
|
}
|
2012-07-18 14:31:32 -07:00
|
|
|
|
2015-01-31 23:56:53 -08:00
|
|
|
static void vhost_scsi_exit(void)
|
2012-07-18 14:31:32 -07:00
|
|
|
{
|
2015-04-08 20:01:35 +02:00
|
|
|
target_unregister_template(&vhost_scsi_ops);
|
2012-07-18 14:31:32 -07:00
|
|
|
vhost_scsi_deregister();
|
2025-06-11 07:39:21 -07:00
|
|
|
}
|
2012-07-18 14:31:32 -07:00
|
|
|
|
2013-05-02 03:52:59 +03:00
|
|
|
MODULE_DESCRIPTION("VHOST_SCSI series fabric driver");
|
|
|
|
MODULE_ALIAS("tcm_vhost");
|
2012-07-18 14:31:32 -07:00
|
|
|
MODULE_LICENSE("GPL");
|
2015-01-31 23:56:53 -08:00
|
|
|
module_init(vhost_scsi_init);
|
|
|
|
module_exit(vhost_scsi_exit);
|