xref: /kvmtool/virtio/scsi.c (revision 3b1cdcf9e78f7d36f0cca805c4172bba53779b69)
1 #include "kvm/virtio-scsi.h"
2 #include "kvm/virtio-pci-dev.h"
3 #include "kvm/disk-image.h"
4 #include "kvm/irq.h"
5 #include "kvm/kvm.h"
6 #include "kvm/pci.h"
7 #include "kvm/ioeventfd.h"
8 #include "kvm/guest_compat.h"
9 #include "kvm/virtio-pci.h"
10 #include "kvm/virtio.h"
11 #include "kvm/strbuf.h"
12 
13 #include <linux/kernel.h>
14 #include <linux/virtio_scsi.h>
15 #include <linux/vhost.h>
16 
17 #define VIRTIO_SCSI_QUEUE_SIZE		128
18 #define NUM_VIRT_QUEUES			3
19 
20 static LIST_HEAD(sdevs);
21 static int compat_id = -1;
22 
23 struct scsi_dev {
24 	struct virt_queue		vqs[NUM_VIRT_QUEUES];
25 	struct virtio_scsi_config	config;
26 	struct vhost_scsi_target	target;
27 	int				vhost_fd;
28 	struct virtio_device		vdev;
29 	struct list_head		list;
30 	struct kvm			*kvm;
31 };
32 
get_config(struct kvm * kvm,void * dev)33 static u8 *get_config(struct kvm *kvm, void *dev)
34 {
35 	struct scsi_dev *sdev = dev;
36 
37 	return ((u8 *)(&sdev->config));
38 }
39 
get_config_size(struct kvm * kvm,void * dev)40 static size_t get_config_size(struct kvm *kvm, void *dev)
41 {
42 	struct scsi_dev *sdev = dev;
43 
44 	return sizeof(sdev->config);
45 }
46 
get_host_features(struct kvm * kvm,void * dev)47 static u64 get_host_features(struct kvm *kvm, void *dev)
48 {
49 	int r;
50 	u64 features;
51 	struct scsi_dev *sdev = dev;
52 
53 	r = ioctl(sdev->vhost_fd, VHOST_GET_FEATURES, &features);
54 	if (r != 0)
55 		die_perror("VHOST_GET_FEATURES failed");
56 
57 	return features &
58 		(1ULL << VIRTIO_RING_F_EVENT_IDX |	\
59 		 1ULL << VIRTIO_RING_F_INDIRECT_DESC |	\
60 		 1ULL << VIRTIO_F_ANY_LAYOUT);
61 }
62 
notify_status(struct kvm * kvm,void * dev,u32 status)63 static void notify_status(struct kvm *kvm, void *dev, u32 status)
64 {
65 	int r;
66 	struct scsi_dev *sdev = dev;
67 	struct virtio_device *vdev = &sdev->vdev;
68 	struct virtio_scsi_config *conf = &sdev->config;
69 	u16 endian = vdev->endian;
70 
71 	if (status & VIRTIO__STATUS_START) {
72 		r = virtio_vhost_set_features(sdev->vhost_fd, sdev->vdev.features);
73 		if (r != 0)
74 			die_perror("VHOST_SET_FEATURES failed");
75 
76 		r = ioctl(sdev->vhost_fd, VHOST_SCSI_SET_ENDPOINT,
77 			      &sdev->target);
78 		if (r != 0)
79 			die("VHOST_SCSI_SET_ENDPOINT failed %d", errno);
80 	}
81 
82 	if (!(status & VIRTIO__STATUS_CONFIG))
83 		return;
84 
85 	conf->num_queues = virtio_host_to_guest_u32(endian, NUM_VIRT_QUEUES - 2);
86 	conf->seg_max = virtio_host_to_guest_u32(endian, VIRTIO_SCSI_CDB_SIZE - 2);
87 	conf->max_sectors = virtio_host_to_guest_u32(endian, 65535);
88 	conf->cmd_per_lun = virtio_host_to_guest_u32(endian, 128);
89 	conf->sense_size = virtio_host_to_guest_u32(endian, VIRTIO_SCSI_SENSE_SIZE);
90 	conf->cdb_size = virtio_host_to_guest_u32(endian, VIRTIO_SCSI_CDB_SIZE);
91 	conf->max_target = virtio_host_to_guest_u16(endian, 255);
92 	conf->max_lun = virtio_host_to_guest_u32(endian, 16383);
93 	conf->event_info_size = virtio_host_to_guest_u32(endian, sizeof(struct virtio_scsi_event));
94 }
95 
init_vq(struct kvm * kvm,void * dev,u32 vq)96 static int init_vq(struct kvm *kvm, void *dev, u32 vq)
97 {
98 	struct scsi_dev *sdev = dev;
99 	struct virt_queue *queue;
100 
101 	compat__remove_message(compat_id);
102 
103 	queue		= &sdev->vqs[vq];
104 
105 	virtio_init_device_vq(kvm, &sdev->vdev, queue, VIRTIO_SCSI_QUEUE_SIZE);
106 
107 	if (sdev->vhost_fd == 0)
108 		return 0;
109 
110 	virtio_vhost_set_vring(kvm, sdev->vhost_fd, vq, queue);
111 	return 0;
112 }
113 
notify_vq_gsi(struct kvm * kvm,void * dev,u32 vq,u32 gsi)114 static void notify_vq_gsi(struct kvm *kvm, void *dev, u32 vq, u32 gsi)
115 {
116 	struct scsi_dev *sdev = dev;
117 
118 	if (sdev->vhost_fd == 0)
119 		return;
120 
121 	virtio_vhost_set_vring_irqfd(kvm, gsi, &sdev->vqs[vq]);
122 }
123 
notify_vq_eventfd(struct kvm * kvm,void * dev,u32 vq,u32 efd)124 static void notify_vq_eventfd(struct kvm *kvm, void *dev, u32 vq, u32 efd)
125 {
126 	struct scsi_dev *sdev = dev;
127 
128 	if (sdev->vhost_fd == 0)
129 		return;
130 
131 	virtio_vhost_set_vring_kick(kvm, sdev->vhost_fd, vq, efd);
132 }
133 
notify_vq(struct kvm * kvm,void * dev,u32 vq)134 static int notify_vq(struct kvm *kvm, void *dev, u32 vq)
135 {
136 	return 0;
137 }
138 
get_vq(struct kvm * kvm,void * dev,u32 vq)139 static struct virt_queue *get_vq(struct kvm *kvm, void *dev, u32 vq)
140 {
141 	struct scsi_dev *sdev = dev;
142 
143 	return &sdev->vqs[vq];
144 }
145 
get_size_vq(struct kvm * kvm,void * dev,u32 vq)146 static int get_size_vq(struct kvm *kvm, void *dev, u32 vq)
147 {
148 	return VIRTIO_SCSI_QUEUE_SIZE;
149 }
150 
set_size_vq(struct kvm * kvm,void * dev,u32 vq,int size)151 static int set_size_vq(struct kvm *kvm, void *dev, u32 vq, int size)
152 {
153 	return size;
154 }
155 
get_vq_count(struct kvm * kvm,void * dev)156 static unsigned int get_vq_count(struct kvm *kvm, void *dev)
157 {
158 	return NUM_VIRT_QUEUES;
159 }
160 
161 static struct virtio_ops scsi_dev_virtio_ops = {
162 	.get_config		= get_config,
163 	.get_config_size	= get_config_size,
164 	.get_host_features	= get_host_features,
165 	.init_vq		= init_vq,
166 	.get_vq			= get_vq,
167 	.get_size_vq		= get_size_vq,
168 	.set_size_vq		= set_size_vq,
169 	.notify_status		= notify_status,
170 	.notify_vq		= notify_vq,
171 	.notify_vq_gsi		= notify_vq_gsi,
172 	.notify_vq_eventfd	= notify_vq_eventfd,
173 	.get_vq_count		= get_vq_count,
174 };
175 
virtio_scsi_vhost_init(struct kvm * kvm,struct scsi_dev * sdev)176 static void virtio_scsi_vhost_init(struct kvm *kvm, struct scsi_dev *sdev)
177 {
178 	sdev->vhost_fd = open("/dev/vhost-scsi", O_RDWR);
179 	if (sdev->vhost_fd < 0)
180 		die_perror("Failed openning vhost-scsi device");
181 
182 	virtio_vhost_init(kvm, sdev->vhost_fd);
183 
184 	sdev->vdev.use_vhost = true;
185 }
186 
187 
virtio_scsi_init_one(struct kvm * kvm,struct disk_image * disk)188 static int virtio_scsi_init_one(struct kvm *kvm, struct disk_image *disk)
189 {
190 	struct scsi_dev *sdev;
191 	int r;
192 
193 	if (!disk)
194 		return -EINVAL;
195 
196 	sdev = calloc(1, sizeof(struct scsi_dev));
197 	if (sdev == NULL)
198 		return -ENOMEM;
199 
200 	*sdev = (struct scsi_dev) {
201 		.kvm			= kvm,
202 	};
203 	strlcpy((char *)&sdev->target.vhost_wwpn, disk->wwpn, sizeof(sdev->target.vhost_wwpn));
204 	sdev->target.abi_version = VHOST_SCSI_ABI_VERSION;
205 
206 	list_add_tail(&sdev->list, &sdevs);
207 
208 	r = virtio_init(kvm, sdev, &sdev->vdev, &scsi_dev_virtio_ops,
209 			kvm->cfg.virtio_transport, PCI_DEVICE_ID_VIRTIO_SCSI,
210 			VIRTIO_ID_SCSI, PCI_CLASS_BLK);
211 	if (r < 0)
212 		return r;
213 
214 	virtio_scsi_vhost_init(kvm, sdev);
215 
216 	if (compat_id == -1)
217 		compat_id = virtio_compat_add_message("virtio-scsi", "CONFIG_SCSI_VIRTIO");
218 
219 	return 0;
220 }
221 
virtio_scsi_exit_one(struct kvm * kvm,struct scsi_dev * sdev)222 static int virtio_scsi_exit_one(struct kvm *kvm, struct scsi_dev *sdev)
223 {
224 	int r;
225 
226 	r = ioctl(sdev->vhost_fd, VHOST_SCSI_CLEAR_ENDPOINT, &sdev->target);
227 	if (r != 0)
228 		die("VHOST_SCSI_CLEAR_ENDPOINT failed %d", errno);
229 
230 	list_del(&sdev->list);
231 	free(sdev);
232 
233 	return 0;
234 }
235 
virtio_scsi_init(struct kvm * kvm)236 int virtio_scsi_init(struct kvm *kvm)
237 {
238 	int i, r = 0;
239 
240 	for (i = 0; i < kvm->nr_disks; i++) {
241 		if (!kvm->disks[i]->wwpn)
242 			continue;
243 		r = virtio_scsi_init_one(kvm, kvm->disks[i]);
244 		if (r < 0)
245 			goto cleanup;
246 	}
247 
248 	return 0;
249 cleanup:
250 	virtio_scsi_exit(kvm);
251 	return r;
252 }
253 virtio_dev_init(virtio_scsi_init);
254 
virtio_scsi_exit(struct kvm * kvm)255 int virtio_scsi_exit(struct kvm *kvm)
256 {
257 	while (!list_empty(&sdevs)) {
258 		struct scsi_dev *sdev;
259 
260 		sdev = list_first_entry(&sdevs, struct scsi_dev, list);
261 		virtio_scsi_exit_one(kvm, sdev);
262 	}
263 
264 	return 0;
265 }
266 virtio_dev_exit(virtio_scsi_exit);
267