18c2ecf20Sopenharmony_ci/******************************************************************************* 28c2ecf20Sopenharmony_ci * Vhost kernel TCM fabric driver for virtio SCSI initiators 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * (C) Copyright 2010-2013 Datera, Inc. 58c2ecf20Sopenharmony_ci * (C) Copyright 2010-2012 IBM Corp. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Licensed to the Linux Foundation under the General Public License (GPL) version 2. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Authors: Nicholas A. Bellinger <nab@daterainc.com> 108c2ecf20Sopenharmony_ci * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com> 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 138c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License as published by 148c2ecf20Sopenharmony_ci * the Free Software Foundation; either version 2 of the License, or 158c2ecf20Sopenharmony_ci * (at your option) any later version. 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, 188c2ecf20Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 198c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 208c2ecf20Sopenharmony_ci * GNU General Public License for more details. 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci ****************************************************************************/ 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include <linux/module.h> 258c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 268c2ecf20Sopenharmony_ci#include <generated/utsrelease.h> 278c2ecf20Sopenharmony_ci#include <linux/utsname.h> 288c2ecf20Sopenharmony_ci#include <linux/init.h> 298c2ecf20Sopenharmony_ci#include <linux/slab.h> 308c2ecf20Sopenharmony_ci#include <linux/kthread.h> 318c2ecf20Sopenharmony_ci#include <linux/types.h> 328c2ecf20Sopenharmony_ci#include <linux/string.h> 338c2ecf20Sopenharmony_ci#include <linux/configfs.h> 348c2ecf20Sopenharmony_ci#include <linux/ctype.h> 358c2ecf20Sopenharmony_ci#include <linux/compat.h> 368c2ecf20Sopenharmony_ci#include <linux/eventfd.h> 378c2ecf20Sopenharmony_ci#include <linux/fs.h> 388c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 398c2ecf20Sopenharmony_ci#include <linux/miscdevice.h> 408c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 418c2ecf20Sopenharmony_ci#include <scsi/scsi_common.h> 428c2ecf20Sopenharmony_ci#include <scsi/scsi_proto.h> 438c2ecf20Sopenharmony_ci#include <target/target_core_base.h> 448c2ecf20Sopenharmony_ci#include <target/target_core_fabric.h> 458c2ecf20Sopenharmony_ci#include <linux/vhost.h> 468c2ecf20Sopenharmony_ci#include <linux/virtio_scsi.h> 478c2ecf20Sopenharmony_ci#include <linux/llist.h> 488c2ecf20Sopenharmony_ci#include <linux/bitmap.h> 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#include "vhost.h" 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define VHOST_SCSI_VERSION "v0.1" 538c2ecf20Sopenharmony_ci#define VHOST_SCSI_NAMELEN 256 548c2ecf20Sopenharmony_ci#define VHOST_SCSI_MAX_CDB_SIZE 32 558c2ecf20Sopenharmony_ci#define VHOST_SCSI_PREALLOC_SGLS 2048 568c2ecf20Sopenharmony_ci#define VHOST_SCSI_PREALLOC_UPAGES 2048 578c2ecf20Sopenharmony_ci#define VHOST_SCSI_PREALLOC_PROT_SGLS 2048 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* Max number of requests before requeueing the job. 608c2ecf20Sopenharmony_ci * Using this limit prevents one virtqueue from starving others with 618c2ecf20Sopenharmony_ci * request. 628c2ecf20Sopenharmony_ci */ 638c2ecf20Sopenharmony_ci#define VHOST_SCSI_WEIGHT 256 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistruct vhost_scsi_inflight { 668c2ecf20Sopenharmony_ci /* Wait for the flush operation to finish */ 678c2ecf20Sopenharmony_ci struct completion comp; 688c2ecf20Sopenharmony_ci /* Refcount for the inflight reqs */ 698c2ecf20Sopenharmony_ci struct kref kref; 708c2ecf20Sopenharmony_ci}; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistruct vhost_scsi_cmd { 738c2ecf20Sopenharmony_ci /* Descriptor from vhost_get_vq_desc() for virt_queue segment */ 748c2ecf20Sopenharmony_ci int tvc_vq_desc; 758c2ecf20Sopenharmony_ci /* virtio-scsi initiator task attribute */ 768c2ecf20Sopenharmony_ci int tvc_task_attr; 778c2ecf20Sopenharmony_ci /* virtio-scsi response incoming iovecs */ 788c2ecf20Sopenharmony_ci int tvc_in_iovs; 798c2ecf20Sopenharmony_ci /* virtio-scsi initiator data direction */ 808c2ecf20Sopenharmony_ci enum dma_data_direction tvc_data_direction; 818c2ecf20Sopenharmony_ci /* Expected data transfer length from virtio-scsi header */ 828c2ecf20Sopenharmony_ci u32 tvc_exp_data_len; 838c2ecf20Sopenharmony_ci /* The Tag from include/linux/virtio_scsi.h:struct virtio_scsi_cmd_req */ 848c2ecf20Sopenharmony_ci u64 tvc_tag; 858c2ecf20Sopenharmony_ci /* The number of scatterlists associated with this cmd */ 868c2ecf20Sopenharmony_ci u32 tvc_sgl_count; 878c2ecf20Sopenharmony_ci u32 tvc_prot_sgl_count; 888c2ecf20Sopenharmony_ci /* Saved unpacked SCSI LUN for vhost_scsi_submission_work() */ 898c2ecf20Sopenharmony_ci u32 tvc_lun; 908c2ecf20Sopenharmony_ci /* Pointer to the SGL formatted memory from virtio-scsi */ 918c2ecf20Sopenharmony_ci struct scatterlist *tvc_sgl; 928c2ecf20Sopenharmony_ci struct scatterlist *tvc_prot_sgl; 938c2ecf20Sopenharmony_ci struct page **tvc_upages; 948c2ecf20Sopenharmony_ci /* Pointer to response header iovec */ 958c2ecf20Sopenharmony_ci struct iovec tvc_resp_iov; 968c2ecf20Sopenharmony_ci /* Pointer to vhost_scsi for our device */ 978c2ecf20Sopenharmony_ci struct vhost_scsi *tvc_vhost; 988c2ecf20Sopenharmony_ci /* Pointer to vhost_virtqueue for the cmd */ 998c2ecf20Sopenharmony_ci struct vhost_virtqueue *tvc_vq; 1008c2ecf20Sopenharmony_ci /* Pointer to vhost nexus memory */ 1018c2ecf20Sopenharmony_ci struct vhost_scsi_nexus *tvc_nexus; 1028c2ecf20Sopenharmony_ci /* The TCM I/O descriptor that is accessed via container_of() */ 1038c2ecf20Sopenharmony_ci struct se_cmd tvc_se_cmd; 1048c2ecf20Sopenharmony_ci /* work item used for cmwq dispatch to vhost_scsi_submission_work() */ 1058c2ecf20Sopenharmony_ci struct work_struct work; 1068c2ecf20Sopenharmony_ci /* Copy of the incoming SCSI command descriptor block (CDB) */ 1078c2ecf20Sopenharmony_ci unsigned char tvc_cdb[VHOST_SCSI_MAX_CDB_SIZE]; 1088c2ecf20Sopenharmony_ci /* Sense buffer that will be mapped into outgoing status */ 1098c2ecf20Sopenharmony_ci unsigned char tvc_sense_buf[TRANSPORT_SENSE_BUFFER]; 1108c2ecf20Sopenharmony_ci /* Completed commands list, serviced from vhost worker thread */ 1118c2ecf20Sopenharmony_ci struct llist_node tvc_completion_list; 1128c2ecf20Sopenharmony_ci /* Used to track inflight cmd */ 1138c2ecf20Sopenharmony_ci struct vhost_scsi_inflight *inflight; 1148c2ecf20Sopenharmony_ci}; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistruct vhost_scsi_nexus { 1178c2ecf20Sopenharmony_ci /* Pointer to TCM session for I_T Nexus */ 1188c2ecf20Sopenharmony_ci struct se_session *tvn_se_sess; 1198c2ecf20Sopenharmony_ci}; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistruct vhost_scsi_tpg { 1228c2ecf20Sopenharmony_ci /* Vhost port target portal group tag for TCM */ 1238c2ecf20Sopenharmony_ci u16 tport_tpgt; 1248c2ecf20Sopenharmony_ci /* Used to track number of TPG Port/Lun Links wrt to explict I_T Nexus shutdown */ 1258c2ecf20Sopenharmony_ci int tv_tpg_port_count; 1268c2ecf20Sopenharmony_ci /* Used for vhost_scsi device reference to tpg_nexus, protected by tv_tpg_mutex */ 1278c2ecf20Sopenharmony_ci int tv_tpg_vhost_count; 1288c2ecf20Sopenharmony_ci /* Used for enabling T10-PI with legacy devices */ 1298c2ecf20Sopenharmony_ci int tv_fabric_prot_type; 1308c2ecf20Sopenharmony_ci /* list for vhost_scsi_list */ 1318c2ecf20Sopenharmony_ci struct list_head tv_tpg_list; 1328c2ecf20Sopenharmony_ci /* Used to protect access for tpg_nexus */ 1338c2ecf20Sopenharmony_ci struct mutex tv_tpg_mutex; 1348c2ecf20Sopenharmony_ci /* Pointer to the TCM VHost I_T Nexus for this TPG endpoint */ 1358c2ecf20Sopenharmony_ci struct vhost_scsi_nexus *tpg_nexus; 1368c2ecf20Sopenharmony_ci /* Pointer back to vhost_scsi_tport */ 1378c2ecf20Sopenharmony_ci struct vhost_scsi_tport *tport; 1388c2ecf20Sopenharmony_ci /* Returned by vhost_scsi_make_tpg() */ 1398c2ecf20Sopenharmony_ci struct se_portal_group se_tpg; 1408c2ecf20Sopenharmony_ci /* Pointer back to vhost_scsi, protected by tv_tpg_mutex */ 1418c2ecf20Sopenharmony_ci struct vhost_scsi *vhost_scsi; 1428c2ecf20Sopenharmony_ci struct list_head tmf_queue; 1438c2ecf20Sopenharmony_ci}; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistruct vhost_scsi_tport { 1468c2ecf20Sopenharmony_ci /* SCSI protocol the tport is providing */ 1478c2ecf20Sopenharmony_ci u8 tport_proto_id; 1488c2ecf20Sopenharmony_ci /* Binary World Wide unique Port Name for Vhost Target port */ 1498c2ecf20Sopenharmony_ci u64 tport_wwpn; 1508c2ecf20Sopenharmony_ci /* ASCII formatted WWPN for Vhost Target port */ 1518c2ecf20Sopenharmony_ci char tport_name[VHOST_SCSI_NAMELEN]; 1528c2ecf20Sopenharmony_ci /* Returned by vhost_scsi_make_tport() */ 1538c2ecf20Sopenharmony_ci struct se_wwn tport_wwn; 1548c2ecf20Sopenharmony_ci}; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistruct vhost_scsi_evt { 1578c2ecf20Sopenharmony_ci /* event to be sent to guest */ 1588c2ecf20Sopenharmony_ci struct virtio_scsi_event event; 1598c2ecf20Sopenharmony_ci /* event list, serviced from vhost worker thread */ 1608c2ecf20Sopenharmony_ci struct llist_node list; 1618c2ecf20Sopenharmony_ci}; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cienum { 1648c2ecf20Sopenharmony_ci VHOST_SCSI_VQ_CTL = 0, 1658c2ecf20Sopenharmony_ci VHOST_SCSI_VQ_EVT = 1, 1668c2ecf20Sopenharmony_ci VHOST_SCSI_VQ_IO = 2, 1678c2ecf20Sopenharmony_ci}; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci/* Note: can't set VIRTIO_F_VERSION_1 yet, since that implies ANY_LAYOUT. */ 1708c2ecf20Sopenharmony_cienum { 1718c2ecf20Sopenharmony_ci VHOST_SCSI_FEATURES = VHOST_FEATURES | (1ULL << VIRTIO_SCSI_F_HOTPLUG) | 1728c2ecf20Sopenharmony_ci (1ULL << VIRTIO_SCSI_F_T10_PI) 1738c2ecf20Sopenharmony_ci}; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci#define VHOST_SCSI_MAX_TARGET 256 1768c2ecf20Sopenharmony_ci#define VHOST_SCSI_MAX_VQ 128 1778c2ecf20Sopenharmony_ci#define VHOST_SCSI_MAX_EVENT 128 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistruct vhost_scsi_virtqueue { 1808c2ecf20Sopenharmony_ci struct vhost_virtqueue vq; 1818c2ecf20Sopenharmony_ci /* 1828c2ecf20Sopenharmony_ci * Reference counting for inflight reqs, used for flush operation. At 1838c2ecf20Sopenharmony_ci * each time, one reference tracks new commands submitted, while we 1848c2ecf20Sopenharmony_ci * wait for another one to reach 0. 1858c2ecf20Sopenharmony_ci */ 1868c2ecf20Sopenharmony_ci struct vhost_scsi_inflight inflights[2]; 1878c2ecf20Sopenharmony_ci /* 1888c2ecf20Sopenharmony_ci * Indicate current inflight in use, protected by vq->mutex. 1898c2ecf20Sopenharmony_ci * Writers must also take dev mutex and flush under it. 1908c2ecf20Sopenharmony_ci */ 1918c2ecf20Sopenharmony_ci int inflight_idx; 1928c2ecf20Sopenharmony_ci struct vhost_scsi_cmd *scsi_cmds; 1938c2ecf20Sopenharmony_ci struct sbitmap scsi_tags; 1948c2ecf20Sopenharmony_ci int max_cmds; 1958c2ecf20Sopenharmony_ci}; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistruct vhost_scsi { 1988c2ecf20Sopenharmony_ci /* Protected by vhost_scsi->dev.mutex */ 1998c2ecf20Sopenharmony_ci struct vhost_scsi_tpg **vs_tpg; 2008c2ecf20Sopenharmony_ci char vs_vhost_wwpn[TRANSPORT_IQN_LEN]; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci struct vhost_dev dev; 2038c2ecf20Sopenharmony_ci struct vhost_scsi_virtqueue vqs[VHOST_SCSI_MAX_VQ]; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci struct vhost_work vs_completion_work; /* cmd completion work item */ 2068c2ecf20Sopenharmony_ci struct llist_head vs_completion_list; /* cmd completion queue */ 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci struct vhost_work vs_event_work; /* evt injection work item */ 2098c2ecf20Sopenharmony_ci struct llist_head vs_event_list; /* evt injection queue */ 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci bool vs_events_missed; /* any missed events, protected by vq->mutex */ 2128c2ecf20Sopenharmony_ci int vs_events_nr; /* num of pending events, protected by vq->mutex */ 2138c2ecf20Sopenharmony_ci}; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistruct vhost_scsi_tmf { 2168c2ecf20Sopenharmony_ci struct vhost_work vwork; 2178c2ecf20Sopenharmony_ci struct vhost_scsi_tpg *tpg; 2188c2ecf20Sopenharmony_ci struct vhost_scsi *vhost; 2198c2ecf20Sopenharmony_ci struct vhost_scsi_virtqueue *svq; 2208c2ecf20Sopenharmony_ci struct list_head queue_entry; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci struct se_cmd se_cmd; 2238c2ecf20Sopenharmony_ci u8 scsi_resp; 2248c2ecf20Sopenharmony_ci struct vhost_scsi_inflight *inflight; 2258c2ecf20Sopenharmony_ci struct iovec resp_iov; 2268c2ecf20Sopenharmony_ci int in_iovs; 2278c2ecf20Sopenharmony_ci int vq_desc; 2288c2ecf20Sopenharmony_ci}; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci/* 2318c2ecf20Sopenharmony_ci * Context for processing request and control queue operations. 2328c2ecf20Sopenharmony_ci */ 2338c2ecf20Sopenharmony_cistruct vhost_scsi_ctx { 2348c2ecf20Sopenharmony_ci int head; 2358c2ecf20Sopenharmony_ci unsigned int out, in; 2368c2ecf20Sopenharmony_ci size_t req_size, rsp_size; 2378c2ecf20Sopenharmony_ci size_t out_size, in_size; 2388c2ecf20Sopenharmony_ci u8 *target, *lunp; 2398c2ecf20Sopenharmony_ci void *req; 2408c2ecf20Sopenharmony_ci struct iov_iter out_iter; 2418c2ecf20Sopenharmony_ci}; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic struct workqueue_struct *vhost_scsi_workqueue; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci/* Global spinlock to protect vhost_scsi TPG list for vhost IOCTL access */ 2468c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(vhost_scsi_mutex); 2478c2ecf20Sopenharmony_cistatic LIST_HEAD(vhost_scsi_list); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic void vhost_scsi_done_inflight(struct kref *kref) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci struct vhost_scsi_inflight *inflight; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci inflight = container_of(kref, struct vhost_scsi_inflight, kref); 2548c2ecf20Sopenharmony_ci complete(&inflight->comp); 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic void vhost_scsi_init_inflight(struct vhost_scsi *vs, 2588c2ecf20Sopenharmony_ci struct vhost_scsi_inflight *old_inflight[]) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci struct vhost_scsi_inflight *new_inflight; 2618c2ecf20Sopenharmony_ci struct vhost_virtqueue *vq; 2628c2ecf20Sopenharmony_ci int idx, i; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci for (i = 0; i < VHOST_SCSI_MAX_VQ; i++) { 2658c2ecf20Sopenharmony_ci vq = &vs->vqs[i].vq; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci mutex_lock(&vq->mutex); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci /* store old infight */ 2708c2ecf20Sopenharmony_ci idx = vs->vqs[i].inflight_idx; 2718c2ecf20Sopenharmony_ci if (old_inflight) 2728c2ecf20Sopenharmony_ci old_inflight[i] = &vs->vqs[i].inflights[idx]; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* setup new infight */ 2758c2ecf20Sopenharmony_ci vs->vqs[i].inflight_idx = idx ^ 1; 2768c2ecf20Sopenharmony_ci new_inflight = &vs->vqs[i].inflights[idx ^ 1]; 2778c2ecf20Sopenharmony_ci kref_init(&new_inflight->kref); 2788c2ecf20Sopenharmony_ci init_completion(&new_inflight->comp); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci mutex_unlock(&vq->mutex); 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic struct vhost_scsi_inflight * 2858c2ecf20Sopenharmony_civhost_scsi_get_inflight(struct vhost_virtqueue *vq) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci struct vhost_scsi_inflight *inflight; 2888c2ecf20Sopenharmony_ci struct vhost_scsi_virtqueue *svq; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci svq = container_of(vq, struct vhost_scsi_virtqueue, vq); 2918c2ecf20Sopenharmony_ci inflight = &svq->inflights[svq->inflight_idx]; 2928c2ecf20Sopenharmony_ci kref_get(&inflight->kref); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci return inflight; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic void vhost_scsi_put_inflight(struct vhost_scsi_inflight *inflight) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci kref_put(&inflight->kref, vhost_scsi_done_inflight); 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic int vhost_scsi_check_true(struct se_portal_group *se_tpg) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci return 1; 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic int vhost_scsi_check_false(struct se_portal_group *se_tpg) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci return 0; 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic char *vhost_scsi_get_fabric_wwn(struct se_portal_group *se_tpg) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci struct vhost_scsi_tpg *tpg = container_of(se_tpg, 3158c2ecf20Sopenharmony_ci struct vhost_scsi_tpg, se_tpg); 3168c2ecf20Sopenharmony_ci struct vhost_scsi_tport *tport = tpg->tport; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci return &tport->tport_name[0]; 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic u16 vhost_scsi_get_tpgt(struct se_portal_group *se_tpg) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci struct vhost_scsi_tpg *tpg = container_of(se_tpg, 3248c2ecf20Sopenharmony_ci struct vhost_scsi_tpg, se_tpg); 3258c2ecf20Sopenharmony_ci return tpg->tport_tpgt; 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic int vhost_scsi_check_prot_fabric_only(struct se_portal_group *se_tpg) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci struct vhost_scsi_tpg *tpg = container_of(se_tpg, 3318c2ecf20Sopenharmony_ci struct vhost_scsi_tpg, se_tpg); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci return tpg->tv_fabric_prot_type; 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic u32 vhost_scsi_tpg_get_inst_index(struct se_portal_group *se_tpg) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci return 1; 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cistatic void vhost_scsi_release_cmd_res(struct se_cmd *se_cmd) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci struct vhost_scsi_cmd *tv_cmd = container_of(se_cmd, 3448c2ecf20Sopenharmony_ci struct vhost_scsi_cmd, tvc_se_cmd); 3458c2ecf20Sopenharmony_ci struct vhost_scsi_virtqueue *svq = container_of(tv_cmd->tvc_vq, 3468c2ecf20Sopenharmony_ci struct vhost_scsi_virtqueue, vq); 3478c2ecf20Sopenharmony_ci struct vhost_scsi_inflight *inflight = tv_cmd->inflight; 3488c2ecf20Sopenharmony_ci int i; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci if (tv_cmd->tvc_sgl_count) { 3518c2ecf20Sopenharmony_ci for (i = 0; i < tv_cmd->tvc_sgl_count; i++) 3528c2ecf20Sopenharmony_ci put_page(sg_page(&tv_cmd->tvc_sgl[i])); 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci if (tv_cmd->tvc_prot_sgl_count) { 3558c2ecf20Sopenharmony_ci for (i = 0; i < tv_cmd->tvc_prot_sgl_count; i++) 3568c2ecf20Sopenharmony_ci put_page(sg_page(&tv_cmd->tvc_prot_sgl[i])); 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci sbitmap_clear_bit(&svq->scsi_tags, se_cmd->map_tag); 3608c2ecf20Sopenharmony_ci vhost_scsi_put_inflight(inflight); 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic void vhost_scsi_release_tmf_res(struct vhost_scsi_tmf *tmf) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci struct vhost_scsi_tpg *tpg = tmf->tpg; 3668c2ecf20Sopenharmony_ci struct vhost_scsi_inflight *inflight = tmf->inflight; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci mutex_lock(&tpg->tv_tpg_mutex); 3698c2ecf20Sopenharmony_ci list_add_tail(&tpg->tmf_queue, &tmf->queue_entry); 3708c2ecf20Sopenharmony_ci mutex_unlock(&tpg->tv_tpg_mutex); 3718c2ecf20Sopenharmony_ci vhost_scsi_put_inflight(inflight); 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic void vhost_scsi_release_cmd(struct se_cmd *se_cmd) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci if (se_cmd->se_cmd_flags & SCF_SCSI_TMR_CDB) { 3778c2ecf20Sopenharmony_ci struct vhost_scsi_tmf *tmf = container_of(se_cmd, 3788c2ecf20Sopenharmony_ci struct vhost_scsi_tmf, se_cmd); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci vhost_work_queue(&tmf->vhost->dev, &tmf->vwork); 3818c2ecf20Sopenharmony_ci } else { 3828c2ecf20Sopenharmony_ci struct vhost_scsi_cmd *cmd = container_of(se_cmd, 3838c2ecf20Sopenharmony_ci struct vhost_scsi_cmd, tvc_se_cmd); 3848c2ecf20Sopenharmony_ci struct vhost_scsi *vs = cmd->tvc_vhost; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci llist_add(&cmd->tvc_completion_list, &vs->vs_completion_list); 3878c2ecf20Sopenharmony_ci vhost_work_queue(&vs->dev, &vs->vs_completion_work); 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic u32 vhost_scsi_sess_get_index(struct se_session *se_sess) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci return 0; 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cistatic int vhost_scsi_write_pending(struct se_cmd *se_cmd) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci /* Go ahead and process the write immediately */ 3998c2ecf20Sopenharmony_ci target_execute_cmd(se_cmd); 4008c2ecf20Sopenharmony_ci return 0; 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cistatic void vhost_scsi_set_default_node_attrs(struct se_node_acl *nacl) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci return; 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cistatic int vhost_scsi_get_cmd_state(struct se_cmd *se_cmd) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci return 0; 4118c2ecf20Sopenharmony_ci} 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_cistatic int vhost_scsi_queue_data_in(struct se_cmd *se_cmd) 4148c2ecf20Sopenharmony_ci{ 4158c2ecf20Sopenharmony_ci transport_generic_free_cmd(se_cmd, 0); 4168c2ecf20Sopenharmony_ci return 0; 4178c2ecf20Sopenharmony_ci} 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_cistatic int vhost_scsi_queue_status(struct se_cmd *se_cmd) 4208c2ecf20Sopenharmony_ci{ 4218c2ecf20Sopenharmony_ci transport_generic_free_cmd(se_cmd, 0); 4228c2ecf20Sopenharmony_ci return 0; 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistatic void vhost_scsi_queue_tm_rsp(struct se_cmd *se_cmd) 4268c2ecf20Sopenharmony_ci{ 4278c2ecf20Sopenharmony_ci struct vhost_scsi_tmf *tmf = container_of(se_cmd, struct vhost_scsi_tmf, 4288c2ecf20Sopenharmony_ci se_cmd); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci tmf->scsi_resp = se_cmd->se_tmr_req->response; 4318c2ecf20Sopenharmony_ci transport_generic_free_cmd(&tmf->se_cmd, 0); 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cistatic void vhost_scsi_aborted_task(struct se_cmd *se_cmd) 4358c2ecf20Sopenharmony_ci{ 4368c2ecf20Sopenharmony_ci return; 4378c2ecf20Sopenharmony_ci} 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_cistatic void vhost_scsi_free_evt(struct vhost_scsi *vs, struct vhost_scsi_evt *evt) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci vs->vs_events_nr--; 4428c2ecf20Sopenharmony_ci kfree(evt); 4438c2ecf20Sopenharmony_ci} 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_cistatic struct vhost_scsi_evt * 4468c2ecf20Sopenharmony_civhost_scsi_allocate_evt(struct vhost_scsi *vs, 4478c2ecf20Sopenharmony_ci u32 event, u32 reason) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci struct vhost_virtqueue *vq = &vs->vqs[VHOST_SCSI_VQ_EVT].vq; 4508c2ecf20Sopenharmony_ci struct vhost_scsi_evt *evt; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci if (vs->vs_events_nr > VHOST_SCSI_MAX_EVENT) { 4538c2ecf20Sopenharmony_ci vs->vs_events_missed = true; 4548c2ecf20Sopenharmony_ci return NULL; 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci evt = kzalloc(sizeof(*evt), GFP_KERNEL); 4588c2ecf20Sopenharmony_ci if (!evt) { 4598c2ecf20Sopenharmony_ci vq_err(vq, "Failed to allocate vhost_scsi_evt\n"); 4608c2ecf20Sopenharmony_ci vs->vs_events_missed = true; 4618c2ecf20Sopenharmony_ci return NULL; 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci evt->event.event = cpu_to_vhost32(vq, event); 4658c2ecf20Sopenharmony_ci evt->event.reason = cpu_to_vhost32(vq, reason); 4668c2ecf20Sopenharmony_ci vs->vs_events_nr++; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci return evt; 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic int vhost_scsi_check_stop_free(struct se_cmd *se_cmd) 4728c2ecf20Sopenharmony_ci{ 4738c2ecf20Sopenharmony_ci return target_put_sess_cmd(se_cmd); 4748c2ecf20Sopenharmony_ci} 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_cistatic void 4778c2ecf20Sopenharmony_civhost_scsi_do_evt_work(struct vhost_scsi *vs, struct vhost_scsi_evt *evt) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci struct vhost_virtqueue *vq = &vs->vqs[VHOST_SCSI_VQ_EVT].vq; 4808c2ecf20Sopenharmony_ci struct virtio_scsi_event *event = &evt->event; 4818c2ecf20Sopenharmony_ci struct virtio_scsi_event __user *eventp; 4828c2ecf20Sopenharmony_ci unsigned out, in; 4838c2ecf20Sopenharmony_ci int head, ret; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci if (!vhost_vq_get_backend(vq)) { 4868c2ecf20Sopenharmony_ci vs->vs_events_missed = true; 4878c2ecf20Sopenharmony_ci return; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ciagain: 4918c2ecf20Sopenharmony_ci vhost_disable_notify(&vs->dev, vq); 4928c2ecf20Sopenharmony_ci head = vhost_get_vq_desc(vq, vq->iov, 4938c2ecf20Sopenharmony_ci ARRAY_SIZE(vq->iov), &out, &in, 4948c2ecf20Sopenharmony_ci NULL, NULL); 4958c2ecf20Sopenharmony_ci if (head < 0) { 4968c2ecf20Sopenharmony_ci vs->vs_events_missed = true; 4978c2ecf20Sopenharmony_ci return; 4988c2ecf20Sopenharmony_ci } 4998c2ecf20Sopenharmony_ci if (head == vq->num) { 5008c2ecf20Sopenharmony_ci if (vhost_enable_notify(&vs->dev, vq)) 5018c2ecf20Sopenharmony_ci goto again; 5028c2ecf20Sopenharmony_ci vs->vs_events_missed = true; 5038c2ecf20Sopenharmony_ci return; 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci if ((vq->iov[out].iov_len != sizeof(struct virtio_scsi_event))) { 5078c2ecf20Sopenharmony_ci vq_err(vq, "Expecting virtio_scsi_event, got %zu bytes\n", 5088c2ecf20Sopenharmony_ci vq->iov[out].iov_len); 5098c2ecf20Sopenharmony_ci vs->vs_events_missed = true; 5108c2ecf20Sopenharmony_ci return; 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci if (vs->vs_events_missed) { 5148c2ecf20Sopenharmony_ci event->event |= cpu_to_vhost32(vq, VIRTIO_SCSI_T_EVENTS_MISSED); 5158c2ecf20Sopenharmony_ci vs->vs_events_missed = false; 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci eventp = vq->iov[out].iov_base; 5198c2ecf20Sopenharmony_ci ret = __copy_to_user(eventp, event, sizeof(*event)); 5208c2ecf20Sopenharmony_ci if (!ret) 5218c2ecf20Sopenharmony_ci vhost_add_used_and_signal(&vs->dev, vq, head, 0); 5228c2ecf20Sopenharmony_ci else 5238c2ecf20Sopenharmony_ci vq_err(vq, "Faulted on vhost_scsi_send_event\n"); 5248c2ecf20Sopenharmony_ci} 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_cistatic void vhost_scsi_evt_work(struct vhost_work *work) 5278c2ecf20Sopenharmony_ci{ 5288c2ecf20Sopenharmony_ci struct vhost_scsi *vs = container_of(work, struct vhost_scsi, 5298c2ecf20Sopenharmony_ci vs_event_work); 5308c2ecf20Sopenharmony_ci struct vhost_virtqueue *vq = &vs->vqs[VHOST_SCSI_VQ_EVT].vq; 5318c2ecf20Sopenharmony_ci struct vhost_scsi_evt *evt, *t; 5328c2ecf20Sopenharmony_ci struct llist_node *llnode; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci mutex_lock(&vq->mutex); 5358c2ecf20Sopenharmony_ci llnode = llist_del_all(&vs->vs_event_list); 5368c2ecf20Sopenharmony_ci llist_for_each_entry_safe(evt, t, llnode, list) { 5378c2ecf20Sopenharmony_ci vhost_scsi_do_evt_work(vs, evt); 5388c2ecf20Sopenharmony_ci vhost_scsi_free_evt(vs, evt); 5398c2ecf20Sopenharmony_ci } 5408c2ecf20Sopenharmony_ci mutex_unlock(&vq->mutex); 5418c2ecf20Sopenharmony_ci} 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci/* Fill in status and signal that we are done processing this command 5448c2ecf20Sopenharmony_ci * 5458c2ecf20Sopenharmony_ci * This is scheduled in the vhost work queue so we are called with the owner 5468c2ecf20Sopenharmony_ci * process mm and can access the vring. 5478c2ecf20Sopenharmony_ci */ 5488c2ecf20Sopenharmony_cistatic void vhost_scsi_complete_cmd_work(struct vhost_work *work) 5498c2ecf20Sopenharmony_ci{ 5508c2ecf20Sopenharmony_ci struct vhost_scsi *vs = container_of(work, struct vhost_scsi, 5518c2ecf20Sopenharmony_ci vs_completion_work); 5528c2ecf20Sopenharmony_ci DECLARE_BITMAP(signal, VHOST_SCSI_MAX_VQ); 5538c2ecf20Sopenharmony_ci struct virtio_scsi_cmd_resp v_rsp; 5548c2ecf20Sopenharmony_ci struct vhost_scsi_cmd *cmd, *t; 5558c2ecf20Sopenharmony_ci struct llist_node *llnode; 5568c2ecf20Sopenharmony_ci struct se_cmd *se_cmd; 5578c2ecf20Sopenharmony_ci struct iov_iter iov_iter; 5588c2ecf20Sopenharmony_ci int ret, vq; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci bitmap_zero(signal, VHOST_SCSI_MAX_VQ); 5618c2ecf20Sopenharmony_ci llnode = llist_del_all(&vs->vs_completion_list); 5628c2ecf20Sopenharmony_ci llist_for_each_entry_safe(cmd, t, llnode, tvc_completion_list) { 5638c2ecf20Sopenharmony_ci se_cmd = &cmd->tvc_se_cmd; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci pr_debug("%s tv_cmd %p resid %u status %#02x\n", __func__, 5668c2ecf20Sopenharmony_ci cmd, se_cmd->residual_count, se_cmd->scsi_status); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci memset(&v_rsp, 0, sizeof(v_rsp)); 5698c2ecf20Sopenharmony_ci v_rsp.resid = cpu_to_vhost32(cmd->tvc_vq, se_cmd->residual_count); 5708c2ecf20Sopenharmony_ci /* TODO is status_qualifier field needed? */ 5718c2ecf20Sopenharmony_ci v_rsp.status = se_cmd->scsi_status; 5728c2ecf20Sopenharmony_ci v_rsp.sense_len = cpu_to_vhost32(cmd->tvc_vq, 5738c2ecf20Sopenharmony_ci se_cmd->scsi_sense_length); 5748c2ecf20Sopenharmony_ci memcpy(v_rsp.sense, cmd->tvc_sense_buf, 5758c2ecf20Sopenharmony_ci se_cmd->scsi_sense_length); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci iov_iter_init(&iov_iter, READ, &cmd->tvc_resp_iov, 5788c2ecf20Sopenharmony_ci cmd->tvc_in_iovs, sizeof(v_rsp)); 5798c2ecf20Sopenharmony_ci ret = copy_to_iter(&v_rsp, sizeof(v_rsp), &iov_iter); 5808c2ecf20Sopenharmony_ci if (likely(ret == sizeof(v_rsp))) { 5818c2ecf20Sopenharmony_ci struct vhost_scsi_virtqueue *q; 5828c2ecf20Sopenharmony_ci vhost_add_used(cmd->tvc_vq, cmd->tvc_vq_desc, 0); 5838c2ecf20Sopenharmony_ci q = container_of(cmd->tvc_vq, struct vhost_scsi_virtqueue, vq); 5848c2ecf20Sopenharmony_ci vq = q - vs->vqs; 5858c2ecf20Sopenharmony_ci __set_bit(vq, signal); 5868c2ecf20Sopenharmony_ci } else 5878c2ecf20Sopenharmony_ci pr_err("Faulted on virtio_scsi_cmd_resp\n"); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci vhost_scsi_release_cmd_res(se_cmd); 5908c2ecf20Sopenharmony_ci } 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci vq = -1; 5938c2ecf20Sopenharmony_ci while ((vq = find_next_bit(signal, VHOST_SCSI_MAX_VQ, vq + 1)) 5948c2ecf20Sopenharmony_ci < VHOST_SCSI_MAX_VQ) 5958c2ecf20Sopenharmony_ci vhost_signal(&vs->dev, &vs->vqs[vq].vq); 5968c2ecf20Sopenharmony_ci} 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_cistatic struct vhost_scsi_cmd * 5998c2ecf20Sopenharmony_civhost_scsi_get_cmd(struct vhost_virtqueue *vq, struct vhost_scsi_tpg *tpg, 6008c2ecf20Sopenharmony_ci unsigned char *cdb, u64 scsi_tag, u16 lun, u8 task_attr, 6018c2ecf20Sopenharmony_ci u32 exp_data_len, int data_direction) 6028c2ecf20Sopenharmony_ci{ 6038c2ecf20Sopenharmony_ci struct vhost_scsi_virtqueue *svq = container_of(vq, 6048c2ecf20Sopenharmony_ci struct vhost_scsi_virtqueue, vq); 6058c2ecf20Sopenharmony_ci struct vhost_scsi_cmd *cmd; 6068c2ecf20Sopenharmony_ci struct vhost_scsi_nexus *tv_nexus; 6078c2ecf20Sopenharmony_ci struct scatterlist *sg, *prot_sg; 6088c2ecf20Sopenharmony_ci struct page **pages; 6098c2ecf20Sopenharmony_ci int tag; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci tv_nexus = tpg->tpg_nexus; 6128c2ecf20Sopenharmony_ci if (!tv_nexus) { 6138c2ecf20Sopenharmony_ci pr_err("Unable to locate active struct vhost_scsi_nexus\n"); 6148c2ecf20Sopenharmony_ci return ERR_PTR(-EIO); 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci tag = sbitmap_get(&svq->scsi_tags, 0, false); 6188c2ecf20Sopenharmony_ci if (tag < 0) { 6198c2ecf20Sopenharmony_ci pr_err("Unable to obtain tag for vhost_scsi_cmd\n"); 6208c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 6218c2ecf20Sopenharmony_ci } 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci cmd = &svq->scsi_cmds[tag]; 6248c2ecf20Sopenharmony_ci sg = cmd->tvc_sgl; 6258c2ecf20Sopenharmony_ci prot_sg = cmd->tvc_prot_sgl; 6268c2ecf20Sopenharmony_ci pages = cmd->tvc_upages; 6278c2ecf20Sopenharmony_ci memset(cmd, 0, sizeof(*cmd)); 6288c2ecf20Sopenharmony_ci cmd->tvc_sgl = sg; 6298c2ecf20Sopenharmony_ci cmd->tvc_prot_sgl = prot_sg; 6308c2ecf20Sopenharmony_ci cmd->tvc_upages = pages; 6318c2ecf20Sopenharmony_ci cmd->tvc_se_cmd.map_tag = tag; 6328c2ecf20Sopenharmony_ci cmd->tvc_tag = scsi_tag; 6338c2ecf20Sopenharmony_ci cmd->tvc_lun = lun; 6348c2ecf20Sopenharmony_ci cmd->tvc_task_attr = task_attr; 6358c2ecf20Sopenharmony_ci cmd->tvc_exp_data_len = exp_data_len; 6368c2ecf20Sopenharmony_ci cmd->tvc_data_direction = data_direction; 6378c2ecf20Sopenharmony_ci cmd->tvc_nexus = tv_nexus; 6388c2ecf20Sopenharmony_ci cmd->inflight = vhost_scsi_get_inflight(vq); 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci memcpy(cmd->tvc_cdb, cdb, VHOST_SCSI_MAX_CDB_SIZE); 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci return cmd; 6438c2ecf20Sopenharmony_ci} 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci/* 6468c2ecf20Sopenharmony_ci * Map a user memory range into a scatterlist 6478c2ecf20Sopenharmony_ci * 6488c2ecf20Sopenharmony_ci * Returns the number of scatterlist entries used or -errno on error. 6498c2ecf20Sopenharmony_ci */ 6508c2ecf20Sopenharmony_cistatic int 6518c2ecf20Sopenharmony_civhost_scsi_map_to_sgl(struct vhost_scsi_cmd *cmd, 6528c2ecf20Sopenharmony_ci struct iov_iter *iter, 6538c2ecf20Sopenharmony_ci struct scatterlist *sgl, 6548c2ecf20Sopenharmony_ci bool write) 6558c2ecf20Sopenharmony_ci{ 6568c2ecf20Sopenharmony_ci struct page **pages = cmd->tvc_upages; 6578c2ecf20Sopenharmony_ci struct scatterlist *sg = sgl; 6588c2ecf20Sopenharmony_ci ssize_t bytes; 6598c2ecf20Sopenharmony_ci size_t offset; 6608c2ecf20Sopenharmony_ci unsigned int npages = 0; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci bytes = iov_iter_get_pages(iter, pages, LONG_MAX, 6638c2ecf20Sopenharmony_ci VHOST_SCSI_PREALLOC_UPAGES, &offset); 6648c2ecf20Sopenharmony_ci /* No pages were pinned */ 6658c2ecf20Sopenharmony_ci if (bytes <= 0) 6668c2ecf20Sopenharmony_ci return bytes < 0 ? bytes : -EFAULT; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci iov_iter_advance(iter, bytes); 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci while (bytes) { 6718c2ecf20Sopenharmony_ci unsigned n = min_t(unsigned, PAGE_SIZE - offset, bytes); 6728c2ecf20Sopenharmony_ci sg_set_page(sg++, pages[npages++], n, offset); 6738c2ecf20Sopenharmony_ci bytes -= n; 6748c2ecf20Sopenharmony_ci offset = 0; 6758c2ecf20Sopenharmony_ci } 6768c2ecf20Sopenharmony_ci return npages; 6778c2ecf20Sopenharmony_ci} 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_cistatic int 6808c2ecf20Sopenharmony_civhost_scsi_calc_sgls(struct iov_iter *iter, size_t bytes, int max_sgls) 6818c2ecf20Sopenharmony_ci{ 6828c2ecf20Sopenharmony_ci int sgl_count = 0; 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci if (!iter || !iter->iov) { 6858c2ecf20Sopenharmony_ci pr_err("%s: iter->iov is NULL, but expected bytes: %zu" 6868c2ecf20Sopenharmony_ci " present\n", __func__, bytes); 6878c2ecf20Sopenharmony_ci return -EINVAL; 6888c2ecf20Sopenharmony_ci } 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci sgl_count = iov_iter_npages(iter, 0xffff); 6918c2ecf20Sopenharmony_ci if (sgl_count > max_sgls) { 6928c2ecf20Sopenharmony_ci pr_err("%s: requested sgl_count: %d exceeds pre-allocated" 6938c2ecf20Sopenharmony_ci " max_sgls: %d\n", __func__, sgl_count, max_sgls); 6948c2ecf20Sopenharmony_ci return -EINVAL; 6958c2ecf20Sopenharmony_ci } 6968c2ecf20Sopenharmony_ci return sgl_count; 6978c2ecf20Sopenharmony_ci} 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_cistatic int 7008c2ecf20Sopenharmony_civhost_scsi_iov_to_sgl(struct vhost_scsi_cmd *cmd, bool write, 7018c2ecf20Sopenharmony_ci struct iov_iter *iter, 7028c2ecf20Sopenharmony_ci struct scatterlist *sg, int sg_count) 7038c2ecf20Sopenharmony_ci{ 7048c2ecf20Sopenharmony_ci struct scatterlist *p = sg; 7058c2ecf20Sopenharmony_ci int ret; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci while (iov_iter_count(iter)) { 7088c2ecf20Sopenharmony_ci ret = vhost_scsi_map_to_sgl(cmd, iter, sg, write); 7098c2ecf20Sopenharmony_ci if (ret < 0) { 7108c2ecf20Sopenharmony_ci while (p < sg) { 7118c2ecf20Sopenharmony_ci struct page *page = sg_page(p++); 7128c2ecf20Sopenharmony_ci if (page) 7138c2ecf20Sopenharmony_ci put_page(page); 7148c2ecf20Sopenharmony_ci } 7158c2ecf20Sopenharmony_ci return ret; 7168c2ecf20Sopenharmony_ci } 7178c2ecf20Sopenharmony_ci sg += ret; 7188c2ecf20Sopenharmony_ci } 7198c2ecf20Sopenharmony_ci return 0; 7208c2ecf20Sopenharmony_ci} 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_cistatic int 7238c2ecf20Sopenharmony_civhost_scsi_mapal(struct vhost_scsi_cmd *cmd, 7248c2ecf20Sopenharmony_ci size_t prot_bytes, struct iov_iter *prot_iter, 7258c2ecf20Sopenharmony_ci size_t data_bytes, struct iov_iter *data_iter) 7268c2ecf20Sopenharmony_ci{ 7278c2ecf20Sopenharmony_ci int sgl_count, ret; 7288c2ecf20Sopenharmony_ci bool write = (cmd->tvc_data_direction == DMA_FROM_DEVICE); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci if (prot_bytes) { 7318c2ecf20Sopenharmony_ci sgl_count = vhost_scsi_calc_sgls(prot_iter, prot_bytes, 7328c2ecf20Sopenharmony_ci VHOST_SCSI_PREALLOC_PROT_SGLS); 7338c2ecf20Sopenharmony_ci if (sgl_count < 0) 7348c2ecf20Sopenharmony_ci return sgl_count; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci sg_init_table(cmd->tvc_prot_sgl, sgl_count); 7378c2ecf20Sopenharmony_ci cmd->tvc_prot_sgl_count = sgl_count; 7388c2ecf20Sopenharmony_ci pr_debug("%s prot_sg %p prot_sgl_count %u\n", __func__, 7398c2ecf20Sopenharmony_ci cmd->tvc_prot_sgl, cmd->tvc_prot_sgl_count); 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci ret = vhost_scsi_iov_to_sgl(cmd, write, prot_iter, 7428c2ecf20Sopenharmony_ci cmd->tvc_prot_sgl, 7438c2ecf20Sopenharmony_ci cmd->tvc_prot_sgl_count); 7448c2ecf20Sopenharmony_ci if (ret < 0) { 7458c2ecf20Sopenharmony_ci cmd->tvc_prot_sgl_count = 0; 7468c2ecf20Sopenharmony_ci return ret; 7478c2ecf20Sopenharmony_ci } 7488c2ecf20Sopenharmony_ci } 7498c2ecf20Sopenharmony_ci sgl_count = vhost_scsi_calc_sgls(data_iter, data_bytes, 7508c2ecf20Sopenharmony_ci VHOST_SCSI_PREALLOC_SGLS); 7518c2ecf20Sopenharmony_ci if (sgl_count < 0) 7528c2ecf20Sopenharmony_ci return sgl_count; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci sg_init_table(cmd->tvc_sgl, sgl_count); 7558c2ecf20Sopenharmony_ci cmd->tvc_sgl_count = sgl_count; 7568c2ecf20Sopenharmony_ci pr_debug("%s data_sg %p data_sgl_count %u\n", __func__, 7578c2ecf20Sopenharmony_ci cmd->tvc_sgl, cmd->tvc_sgl_count); 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci ret = vhost_scsi_iov_to_sgl(cmd, write, data_iter, 7608c2ecf20Sopenharmony_ci cmd->tvc_sgl, cmd->tvc_sgl_count); 7618c2ecf20Sopenharmony_ci if (ret < 0) { 7628c2ecf20Sopenharmony_ci cmd->tvc_sgl_count = 0; 7638c2ecf20Sopenharmony_ci return ret; 7648c2ecf20Sopenharmony_ci } 7658c2ecf20Sopenharmony_ci return 0; 7668c2ecf20Sopenharmony_ci} 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_cistatic int vhost_scsi_to_tcm_attr(int attr) 7698c2ecf20Sopenharmony_ci{ 7708c2ecf20Sopenharmony_ci switch (attr) { 7718c2ecf20Sopenharmony_ci case VIRTIO_SCSI_S_SIMPLE: 7728c2ecf20Sopenharmony_ci return TCM_SIMPLE_TAG; 7738c2ecf20Sopenharmony_ci case VIRTIO_SCSI_S_ORDERED: 7748c2ecf20Sopenharmony_ci return TCM_ORDERED_TAG; 7758c2ecf20Sopenharmony_ci case VIRTIO_SCSI_S_HEAD: 7768c2ecf20Sopenharmony_ci return TCM_HEAD_TAG; 7778c2ecf20Sopenharmony_ci case VIRTIO_SCSI_S_ACA: 7788c2ecf20Sopenharmony_ci return TCM_ACA_TAG; 7798c2ecf20Sopenharmony_ci default: 7808c2ecf20Sopenharmony_ci break; 7818c2ecf20Sopenharmony_ci } 7828c2ecf20Sopenharmony_ci return TCM_SIMPLE_TAG; 7838c2ecf20Sopenharmony_ci} 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_cistatic void vhost_scsi_submission_work(struct work_struct *work) 7868c2ecf20Sopenharmony_ci{ 7878c2ecf20Sopenharmony_ci struct vhost_scsi_cmd *cmd = 7888c2ecf20Sopenharmony_ci container_of(work, struct vhost_scsi_cmd, work); 7898c2ecf20Sopenharmony_ci struct vhost_scsi_nexus *tv_nexus; 7908c2ecf20Sopenharmony_ci struct se_cmd *se_cmd = &cmd->tvc_se_cmd; 7918c2ecf20Sopenharmony_ci struct scatterlist *sg_ptr, *sg_prot_ptr = NULL; 7928c2ecf20Sopenharmony_ci int rc; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci /* FIXME: BIDI operation */ 7958c2ecf20Sopenharmony_ci if (cmd->tvc_sgl_count) { 7968c2ecf20Sopenharmony_ci sg_ptr = cmd->tvc_sgl; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci if (cmd->tvc_prot_sgl_count) 7998c2ecf20Sopenharmony_ci sg_prot_ptr = cmd->tvc_prot_sgl; 8008c2ecf20Sopenharmony_ci else 8018c2ecf20Sopenharmony_ci se_cmd->prot_pto = true; 8028c2ecf20Sopenharmony_ci } else { 8038c2ecf20Sopenharmony_ci sg_ptr = NULL; 8048c2ecf20Sopenharmony_ci } 8058c2ecf20Sopenharmony_ci tv_nexus = cmd->tvc_nexus; 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci se_cmd->tag = 0; 8088c2ecf20Sopenharmony_ci rc = target_submit_cmd_map_sgls(se_cmd, tv_nexus->tvn_se_sess, 8098c2ecf20Sopenharmony_ci cmd->tvc_cdb, &cmd->tvc_sense_buf[0], 8108c2ecf20Sopenharmony_ci cmd->tvc_lun, cmd->tvc_exp_data_len, 8118c2ecf20Sopenharmony_ci vhost_scsi_to_tcm_attr(cmd->tvc_task_attr), 8128c2ecf20Sopenharmony_ci cmd->tvc_data_direction, TARGET_SCF_ACK_KREF, 8138c2ecf20Sopenharmony_ci sg_ptr, cmd->tvc_sgl_count, NULL, 0, sg_prot_ptr, 8148c2ecf20Sopenharmony_ci cmd->tvc_prot_sgl_count); 8158c2ecf20Sopenharmony_ci if (rc < 0) { 8168c2ecf20Sopenharmony_ci transport_send_check_condition_and_sense(se_cmd, 8178c2ecf20Sopenharmony_ci TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE, 0); 8188c2ecf20Sopenharmony_ci transport_generic_free_cmd(se_cmd, 0); 8198c2ecf20Sopenharmony_ci } 8208c2ecf20Sopenharmony_ci} 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_cistatic void 8238c2ecf20Sopenharmony_civhost_scsi_send_bad_target(struct vhost_scsi *vs, 8248c2ecf20Sopenharmony_ci struct vhost_virtqueue *vq, 8258c2ecf20Sopenharmony_ci int head, unsigned out) 8268c2ecf20Sopenharmony_ci{ 8278c2ecf20Sopenharmony_ci struct virtio_scsi_cmd_resp __user *resp; 8288c2ecf20Sopenharmony_ci struct virtio_scsi_cmd_resp rsp; 8298c2ecf20Sopenharmony_ci int ret; 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci memset(&rsp, 0, sizeof(rsp)); 8328c2ecf20Sopenharmony_ci rsp.response = VIRTIO_SCSI_S_BAD_TARGET; 8338c2ecf20Sopenharmony_ci resp = vq->iov[out].iov_base; 8348c2ecf20Sopenharmony_ci ret = __copy_to_user(resp, &rsp, sizeof(rsp)); 8358c2ecf20Sopenharmony_ci if (!ret) 8368c2ecf20Sopenharmony_ci vhost_add_used_and_signal(&vs->dev, vq, head, 0); 8378c2ecf20Sopenharmony_ci else 8388c2ecf20Sopenharmony_ci pr_err("Faulted on virtio_scsi_cmd_resp\n"); 8398c2ecf20Sopenharmony_ci} 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_cistatic int 8428c2ecf20Sopenharmony_civhost_scsi_get_desc(struct vhost_scsi *vs, struct vhost_virtqueue *vq, 8438c2ecf20Sopenharmony_ci struct vhost_scsi_ctx *vc) 8448c2ecf20Sopenharmony_ci{ 8458c2ecf20Sopenharmony_ci int ret = -ENXIO; 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci vc->head = vhost_get_vq_desc(vq, vq->iov, 8488c2ecf20Sopenharmony_ci ARRAY_SIZE(vq->iov), &vc->out, &vc->in, 8498c2ecf20Sopenharmony_ci NULL, NULL); 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci pr_debug("vhost_get_vq_desc: head: %d, out: %u in: %u\n", 8528c2ecf20Sopenharmony_ci vc->head, vc->out, vc->in); 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci /* On error, stop handling until the next kick. */ 8558c2ecf20Sopenharmony_ci if (unlikely(vc->head < 0)) 8568c2ecf20Sopenharmony_ci goto done; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci /* Nothing new? Wait for eventfd to tell us they refilled. */ 8598c2ecf20Sopenharmony_ci if (vc->head == vq->num) { 8608c2ecf20Sopenharmony_ci if (unlikely(vhost_enable_notify(&vs->dev, vq))) { 8618c2ecf20Sopenharmony_ci vhost_disable_notify(&vs->dev, vq); 8628c2ecf20Sopenharmony_ci ret = -EAGAIN; 8638c2ecf20Sopenharmony_ci } 8648c2ecf20Sopenharmony_ci goto done; 8658c2ecf20Sopenharmony_ci } 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci /* 8688c2ecf20Sopenharmony_ci * Get the size of request and response buffers. 8698c2ecf20Sopenharmony_ci * FIXME: Not correct for BIDI operation 8708c2ecf20Sopenharmony_ci */ 8718c2ecf20Sopenharmony_ci vc->out_size = iov_length(vq->iov, vc->out); 8728c2ecf20Sopenharmony_ci vc->in_size = iov_length(&vq->iov[vc->out], vc->in); 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci /* 8758c2ecf20Sopenharmony_ci * Copy over the virtio-scsi request header, which for a 8768c2ecf20Sopenharmony_ci * ANY_LAYOUT enabled guest may span multiple iovecs, or a 8778c2ecf20Sopenharmony_ci * single iovec may contain both the header + outgoing 8788c2ecf20Sopenharmony_ci * WRITE payloads. 8798c2ecf20Sopenharmony_ci * 8808c2ecf20Sopenharmony_ci * copy_from_iter() will advance out_iter, so that it will 8818c2ecf20Sopenharmony_ci * point at the start of the outgoing WRITE payload, if 8828c2ecf20Sopenharmony_ci * DMA_TO_DEVICE is set. 8838c2ecf20Sopenharmony_ci */ 8848c2ecf20Sopenharmony_ci iov_iter_init(&vc->out_iter, WRITE, vq->iov, vc->out, vc->out_size); 8858c2ecf20Sopenharmony_ci ret = 0; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_cidone: 8888c2ecf20Sopenharmony_ci return ret; 8898c2ecf20Sopenharmony_ci} 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_cistatic int 8928c2ecf20Sopenharmony_civhost_scsi_chk_size(struct vhost_virtqueue *vq, struct vhost_scsi_ctx *vc) 8938c2ecf20Sopenharmony_ci{ 8948c2ecf20Sopenharmony_ci if (unlikely(vc->in_size < vc->rsp_size)) { 8958c2ecf20Sopenharmony_ci vq_err(vq, 8968c2ecf20Sopenharmony_ci "Response buf too small, need min %zu bytes got %zu", 8978c2ecf20Sopenharmony_ci vc->rsp_size, vc->in_size); 8988c2ecf20Sopenharmony_ci return -EINVAL; 8998c2ecf20Sopenharmony_ci } else if (unlikely(vc->out_size < vc->req_size)) { 9008c2ecf20Sopenharmony_ci vq_err(vq, 9018c2ecf20Sopenharmony_ci "Request buf too small, need min %zu bytes got %zu", 9028c2ecf20Sopenharmony_ci vc->req_size, vc->out_size); 9038c2ecf20Sopenharmony_ci return -EIO; 9048c2ecf20Sopenharmony_ci } 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci return 0; 9078c2ecf20Sopenharmony_ci} 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_cistatic int 9108c2ecf20Sopenharmony_civhost_scsi_get_req(struct vhost_virtqueue *vq, struct vhost_scsi_ctx *vc, 9118c2ecf20Sopenharmony_ci struct vhost_scsi_tpg **tpgp) 9128c2ecf20Sopenharmony_ci{ 9138c2ecf20Sopenharmony_ci int ret = -EIO; 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci if (unlikely(!copy_from_iter_full(vc->req, vc->req_size, 9168c2ecf20Sopenharmony_ci &vc->out_iter))) { 9178c2ecf20Sopenharmony_ci vq_err(vq, "Faulted on copy_from_iter_full\n"); 9188c2ecf20Sopenharmony_ci } else if (unlikely(*vc->lunp != 1)) { 9198c2ecf20Sopenharmony_ci /* virtio-scsi spec requires byte 0 of the lun to be 1 */ 9208c2ecf20Sopenharmony_ci vq_err(vq, "Illegal virtio-scsi lun: %u\n", *vc->lunp); 9218c2ecf20Sopenharmony_ci } else { 9228c2ecf20Sopenharmony_ci struct vhost_scsi_tpg **vs_tpg, *tpg; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci vs_tpg = vhost_vq_get_backend(vq); /* validated at handler entry */ 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci tpg = READ_ONCE(vs_tpg[*vc->target]); 9278c2ecf20Sopenharmony_ci if (unlikely(!tpg)) { 9288c2ecf20Sopenharmony_ci vq_err(vq, "Target 0x%x does not exist\n", *vc->target); 9298c2ecf20Sopenharmony_ci } else { 9308c2ecf20Sopenharmony_ci if (tpgp) 9318c2ecf20Sopenharmony_ci *tpgp = tpg; 9328c2ecf20Sopenharmony_ci ret = 0; 9338c2ecf20Sopenharmony_ci } 9348c2ecf20Sopenharmony_ci } 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci return ret; 9378c2ecf20Sopenharmony_ci} 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_cistatic u16 vhost_buf_to_lun(u8 *lun_buf) 9408c2ecf20Sopenharmony_ci{ 9418c2ecf20Sopenharmony_ci return ((lun_buf[2] << 8) | lun_buf[3]) & 0x3FFF; 9428c2ecf20Sopenharmony_ci} 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_cistatic void 9458c2ecf20Sopenharmony_civhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq) 9468c2ecf20Sopenharmony_ci{ 9478c2ecf20Sopenharmony_ci struct vhost_scsi_tpg **vs_tpg, *tpg; 9488c2ecf20Sopenharmony_ci struct virtio_scsi_cmd_req v_req; 9498c2ecf20Sopenharmony_ci struct virtio_scsi_cmd_req_pi v_req_pi; 9508c2ecf20Sopenharmony_ci struct vhost_scsi_ctx vc; 9518c2ecf20Sopenharmony_ci struct vhost_scsi_cmd *cmd; 9528c2ecf20Sopenharmony_ci struct iov_iter in_iter, prot_iter, data_iter; 9538c2ecf20Sopenharmony_ci u64 tag; 9548c2ecf20Sopenharmony_ci u32 exp_data_len, data_direction; 9558c2ecf20Sopenharmony_ci int ret, prot_bytes, c = 0; 9568c2ecf20Sopenharmony_ci u16 lun; 9578c2ecf20Sopenharmony_ci u8 task_attr; 9588c2ecf20Sopenharmony_ci bool t10_pi = vhost_has_feature(vq, VIRTIO_SCSI_F_T10_PI); 9598c2ecf20Sopenharmony_ci void *cdb; 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci mutex_lock(&vq->mutex); 9628c2ecf20Sopenharmony_ci /* 9638c2ecf20Sopenharmony_ci * We can handle the vq only after the endpoint is setup by calling the 9648c2ecf20Sopenharmony_ci * VHOST_SCSI_SET_ENDPOINT ioctl. 9658c2ecf20Sopenharmony_ci */ 9668c2ecf20Sopenharmony_ci vs_tpg = vhost_vq_get_backend(vq); 9678c2ecf20Sopenharmony_ci if (!vs_tpg) 9688c2ecf20Sopenharmony_ci goto out; 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci memset(&vc, 0, sizeof(vc)); 9718c2ecf20Sopenharmony_ci vc.rsp_size = sizeof(struct virtio_scsi_cmd_resp); 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci vhost_disable_notify(&vs->dev, vq); 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci do { 9768c2ecf20Sopenharmony_ci ret = vhost_scsi_get_desc(vs, vq, &vc); 9778c2ecf20Sopenharmony_ci if (ret) 9788c2ecf20Sopenharmony_ci goto err; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci /* 9818c2ecf20Sopenharmony_ci * Setup pointers and values based upon different virtio-scsi 9828c2ecf20Sopenharmony_ci * request header if T10_PI is enabled in KVM guest. 9838c2ecf20Sopenharmony_ci */ 9848c2ecf20Sopenharmony_ci if (t10_pi) { 9858c2ecf20Sopenharmony_ci vc.req = &v_req_pi; 9868c2ecf20Sopenharmony_ci vc.req_size = sizeof(v_req_pi); 9878c2ecf20Sopenharmony_ci vc.lunp = &v_req_pi.lun[0]; 9888c2ecf20Sopenharmony_ci vc.target = &v_req_pi.lun[1]; 9898c2ecf20Sopenharmony_ci } else { 9908c2ecf20Sopenharmony_ci vc.req = &v_req; 9918c2ecf20Sopenharmony_ci vc.req_size = sizeof(v_req); 9928c2ecf20Sopenharmony_ci vc.lunp = &v_req.lun[0]; 9938c2ecf20Sopenharmony_ci vc.target = &v_req.lun[1]; 9948c2ecf20Sopenharmony_ci } 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci /* 9978c2ecf20Sopenharmony_ci * Validate the size of request and response buffers. 9988c2ecf20Sopenharmony_ci * Check for a sane response buffer so we can report 9998c2ecf20Sopenharmony_ci * early errors back to the guest. 10008c2ecf20Sopenharmony_ci */ 10018c2ecf20Sopenharmony_ci ret = vhost_scsi_chk_size(vq, &vc); 10028c2ecf20Sopenharmony_ci if (ret) 10038c2ecf20Sopenharmony_ci goto err; 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci ret = vhost_scsi_get_req(vq, &vc, &tpg); 10068c2ecf20Sopenharmony_ci if (ret) 10078c2ecf20Sopenharmony_ci goto err; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci ret = -EIO; /* bad target on any error from here on */ 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci /* 10128c2ecf20Sopenharmony_ci * Determine data_direction by calculating the total outgoing 10138c2ecf20Sopenharmony_ci * iovec sizes + incoming iovec sizes vs. virtio-scsi request + 10148c2ecf20Sopenharmony_ci * response headers respectively. 10158c2ecf20Sopenharmony_ci * 10168c2ecf20Sopenharmony_ci * For DMA_TO_DEVICE this is out_iter, which is already pointing 10178c2ecf20Sopenharmony_ci * to the right place. 10188c2ecf20Sopenharmony_ci * 10198c2ecf20Sopenharmony_ci * For DMA_FROM_DEVICE, the iovec will be just past the end 10208c2ecf20Sopenharmony_ci * of the virtio-scsi response header in either the same 10218c2ecf20Sopenharmony_ci * or immediately following iovec. 10228c2ecf20Sopenharmony_ci * 10238c2ecf20Sopenharmony_ci * Any associated T10_PI bytes for the outgoing / incoming 10248c2ecf20Sopenharmony_ci * payloads are included in calculation of exp_data_len here. 10258c2ecf20Sopenharmony_ci */ 10268c2ecf20Sopenharmony_ci prot_bytes = 0; 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci if (vc.out_size > vc.req_size) { 10298c2ecf20Sopenharmony_ci data_direction = DMA_TO_DEVICE; 10308c2ecf20Sopenharmony_ci exp_data_len = vc.out_size - vc.req_size; 10318c2ecf20Sopenharmony_ci data_iter = vc.out_iter; 10328c2ecf20Sopenharmony_ci } else if (vc.in_size > vc.rsp_size) { 10338c2ecf20Sopenharmony_ci data_direction = DMA_FROM_DEVICE; 10348c2ecf20Sopenharmony_ci exp_data_len = vc.in_size - vc.rsp_size; 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci iov_iter_init(&in_iter, READ, &vq->iov[vc.out], vc.in, 10378c2ecf20Sopenharmony_ci vc.rsp_size + exp_data_len); 10388c2ecf20Sopenharmony_ci iov_iter_advance(&in_iter, vc.rsp_size); 10398c2ecf20Sopenharmony_ci data_iter = in_iter; 10408c2ecf20Sopenharmony_ci } else { 10418c2ecf20Sopenharmony_ci data_direction = DMA_NONE; 10428c2ecf20Sopenharmony_ci exp_data_len = 0; 10438c2ecf20Sopenharmony_ci } 10448c2ecf20Sopenharmony_ci /* 10458c2ecf20Sopenharmony_ci * If T10_PI header + payload is present, setup prot_iter values 10468c2ecf20Sopenharmony_ci * and recalculate data_iter for vhost_scsi_mapal() mapping to 10478c2ecf20Sopenharmony_ci * host scatterlists via get_user_pages_fast(). 10488c2ecf20Sopenharmony_ci */ 10498c2ecf20Sopenharmony_ci if (t10_pi) { 10508c2ecf20Sopenharmony_ci if (v_req_pi.pi_bytesout) { 10518c2ecf20Sopenharmony_ci if (data_direction != DMA_TO_DEVICE) { 10528c2ecf20Sopenharmony_ci vq_err(vq, "Received non zero pi_bytesout," 10538c2ecf20Sopenharmony_ci " but wrong data_direction\n"); 10548c2ecf20Sopenharmony_ci goto err; 10558c2ecf20Sopenharmony_ci } 10568c2ecf20Sopenharmony_ci prot_bytes = vhost32_to_cpu(vq, v_req_pi.pi_bytesout); 10578c2ecf20Sopenharmony_ci } else if (v_req_pi.pi_bytesin) { 10588c2ecf20Sopenharmony_ci if (data_direction != DMA_FROM_DEVICE) { 10598c2ecf20Sopenharmony_ci vq_err(vq, "Received non zero pi_bytesin," 10608c2ecf20Sopenharmony_ci " but wrong data_direction\n"); 10618c2ecf20Sopenharmony_ci goto err; 10628c2ecf20Sopenharmony_ci } 10638c2ecf20Sopenharmony_ci prot_bytes = vhost32_to_cpu(vq, v_req_pi.pi_bytesin); 10648c2ecf20Sopenharmony_ci } 10658c2ecf20Sopenharmony_ci /* 10668c2ecf20Sopenharmony_ci * Set prot_iter to data_iter and truncate it to 10678c2ecf20Sopenharmony_ci * prot_bytes, and advance data_iter past any 10688c2ecf20Sopenharmony_ci * preceeding prot_bytes that may be present. 10698c2ecf20Sopenharmony_ci * 10708c2ecf20Sopenharmony_ci * Also fix up the exp_data_len to reflect only the 10718c2ecf20Sopenharmony_ci * actual data payload length. 10728c2ecf20Sopenharmony_ci */ 10738c2ecf20Sopenharmony_ci if (prot_bytes) { 10748c2ecf20Sopenharmony_ci exp_data_len -= prot_bytes; 10758c2ecf20Sopenharmony_ci prot_iter = data_iter; 10768c2ecf20Sopenharmony_ci iov_iter_truncate(&prot_iter, prot_bytes); 10778c2ecf20Sopenharmony_ci iov_iter_advance(&data_iter, prot_bytes); 10788c2ecf20Sopenharmony_ci } 10798c2ecf20Sopenharmony_ci tag = vhost64_to_cpu(vq, v_req_pi.tag); 10808c2ecf20Sopenharmony_ci task_attr = v_req_pi.task_attr; 10818c2ecf20Sopenharmony_ci cdb = &v_req_pi.cdb[0]; 10828c2ecf20Sopenharmony_ci lun = vhost_buf_to_lun(v_req_pi.lun); 10838c2ecf20Sopenharmony_ci } else { 10848c2ecf20Sopenharmony_ci tag = vhost64_to_cpu(vq, v_req.tag); 10858c2ecf20Sopenharmony_ci task_attr = v_req.task_attr; 10868c2ecf20Sopenharmony_ci cdb = &v_req.cdb[0]; 10878c2ecf20Sopenharmony_ci lun = vhost_buf_to_lun(v_req.lun); 10888c2ecf20Sopenharmony_ci } 10898c2ecf20Sopenharmony_ci /* 10908c2ecf20Sopenharmony_ci * Check that the received CDB size does not exceeded our 10918c2ecf20Sopenharmony_ci * hardcoded max for vhost-scsi, then get a pre-allocated 10928c2ecf20Sopenharmony_ci * cmd descriptor for the new virtio-scsi tag. 10938c2ecf20Sopenharmony_ci * 10948c2ecf20Sopenharmony_ci * TODO what if cdb was too small for varlen cdb header? 10958c2ecf20Sopenharmony_ci */ 10968c2ecf20Sopenharmony_ci if (unlikely(scsi_command_size(cdb) > VHOST_SCSI_MAX_CDB_SIZE)) { 10978c2ecf20Sopenharmony_ci vq_err(vq, "Received SCSI CDB with command_size: %d that" 10988c2ecf20Sopenharmony_ci " exceeds SCSI_MAX_VARLEN_CDB_SIZE: %d\n", 10998c2ecf20Sopenharmony_ci scsi_command_size(cdb), VHOST_SCSI_MAX_CDB_SIZE); 11008c2ecf20Sopenharmony_ci goto err; 11018c2ecf20Sopenharmony_ci } 11028c2ecf20Sopenharmony_ci cmd = vhost_scsi_get_cmd(vq, tpg, cdb, tag, lun, task_attr, 11038c2ecf20Sopenharmony_ci exp_data_len + prot_bytes, 11048c2ecf20Sopenharmony_ci data_direction); 11058c2ecf20Sopenharmony_ci if (IS_ERR(cmd)) { 11068c2ecf20Sopenharmony_ci vq_err(vq, "vhost_scsi_get_cmd failed %ld\n", 11078c2ecf20Sopenharmony_ci PTR_ERR(cmd)); 11088c2ecf20Sopenharmony_ci goto err; 11098c2ecf20Sopenharmony_ci } 11108c2ecf20Sopenharmony_ci cmd->tvc_vhost = vs; 11118c2ecf20Sopenharmony_ci cmd->tvc_vq = vq; 11128c2ecf20Sopenharmony_ci cmd->tvc_resp_iov = vq->iov[vc.out]; 11138c2ecf20Sopenharmony_ci cmd->tvc_in_iovs = vc.in; 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci pr_debug("vhost_scsi got command opcode: %#02x, lun: %d\n", 11168c2ecf20Sopenharmony_ci cmd->tvc_cdb[0], cmd->tvc_lun); 11178c2ecf20Sopenharmony_ci pr_debug("cmd: %p exp_data_len: %d, prot_bytes: %d data_direction:" 11188c2ecf20Sopenharmony_ci " %d\n", cmd, exp_data_len, prot_bytes, data_direction); 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci if (data_direction != DMA_NONE) { 11218c2ecf20Sopenharmony_ci if (unlikely(vhost_scsi_mapal(cmd, prot_bytes, 11228c2ecf20Sopenharmony_ci &prot_iter, exp_data_len, 11238c2ecf20Sopenharmony_ci &data_iter))) { 11248c2ecf20Sopenharmony_ci vq_err(vq, "Failed to map iov to sgl\n"); 11258c2ecf20Sopenharmony_ci vhost_scsi_release_cmd_res(&cmd->tvc_se_cmd); 11268c2ecf20Sopenharmony_ci goto err; 11278c2ecf20Sopenharmony_ci } 11288c2ecf20Sopenharmony_ci } 11298c2ecf20Sopenharmony_ci /* 11308c2ecf20Sopenharmony_ci * Save the descriptor from vhost_get_vq_desc() to be used to 11318c2ecf20Sopenharmony_ci * complete the virtio-scsi request in TCM callback context via 11328c2ecf20Sopenharmony_ci * vhost_scsi_queue_data_in() and vhost_scsi_queue_status() 11338c2ecf20Sopenharmony_ci */ 11348c2ecf20Sopenharmony_ci cmd->tvc_vq_desc = vc.head; 11358c2ecf20Sopenharmony_ci /* 11368c2ecf20Sopenharmony_ci * Dispatch cmd descriptor for cmwq execution in process 11378c2ecf20Sopenharmony_ci * context provided by vhost_scsi_workqueue. This also ensures 11388c2ecf20Sopenharmony_ci * cmd is executed on the same kworker CPU as this vhost 11398c2ecf20Sopenharmony_ci * thread to gain positive L2 cache locality effects. 11408c2ecf20Sopenharmony_ci */ 11418c2ecf20Sopenharmony_ci INIT_WORK(&cmd->work, vhost_scsi_submission_work); 11428c2ecf20Sopenharmony_ci queue_work(vhost_scsi_workqueue, &cmd->work); 11438c2ecf20Sopenharmony_ci ret = 0; 11448c2ecf20Sopenharmony_cierr: 11458c2ecf20Sopenharmony_ci /* 11468c2ecf20Sopenharmony_ci * ENXIO: No more requests, or read error, wait for next kick 11478c2ecf20Sopenharmony_ci * EINVAL: Invalid response buffer, drop the request 11488c2ecf20Sopenharmony_ci * EIO: Respond with bad target 11498c2ecf20Sopenharmony_ci * EAGAIN: Pending request 11508c2ecf20Sopenharmony_ci */ 11518c2ecf20Sopenharmony_ci if (ret == -ENXIO) 11528c2ecf20Sopenharmony_ci break; 11538c2ecf20Sopenharmony_ci else if (ret == -EIO) 11548c2ecf20Sopenharmony_ci vhost_scsi_send_bad_target(vs, vq, vc.head, vc.out); 11558c2ecf20Sopenharmony_ci } while (likely(!vhost_exceeds_weight(vq, ++c, 0))); 11568c2ecf20Sopenharmony_ciout: 11578c2ecf20Sopenharmony_ci mutex_unlock(&vq->mutex); 11588c2ecf20Sopenharmony_ci} 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_cistatic void 11618c2ecf20Sopenharmony_civhost_scsi_send_tmf_resp(struct vhost_scsi *vs, struct vhost_virtqueue *vq, 11628c2ecf20Sopenharmony_ci int in_iovs, int vq_desc, struct iovec *resp_iov, 11638c2ecf20Sopenharmony_ci int tmf_resp_code) 11648c2ecf20Sopenharmony_ci{ 11658c2ecf20Sopenharmony_ci struct virtio_scsi_ctrl_tmf_resp rsp; 11668c2ecf20Sopenharmony_ci struct iov_iter iov_iter; 11678c2ecf20Sopenharmony_ci int ret; 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci pr_debug("%s\n", __func__); 11708c2ecf20Sopenharmony_ci memset(&rsp, 0, sizeof(rsp)); 11718c2ecf20Sopenharmony_ci rsp.response = tmf_resp_code; 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci iov_iter_init(&iov_iter, READ, resp_iov, in_iovs, sizeof(rsp)); 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci ret = copy_to_iter(&rsp, sizeof(rsp), &iov_iter); 11768c2ecf20Sopenharmony_ci if (likely(ret == sizeof(rsp))) 11778c2ecf20Sopenharmony_ci vhost_add_used_and_signal(&vs->dev, vq, vq_desc, 0); 11788c2ecf20Sopenharmony_ci else 11798c2ecf20Sopenharmony_ci pr_err("Faulted on virtio_scsi_ctrl_tmf_resp\n"); 11808c2ecf20Sopenharmony_ci} 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_cistatic void vhost_scsi_tmf_resp_work(struct vhost_work *work) 11838c2ecf20Sopenharmony_ci{ 11848c2ecf20Sopenharmony_ci struct vhost_scsi_tmf *tmf = container_of(work, struct vhost_scsi_tmf, 11858c2ecf20Sopenharmony_ci vwork); 11868c2ecf20Sopenharmony_ci int resp_code; 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci if (tmf->scsi_resp == TMR_FUNCTION_COMPLETE) 11898c2ecf20Sopenharmony_ci resp_code = VIRTIO_SCSI_S_FUNCTION_SUCCEEDED; 11908c2ecf20Sopenharmony_ci else 11918c2ecf20Sopenharmony_ci resp_code = VIRTIO_SCSI_S_FUNCTION_REJECTED; 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci vhost_scsi_send_tmf_resp(tmf->vhost, &tmf->svq->vq, tmf->in_iovs, 11948c2ecf20Sopenharmony_ci tmf->vq_desc, &tmf->resp_iov, resp_code); 11958c2ecf20Sopenharmony_ci vhost_scsi_release_tmf_res(tmf); 11968c2ecf20Sopenharmony_ci} 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_cistatic void 11998c2ecf20Sopenharmony_civhost_scsi_handle_tmf(struct vhost_scsi *vs, struct vhost_scsi_tpg *tpg, 12008c2ecf20Sopenharmony_ci struct vhost_virtqueue *vq, 12018c2ecf20Sopenharmony_ci struct virtio_scsi_ctrl_tmf_req *vtmf, 12028c2ecf20Sopenharmony_ci struct vhost_scsi_ctx *vc) 12038c2ecf20Sopenharmony_ci{ 12048c2ecf20Sopenharmony_ci struct vhost_scsi_virtqueue *svq = container_of(vq, 12058c2ecf20Sopenharmony_ci struct vhost_scsi_virtqueue, vq); 12068c2ecf20Sopenharmony_ci struct vhost_scsi_tmf *tmf; 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci if (vhost32_to_cpu(vq, vtmf->subtype) != 12098c2ecf20Sopenharmony_ci VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET) 12108c2ecf20Sopenharmony_ci goto send_reject; 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci if (!tpg->tpg_nexus || !tpg->tpg_nexus->tvn_se_sess) { 12138c2ecf20Sopenharmony_ci pr_err("Unable to locate active struct vhost_scsi_nexus for LUN RESET.\n"); 12148c2ecf20Sopenharmony_ci goto send_reject; 12158c2ecf20Sopenharmony_ci } 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci mutex_lock(&tpg->tv_tpg_mutex); 12188c2ecf20Sopenharmony_ci if (list_empty(&tpg->tmf_queue)) { 12198c2ecf20Sopenharmony_ci pr_err("Missing reserve TMF. Could not handle LUN RESET.\n"); 12208c2ecf20Sopenharmony_ci mutex_unlock(&tpg->tv_tpg_mutex); 12218c2ecf20Sopenharmony_ci goto send_reject; 12228c2ecf20Sopenharmony_ci } 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci tmf = list_first_entry(&tpg->tmf_queue, struct vhost_scsi_tmf, 12258c2ecf20Sopenharmony_ci queue_entry); 12268c2ecf20Sopenharmony_ci list_del_init(&tmf->queue_entry); 12278c2ecf20Sopenharmony_ci mutex_unlock(&tpg->tv_tpg_mutex); 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci tmf->tpg = tpg; 12308c2ecf20Sopenharmony_ci tmf->vhost = vs; 12318c2ecf20Sopenharmony_ci tmf->svq = svq; 12328c2ecf20Sopenharmony_ci tmf->resp_iov = vq->iov[vc->out]; 12338c2ecf20Sopenharmony_ci tmf->vq_desc = vc->head; 12348c2ecf20Sopenharmony_ci tmf->in_iovs = vc->in; 12358c2ecf20Sopenharmony_ci tmf->inflight = vhost_scsi_get_inflight(vq); 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci if (target_submit_tmr(&tmf->se_cmd, tpg->tpg_nexus->tvn_se_sess, NULL, 12388c2ecf20Sopenharmony_ci vhost_buf_to_lun(vtmf->lun), NULL, 12398c2ecf20Sopenharmony_ci TMR_LUN_RESET, GFP_KERNEL, 0, 12408c2ecf20Sopenharmony_ci TARGET_SCF_ACK_KREF) < 0) { 12418c2ecf20Sopenharmony_ci vhost_scsi_release_tmf_res(tmf); 12428c2ecf20Sopenharmony_ci goto send_reject; 12438c2ecf20Sopenharmony_ci } 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci return; 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_cisend_reject: 12488c2ecf20Sopenharmony_ci vhost_scsi_send_tmf_resp(vs, vq, vc->in, vc->head, &vq->iov[vc->out], 12498c2ecf20Sopenharmony_ci VIRTIO_SCSI_S_FUNCTION_REJECTED); 12508c2ecf20Sopenharmony_ci} 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_cistatic void 12538c2ecf20Sopenharmony_civhost_scsi_send_an_resp(struct vhost_scsi *vs, 12548c2ecf20Sopenharmony_ci struct vhost_virtqueue *vq, 12558c2ecf20Sopenharmony_ci struct vhost_scsi_ctx *vc) 12568c2ecf20Sopenharmony_ci{ 12578c2ecf20Sopenharmony_ci struct virtio_scsi_ctrl_an_resp rsp; 12588c2ecf20Sopenharmony_ci struct iov_iter iov_iter; 12598c2ecf20Sopenharmony_ci int ret; 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci pr_debug("%s\n", __func__); 12628c2ecf20Sopenharmony_ci memset(&rsp, 0, sizeof(rsp)); /* event_actual = 0 */ 12638c2ecf20Sopenharmony_ci rsp.response = VIRTIO_SCSI_S_OK; 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci iov_iter_init(&iov_iter, READ, &vq->iov[vc->out], vc->in, sizeof(rsp)); 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci ret = copy_to_iter(&rsp, sizeof(rsp), &iov_iter); 12688c2ecf20Sopenharmony_ci if (likely(ret == sizeof(rsp))) 12698c2ecf20Sopenharmony_ci vhost_add_used_and_signal(&vs->dev, vq, vc->head, 0); 12708c2ecf20Sopenharmony_ci else 12718c2ecf20Sopenharmony_ci pr_err("Faulted on virtio_scsi_ctrl_an_resp\n"); 12728c2ecf20Sopenharmony_ci} 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_cistatic void 12758c2ecf20Sopenharmony_civhost_scsi_ctl_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq) 12768c2ecf20Sopenharmony_ci{ 12778c2ecf20Sopenharmony_ci struct vhost_scsi_tpg *tpg; 12788c2ecf20Sopenharmony_ci union { 12798c2ecf20Sopenharmony_ci __virtio32 type; 12808c2ecf20Sopenharmony_ci struct virtio_scsi_ctrl_an_req an; 12818c2ecf20Sopenharmony_ci struct virtio_scsi_ctrl_tmf_req tmf; 12828c2ecf20Sopenharmony_ci } v_req; 12838c2ecf20Sopenharmony_ci struct vhost_scsi_ctx vc; 12848c2ecf20Sopenharmony_ci size_t typ_size; 12858c2ecf20Sopenharmony_ci int ret, c = 0; 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci mutex_lock(&vq->mutex); 12888c2ecf20Sopenharmony_ci /* 12898c2ecf20Sopenharmony_ci * We can handle the vq only after the endpoint is setup by calling the 12908c2ecf20Sopenharmony_ci * VHOST_SCSI_SET_ENDPOINT ioctl. 12918c2ecf20Sopenharmony_ci */ 12928c2ecf20Sopenharmony_ci if (!vhost_vq_get_backend(vq)) 12938c2ecf20Sopenharmony_ci goto out; 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci memset(&vc, 0, sizeof(vc)); 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci vhost_disable_notify(&vs->dev, vq); 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci do { 13008c2ecf20Sopenharmony_ci ret = vhost_scsi_get_desc(vs, vq, &vc); 13018c2ecf20Sopenharmony_ci if (ret) 13028c2ecf20Sopenharmony_ci goto err; 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci /* 13058c2ecf20Sopenharmony_ci * Get the request type first in order to setup 13068c2ecf20Sopenharmony_ci * other parameters dependent on the type. 13078c2ecf20Sopenharmony_ci */ 13088c2ecf20Sopenharmony_ci vc.req = &v_req.type; 13098c2ecf20Sopenharmony_ci typ_size = sizeof(v_req.type); 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci if (unlikely(!copy_from_iter_full(vc.req, typ_size, 13128c2ecf20Sopenharmony_ci &vc.out_iter))) { 13138c2ecf20Sopenharmony_ci vq_err(vq, "Faulted on copy_from_iter tmf type\n"); 13148c2ecf20Sopenharmony_ci /* 13158c2ecf20Sopenharmony_ci * The size of the response buffer depends on the 13168c2ecf20Sopenharmony_ci * request type and must be validated against it. 13178c2ecf20Sopenharmony_ci * Since the request type is not known, don't send 13188c2ecf20Sopenharmony_ci * a response. 13198c2ecf20Sopenharmony_ci */ 13208c2ecf20Sopenharmony_ci continue; 13218c2ecf20Sopenharmony_ci } 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci switch (vhost32_to_cpu(vq, v_req.type)) { 13248c2ecf20Sopenharmony_ci case VIRTIO_SCSI_T_TMF: 13258c2ecf20Sopenharmony_ci vc.req = &v_req.tmf; 13268c2ecf20Sopenharmony_ci vc.req_size = sizeof(struct virtio_scsi_ctrl_tmf_req); 13278c2ecf20Sopenharmony_ci vc.rsp_size = sizeof(struct virtio_scsi_ctrl_tmf_resp); 13288c2ecf20Sopenharmony_ci vc.lunp = &v_req.tmf.lun[0]; 13298c2ecf20Sopenharmony_ci vc.target = &v_req.tmf.lun[1]; 13308c2ecf20Sopenharmony_ci break; 13318c2ecf20Sopenharmony_ci case VIRTIO_SCSI_T_AN_QUERY: 13328c2ecf20Sopenharmony_ci case VIRTIO_SCSI_T_AN_SUBSCRIBE: 13338c2ecf20Sopenharmony_ci vc.req = &v_req.an; 13348c2ecf20Sopenharmony_ci vc.req_size = sizeof(struct virtio_scsi_ctrl_an_req); 13358c2ecf20Sopenharmony_ci vc.rsp_size = sizeof(struct virtio_scsi_ctrl_an_resp); 13368c2ecf20Sopenharmony_ci vc.lunp = &v_req.an.lun[0]; 13378c2ecf20Sopenharmony_ci vc.target = NULL; 13388c2ecf20Sopenharmony_ci break; 13398c2ecf20Sopenharmony_ci default: 13408c2ecf20Sopenharmony_ci vq_err(vq, "Unknown control request %d", v_req.type); 13418c2ecf20Sopenharmony_ci continue; 13428c2ecf20Sopenharmony_ci } 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci /* 13458c2ecf20Sopenharmony_ci * Validate the size of request and response buffers. 13468c2ecf20Sopenharmony_ci * Check for a sane response buffer so we can report 13478c2ecf20Sopenharmony_ci * early errors back to the guest. 13488c2ecf20Sopenharmony_ci */ 13498c2ecf20Sopenharmony_ci ret = vhost_scsi_chk_size(vq, &vc); 13508c2ecf20Sopenharmony_ci if (ret) 13518c2ecf20Sopenharmony_ci goto err; 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci /* 13548c2ecf20Sopenharmony_ci * Get the rest of the request now that its size is known. 13558c2ecf20Sopenharmony_ci */ 13568c2ecf20Sopenharmony_ci vc.req += typ_size; 13578c2ecf20Sopenharmony_ci vc.req_size -= typ_size; 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ci ret = vhost_scsi_get_req(vq, &vc, &tpg); 13608c2ecf20Sopenharmony_ci if (ret) 13618c2ecf20Sopenharmony_ci goto err; 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci if (v_req.type == VIRTIO_SCSI_T_TMF) 13648c2ecf20Sopenharmony_ci vhost_scsi_handle_tmf(vs, tpg, vq, &v_req.tmf, &vc); 13658c2ecf20Sopenharmony_ci else 13668c2ecf20Sopenharmony_ci vhost_scsi_send_an_resp(vs, vq, &vc); 13678c2ecf20Sopenharmony_cierr: 13688c2ecf20Sopenharmony_ci /* 13698c2ecf20Sopenharmony_ci * ENXIO: No more requests, or read error, wait for next kick 13708c2ecf20Sopenharmony_ci * EINVAL: Invalid response buffer, drop the request 13718c2ecf20Sopenharmony_ci * EIO: Respond with bad target 13728c2ecf20Sopenharmony_ci * EAGAIN: Pending request 13738c2ecf20Sopenharmony_ci */ 13748c2ecf20Sopenharmony_ci if (ret == -ENXIO) 13758c2ecf20Sopenharmony_ci break; 13768c2ecf20Sopenharmony_ci else if (ret == -EIO) 13778c2ecf20Sopenharmony_ci vhost_scsi_send_bad_target(vs, vq, vc.head, vc.out); 13788c2ecf20Sopenharmony_ci } while (likely(!vhost_exceeds_weight(vq, ++c, 0))); 13798c2ecf20Sopenharmony_ciout: 13808c2ecf20Sopenharmony_ci mutex_unlock(&vq->mutex); 13818c2ecf20Sopenharmony_ci} 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_cistatic void vhost_scsi_ctl_handle_kick(struct vhost_work *work) 13848c2ecf20Sopenharmony_ci{ 13858c2ecf20Sopenharmony_ci struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue, 13868c2ecf20Sopenharmony_ci poll.work); 13878c2ecf20Sopenharmony_ci struct vhost_scsi *vs = container_of(vq->dev, struct vhost_scsi, dev); 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci pr_debug("%s: The handling func for control queue.\n", __func__); 13908c2ecf20Sopenharmony_ci vhost_scsi_ctl_handle_vq(vs, vq); 13918c2ecf20Sopenharmony_ci} 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_cistatic void 13948c2ecf20Sopenharmony_civhost_scsi_send_evt(struct vhost_scsi *vs, 13958c2ecf20Sopenharmony_ci struct vhost_scsi_tpg *tpg, 13968c2ecf20Sopenharmony_ci struct se_lun *lun, 13978c2ecf20Sopenharmony_ci u32 event, 13988c2ecf20Sopenharmony_ci u32 reason) 13998c2ecf20Sopenharmony_ci{ 14008c2ecf20Sopenharmony_ci struct vhost_scsi_evt *evt; 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_ci evt = vhost_scsi_allocate_evt(vs, event, reason); 14038c2ecf20Sopenharmony_ci if (!evt) 14048c2ecf20Sopenharmony_ci return; 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci if (tpg && lun) { 14078c2ecf20Sopenharmony_ci /* TODO: share lun setup code with virtio-scsi.ko */ 14088c2ecf20Sopenharmony_ci /* 14098c2ecf20Sopenharmony_ci * Note: evt->event is zeroed when we allocate it and 14108c2ecf20Sopenharmony_ci * lun[4-7] need to be zero according to virtio-scsi spec. 14118c2ecf20Sopenharmony_ci */ 14128c2ecf20Sopenharmony_ci evt->event.lun[0] = 0x01; 14138c2ecf20Sopenharmony_ci evt->event.lun[1] = tpg->tport_tpgt; 14148c2ecf20Sopenharmony_ci if (lun->unpacked_lun >= 256) 14158c2ecf20Sopenharmony_ci evt->event.lun[2] = lun->unpacked_lun >> 8 | 0x40 ; 14168c2ecf20Sopenharmony_ci evt->event.lun[3] = lun->unpacked_lun & 0xFF; 14178c2ecf20Sopenharmony_ci } 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci llist_add(&evt->list, &vs->vs_event_list); 14208c2ecf20Sopenharmony_ci vhost_work_queue(&vs->dev, &vs->vs_event_work); 14218c2ecf20Sopenharmony_ci} 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_cistatic void vhost_scsi_evt_handle_kick(struct vhost_work *work) 14248c2ecf20Sopenharmony_ci{ 14258c2ecf20Sopenharmony_ci struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue, 14268c2ecf20Sopenharmony_ci poll.work); 14278c2ecf20Sopenharmony_ci struct vhost_scsi *vs = container_of(vq->dev, struct vhost_scsi, dev); 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci mutex_lock(&vq->mutex); 14308c2ecf20Sopenharmony_ci if (!vhost_vq_get_backend(vq)) 14318c2ecf20Sopenharmony_ci goto out; 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci if (vs->vs_events_missed) 14348c2ecf20Sopenharmony_ci vhost_scsi_send_evt(vs, NULL, NULL, VIRTIO_SCSI_T_NO_EVENT, 0); 14358c2ecf20Sopenharmony_ciout: 14368c2ecf20Sopenharmony_ci mutex_unlock(&vq->mutex); 14378c2ecf20Sopenharmony_ci} 14388c2ecf20Sopenharmony_ci 14398c2ecf20Sopenharmony_cistatic void vhost_scsi_handle_kick(struct vhost_work *work) 14408c2ecf20Sopenharmony_ci{ 14418c2ecf20Sopenharmony_ci struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue, 14428c2ecf20Sopenharmony_ci poll.work); 14438c2ecf20Sopenharmony_ci struct vhost_scsi *vs = container_of(vq->dev, struct vhost_scsi, dev); 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci vhost_scsi_handle_vq(vs, vq); 14468c2ecf20Sopenharmony_ci} 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_cistatic void vhost_scsi_flush_vq(struct vhost_scsi *vs, int index) 14498c2ecf20Sopenharmony_ci{ 14508c2ecf20Sopenharmony_ci vhost_poll_flush(&vs->vqs[index].vq.poll); 14518c2ecf20Sopenharmony_ci} 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci/* Callers must hold dev mutex */ 14548c2ecf20Sopenharmony_cistatic void vhost_scsi_flush(struct vhost_scsi *vs) 14558c2ecf20Sopenharmony_ci{ 14568c2ecf20Sopenharmony_ci struct vhost_scsi_inflight *old_inflight[VHOST_SCSI_MAX_VQ]; 14578c2ecf20Sopenharmony_ci int i; 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_ci /* Init new inflight and remember the old inflight */ 14608c2ecf20Sopenharmony_ci vhost_scsi_init_inflight(vs, old_inflight); 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci /* 14638c2ecf20Sopenharmony_ci * The inflight->kref was initialized to 1. We decrement it here to 14648c2ecf20Sopenharmony_ci * indicate the start of the flush operation so that it will reach 0 14658c2ecf20Sopenharmony_ci * when all the reqs are finished. 14668c2ecf20Sopenharmony_ci */ 14678c2ecf20Sopenharmony_ci for (i = 0; i < VHOST_SCSI_MAX_VQ; i++) 14688c2ecf20Sopenharmony_ci kref_put(&old_inflight[i]->kref, vhost_scsi_done_inflight); 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci /* Flush both the vhost poll and vhost work */ 14718c2ecf20Sopenharmony_ci for (i = 0; i < VHOST_SCSI_MAX_VQ; i++) 14728c2ecf20Sopenharmony_ci vhost_scsi_flush_vq(vs, i); 14738c2ecf20Sopenharmony_ci vhost_work_flush(&vs->dev, &vs->vs_completion_work); 14748c2ecf20Sopenharmony_ci vhost_work_flush(&vs->dev, &vs->vs_event_work); 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_ci /* Wait for all reqs issued before the flush to be finished */ 14778c2ecf20Sopenharmony_ci for (i = 0; i < VHOST_SCSI_MAX_VQ; i++) 14788c2ecf20Sopenharmony_ci wait_for_completion(&old_inflight[i]->comp); 14798c2ecf20Sopenharmony_ci} 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_cistatic void vhost_scsi_destroy_vq_cmds(struct vhost_virtqueue *vq) 14828c2ecf20Sopenharmony_ci{ 14838c2ecf20Sopenharmony_ci struct vhost_scsi_virtqueue *svq = container_of(vq, 14848c2ecf20Sopenharmony_ci struct vhost_scsi_virtqueue, vq); 14858c2ecf20Sopenharmony_ci struct vhost_scsi_cmd *tv_cmd; 14868c2ecf20Sopenharmony_ci unsigned int i; 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci if (!svq->scsi_cmds) 14898c2ecf20Sopenharmony_ci return; 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci for (i = 0; i < svq->max_cmds; i++) { 14928c2ecf20Sopenharmony_ci tv_cmd = &svq->scsi_cmds[i]; 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ci kfree(tv_cmd->tvc_sgl); 14958c2ecf20Sopenharmony_ci kfree(tv_cmd->tvc_prot_sgl); 14968c2ecf20Sopenharmony_ci kfree(tv_cmd->tvc_upages); 14978c2ecf20Sopenharmony_ci } 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci sbitmap_free(&svq->scsi_tags); 15008c2ecf20Sopenharmony_ci kfree(svq->scsi_cmds); 15018c2ecf20Sopenharmony_ci svq->scsi_cmds = NULL; 15028c2ecf20Sopenharmony_ci} 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_cistatic int vhost_scsi_setup_vq_cmds(struct vhost_virtqueue *vq, int max_cmds) 15058c2ecf20Sopenharmony_ci{ 15068c2ecf20Sopenharmony_ci struct vhost_scsi_virtqueue *svq = container_of(vq, 15078c2ecf20Sopenharmony_ci struct vhost_scsi_virtqueue, vq); 15088c2ecf20Sopenharmony_ci struct vhost_scsi_cmd *tv_cmd; 15098c2ecf20Sopenharmony_ci unsigned int i; 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_ci if (svq->scsi_cmds) 15128c2ecf20Sopenharmony_ci return 0; 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_ci if (sbitmap_init_node(&svq->scsi_tags, max_cmds, -1, GFP_KERNEL, 15158c2ecf20Sopenharmony_ci NUMA_NO_NODE)) 15168c2ecf20Sopenharmony_ci return -ENOMEM; 15178c2ecf20Sopenharmony_ci svq->max_cmds = max_cmds; 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_ci svq->scsi_cmds = kcalloc(max_cmds, sizeof(*tv_cmd), GFP_KERNEL); 15208c2ecf20Sopenharmony_ci if (!svq->scsi_cmds) { 15218c2ecf20Sopenharmony_ci sbitmap_free(&svq->scsi_tags); 15228c2ecf20Sopenharmony_ci return -ENOMEM; 15238c2ecf20Sopenharmony_ci } 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci for (i = 0; i < max_cmds; i++) { 15268c2ecf20Sopenharmony_ci tv_cmd = &svq->scsi_cmds[i]; 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci tv_cmd->tvc_sgl = kcalloc(VHOST_SCSI_PREALLOC_SGLS, 15298c2ecf20Sopenharmony_ci sizeof(struct scatterlist), 15308c2ecf20Sopenharmony_ci GFP_KERNEL); 15318c2ecf20Sopenharmony_ci if (!tv_cmd->tvc_sgl) { 15328c2ecf20Sopenharmony_ci pr_err("Unable to allocate tv_cmd->tvc_sgl\n"); 15338c2ecf20Sopenharmony_ci goto out; 15348c2ecf20Sopenharmony_ci } 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci tv_cmd->tvc_upages = kcalloc(VHOST_SCSI_PREALLOC_UPAGES, 15378c2ecf20Sopenharmony_ci sizeof(struct page *), 15388c2ecf20Sopenharmony_ci GFP_KERNEL); 15398c2ecf20Sopenharmony_ci if (!tv_cmd->tvc_upages) { 15408c2ecf20Sopenharmony_ci pr_err("Unable to allocate tv_cmd->tvc_upages\n"); 15418c2ecf20Sopenharmony_ci goto out; 15428c2ecf20Sopenharmony_ci } 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci tv_cmd->tvc_prot_sgl = kcalloc(VHOST_SCSI_PREALLOC_PROT_SGLS, 15458c2ecf20Sopenharmony_ci sizeof(struct scatterlist), 15468c2ecf20Sopenharmony_ci GFP_KERNEL); 15478c2ecf20Sopenharmony_ci if (!tv_cmd->tvc_prot_sgl) { 15488c2ecf20Sopenharmony_ci pr_err("Unable to allocate tv_cmd->tvc_prot_sgl\n"); 15498c2ecf20Sopenharmony_ci goto out; 15508c2ecf20Sopenharmony_ci } 15518c2ecf20Sopenharmony_ci } 15528c2ecf20Sopenharmony_ci return 0; 15538c2ecf20Sopenharmony_ciout: 15548c2ecf20Sopenharmony_ci vhost_scsi_destroy_vq_cmds(vq); 15558c2ecf20Sopenharmony_ci return -ENOMEM; 15568c2ecf20Sopenharmony_ci} 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ci/* 15598c2ecf20Sopenharmony_ci * Called from vhost_scsi_ioctl() context to walk the list of available 15608c2ecf20Sopenharmony_ci * vhost_scsi_tpg with an active struct vhost_scsi_nexus 15618c2ecf20Sopenharmony_ci * 15628c2ecf20Sopenharmony_ci * The lock nesting rule is: 15638c2ecf20Sopenharmony_ci * vhost_scsi_mutex -> vs->dev.mutex -> tpg->tv_tpg_mutex -> vq->mutex 15648c2ecf20Sopenharmony_ci */ 15658c2ecf20Sopenharmony_cistatic int 15668c2ecf20Sopenharmony_civhost_scsi_set_endpoint(struct vhost_scsi *vs, 15678c2ecf20Sopenharmony_ci struct vhost_scsi_target *t) 15688c2ecf20Sopenharmony_ci{ 15698c2ecf20Sopenharmony_ci struct se_portal_group *se_tpg; 15708c2ecf20Sopenharmony_ci struct vhost_scsi_tport *tv_tport; 15718c2ecf20Sopenharmony_ci struct vhost_scsi_tpg *tpg; 15728c2ecf20Sopenharmony_ci struct vhost_scsi_tpg **vs_tpg; 15738c2ecf20Sopenharmony_ci struct vhost_virtqueue *vq; 15748c2ecf20Sopenharmony_ci int index, ret, i, len; 15758c2ecf20Sopenharmony_ci bool match = false; 15768c2ecf20Sopenharmony_ci 15778c2ecf20Sopenharmony_ci mutex_lock(&vhost_scsi_mutex); 15788c2ecf20Sopenharmony_ci mutex_lock(&vs->dev.mutex); 15798c2ecf20Sopenharmony_ci 15808c2ecf20Sopenharmony_ci /* Verify that ring has been setup correctly. */ 15818c2ecf20Sopenharmony_ci for (index = 0; index < vs->dev.nvqs; ++index) { 15828c2ecf20Sopenharmony_ci /* Verify that ring has been setup correctly. */ 15838c2ecf20Sopenharmony_ci if (!vhost_vq_access_ok(&vs->vqs[index].vq)) { 15848c2ecf20Sopenharmony_ci ret = -EFAULT; 15858c2ecf20Sopenharmony_ci goto out; 15868c2ecf20Sopenharmony_ci } 15878c2ecf20Sopenharmony_ci } 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_ci len = sizeof(vs_tpg[0]) * VHOST_SCSI_MAX_TARGET; 15908c2ecf20Sopenharmony_ci vs_tpg = kzalloc(len, GFP_KERNEL); 15918c2ecf20Sopenharmony_ci if (!vs_tpg) { 15928c2ecf20Sopenharmony_ci ret = -ENOMEM; 15938c2ecf20Sopenharmony_ci goto out; 15948c2ecf20Sopenharmony_ci } 15958c2ecf20Sopenharmony_ci if (vs->vs_tpg) 15968c2ecf20Sopenharmony_ci memcpy(vs_tpg, vs->vs_tpg, len); 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci list_for_each_entry(tpg, &vhost_scsi_list, tv_tpg_list) { 15998c2ecf20Sopenharmony_ci mutex_lock(&tpg->tv_tpg_mutex); 16008c2ecf20Sopenharmony_ci if (!tpg->tpg_nexus) { 16018c2ecf20Sopenharmony_ci mutex_unlock(&tpg->tv_tpg_mutex); 16028c2ecf20Sopenharmony_ci continue; 16038c2ecf20Sopenharmony_ci } 16048c2ecf20Sopenharmony_ci if (tpg->tv_tpg_vhost_count != 0) { 16058c2ecf20Sopenharmony_ci mutex_unlock(&tpg->tv_tpg_mutex); 16068c2ecf20Sopenharmony_ci continue; 16078c2ecf20Sopenharmony_ci } 16088c2ecf20Sopenharmony_ci tv_tport = tpg->tport; 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci if (!strcmp(tv_tport->tport_name, t->vhost_wwpn)) { 16118c2ecf20Sopenharmony_ci if (vs->vs_tpg && vs->vs_tpg[tpg->tport_tpgt]) { 16128c2ecf20Sopenharmony_ci mutex_unlock(&tpg->tv_tpg_mutex); 16138c2ecf20Sopenharmony_ci ret = -EEXIST; 16148c2ecf20Sopenharmony_ci goto undepend; 16158c2ecf20Sopenharmony_ci } 16168c2ecf20Sopenharmony_ci /* 16178c2ecf20Sopenharmony_ci * In order to ensure individual vhost-scsi configfs 16188c2ecf20Sopenharmony_ci * groups cannot be removed while in use by vhost ioctl, 16198c2ecf20Sopenharmony_ci * go ahead and take an explicit se_tpg->tpg_group.cg_item 16208c2ecf20Sopenharmony_ci * dependency now. 16218c2ecf20Sopenharmony_ci */ 16228c2ecf20Sopenharmony_ci se_tpg = &tpg->se_tpg; 16238c2ecf20Sopenharmony_ci ret = target_depend_item(&se_tpg->tpg_group.cg_item); 16248c2ecf20Sopenharmony_ci if (ret) { 16258c2ecf20Sopenharmony_ci pr_warn("target_depend_item() failed: %d\n", ret); 16268c2ecf20Sopenharmony_ci mutex_unlock(&tpg->tv_tpg_mutex); 16278c2ecf20Sopenharmony_ci goto undepend; 16288c2ecf20Sopenharmony_ci } 16298c2ecf20Sopenharmony_ci tpg->tv_tpg_vhost_count++; 16308c2ecf20Sopenharmony_ci tpg->vhost_scsi = vs; 16318c2ecf20Sopenharmony_ci vs_tpg[tpg->tport_tpgt] = tpg; 16328c2ecf20Sopenharmony_ci match = true; 16338c2ecf20Sopenharmony_ci } 16348c2ecf20Sopenharmony_ci mutex_unlock(&tpg->tv_tpg_mutex); 16358c2ecf20Sopenharmony_ci } 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci if (match) { 16388c2ecf20Sopenharmony_ci memcpy(vs->vs_vhost_wwpn, t->vhost_wwpn, 16398c2ecf20Sopenharmony_ci sizeof(vs->vs_vhost_wwpn)); 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci for (i = VHOST_SCSI_VQ_IO; i < VHOST_SCSI_MAX_VQ; i++) { 16428c2ecf20Sopenharmony_ci vq = &vs->vqs[i].vq; 16438c2ecf20Sopenharmony_ci if (!vhost_vq_is_setup(vq)) 16448c2ecf20Sopenharmony_ci continue; 16458c2ecf20Sopenharmony_ci 16468c2ecf20Sopenharmony_ci ret = vhost_scsi_setup_vq_cmds(vq, vq->num); 16478c2ecf20Sopenharmony_ci if (ret) 16488c2ecf20Sopenharmony_ci goto destroy_vq_cmds; 16498c2ecf20Sopenharmony_ci } 16508c2ecf20Sopenharmony_ci 16518c2ecf20Sopenharmony_ci for (i = 0; i < VHOST_SCSI_MAX_VQ; i++) { 16528c2ecf20Sopenharmony_ci vq = &vs->vqs[i].vq; 16538c2ecf20Sopenharmony_ci mutex_lock(&vq->mutex); 16548c2ecf20Sopenharmony_ci vhost_vq_set_backend(vq, vs_tpg); 16558c2ecf20Sopenharmony_ci vhost_vq_init_access(vq); 16568c2ecf20Sopenharmony_ci mutex_unlock(&vq->mutex); 16578c2ecf20Sopenharmony_ci } 16588c2ecf20Sopenharmony_ci ret = 0; 16598c2ecf20Sopenharmony_ci } else { 16608c2ecf20Sopenharmony_ci ret = -EEXIST; 16618c2ecf20Sopenharmony_ci } 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_ci /* 16648c2ecf20Sopenharmony_ci * Act as synchronize_rcu to make sure access to 16658c2ecf20Sopenharmony_ci * old vs->vs_tpg is finished. 16668c2ecf20Sopenharmony_ci */ 16678c2ecf20Sopenharmony_ci vhost_scsi_flush(vs); 16688c2ecf20Sopenharmony_ci kfree(vs->vs_tpg); 16698c2ecf20Sopenharmony_ci vs->vs_tpg = vs_tpg; 16708c2ecf20Sopenharmony_ci goto out; 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_cidestroy_vq_cmds: 16738c2ecf20Sopenharmony_ci for (i--; i >= VHOST_SCSI_VQ_IO; i--) { 16748c2ecf20Sopenharmony_ci if (!vhost_vq_get_backend(&vs->vqs[i].vq)) 16758c2ecf20Sopenharmony_ci vhost_scsi_destroy_vq_cmds(&vs->vqs[i].vq); 16768c2ecf20Sopenharmony_ci } 16778c2ecf20Sopenharmony_ciundepend: 16788c2ecf20Sopenharmony_ci for (i = 0; i < VHOST_SCSI_MAX_TARGET; i++) { 16798c2ecf20Sopenharmony_ci tpg = vs_tpg[i]; 16808c2ecf20Sopenharmony_ci if (tpg) { 16818c2ecf20Sopenharmony_ci tpg->tv_tpg_vhost_count--; 16828c2ecf20Sopenharmony_ci target_undepend_item(&tpg->se_tpg.tpg_group.cg_item); 16838c2ecf20Sopenharmony_ci } 16848c2ecf20Sopenharmony_ci } 16858c2ecf20Sopenharmony_ci kfree(vs_tpg); 16868c2ecf20Sopenharmony_ciout: 16878c2ecf20Sopenharmony_ci mutex_unlock(&vs->dev.mutex); 16888c2ecf20Sopenharmony_ci mutex_unlock(&vhost_scsi_mutex); 16898c2ecf20Sopenharmony_ci return ret; 16908c2ecf20Sopenharmony_ci} 16918c2ecf20Sopenharmony_ci 16928c2ecf20Sopenharmony_cistatic int 16938c2ecf20Sopenharmony_civhost_scsi_clear_endpoint(struct vhost_scsi *vs, 16948c2ecf20Sopenharmony_ci struct vhost_scsi_target *t) 16958c2ecf20Sopenharmony_ci{ 16968c2ecf20Sopenharmony_ci struct se_portal_group *se_tpg; 16978c2ecf20Sopenharmony_ci struct vhost_scsi_tport *tv_tport; 16988c2ecf20Sopenharmony_ci struct vhost_scsi_tpg *tpg; 16998c2ecf20Sopenharmony_ci struct vhost_virtqueue *vq; 17008c2ecf20Sopenharmony_ci bool match = false; 17018c2ecf20Sopenharmony_ci int index, ret, i; 17028c2ecf20Sopenharmony_ci u8 target; 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ci mutex_lock(&vhost_scsi_mutex); 17058c2ecf20Sopenharmony_ci mutex_lock(&vs->dev.mutex); 17068c2ecf20Sopenharmony_ci /* Verify that ring has been setup correctly. */ 17078c2ecf20Sopenharmony_ci for (index = 0; index < vs->dev.nvqs; ++index) { 17088c2ecf20Sopenharmony_ci if (!vhost_vq_access_ok(&vs->vqs[index].vq)) { 17098c2ecf20Sopenharmony_ci ret = -EFAULT; 17108c2ecf20Sopenharmony_ci goto err_dev; 17118c2ecf20Sopenharmony_ci } 17128c2ecf20Sopenharmony_ci } 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_ci if (!vs->vs_tpg) { 17158c2ecf20Sopenharmony_ci ret = 0; 17168c2ecf20Sopenharmony_ci goto err_dev; 17178c2ecf20Sopenharmony_ci } 17188c2ecf20Sopenharmony_ci 17198c2ecf20Sopenharmony_ci for (i = 0; i < VHOST_SCSI_MAX_TARGET; i++) { 17208c2ecf20Sopenharmony_ci target = i; 17218c2ecf20Sopenharmony_ci tpg = vs->vs_tpg[target]; 17228c2ecf20Sopenharmony_ci if (!tpg) 17238c2ecf20Sopenharmony_ci continue; 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_ci mutex_lock(&tpg->tv_tpg_mutex); 17268c2ecf20Sopenharmony_ci tv_tport = tpg->tport; 17278c2ecf20Sopenharmony_ci if (!tv_tport) { 17288c2ecf20Sopenharmony_ci ret = -ENODEV; 17298c2ecf20Sopenharmony_ci goto err_tpg; 17308c2ecf20Sopenharmony_ci } 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_ci if (strcmp(tv_tport->tport_name, t->vhost_wwpn)) { 17338c2ecf20Sopenharmony_ci pr_warn("tv_tport->tport_name: %s, tpg->tport_tpgt: %hu" 17348c2ecf20Sopenharmony_ci " does not match t->vhost_wwpn: %s, t->vhost_tpgt: %hu\n", 17358c2ecf20Sopenharmony_ci tv_tport->tport_name, tpg->tport_tpgt, 17368c2ecf20Sopenharmony_ci t->vhost_wwpn, t->vhost_tpgt); 17378c2ecf20Sopenharmony_ci ret = -EINVAL; 17388c2ecf20Sopenharmony_ci goto err_tpg; 17398c2ecf20Sopenharmony_ci } 17408c2ecf20Sopenharmony_ci tpg->tv_tpg_vhost_count--; 17418c2ecf20Sopenharmony_ci tpg->vhost_scsi = NULL; 17428c2ecf20Sopenharmony_ci vs->vs_tpg[target] = NULL; 17438c2ecf20Sopenharmony_ci match = true; 17448c2ecf20Sopenharmony_ci mutex_unlock(&tpg->tv_tpg_mutex); 17458c2ecf20Sopenharmony_ci /* 17468c2ecf20Sopenharmony_ci * Release se_tpg->tpg_group.cg_item configfs dependency now 17478c2ecf20Sopenharmony_ci * to allow vhost-scsi WWPN se_tpg->tpg_group shutdown to occur. 17488c2ecf20Sopenharmony_ci */ 17498c2ecf20Sopenharmony_ci se_tpg = &tpg->se_tpg; 17508c2ecf20Sopenharmony_ci target_undepend_item(&se_tpg->tpg_group.cg_item); 17518c2ecf20Sopenharmony_ci } 17528c2ecf20Sopenharmony_ci if (match) { 17538c2ecf20Sopenharmony_ci for (i = 0; i < VHOST_SCSI_MAX_VQ; i++) { 17548c2ecf20Sopenharmony_ci vq = &vs->vqs[i].vq; 17558c2ecf20Sopenharmony_ci mutex_lock(&vq->mutex); 17568c2ecf20Sopenharmony_ci vhost_vq_set_backend(vq, NULL); 17578c2ecf20Sopenharmony_ci mutex_unlock(&vq->mutex); 17588c2ecf20Sopenharmony_ci /* 17598c2ecf20Sopenharmony_ci * Make sure cmds are not running before tearing them 17608c2ecf20Sopenharmony_ci * down. 17618c2ecf20Sopenharmony_ci */ 17628c2ecf20Sopenharmony_ci vhost_scsi_flush(vs); 17638c2ecf20Sopenharmony_ci vhost_scsi_destroy_vq_cmds(vq); 17648c2ecf20Sopenharmony_ci } 17658c2ecf20Sopenharmony_ci } 17668c2ecf20Sopenharmony_ci /* 17678c2ecf20Sopenharmony_ci * Act as synchronize_rcu to make sure access to 17688c2ecf20Sopenharmony_ci * old vs->vs_tpg is finished. 17698c2ecf20Sopenharmony_ci */ 17708c2ecf20Sopenharmony_ci vhost_scsi_flush(vs); 17718c2ecf20Sopenharmony_ci kfree(vs->vs_tpg); 17728c2ecf20Sopenharmony_ci vs->vs_tpg = NULL; 17738c2ecf20Sopenharmony_ci WARN_ON(vs->vs_events_nr); 17748c2ecf20Sopenharmony_ci mutex_unlock(&vs->dev.mutex); 17758c2ecf20Sopenharmony_ci mutex_unlock(&vhost_scsi_mutex); 17768c2ecf20Sopenharmony_ci return 0; 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_cierr_tpg: 17798c2ecf20Sopenharmony_ci mutex_unlock(&tpg->tv_tpg_mutex); 17808c2ecf20Sopenharmony_cierr_dev: 17818c2ecf20Sopenharmony_ci mutex_unlock(&vs->dev.mutex); 17828c2ecf20Sopenharmony_ci mutex_unlock(&vhost_scsi_mutex); 17838c2ecf20Sopenharmony_ci return ret; 17848c2ecf20Sopenharmony_ci} 17858c2ecf20Sopenharmony_ci 17868c2ecf20Sopenharmony_cistatic int vhost_scsi_set_features(struct vhost_scsi *vs, u64 features) 17878c2ecf20Sopenharmony_ci{ 17888c2ecf20Sopenharmony_ci struct vhost_virtqueue *vq; 17898c2ecf20Sopenharmony_ci int i; 17908c2ecf20Sopenharmony_ci 17918c2ecf20Sopenharmony_ci if (features & ~VHOST_SCSI_FEATURES) 17928c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 17938c2ecf20Sopenharmony_ci 17948c2ecf20Sopenharmony_ci mutex_lock(&vs->dev.mutex); 17958c2ecf20Sopenharmony_ci if ((features & (1 << VHOST_F_LOG_ALL)) && 17968c2ecf20Sopenharmony_ci !vhost_log_access_ok(&vs->dev)) { 17978c2ecf20Sopenharmony_ci mutex_unlock(&vs->dev.mutex); 17988c2ecf20Sopenharmony_ci return -EFAULT; 17998c2ecf20Sopenharmony_ci } 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci for (i = 0; i < VHOST_SCSI_MAX_VQ; i++) { 18028c2ecf20Sopenharmony_ci vq = &vs->vqs[i].vq; 18038c2ecf20Sopenharmony_ci mutex_lock(&vq->mutex); 18048c2ecf20Sopenharmony_ci vq->acked_features = features; 18058c2ecf20Sopenharmony_ci mutex_unlock(&vq->mutex); 18068c2ecf20Sopenharmony_ci } 18078c2ecf20Sopenharmony_ci mutex_unlock(&vs->dev.mutex); 18088c2ecf20Sopenharmony_ci return 0; 18098c2ecf20Sopenharmony_ci} 18108c2ecf20Sopenharmony_ci 18118c2ecf20Sopenharmony_cistatic int vhost_scsi_open(struct inode *inode, struct file *f) 18128c2ecf20Sopenharmony_ci{ 18138c2ecf20Sopenharmony_ci struct vhost_scsi *vs; 18148c2ecf20Sopenharmony_ci struct vhost_virtqueue **vqs; 18158c2ecf20Sopenharmony_ci int r = -ENOMEM, i; 18168c2ecf20Sopenharmony_ci 18178c2ecf20Sopenharmony_ci vs = kzalloc(sizeof(*vs), GFP_KERNEL | __GFP_NOWARN | __GFP_RETRY_MAYFAIL); 18188c2ecf20Sopenharmony_ci if (!vs) { 18198c2ecf20Sopenharmony_ci vs = vzalloc(sizeof(*vs)); 18208c2ecf20Sopenharmony_ci if (!vs) 18218c2ecf20Sopenharmony_ci goto err_vs; 18228c2ecf20Sopenharmony_ci } 18238c2ecf20Sopenharmony_ci 18248c2ecf20Sopenharmony_ci vqs = kmalloc_array(VHOST_SCSI_MAX_VQ, sizeof(*vqs), GFP_KERNEL); 18258c2ecf20Sopenharmony_ci if (!vqs) 18268c2ecf20Sopenharmony_ci goto err_vqs; 18278c2ecf20Sopenharmony_ci 18288c2ecf20Sopenharmony_ci vhost_work_init(&vs->vs_completion_work, vhost_scsi_complete_cmd_work); 18298c2ecf20Sopenharmony_ci vhost_work_init(&vs->vs_event_work, vhost_scsi_evt_work); 18308c2ecf20Sopenharmony_ci 18318c2ecf20Sopenharmony_ci vs->vs_events_nr = 0; 18328c2ecf20Sopenharmony_ci vs->vs_events_missed = false; 18338c2ecf20Sopenharmony_ci 18348c2ecf20Sopenharmony_ci vqs[VHOST_SCSI_VQ_CTL] = &vs->vqs[VHOST_SCSI_VQ_CTL].vq; 18358c2ecf20Sopenharmony_ci vqs[VHOST_SCSI_VQ_EVT] = &vs->vqs[VHOST_SCSI_VQ_EVT].vq; 18368c2ecf20Sopenharmony_ci vs->vqs[VHOST_SCSI_VQ_CTL].vq.handle_kick = vhost_scsi_ctl_handle_kick; 18378c2ecf20Sopenharmony_ci vs->vqs[VHOST_SCSI_VQ_EVT].vq.handle_kick = vhost_scsi_evt_handle_kick; 18388c2ecf20Sopenharmony_ci for (i = VHOST_SCSI_VQ_IO; i < VHOST_SCSI_MAX_VQ; i++) { 18398c2ecf20Sopenharmony_ci vqs[i] = &vs->vqs[i].vq; 18408c2ecf20Sopenharmony_ci vs->vqs[i].vq.handle_kick = vhost_scsi_handle_kick; 18418c2ecf20Sopenharmony_ci } 18428c2ecf20Sopenharmony_ci vhost_dev_init(&vs->dev, vqs, VHOST_SCSI_MAX_VQ, UIO_MAXIOV, 18438c2ecf20Sopenharmony_ci VHOST_SCSI_WEIGHT, 0, true, NULL); 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_ci vhost_scsi_init_inflight(vs, NULL); 18468c2ecf20Sopenharmony_ci 18478c2ecf20Sopenharmony_ci f->private_data = vs; 18488c2ecf20Sopenharmony_ci return 0; 18498c2ecf20Sopenharmony_ci 18508c2ecf20Sopenharmony_cierr_vqs: 18518c2ecf20Sopenharmony_ci kvfree(vs); 18528c2ecf20Sopenharmony_cierr_vs: 18538c2ecf20Sopenharmony_ci return r; 18548c2ecf20Sopenharmony_ci} 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_cistatic int vhost_scsi_release(struct inode *inode, struct file *f) 18578c2ecf20Sopenharmony_ci{ 18588c2ecf20Sopenharmony_ci struct vhost_scsi *vs = f->private_data; 18598c2ecf20Sopenharmony_ci struct vhost_scsi_target t; 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_ci mutex_lock(&vs->dev.mutex); 18628c2ecf20Sopenharmony_ci memcpy(t.vhost_wwpn, vs->vs_vhost_wwpn, sizeof(t.vhost_wwpn)); 18638c2ecf20Sopenharmony_ci mutex_unlock(&vs->dev.mutex); 18648c2ecf20Sopenharmony_ci vhost_scsi_clear_endpoint(vs, &t); 18658c2ecf20Sopenharmony_ci vhost_dev_stop(&vs->dev); 18668c2ecf20Sopenharmony_ci vhost_dev_cleanup(&vs->dev); 18678c2ecf20Sopenharmony_ci /* Jobs can re-queue themselves in evt kick handler. Do extra flush. */ 18688c2ecf20Sopenharmony_ci vhost_scsi_flush(vs); 18698c2ecf20Sopenharmony_ci kfree(vs->dev.vqs); 18708c2ecf20Sopenharmony_ci kvfree(vs); 18718c2ecf20Sopenharmony_ci return 0; 18728c2ecf20Sopenharmony_ci} 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_cistatic long 18758c2ecf20Sopenharmony_civhost_scsi_ioctl(struct file *f, 18768c2ecf20Sopenharmony_ci unsigned int ioctl, 18778c2ecf20Sopenharmony_ci unsigned long arg) 18788c2ecf20Sopenharmony_ci{ 18798c2ecf20Sopenharmony_ci struct vhost_scsi *vs = f->private_data; 18808c2ecf20Sopenharmony_ci struct vhost_scsi_target backend; 18818c2ecf20Sopenharmony_ci void __user *argp = (void __user *)arg; 18828c2ecf20Sopenharmony_ci u64 __user *featurep = argp; 18838c2ecf20Sopenharmony_ci u32 __user *eventsp = argp; 18848c2ecf20Sopenharmony_ci u32 events_missed; 18858c2ecf20Sopenharmony_ci u64 features; 18868c2ecf20Sopenharmony_ci int r, abi_version = VHOST_SCSI_ABI_VERSION; 18878c2ecf20Sopenharmony_ci struct vhost_virtqueue *vq = &vs->vqs[VHOST_SCSI_VQ_EVT].vq; 18888c2ecf20Sopenharmony_ci 18898c2ecf20Sopenharmony_ci switch (ioctl) { 18908c2ecf20Sopenharmony_ci case VHOST_SCSI_SET_ENDPOINT: 18918c2ecf20Sopenharmony_ci if (copy_from_user(&backend, argp, sizeof backend)) 18928c2ecf20Sopenharmony_ci return -EFAULT; 18938c2ecf20Sopenharmony_ci if (backend.reserved != 0) 18948c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 18958c2ecf20Sopenharmony_ci 18968c2ecf20Sopenharmony_ci return vhost_scsi_set_endpoint(vs, &backend); 18978c2ecf20Sopenharmony_ci case VHOST_SCSI_CLEAR_ENDPOINT: 18988c2ecf20Sopenharmony_ci if (copy_from_user(&backend, argp, sizeof backend)) 18998c2ecf20Sopenharmony_ci return -EFAULT; 19008c2ecf20Sopenharmony_ci if (backend.reserved != 0) 19018c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 19028c2ecf20Sopenharmony_ci 19038c2ecf20Sopenharmony_ci return vhost_scsi_clear_endpoint(vs, &backend); 19048c2ecf20Sopenharmony_ci case VHOST_SCSI_GET_ABI_VERSION: 19058c2ecf20Sopenharmony_ci if (copy_to_user(argp, &abi_version, sizeof abi_version)) 19068c2ecf20Sopenharmony_ci return -EFAULT; 19078c2ecf20Sopenharmony_ci return 0; 19088c2ecf20Sopenharmony_ci case VHOST_SCSI_SET_EVENTS_MISSED: 19098c2ecf20Sopenharmony_ci if (get_user(events_missed, eventsp)) 19108c2ecf20Sopenharmony_ci return -EFAULT; 19118c2ecf20Sopenharmony_ci mutex_lock(&vq->mutex); 19128c2ecf20Sopenharmony_ci vs->vs_events_missed = events_missed; 19138c2ecf20Sopenharmony_ci mutex_unlock(&vq->mutex); 19148c2ecf20Sopenharmony_ci return 0; 19158c2ecf20Sopenharmony_ci case VHOST_SCSI_GET_EVENTS_MISSED: 19168c2ecf20Sopenharmony_ci mutex_lock(&vq->mutex); 19178c2ecf20Sopenharmony_ci events_missed = vs->vs_events_missed; 19188c2ecf20Sopenharmony_ci mutex_unlock(&vq->mutex); 19198c2ecf20Sopenharmony_ci if (put_user(events_missed, eventsp)) 19208c2ecf20Sopenharmony_ci return -EFAULT; 19218c2ecf20Sopenharmony_ci return 0; 19228c2ecf20Sopenharmony_ci case VHOST_GET_FEATURES: 19238c2ecf20Sopenharmony_ci features = VHOST_SCSI_FEATURES; 19248c2ecf20Sopenharmony_ci if (copy_to_user(featurep, &features, sizeof features)) 19258c2ecf20Sopenharmony_ci return -EFAULT; 19268c2ecf20Sopenharmony_ci return 0; 19278c2ecf20Sopenharmony_ci case VHOST_SET_FEATURES: 19288c2ecf20Sopenharmony_ci if (copy_from_user(&features, featurep, sizeof features)) 19298c2ecf20Sopenharmony_ci return -EFAULT; 19308c2ecf20Sopenharmony_ci return vhost_scsi_set_features(vs, features); 19318c2ecf20Sopenharmony_ci default: 19328c2ecf20Sopenharmony_ci mutex_lock(&vs->dev.mutex); 19338c2ecf20Sopenharmony_ci r = vhost_dev_ioctl(&vs->dev, ioctl, argp); 19348c2ecf20Sopenharmony_ci /* TODO: flush backend after dev ioctl. */ 19358c2ecf20Sopenharmony_ci if (r == -ENOIOCTLCMD) 19368c2ecf20Sopenharmony_ci r = vhost_vring_ioctl(&vs->dev, ioctl, argp); 19378c2ecf20Sopenharmony_ci mutex_unlock(&vs->dev.mutex); 19388c2ecf20Sopenharmony_ci return r; 19398c2ecf20Sopenharmony_ci } 19408c2ecf20Sopenharmony_ci} 19418c2ecf20Sopenharmony_ci 19428c2ecf20Sopenharmony_cistatic const struct file_operations vhost_scsi_fops = { 19438c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 19448c2ecf20Sopenharmony_ci .release = vhost_scsi_release, 19458c2ecf20Sopenharmony_ci .unlocked_ioctl = vhost_scsi_ioctl, 19468c2ecf20Sopenharmony_ci .compat_ioctl = compat_ptr_ioctl, 19478c2ecf20Sopenharmony_ci .open = vhost_scsi_open, 19488c2ecf20Sopenharmony_ci .llseek = noop_llseek, 19498c2ecf20Sopenharmony_ci}; 19508c2ecf20Sopenharmony_ci 19518c2ecf20Sopenharmony_cistatic struct miscdevice vhost_scsi_misc = { 19528c2ecf20Sopenharmony_ci MISC_DYNAMIC_MINOR, 19538c2ecf20Sopenharmony_ci "vhost-scsi", 19548c2ecf20Sopenharmony_ci &vhost_scsi_fops, 19558c2ecf20Sopenharmony_ci}; 19568c2ecf20Sopenharmony_ci 19578c2ecf20Sopenharmony_cistatic int __init vhost_scsi_register(void) 19588c2ecf20Sopenharmony_ci{ 19598c2ecf20Sopenharmony_ci return misc_register(&vhost_scsi_misc); 19608c2ecf20Sopenharmony_ci} 19618c2ecf20Sopenharmony_ci 19628c2ecf20Sopenharmony_cistatic void vhost_scsi_deregister(void) 19638c2ecf20Sopenharmony_ci{ 19648c2ecf20Sopenharmony_ci misc_deregister(&vhost_scsi_misc); 19658c2ecf20Sopenharmony_ci} 19668c2ecf20Sopenharmony_ci 19678c2ecf20Sopenharmony_cistatic char *vhost_scsi_dump_proto_id(struct vhost_scsi_tport *tport) 19688c2ecf20Sopenharmony_ci{ 19698c2ecf20Sopenharmony_ci switch (tport->tport_proto_id) { 19708c2ecf20Sopenharmony_ci case SCSI_PROTOCOL_SAS: 19718c2ecf20Sopenharmony_ci return "SAS"; 19728c2ecf20Sopenharmony_ci case SCSI_PROTOCOL_FCP: 19738c2ecf20Sopenharmony_ci return "FCP"; 19748c2ecf20Sopenharmony_ci case SCSI_PROTOCOL_ISCSI: 19758c2ecf20Sopenharmony_ci return "iSCSI"; 19768c2ecf20Sopenharmony_ci default: 19778c2ecf20Sopenharmony_ci break; 19788c2ecf20Sopenharmony_ci } 19798c2ecf20Sopenharmony_ci 19808c2ecf20Sopenharmony_ci return "Unknown"; 19818c2ecf20Sopenharmony_ci} 19828c2ecf20Sopenharmony_ci 19838c2ecf20Sopenharmony_cistatic void 19848c2ecf20Sopenharmony_civhost_scsi_do_plug(struct vhost_scsi_tpg *tpg, 19858c2ecf20Sopenharmony_ci struct se_lun *lun, bool plug) 19868c2ecf20Sopenharmony_ci{ 19878c2ecf20Sopenharmony_ci 19888c2ecf20Sopenharmony_ci struct vhost_scsi *vs = tpg->vhost_scsi; 19898c2ecf20Sopenharmony_ci struct vhost_virtqueue *vq; 19908c2ecf20Sopenharmony_ci u32 reason; 19918c2ecf20Sopenharmony_ci 19928c2ecf20Sopenharmony_ci if (!vs) 19938c2ecf20Sopenharmony_ci return; 19948c2ecf20Sopenharmony_ci 19958c2ecf20Sopenharmony_ci mutex_lock(&vs->dev.mutex); 19968c2ecf20Sopenharmony_ci 19978c2ecf20Sopenharmony_ci if (plug) 19988c2ecf20Sopenharmony_ci reason = VIRTIO_SCSI_EVT_RESET_RESCAN; 19998c2ecf20Sopenharmony_ci else 20008c2ecf20Sopenharmony_ci reason = VIRTIO_SCSI_EVT_RESET_REMOVED; 20018c2ecf20Sopenharmony_ci 20028c2ecf20Sopenharmony_ci vq = &vs->vqs[VHOST_SCSI_VQ_EVT].vq; 20038c2ecf20Sopenharmony_ci mutex_lock(&vq->mutex); 20048c2ecf20Sopenharmony_ci if (vhost_has_feature(vq, VIRTIO_SCSI_F_HOTPLUG)) 20058c2ecf20Sopenharmony_ci vhost_scsi_send_evt(vs, tpg, lun, 20068c2ecf20Sopenharmony_ci VIRTIO_SCSI_T_TRANSPORT_RESET, reason); 20078c2ecf20Sopenharmony_ci mutex_unlock(&vq->mutex); 20088c2ecf20Sopenharmony_ci mutex_unlock(&vs->dev.mutex); 20098c2ecf20Sopenharmony_ci} 20108c2ecf20Sopenharmony_ci 20118c2ecf20Sopenharmony_cistatic void vhost_scsi_hotplug(struct vhost_scsi_tpg *tpg, struct se_lun *lun) 20128c2ecf20Sopenharmony_ci{ 20138c2ecf20Sopenharmony_ci vhost_scsi_do_plug(tpg, lun, true); 20148c2ecf20Sopenharmony_ci} 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_cistatic void vhost_scsi_hotunplug(struct vhost_scsi_tpg *tpg, struct se_lun *lun) 20178c2ecf20Sopenharmony_ci{ 20188c2ecf20Sopenharmony_ci vhost_scsi_do_plug(tpg, lun, false); 20198c2ecf20Sopenharmony_ci} 20208c2ecf20Sopenharmony_ci 20218c2ecf20Sopenharmony_cistatic int vhost_scsi_port_link(struct se_portal_group *se_tpg, 20228c2ecf20Sopenharmony_ci struct se_lun *lun) 20238c2ecf20Sopenharmony_ci{ 20248c2ecf20Sopenharmony_ci struct vhost_scsi_tpg *tpg = container_of(se_tpg, 20258c2ecf20Sopenharmony_ci struct vhost_scsi_tpg, se_tpg); 20268c2ecf20Sopenharmony_ci struct vhost_scsi_tmf *tmf; 20278c2ecf20Sopenharmony_ci 20288c2ecf20Sopenharmony_ci tmf = kzalloc(sizeof(*tmf), GFP_KERNEL); 20298c2ecf20Sopenharmony_ci if (!tmf) 20308c2ecf20Sopenharmony_ci return -ENOMEM; 20318c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&tmf->queue_entry); 20328c2ecf20Sopenharmony_ci vhost_work_init(&tmf->vwork, vhost_scsi_tmf_resp_work); 20338c2ecf20Sopenharmony_ci 20348c2ecf20Sopenharmony_ci mutex_lock(&vhost_scsi_mutex); 20358c2ecf20Sopenharmony_ci 20368c2ecf20Sopenharmony_ci mutex_lock(&tpg->tv_tpg_mutex); 20378c2ecf20Sopenharmony_ci tpg->tv_tpg_port_count++; 20388c2ecf20Sopenharmony_ci list_add_tail(&tmf->queue_entry, &tpg->tmf_queue); 20398c2ecf20Sopenharmony_ci mutex_unlock(&tpg->tv_tpg_mutex); 20408c2ecf20Sopenharmony_ci 20418c2ecf20Sopenharmony_ci vhost_scsi_hotplug(tpg, lun); 20428c2ecf20Sopenharmony_ci 20438c2ecf20Sopenharmony_ci mutex_unlock(&vhost_scsi_mutex); 20448c2ecf20Sopenharmony_ci 20458c2ecf20Sopenharmony_ci return 0; 20468c2ecf20Sopenharmony_ci} 20478c2ecf20Sopenharmony_ci 20488c2ecf20Sopenharmony_cistatic void vhost_scsi_port_unlink(struct se_portal_group *se_tpg, 20498c2ecf20Sopenharmony_ci struct se_lun *lun) 20508c2ecf20Sopenharmony_ci{ 20518c2ecf20Sopenharmony_ci struct vhost_scsi_tpg *tpg = container_of(se_tpg, 20528c2ecf20Sopenharmony_ci struct vhost_scsi_tpg, se_tpg); 20538c2ecf20Sopenharmony_ci struct vhost_scsi_tmf *tmf; 20548c2ecf20Sopenharmony_ci 20558c2ecf20Sopenharmony_ci mutex_lock(&vhost_scsi_mutex); 20568c2ecf20Sopenharmony_ci 20578c2ecf20Sopenharmony_ci mutex_lock(&tpg->tv_tpg_mutex); 20588c2ecf20Sopenharmony_ci tpg->tv_tpg_port_count--; 20598c2ecf20Sopenharmony_ci tmf = list_first_entry(&tpg->tmf_queue, struct vhost_scsi_tmf, 20608c2ecf20Sopenharmony_ci queue_entry); 20618c2ecf20Sopenharmony_ci list_del(&tmf->queue_entry); 20628c2ecf20Sopenharmony_ci kfree(tmf); 20638c2ecf20Sopenharmony_ci mutex_unlock(&tpg->tv_tpg_mutex); 20648c2ecf20Sopenharmony_ci 20658c2ecf20Sopenharmony_ci vhost_scsi_hotunplug(tpg, lun); 20668c2ecf20Sopenharmony_ci 20678c2ecf20Sopenharmony_ci mutex_unlock(&vhost_scsi_mutex); 20688c2ecf20Sopenharmony_ci} 20698c2ecf20Sopenharmony_ci 20708c2ecf20Sopenharmony_cistatic ssize_t vhost_scsi_tpg_attrib_fabric_prot_type_store( 20718c2ecf20Sopenharmony_ci struct config_item *item, const char *page, size_t count) 20728c2ecf20Sopenharmony_ci{ 20738c2ecf20Sopenharmony_ci struct se_portal_group *se_tpg = attrib_to_tpg(item); 20748c2ecf20Sopenharmony_ci struct vhost_scsi_tpg *tpg = container_of(se_tpg, 20758c2ecf20Sopenharmony_ci struct vhost_scsi_tpg, se_tpg); 20768c2ecf20Sopenharmony_ci unsigned long val; 20778c2ecf20Sopenharmony_ci int ret = kstrtoul(page, 0, &val); 20788c2ecf20Sopenharmony_ci 20798c2ecf20Sopenharmony_ci if (ret) { 20808c2ecf20Sopenharmony_ci pr_err("kstrtoul() returned %d for fabric_prot_type\n", ret); 20818c2ecf20Sopenharmony_ci return ret; 20828c2ecf20Sopenharmony_ci } 20838c2ecf20Sopenharmony_ci if (val != 0 && val != 1 && val != 3) { 20848c2ecf20Sopenharmony_ci pr_err("Invalid vhost_scsi fabric_prot_type: %lu\n", val); 20858c2ecf20Sopenharmony_ci return -EINVAL; 20868c2ecf20Sopenharmony_ci } 20878c2ecf20Sopenharmony_ci tpg->tv_fabric_prot_type = val; 20888c2ecf20Sopenharmony_ci 20898c2ecf20Sopenharmony_ci return count; 20908c2ecf20Sopenharmony_ci} 20918c2ecf20Sopenharmony_ci 20928c2ecf20Sopenharmony_cistatic ssize_t vhost_scsi_tpg_attrib_fabric_prot_type_show( 20938c2ecf20Sopenharmony_ci struct config_item *item, char *page) 20948c2ecf20Sopenharmony_ci{ 20958c2ecf20Sopenharmony_ci struct se_portal_group *se_tpg = attrib_to_tpg(item); 20968c2ecf20Sopenharmony_ci struct vhost_scsi_tpg *tpg = container_of(se_tpg, 20978c2ecf20Sopenharmony_ci struct vhost_scsi_tpg, se_tpg); 20988c2ecf20Sopenharmony_ci 20998c2ecf20Sopenharmony_ci return sprintf(page, "%d\n", tpg->tv_fabric_prot_type); 21008c2ecf20Sopenharmony_ci} 21018c2ecf20Sopenharmony_ci 21028c2ecf20Sopenharmony_ciCONFIGFS_ATTR(vhost_scsi_tpg_attrib_, fabric_prot_type); 21038c2ecf20Sopenharmony_ci 21048c2ecf20Sopenharmony_cistatic struct configfs_attribute *vhost_scsi_tpg_attrib_attrs[] = { 21058c2ecf20Sopenharmony_ci &vhost_scsi_tpg_attrib_attr_fabric_prot_type, 21068c2ecf20Sopenharmony_ci NULL, 21078c2ecf20Sopenharmony_ci}; 21088c2ecf20Sopenharmony_ci 21098c2ecf20Sopenharmony_cistatic int vhost_scsi_make_nexus(struct vhost_scsi_tpg *tpg, 21108c2ecf20Sopenharmony_ci const char *name) 21118c2ecf20Sopenharmony_ci{ 21128c2ecf20Sopenharmony_ci struct vhost_scsi_nexus *tv_nexus; 21138c2ecf20Sopenharmony_ci 21148c2ecf20Sopenharmony_ci mutex_lock(&tpg->tv_tpg_mutex); 21158c2ecf20Sopenharmony_ci if (tpg->tpg_nexus) { 21168c2ecf20Sopenharmony_ci mutex_unlock(&tpg->tv_tpg_mutex); 21178c2ecf20Sopenharmony_ci pr_debug("tpg->tpg_nexus already exists\n"); 21188c2ecf20Sopenharmony_ci return -EEXIST; 21198c2ecf20Sopenharmony_ci } 21208c2ecf20Sopenharmony_ci 21218c2ecf20Sopenharmony_ci tv_nexus = kzalloc(sizeof(*tv_nexus), GFP_KERNEL); 21228c2ecf20Sopenharmony_ci if (!tv_nexus) { 21238c2ecf20Sopenharmony_ci mutex_unlock(&tpg->tv_tpg_mutex); 21248c2ecf20Sopenharmony_ci pr_err("Unable to allocate struct vhost_scsi_nexus\n"); 21258c2ecf20Sopenharmony_ci return -ENOMEM; 21268c2ecf20Sopenharmony_ci } 21278c2ecf20Sopenharmony_ci /* 21288c2ecf20Sopenharmony_ci * Since we are running in 'demo mode' this call with generate a 21298c2ecf20Sopenharmony_ci * struct se_node_acl for the vhost_scsi struct se_portal_group with 21308c2ecf20Sopenharmony_ci * the SCSI Initiator port name of the passed configfs group 'name'. 21318c2ecf20Sopenharmony_ci */ 21328c2ecf20Sopenharmony_ci tv_nexus->tvn_se_sess = target_setup_session(&tpg->se_tpg, 0, 0, 21338c2ecf20Sopenharmony_ci TARGET_PROT_DIN_PASS | TARGET_PROT_DOUT_PASS, 21348c2ecf20Sopenharmony_ci (unsigned char *)name, tv_nexus, NULL); 21358c2ecf20Sopenharmony_ci if (IS_ERR(tv_nexus->tvn_se_sess)) { 21368c2ecf20Sopenharmony_ci mutex_unlock(&tpg->tv_tpg_mutex); 21378c2ecf20Sopenharmony_ci kfree(tv_nexus); 21388c2ecf20Sopenharmony_ci return -ENOMEM; 21398c2ecf20Sopenharmony_ci } 21408c2ecf20Sopenharmony_ci tpg->tpg_nexus = tv_nexus; 21418c2ecf20Sopenharmony_ci 21428c2ecf20Sopenharmony_ci mutex_unlock(&tpg->tv_tpg_mutex); 21438c2ecf20Sopenharmony_ci return 0; 21448c2ecf20Sopenharmony_ci} 21458c2ecf20Sopenharmony_ci 21468c2ecf20Sopenharmony_cistatic int vhost_scsi_drop_nexus(struct vhost_scsi_tpg *tpg) 21478c2ecf20Sopenharmony_ci{ 21488c2ecf20Sopenharmony_ci struct se_session *se_sess; 21498c2ecf20Sopenharmony_ci struct vhost_scsi_nexus *tv_nexus; 21508c2ecf20Sopenharmony_ci 21518c2ecf20Sopenharmony_ci mutex_lock(&tpg->tv_tpg_mutex); 21528c2ecf20Sopenharmony_ci tv_nexus = tpg->tpg_nexus; 21538c2ecf20Sopenharmony_ci if (!tv_nexus) { 21548c2ecf20Sopenharmony_ci mutex_unlock(&tpg->tv_tpg_mutex); 21558c2ecf20Sopenharmony_ci return -ENODEV; 21568c2ecf20Sopenharmony_ci } 21578c2ecf20Sopenharmony_ci 21588c2ecf20Sopenharmony_ci se_sess = tv_nexus->tvn_se_sess; 21598c2ecf20Sopenharmony_ci if (!se_sess) { 21608c2ecf20Sopenharmony_ci mutex_unlock(&tpg->tv_tpg_mutex); 21618c2ecf20Sopenharmony_ci return -ENODEV; 21628c2ecf20Sopenharmony_ci } 21638c2ecf20Sopenharmony_ci 21648c2ecf20Sopenharmony_ci if (tpg->tv_tpg_port_count != 0) { 21658c2ecf20Sopenharmony_ci mutex_unlock(&tpg->tv_tpg_mutex); 21668c2ecf20Sopenharmony_ci pr_err("Unable to remove TCM_vhost I_T Nexus with" 21678c2ecf20Sopenharmony_ci " active TPG port count: %d\n", 21688c2ecf20Sopenharmony_ci tpg->tv_tpg_port_count); 21698c2ecf20Sopenharmony_ci return -EBUSY; 21708c2ecf20Sopenharmony_ci } 21718c2ecf20Sopenharmony_ci 21728c2ecf20Sopenharmony_ci if (tpg->tv_tpg_vhost_count != 0) { 21738c2ecf20Sopenharmony_ci mutex_unlock(&tpg->tv_tpg_mutex); 21748c2ecf20Sopenharmony_ci pr_err("Unable to remove TCM_vhost I_T Nexus with" 21758c2ecf20Sopenharmony_ci " active TPG vhost count: %d\n", 21768c2ecf20Sopenharmony_ci tpg->tv_tpg_vhost_count); 21778c2ecf20Sopenharmony_ci return -EBUSY; 21788c2ecf20Sopenharmony_ci } 21798c2ecf20Sopenharmony_ci 21808c2ecf20Sopenharmony_ci pr_debug("TCM_vhost_ConfigFS: Removing I_T Nexus to emulated" 21818c2ecf20Sopenharmony_ci " %s Initiator Port: %s\n", vhost_scsi_dump_proto_id(tpg->tport), 21828c2ecf20Sopenharmony_ci tv_nexus->tvn_se_sess->se_node_acl->initiatorname); 21838c2ecf20Sopenharmony_ci 21848c2ecf20Sopenharmony_ci /* 21858c2ecf20Sopenharmony_ci * Release the SCSI I_T Nexus to the emulated vhost Target Port 21868c2ecf20Sopenharmony_ci */ 21878c2ecf20Sopenharmony_ci target_remove_session(se_sess); 21888c2ecf20Sopenharmony_ci tpg->tpg_nexus = NULL; 21898c2ecf20Sopenharmony_ci mutex_unlock(&tpg->tv_tpg_mutex); 21908c2ecf20Sopenharmony_ci 21918c2ecf20Sopenharmony_ci kfree(tv_nexus); 21928c2ecf20Sopenharmony_ci return 0; 21938c2ecf20Sopenharmony_ci} 21948c2ecf20Sopenharmony_ci 21958c2ecf20Sopenharmony_cistatic ssize_t vhost_scsi_tpg_nexus_show(struct config_item *item, char *page) 21968c2ecf20Sopenharmony_ci{ 21978c2ecf20Sopenharmony_ci struct se_portal_group *se_tpg = to_tpg(item); 21988c2ecf20Sopenharmony_ci struct vhost_scsi_tpg *tpg = container_of(se_tpg, 21998c2ecf20Sopenharmony_ci struct vhost_scsi_tpg, se_tpg); 22008c2ecf20Sopenharmony_ci struct vhost_scsi_nexus *tv_nexus; 22018c2ecf20Sopenharmony_ci ssize_t ret; 22028c2ecf20Sopenharmony_ci 22038c2ecf20Sopenharmony_ci mutex_lock(&tpg->tv_tpg_mutex); 22048c2ecf20Sopenharmony_ci tv_nexus = tpg->tpg_nexus; 22058c2ecf20Sopenharmony_ci if (!tv_nexus) { 22068c2ecf20Sopenharmony_ci mutex_unlock(&tpg->tv_tpg_mutex); 22078c2ecf20Sopenharmony_ci return -ENODEV; 22088c2ecf20Sopenharmony_ci } 22098c2ecf20Sopenharmony_ci ret = snprintf(page, PAGE_SIZE, "%s\n", 22108c2ecf20Sopenharmony_ci tv_nexus->tvn_se_sess->se_node_acl->initiatorname); 22118c2ecf20Sopenharmony_ci mutex_unlock(&tpg->tv_tpg_mutex); 22128c2ecf20Sopenharmony_ci 22138c2ecf20Sopenharmony_ci return ret; 22148c2ecf20Sopenharmony_ci} 22158c2ecf20Sopenharmony_ci 22168c2ecf20Sopenharmony_cistatic ssize_t vhost_scsi_tpg_nexus_store(struct config_item *item, 22178c2ecf20Sopenharmony_ci const char *page, size_t count) 22188c2ecf20Sopenharmony_ci{ 22198c2ecf20Sopenharmony_ci struct se_portal_group *se_tpg = to_tpg(item); 22208c2ecf20Sopenharmony_ci struct vhost_scsi_tpg *tpg = container_of(se_tpg, 22218c2ecf20Sopenharmony_ci struct vhost_scsi_tpg, se_tpg); 22228c2ecf20Sopenharmony_ci struct vhost_scsi_tport *tport_wwn = tpg->tport; 22238c2ecf20Sopenharmony_ci unsigned char i_port[VHOST_SCSI_NAMELEN], *ptr, *port_ptr; 22248c2ecf20Sopenharmony_ci int ret; 22258c2ecf20Sopenharmony_ci /* 22268c2ecf20Sopenharmony_ci * Shutdown the active I_T nexus if 'NULL' is passed.. 22278c2ecf20Sopenharmony_ci */ 22288c2ecf20Sopenharmony_ci if (!strncmp(page, "NULL", 4)) { 22298c2ecf20Sopenharmony_ci ret = vhost_scsi_drop_nexus(tpg); 22308c2ecf20Sopenharmony_ci return (!ret) ? count : ret; 22318c2ecf20Sopenharmony_ci } 22328c2ecf20Sopenharmony_ci /* 22338c2ecf20Sopenharmony_ci * Otherwise make sure the passed virtual Initiator port WWN matches 22348c2ecf20Sopenharmony_ci * the fabric protocol_id set in vhost_scsi_make_tport(), and call 22358c2ecf20Sopenharmony_ci * vhost_scsi_make_nexus(). 22368c2ecf20Sopenharmony_ci */ 22378c2ecf20Sopenharmony_ci if (strlen(page) >= VHOST_SCSI_NAMELEN) { 22388c2ecf20Sopenharmony_ci pr_err("Emulated NAA Sas Address: %s, exceeds" 22398c2ecf20Sopenharmony_ci " max: %d\n", page, VHOST_SCSI_NAMELEN); 22408c2ecf20Sopenharmony_ci return -EINVAL; 22418c2ecf20Sopenharmony_ci } 22428c2ecf20Sopenharmony_ci snprintf(&i_port[0], VHOST_SCSI_NAMELEN, "%s", page); 22438c2ecf20Sopenharmony_ci 22448c2ecf20Sopenharmony_ci ptr = strstr(i_port, "naa."); 22458c2ecf20Sopenharmony_ci if (ptr) { 22468c2ecf20Sopenharmony_ci if (tport_wwn->tport_proto_id != SCSI_PROTOCOL_SAS) { 22478c2ecf20Sopenharmony_ci pr_err("Passed SAS Initiator Port %s does not" 22488c2ecf20Sopenharmony_ci " match target port protoid: %s\n", i_port, 22498c2ecf20Sopenharmony_ci vhost_scsi_dump_proto_id(tport_wwn)); 22508c2ecf20Sopenharmony_ci return -EINVAL; 22518c2ecf20Sopenharmony_ci } 22528c2ecf20Sopenharmony_ci port_ptr = &i_port[0]; 22538c2ecf20Sopenharmony_ci goto check_newline; 22548c2ecf20Sopenharmony_ci } 22558c2ecf20Sopenharmony_ci ptr = strstr(i_port, "fc."); 22568c2ecf20Sopenharmony_ci if (ptr) { 22578c2ecf20Sopenharmony_ci if (tport_wwn->tport_proto_id != SCSI_PROTOCOL_FCP) { 22588c2ecf20Sopenharmony_ci pr_err("Passed FCP Initiator Port %s does not" 22598c2ecf20Sopenharmony_ci " match target port protoid: %s\n", i_port, 22608c2ecf20Sopenharmony_ci vhost_scsi_dump_proto_id(tport_wwn)); 22618c2ecf20Sopenharmony_ci return -EINVAL; 22628c2ecf20Sopenharmony_ci } 22638c2ecf20Sopenharmony_ci port_ptr = &i_port[3]; /* Skip over "fc." */ 22648c2ecf20Sopenharmony_ci goto check_newline; 22658c2ecf20Sopenharmony_ci } 22668c2ecf20Sopenharmony_ci ptr = strstr(i_port, "iqn."); 22678c2ecf20Sopenharmony_ci if (ptr) { 22688c2ecf20Sopenharmony_ci if (tport_wwn->tport_proto_id != SCSI_PROTOCOL_ISCSI) { 22698c2ecf20Sopenharmony_ci pr_err("Passed iSCSI Initiator Port %s does not" 22708c2ecf20Sopenharmony_ci " match target port protoid: %s\n", i_port, 22718c2ecf20Sopenharmony_ci vhost_scsi_dump_proto_id(tport_wwn)); 22728c2ecf20Sopenharmony_ci return -EINVAL; 22738c2ecf20Sopenharmony_ci } 22748c2ecf20Sopenharmony_ci port_ptr = &i_port[0]; 22758c2ecf20Sopenharmony_ci goto check_newline; 22768c2ecf20Sopenharmony_ci } 22778c2ecf20Sopenharmony_ci pr_err("Unable to locate prefix for emulated Initiator Port:" 22788c2ecf20Sopenharmony_ci " %s\n", i_port); 22798c2ecf20Sopenharmony_ci return -EINVAL; 22808c2ecf20Sopenharmony_ci /* 22818c2ecf20Sopenharmony_ci * Clear any trailing newline for the NAA WWN 22828c2ecf20Sopenharmony_ci */ 22838c2ecf20Sopenharmony_cicheck_newline: 22848c2ecf20Sopenharmony_ci if (i_port[strlen(i_port)-1] == '\n') 22858c2ecf20Sopenharmony_ci i_port[strlen(i_port)-1] = '\0'; 22868c2ecf20Sopenharmony_ci 22878c2ecf20Sopenharmony_ci ret = vhost_scsi_make_nexus(tpg, port_ptr); 22888c2ecf20Sopenharmony_ci if (ret < 0) 22898c2ecf20Sopenharmony_ci return ret; 22908c2ecf20Sopenharmony_ci 22918c2ecf20Sopenharmony_ci return count; 22928c2ecf20Sopenharmony_ci} 22938c2ecf20Sopenharmony_ci 22948c2ecf20Sopenharmony_ciCONFIGFS_ATTR(vhost_scsi_tpg_, nexus); 22958c2ecf20Sopenharmony_ci 22968c2ecf20Sopenharmony_cistatic struct configfs_attribute *vhost_scsi_tpg_attrs[] = { 22978c2ecf20Sopenharmony_ci &vhost_scsi_tpg_attr_nexus, 22988c2ecf20Sopenharmony_ci NULL, 22998c2ecf20Sopenharmony_ci}; 23008c2ecf20Sopenharmony_ci 23018c2ecf20Sopenharmony_cistatic struct se_portal_group * 23028c2ecf20Sopenharmony_civhost_scsi_make_tpg(struct se_wwn *wwn, const char *name) 23038c2ecf20Sopenharmony_ci{ 23048c2ecf20Sopenharmony_ci struct vhost_scsi_tport *tport = container_of(wwn, 23058c2ecf20Sopenharmony_ci struct vhost_scsi_tport, tport_wwn); 23068c2ecf20Sopenharmony_ci 23078c2ecf20Sopenharmony_ci struct vhost_scsi_tpg *tpg; 23088c2ecf20Sopenharmony_ci u16 tpgt; 23098c2ecf20Sopenharmony_ci int ret; 23108c2ecf20Sopenharmony_ci 23118c2ecf20Sopenharmony_ci if (strstr(name, "tpgt_") != name) 23128c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 23138c2ecf20Sopenharmony_ci if (kstrtou16(name + 5, 10, &tpgt) || tpgt >= VHOST_SCSI_MAX_TARGET) 23148c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 23158c2ecf20Sopenharmony_ci 23168c2ecf20Sopenharmony_ci tpg = kzalloc(sizeof(*tpg), GFP_KERNEL); 23178c2ecf20Sopenharmony_ci if (!tpg) { 23188c2ecf20Sopenharmony_ci pr_err("Unable to allocate struct vhost_scsi_tpg"); 23198c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 23208c2ecf20Sopenharmony_ci } 23218c2ecf20Sopenharmony_ci mutex_init(&tpg->tv_tpg_mutex); 23228c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&tpg->tv_tpg_list); 23238c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&tpg->tmf_queue); 23248c2ecf20Sopenharmony_ci tpg->tport = tport; 23258c2ecf20Sopenharmony_ci tpg->tport_tpgt = tpgt; 23268c2ecf20Sopenharmony_ci 23278c2ecf20Sopenharmony_ci ret = core_tpg_register(wwn, &tpg->se_tpg, tport->tport_proto_id); 23288c2ecf20Sopenharmony_ci if (ret < 0) { 23298c2ecf20Sopenharmony_ci kfree(tpg); 23308c2ecf20Sopenharmony_ci return NULL; 23318c2ecf20Sopenharmony_ci } 23328c2ecf20Sopenharmony_ci mutex_lock(&vhost_scsi_mutex); 23338c2ecf20Sopenharmony_ci list_add_tail(&tpg->tv_tpg_list, &vhost_scsi_list); 23348c2ecf20Sopenharmony_ci mutex_unlock(&vhost_scsi_mutex); 23358c2ecf20Sopenharmony_ci 23368c2ecf20Sopenharmony_ci return &tpg->se_tpg; 23378c2ecf20Sopenharmony_ci} 23388c2ecf20Sopenharmony_ci 23398c2ecf20Sopenharmony_cistatic void vhost_scsi_drop_tpg(struct se_portal_group *se_tpg) 23408c2ecf20Sopenharmony_ci{ 23418c2ecf20Sopenharmony_ci struct vhost_scsi_tpg *tpg = container_of(se_tpg, 23428c2ecf20Sopenharmony_ci struct vhost_scsi_tpg, se_tpg); 23438c2ecf20Sopenharmony_ci 23448c2ecf20Sopenharmony_ci mutex_lock(&vhost_scsi_mutex); 23458c2ecf20Sopenharmony_ci list_del(&tpg->tv_tpg_list); 23468c2ecf20Sopenharmony_ci mutex_unlock(&vhost_scsi_mutex); 23478c2ecf20Sopenharmony_ci /* 23488c2ecf20Sopenharmony_ci * Release the virtual I_T Nexus for this vhost TPG 23498c2ecf20Sopenharmony_ci */ 23508c2ecf20Sopenharmony_ci vhost_scsi_drop_nexus(tpg); 23518c2ecf20Sopenharmony_ci /* 23528c2ecf20Sopenharmony_ci * Deregister the se_tpg from TCM.. 23538c2ecf20Sopenharmony_ci */ 23548c2ecf20Sopenharmony_ci core_tpg_deregister(se_tpg); 23558c2ecf20Sopenharmony_ci kfree(tpg); 23568c2ecf20Sopenharmony_ci} 23578c2ecf20Sopenharmony_ci 23588c2ecf20Sopenharmony_cistatic struct se_wwn * 23598c2ecf20Sopenharmony_civhost_scsi_make_tport(struct target_fabric_configfs *tf, 23608c2ecf20Sopenharmony_ci struct config_group *group, 23618c2ecf20Sopenharmony_ci const char *name) 23628c2ecf20Sopenharmony_ci{ 23638c2ecf20Sopenharmony_ci struct vhost_scsi_tport *tport; 23648c2ecf20Sopenharmony_ci char *ptr; 23658c2ecf20Sopenharmony_ci u64 wwpn = 0; 23668c2ecf20Sopenharmony_ci int off = 0; 23678c2ecf20Sopenharmony_ci 23688c2ecf20Sopenharmony_ci /* if (vhost_scsi_parse_wwn(name, &wwpn, 1) < 0) 23698c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); */ 23708c2ecf20Sopenharmony_ci 23718c2ecf20Sopenharmony_ci tport = kzalloc(sizeof(*tport), GFP_KERNEL); 23728c2ecf20Sopenharmony_ci if (!tport) { 23738c2ecf20Sopenharmony_ci pr_err("Unable to allocate struct vhost_scsi_tport"); 23748c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 23758c2ecf20Sopenharmony_ci } 23768c2ecf20Sopenharmony_ci tport->tport_wwpn = wwpn; 23778c2ecf20Sopenharmony_ci /* 23788c2ecf20Sopenharmony_ci * Determine the emulated Protocol Identifier and Target Port Name 23798c2ecf20Sopenharmony_ci * based on the incoming configfs directory name. 23808c2ecf20Sopenharmony_ci */ 23818c2ecf20Sopenharmony_ci ptr = strstr(name, "naa."); 23828c2ecf20Sopenharmony_ci if (ptr) { 23838c2ecf20Sopenharmony_ci tport->tport_proto_id = SCSI_PROTOCOL_SAS; 23848c2ecf20Sopenharmony_ci goto check_len; 23858c2ecf20Sopenharmony_ci } 23868c2ecf20Sopenharmony_ci ptr = strstr(name, "fc."); 23878c2ecf20Sopenharmony_ci if (ptr) { 23888c2ecf20Sopenharmony_ci tport->tport_proto_id = SCSI_PROTOCOL_FCP; 23898c2ecf20Sopenharmony_ci off = 3; /* Skip over "fc." */ 23908c2ecf20Sopenharmony_ci goto check_len; 23918c2ecf20Sopenharmony_ci } 23928c2ecf20Sopenharmony_ci ptr = strstr(name, "iqn."); 23938c2ecf20Sopenharmony_ci if (ptr) { 23948c2ecf20Sopenharmony_ci tport->tport_proto_id = SCSI_PROTOCOL_ISCSI; 23958c2ecf20Sopenharmony_ci goto check_len; 23968c2ecf20Sopenharmony_ci } 23978c2ecf20Sopenharmony_ci 23988c2ecf20Sopenharmony_ci pr_err("Unable to locate prefix for emulated Target Port:" 23998c2ecf20Sopenharmony_ci " %s\n", name); 24008c2ecf20Sopenharmony_ci kfree(tport); 24018c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 24028c2ecf20Sopenharmony_ci 24038c2ecf20Sopenharmony_cicheck_len: 24048c2ecf20Sopenharmony_ci if (strlen(name) >= VHOST_SCSI_NAMELEN) { 24058c2ecf20Sopenharmony_ci pr_err("Emulated %s Address: %s, exceeds" 24068c2ecf20Sopenharmony_ci " max: %d\n", name, vhost_scsi_dump_proto_id(tport), 24078c2ecf20Sopenharmony_ci VHOST_SCSI_NAMELEN); 24088c2ecf20Sopenharmony_ci kfree(tport); 24098c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 24108c2ecf20Sopenharmony_ci } 24118c2ecf20Sopenharmony_ci snprintf(&tport->tport_name[0], VHOST_SCSI_NAMELEN, "%s", &name[off]); 24128c2ecf20Sopenharmony_ci 24138c2ecf20Sopenharmony_ci pr_debug("TCM_VHost_ConfigFS: Allocated emulated Target" 24148c2ecf20Sopenharmony_ci " %s Address: %s\n", vhost_scsi_dump_proto_id(tport), name); 24158c2ecf20Sopenharmony_ci 24168c2ecf20Sopenharmony_ci return &tport->tport_wwn; 24178c2ecf20Sopenharmony_ci} 24188c2ecf20Sopenharmony_ci 24198c2ecf20Sopenharmony_cistatic void vhost_scsi_drop_tport(struct se_wwn *wwn) 24208c2ecf20Sopenharmony_ci{ 24218c2ecf20Sopenharmony_ci struct vhost_scsi_tport *tport = container_of(wwn, 24228c2ecf20Sopenharmony_ci struct vhost_scsi_tport, tport_wwn); 24238c2ecf20Sopenharmony_ci 24248c2ecf20Sopenharmony_ci pr_debug("TCM_VHost_ConfigFS: Deallocating emulated Target" 24258c2ecf20Sopenharmony_ci " %s Address: %s\n", vhost_scsi_dump_proto_id(tport), 24268c2ecf20Sopenharmony_ci tport->tport_name); 24278c2ecf20Sopenharmony_ci 24288c2ecf20Sopenharmony_ci kfree(tport); 24298c2ecf20Sopenharmony_ci} 24308c2ecf20Sopenharmony_ci 24318c2ecf20Sopenharmony_cistatic ssize_t 24328c2ecf20Sopenharmony_civhost_scsi_wwn_version_show(struct config_item *item, char *page) 24338c2ecf20Sopenharmony_ci{ 24348c2ecf20Sopenharmony_ci return sprintf(page, "TCM_VHOST fabric module %s on %s/%s" 24358c2ecf20Sopenharmony_ci "on "UTS_RELEASE"\n", VHOST_SCSI_VERSION, utsname()->sysname, 24368c2ecf20Sopenharmony_ci utsname()->machine); 24378c2ecf20Sopenharmony_ci} 24388c2ecf20Sopenharmony_ci 24398c2ecf20Sopenharmony_ciCONFIGFS_ATTR_RO(vhost_scsi_wwn_, version); 24408c2ecf20Sopenharmony_ci 24418c2ecf20Sopenharmony_cistatic struct configfs_attribute *vhost_scsi_wwn_attrs[] = { 24428c2ecf20Sopenharmony_ci &vhost_scsi_wwn_attr_version, 24438c2ecf20Sopenharmony_ci NULL, 24448c2ecf20Sopenharmony_ci}; 24458c2ecf20Sopenharmony_ci 24468c2ecf20Sopenharmony_cistatic const struct target_core_fabric_ops vhost_scsi_ops = { 24478c2ecf20Sopenharmony_ci .module = THIS_MODULE, 24488c2ecf20Sopenharmony_ci .fabric_name = "vhost", 24498c2ecf20Sopenharmony_ci .max_data_sg_nents = VHOST_SCSI_PREALLOC_SGLS, 24508c2ecf20Sopenharmony_ci .tpg_get_wwn = vhost_scsi_get_fabric_wwn, 24518c2ecf20Sopenharmony_ci .tpg_get_tag = vhost_scsi_get_tpgt, 24528c2ecf20Sopenharmony_ci .tpg_check_demo_mode = vhost_scsi_check_true, 24538c2ecf20Sopenharmony_ci .tpg_check_demo_mode_cache = vhost_scsi_check_true, 24548c2ecf20Sopenharmony_ci .tpg_check_demo_mode_write_protect = vhost_scsi_check_false, 24558c2ecf20Sopenharmony_ci .tpg_check_prod_mode_write_protect = vhost_scsi_check_false, 24568c2ecf20Sopenharmony_ci .tpg_check_prot_fabric_only = vhost_scsi_check_prot_fabric_only, 24578c2ecf20Sopenharmony_ci .tpg_get_inst_index = vhost_scsi_tpg_get_inst_index, 24588c2ecf20Sopenharmony_ci .release_cmd = vhost_scsi_release_cmd, 24598c2ecf20Sopenharmony_ci .check_stop_free = vhost_scsi_check_stop_free, 24608c2ecf20Sopenharmony_ci .sess_get_index = vhost_scsi_sess_get_index, 24618c2ecf20Sopenharmony_ci .sess_get_initiator_sid = NULL, 24628c2ecf20Sopenharmony_ci .write_pending = vhost_scsi_write_pending, 24638c2ecf20Sopenharmony_ci .set_default_node_attributes = vhost_scsi_set_default_node_attrs, 24648c2ecf20Sopenharmony_ci .get_cmd_state = vhost_scsi_get_cmd_state, 24658c2ecf20Sopenharmony_ci .queue_data_in = vhost_scsi_queue_data_in, 24668c2ecf20Sopenharmony_ci .queue_status = vhost_scsi_queue_status, 24678c2ecf20Sopenharmony_ci .queue_tm_rsp = vhost_scsi_queue_tm_rsp, 24688c2ecf20Sopenharmony_ci .aborted_task = vhost_scsi_aborted_task, 24698c2ecf20Sopenharmony_ci /* 24708c2ecf20Sopenharmony_ci * Setup callers for generic logic in target_core_fabric_configfs.c 24718c2ecf20Sopenharmony_ci */ 24728c2ecf20Sopenharmony_ci .fabric_make_wwn = vhost_scsi_make_tport, 24738c2ecf20Sopenharmony_ci .fabric_drop_wwn = vhost_scsi_drop_tport, 24748c2ecf20Sopenharmony_ci .fabric_make_tpg = vhost_scsi_make_tpg, 24758c2ecf20Sopenharmony_ci .fabric_drop_tpg = vhost_scsi_drop_tpg, 24768c2ecf20Sopenharmony_ci .fabric_post_link = vhost_scsi_port_link, 24778c2ecf20Sopenharmony_ci .fabric_pre_unlink = vhost_scsi_port_unlink, 24788c2ecf20Sopenharmony_ci 24798c2ecf20Sopenharmony_ci .tfc_wwn_attrs = vhost_scsi_wwn_attrs, 24808c2ecf20Sopenharmony_ci .tfc_tpg_base_attrs = vhost_scsi_tpg_attrs, 24818c2ecf20Sopenharmony_ci .tfc_tpg_attrib_attrs = vhost_scsi_tpg_attrib_attrs, 24828c2ecf20Sopenharmony_ci}; 24838c2ecf20Sopenharmony_ci 24848c2ecf20Sopenharmony_cistatic int __init vhost_scsi_init(void) 24858c2ecf20Sopenharmony_ci{ 24868c2ecf20Sopenharmony_ci int ret = -ENOMEM; 24878c2ecf20Sopenharmony_ci 24888c2ecf20Sopenharmony_ci pr_debug("TCM_VHOST fabric module %s on %s/%s" 24898c2ecf20Sopenharmony_ci " on "UTS_RELEASE"\n", VHOST_SCSI_VERSION, utsname()->sysname, 24908c2ecf20Sopenharmony_ci utsname()->machine); 24918c2ecf20Sopenharmony_ci 24928c2ecf20Sopenharmony_ci /* 24938c2ecf20Sopenharmony_ci * Use our own dedicated workqueue for submitting I/O into 24948c2ecf20Sopenharmony_ci * target core to avoid contention within system_wq. 24958c2ecf20Sopenharmony_ci */ 24968c2ecf20Sopenharmony_ci vhost_scsi_workqueue = alloc_workqueue("vhost_scsi", 0, 0); 24978c2ecf20Sopenharmony_ci if (!vhost_scsi_workqueue) 24988c2ecf20Sopenharmony_ci goto out; 24998c2ecf20Sopenharmony_ci 25008c2ecf20Sopenharmony_ci ret = vhost_scsi_register(); 25018c2ecf20Sopenharmony_ci if (ret < 0) 25028c2ecf20Sopenharmony_ci goto out_destroy_workqueue; 25038c2ecf20Sopenharmony_ci 25048c2ecf20Sopenharmony_ci ret = target_register_template(&vhost_scsi_ops); 25058c2ecf20Sopenharmony_ci if (ret < 0) 25068c2ecf20Sopenharmony_ci goto out_vhost_scsi_deregister; 25078c2ecf20Sopenharmony_ci 25088c2ecf20Sopenharmony_ci return 0; 25098c2ecf20Sopenharmony_ci 25108c2ecf20Sopenharmony_ciout_vhost_scsi_deregister: 25118c2ecf20Sopenharmony_ci vhost_scsi_deregister(); 25128c2ecf20Sopenharmony_ciout_destroy_workqueue: 25138c2ecf20Sopenharmony_ci destroy_workqueue(vhost_scsi_workqueue); 25148c2ecf20Sopenharmony_ciout: 25158c2ecf20Sopenharmony_ci return ret; 25168c2ecf20Sopenharmony_ci}; 25178c2ecf20Sopenharmony_ci 25188c2ecf20Sopenharmony_cistatic void vhost_scsi_exit(void) 25198c2ecf20Sopenharmony_ci{ 25208c2ecf20Sopenharmony_ci target_unregister_template(&vhost_scsi_ops); 25218c2ecf20Sopenharmony_ci vhost_scsi_deregister(); 25228c2ecf20Sopenharmony_ci destroy_workqueue(vhost_scsi_workqueue); 25238c2ecf20Sopenharmony_ci}; 25248c2ecf20Sopenharmony_ci 25258c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("VHOST_SCSI series fabric driver"); 25268c2ecf20Sopenharmony_ciMODULE_ALIAS("tcm_vhost"); 25278c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 25288c2ecf20Sopenharmony_cimodule_init(vhost_scsi_init); 25298c2ecf20Sopenharmony_cimodule_exit(vhost_scsi_exit); 2530