xref: /kvmtool/virtio/scsi.c (revision 867b15ccd7dae9ba7a174f97d4fe76e90a79d957)
1a67da3beSAsias He #include "kvm/virtio-scsi.h"
2a67da3beSAsias He #include "kvm/virtio-pci-dev.h"
3a67da3beSAsias He #include "kvm/disk-image.h"
4e59679d2SJean-Philippe Brucker #include "kvm/irq.h"
5a67da3beSAsias He #include "kvm/kvm.h"
6a67da3beSAsias He #include "kvm/pci.h"
7a67da3beSAsias He #include "kvm/ioeventfd.h"
8a67da3beSAsias He #include "kvm/guest_compat.h"
9a67da3beSAsias He #include "kvm/virtio-pci.h"
10a67da3beSAsias He #include "kvm/virtio.h"
1105755b29SAndre Przywara #include "kvm/strbuf.h"
12a67da3beSAsias He 
13a67da3beSAsias He #include <linux/kernel.h>
14a67da3beSAsias He #include <linux/virtio_scsi.h>
15a67da3beSAsias He #include <linux/vhost.h>
16a67da3beSAsias He 
17a67da3beSAsias He #define VIRTIO_SCSI_QUEUE_SIZE		128
18a67da3beSAsias He #define NUM_VIRT_QUEUES			3
19a67da3beSAsias He 
20a67da3beSAsias He static LIST_HEAD(sdevs);
21a67da3beSAsias He static int compat_id = -1;
22a67da3beSAsias He 
23a67da3beSAsias He struct scsi_dev {
24a67da3beSAsias He 	struct virt_queue		vqs[NUM_VIRT_QUEUES];
25a67da3beSAsias He 	struct virtio_scsi_config	config;
26a67da3beSAsias He 	struct vhost_scsi_target	target;
27a67da3beSAsias He 	u32				features;
28a67da3beSAsias He 	int				vhost_fd;
29a67da3beSAsias He 	struct virtio_device		vdev;
30a67da3beSAsias He 	struct list_head		list;
31a67da3beSAsias He 	struct kvm			*kvm;
32a67da3beSAsias He };
33a67da3beSAsias He 
34c5ae742bSSasha Levin static u8 *get_config(struct kvm *kvm, void *dev)
35a67da3beSAsias He {
36a67da3beSAsias He 	struct scsi_dev *sdev = dev;
37a67da3beSAsias He 
38c5ae742bSSasha Levin 	return ((u8 *)(&sdev->config));
39a67da3beSAsias He }
40a67da3beSAsias He 
41e4730284SMartin Radev static size_t get_config_size(struct kvm *kvm, void *dev)
42e4730284SMartin Radev {
43e4730284SMartin Radev 	struct scsi_dev *sdev = dev;
44e4730284SMartin Radev 
45e4730284SMartin Radev 	return sizeof(sdev->config);
46e4730284SMartin Radev }
47e4730284SMartin Radev 
48a67da3beSAsias He static u32 get_host_features(struct kvm *kvm, void *dev)
49a67da3beSAsias He {
50a67da3beSAsias He 	return	1UL << VIRTIO_RING_F_EVENT_IDX |
51a67da3beSAsias He 		1UL << VIRTIO_RING_F_INDIRECT_DESC;
52a67da3beSAsias He }
53a67da3beSAsias He 
54a67da3beSAsias He static void set_guest_features(struct kvm *kvm, void *dev, u32 features)
55a67da3beSAsias He {
56a67da3beSAsias He 	struct scsi_dev *sdev = dev;
57a67da3beSAsias He 
58a67da3beSAsias He 	sdev->features = features;
59a67da3beSAsias He }
60a67da3beSAsias He 
6195242e44SJean-Philippe Brucker static void notify_status(struct kvm *kvm, void *dev, u32 status)
6295242e44SJean-Philippe Brucker {
63*867b15ccSJean-Philippe Brucker 	struct scsi_dev *sdev = dev;
64*867b15ccSJean-Philippe Brucker 	struct virtio_device *vdev = &sdev->vdev;
65*867b15ccSJean-Philippe Brucker 	struct virtio_scsi_config *conf = &sdev->config;
66*867b15ccSJean-Philippe Brucker 
67*867b15ccSJean-Philippe Brucker 	if (!(status & VIRTIO__STATUS_CONFIG))
68*867b15ccSJean-Philippe Brucker 		return;
69*867b15ccSJean-Philippe Brucker 
70*867b15ccSJean-Philippe Brucker 	/* Avoid warning when endianness helpers are compiled out */
71*867b15ccSJean-Philippe Brucker 	vdev = vdev;
72*867b15ccSJean-Philippe Brucker 
73*867b15ccSJean-Philippe Brucker 	conf->num_queues = virtio_host_to_guest_u32(vdev, NUM_VIRT_QUEUES - 2);
74*867b15ccSJean-Philippe Brucker 	conf->seg_max = virtio_host_to_guest_u32(vdev, VIRTIO_SCSI_CDB_SIZE - 2);
75*867b15ccSJean-Philippe Brucker 	conf->max_sectors = virtio_host_to_guest_u32(vdev, 65535);
76*867b15ccSJean-Philippe Brucker 	conf->cmd_per_lun = virtio_host_to_guest_u32(vdev, 128);
77*867b15ccSJean-Philippe Brucker 	conf->sense_size = virtio_host_to_guest_u32(vdev, VIRTIO_SCSI_SENSE_SIZE);
78*867b15ccSJean-Philippe Brucker 	conf->cdb_size = virtio_host_to_guest_u32(vdev, VIRTIO_SCSI_CDB_SIZE);
79*867b15ccSJean-Philippe Brucker 	conf->max_lun = virtio_host_to_guest_u32(vdev, 16383);
80*867b15ccSJean-Philippe Brucker 	conf->event_info_size = virtio_host_to_guest_u32(vdev, sizeof(struct virtio_scsi_event));
8195242e44SJean-Philippe Brucker }
8295242e44SJean-Philippe Brucker 
83609ee906SJean-Philippe Brucker static int init_vq(struct kvm *kvm, void *dev, u32 vq)
84a67da3beSAsias He {
85a67da3beSAsias He 	struct vhost_vring_state state = { .index = vq };
86a67da3beSAsias He 	struct vhost_vring_addr addr;
87a67da3beSAsias He 	struct scsi_dev *sdev = dev;
88a67da3beSAsias He 	struct virt_queue *queue;
89a67da3beSAsias He 	int r;
90a67da3beSAsias He 
91a67da3beSAsias He 	compat__remove_message(compat_id);
92a67da3beSAsias He 
93a67da3beSAsias He 	queue		= &sdev->vqs[vq];
94a67da3beSAsias He 
95609ee906SJean-Philippe Brucker 	virtio_init_device_vq(kvm, &sdev->vdev, queue, VIRTIO_SCSI_QUEUE_SIZE);
96a67da3beSAsias He 
97a67da3beSAsias He 	if (sdev->vhost_fd == 0)
98a67da3beSAsias He 		return 0;
99a67da3beSAsias He 
100a67da3beSAsias He 	state.num = queue->vring.num;
101a67da3beSAsias He 	r = ioctl(sdev->vhost_fd, VHOST_SET_VRING_NUM, &state);
102a67da3beSAsias He 	if (r < 0)
103a67da3beSAsias He 		die_perror("VHOST_SET_VRING_NUM failed");
104a67da3beSAsias He 	state.num = 0;
105a67da3beSAsias He 	r = ioctl(sdev->vhost_fd, VHOST_SET_VRING_BASE, &state);
106a67da3beSAsias He 	if (r < 0)
107a67da3beSAsias He 		die_perror("VHOST_SET_VRING_BASE failed");
108a67da3beSAsias He 
109a67da3beSAsias He 	addr = (struct vhost_vring_addr) {
110a67da3beSAsias He 		.index = vq,
111a67da3beSAsias He 		.desc_user_addr = (u64)(unsigned long)queue->vring.desc,
112a67da3beSAsias He 		.avail_user_addr = (u64)(unsigned long)queue->vring.avail,
113a67da3beSAsias He 		.used_user_addr = (u64)(unsigned long)queue->vring.used,
114a67da3beSAsias He 	};
115a67da3beSAsias He 
116a67da3beSAsias He 	r = ioctl(sdev->vhost_fd, VHOST_SET_VRING_ADDR, &addr);
117a67da3beSAsias He 	if (r < 0)
118a67da3beSAsias He 		die_perror("VHOST_SET_VRING_ADDR failed");
119a67da3beSAsias He 
120a67da3beSAsias He 	return 0;
121a67da3beSAsias He }
122a67da3beSAsias He 
123a67da3beSAsias He static void notify_vq_gsi(struct kvm *kvm, void *dev, u32 vq, u32 gsi)
124a67da3beSAsias He {
125a67da3beSAsias He 	struct vhost_vring_file file;
126a67da3beSAsias He 	struct scsi_dev *sdev = dev;
127a67da3beSAsias He 	int r;
128a67da3beSAsias He 
129a67da3beSAsias He 	if (sdev->vhost_fd == 0)
130a67da3beSAsias He 		return;
131a67da3beSAsias He 
132a67da3beSAsias He 	file = (struct vhost_vring_file) {
133a67da3beSAsias He 		.index	= vq,
134e59679d2SJean-Philippe Brucker 		.fd	= eventfd(0, 0),
135a67da3beSAsias He 	};
136a67da3beSAsias He 
137e59679d2SJean-Philippe Brucker 	r = irq__add_irqfd(kvm, gsi, file.fd, -1);
138a67da3beSAsias He 	if (r < 0)
139a67da3beSAsias He 		die_perror("KVM_IRQFD failed");
140a67da3beSAsias He 
141a67da3beSAsias He 	r = ioctl(sdev->vhost_fd, VHOST_SET_VRING_CALL, &file);
142a67da3beSAsias He 	if (r < 0)
143a67da3beSAsias He 		die_perror("VHOST_SET_VRING_CALL failed");
144a67da3beSAsias He 
145a67da3beSAsias He 	if (vq > 0)
146a67da3beSAsias He 		return;
147a67da3beSAsias He 
148a67da3beSAsias He 	r = ioctl(sdev->vhost_fd, VHOST_SCSI_SET_ENDPOINT, &sdev->target);
149a67da3beSAsias He 	if (r != 0)
150a67da3beSAsias He 		die("VHOST_SCSI_SET_ENDPOINT failed %d", errno);
151a67da3beSAsias He }
152a67da3beSAsias He 
153a67da3beSAsias He static void notify_vq_eventfd(struct kvm *kvm, void *dev, u32 vq, u32 efd)
154a67da3beSAsias He {
155a67da3beSAsias He 	struct scsi_dev *sdev = dev;
156a67da3beSAsias He 	struct vhost_vring_file file = {
157a67da3beSAsias He 		.index	= vq,
158a67da3beSAsias He 		.fd	= efd,
159a67da3beSAsias He 	};
160a67da3beSAsias He 	int r;
161a67da3beSAsias He 
162a67da3beSAsias He 	if (sdev->vhost_fd == 0)
163a67da3beSAsias He 		return;
164a67da3beSAsias He 
165a67da3beSAsias He 	r = ioctl(sdev->vhost_fd, VHOST_SET_VRING_KICK, &file);
166a67da3beSAsias He 	if (r < 0)
167a67da3beSAsias He 		die_perror("VHOST_SET_VRING_KICK failed");
168a67da3beSAsias He }
169a67da3beSAsias He 
170a67da3beSAsias He static int notify_vq(struct kvm *kvm, void *dev, u32 vq)
171a67da3beSAsias He {
172a67da3beSAsias He 	return 0;
173a67da3beSAsias He }
174a67da3beSAsias He 
17553fbb17bSJean-Philippe Brucker static struct virt_queue *get_vq(struct kvm *kvm, void *dev, u32 vq)
176a67da3beSAsias He {
177a67da3beSAsias He 	struct scsi_dev *sdev = dev;
178a67da3beSAsias He 
17953fbb17bSJean-Philippe Brucker 	return &sdev->vqs[vq];
180a67da3beSAsias He }
181a67da3beSAsias He 
182a67da3beSAsias He static int get_size_vq(struct kvm *kvm, void *dev, u32 vq)
183a67da3beSAsias He {
184a67da3beSAsias He 	return VIRTIO_SCSI_QUEUE_SIZE;
185a67da3beSAsias He }
186a67da3beSAsias He 
187a67da3beSAsias He static int set_size_vq(struct kvm *kvm, void *dev, u32 vq, int size)
188a67da3beSAsias He {
189a67da3beSAsias He 	return size;
190a67da3beSAsias He }
191a67da3beSAsias He 
19231e0eaccSMartin Radev static unsigned int get_vq_count(struct kvm *kvm, void *dev)
193b98ac591SJean-Philippe Brucker {
194b98ac591SJean-Philippe Brucker 	return NUM_VIRT_QUEUES;
195b98ac591SJean-Philippe Brucker }
196b98ac591SJean-Philippe Brucker 
19715542babSAndre Przywara static struct virtio_ops scsi_dev_virtio_ops = {
198a67da3beSAsias He 	.get_config		= get_config,
199e4730284SMartin Radev 	.get_config_size	= get_config_size,
200a67da3beSAsias He 	.get_host_features	= get_host_features,
201a67da3beSAsias He 	.set_guest_features	= set_guest_features,
202a67da3beSAsias He 	.init_vq		= init_vq,
20353fbb17bSJean-Philippe Brucker 	.get_vq			= get_vq,
204a67da3beSAsias He 	.get_size_vq		= get_size_vq,
205a67da3beSAsias He 	.set_size_vq		= set_size_vq,
20695242e44SJean-Philippe Brucker 	.notify_status		= notify_status,
207a67da3beSAsias He 	.notify_vq		= notify_vq,
208a67da3beSAsias He 	.notify_vq_gsi		= notify_vq_gsi,
209a67da3beSAsias He 	.notify_vq_eventfd	= notify_vq_eventfd,
210b98ac591SJean-Philippe Brucker 	.get_vq_count		= get_vq_count,
211a67da3beSAsias He };
212a67da3beSAsias He 
213a67da3beSAsias He static void virtio_scsi_vhost_init(struct kvm *kvm, struct scsi_dev *sdev)
214a67da3beSAsias He {
215a67da3beSAsias He 	struct vhost_memory *mem;
216a67da3beSAsias He 	u64 features;
217a67da3beSAsias He 	int r;
218a67da3beSAsias He 
219a67da3beSAsias He 	sdev->vhost_fd = open("/dev/vhost-scsi", O_RDWR);
220a67da3beSAsias He 	if (sdev->vhost_fd < 0)
221a67da3beSAsias He 		die_perror("Failed openning vhost-scsi device");
222a67da3beSAsias He 
223a67da3beSAsias He 	mem = calloc(1, sizeof(*mem) + sizeof(struct vhost_memory_region));
224a67da3beSAsias He 	if (mem == NULL)
225a67da3beSAsias He 		die("Failed allocating memory for vhost memory map");
226a67da3beSAsias He 
227a67da3beSAsias He 	mem->nregions = 1;
228a67da3beSAsias He 	mem->regions[0] = (struct vhost_memory_region) {
229a67da3beSAsias He 		.guest_phys_addr	= 0,
230a67da3beSAsias He 		.memory_size		= kvm->ram_size,
231a67da3beSAsias He 		.userspace_addr		= (unsigned long)kvm->ram_start,
232a67da3beSAsias He 	};
233a67da3beSAsias He 
234a67da3beSAsias He 	r = ioctl(sdev->vhost_fd, VHOST_SET_OWNER);
235a67da3beSAsias He 	if (r != 0)
236a67da3beSAsias He 		die_perror("VHOST_SET_OWNER failed");
237a67da3beSAsias He 
238a67da3beSAsias He 	r = ioctl(sdev->vhost_fd, VHOST_GET_FEATURES, &features);
239a67da3beSAsias He 	if (r != 0)
240a67da3beSAsias He 		die_perror("VHOST_GET_FEATURES failed");
241a67da3beSAsias He 
242a67da3beSAsias He 	r = ioctl(sdev->vhost_fd, VHOST_SET_FEATURES, &features);
243a67da3beSAsias He 	if (r != 0)
244a67da3beSAsias He 		die_perror("VHOST_SET_FEATURES failed");
245a67da3beSAsias He 	r = ioctl(sdev->vhost_fd, VHOST_SET_MEM_TABLE, mem);
246a67da3beSAsias He 	if (r != 0)
247a67da3beSAsias He 		die_perror("VHOST_SET_MEM_TABLE failed");
248a67da3beSAsias He 
249a67da3beSAsias He 	sdev->vdev.use_vhost = true;
250a67da3beSAsias He 
251a67da3beSAsias He 	free(mem);
252a67da3beSAsias He }
253a67da3beSAsias He 
254a67da3beSAsias He 
255a67da3beSAsias He static int virtio_scsi_init_one(struct kvm *kvm, struct disk_image *disk)
256a67da3beSAsias He {
257a67da3beSAsias He 	struct scsi_dev *sdev;
258db927775SAlexandru Elisei 	int r;
259a67da3beSAsias He 
260a67da3beSAsias He 	if (!disk)
261a67da3beSAsias He 		return -EINVAL;
262a67da3beSAsias He 
263a67da3beSAsias He 	sdev = calloc(1, sizeof(struct scsi_dev));
264a67da3beSAsias He 	if (sdev == NULL)
265a67da3beSAsias He 		return -ENOMEM;
266a67da3beSAsias He 
267a67da3beSAsias He 	*sdev = (struct scsi_dev) {
268a67da3beSAsias He 		.kvm			= kvm,
269a67da3beSAsias He 	};
27005755b29SAndre Przywara 	strlcpy((char *)&sdev->target.vhost_wwpn, disk->wwpn, sizeof(sdev->target.vhost_wwpn));
271a67da3beSAsias He 	sdev->target.vhost_tpgt = strtol(disk->tpgt, NULL, 0);
272a67da3beSAsias He 
273db927775SAlexandru Elisei 	list_add_tail(&sdev->list, &sdevs);
274db927775SAlexandru Elisei 
275db927775SAlexandru Elisei 	r = virtio_init(kvm, sdev, &sdev->vdev, &scsi_dev_virtio_ops,
276d97dadecSWill Deacon 			VIRTIO_DEFAULT_TRANS(kvm), PCI_DEVICE_ID_VIRTIO_SCSI,
277ae06ce71SWill Deacon 			VIRTIO_ID_SCSI, PCI_CLASS_BLK);
278db927775SAlexandru Elisei 	if (r < 0)
279db927775SAlexandru Elisei 		return r;
280a67da3beSAsias He 
281a67da3beSAsias He 	virtio_scsi_vhost_init(kvm, sdev);
282a67da3beSAsias He 
283a67da3beSAsias He 	if (compat_id == -1)
284a67da3beSAsias He 		compat_id = virtio_compat_add_message("virtio-scsi", "CONFIG_VIRTIO_SCSI");
285a67da3beSAsias He 
286a67da3beSAsias He 	return 0;
287a67da3beSAsias He }
288a67da3beSAsias He 
289a67da3beSAsias He static int virtio_scsi_exit_one(struct kvm *kvm, struct scsi_dev *sdev)
290a67da3beSAsias He {
291a67da3beSAsias He 	int r;
292a67da3beSAsias He 
293a67da3beSAsias He 	r = ioctl(sdev->vhost_fd, VHOST_SCSI_CLEAR_ENDPOINT, &sdev->target);
294a67da3beSAsias He 	if (r != 0)
295a67da3beSAsias He 		die("VHOST_SCSI_CLEAR_ENDPOINT failed %d", errno);
296a67da3beSAsias He 
297a67da3beSAsias He 	list_del(&sdev->list);
298a67da3beSAsias He 	free(sdev);
299a67da3beSAsias He 
300a67da3beSAsias He 	return 0;
301a67da3beSAsias He }
302a67da3beSAsias He 
303a67da3beSAsias He int virtio_scsi_init(struct kvm *kvm)
304a67da3beSAsias He {
305a67da3beSAsias He 	int i, r = 0;
306a67da3beSAsias He 
307a67da3beSAsias He 	for (i = 0; i < kvm->nr_disks; i++) {
308a67da3beSAsias He 		if (!kvm->disks[i]->wwpn)
309a67da3beSAsias He 			continue;
310a67da3beSAsias He 		r = virtio_scsi_init_one(kvm, kvm->disks[i]);
311a67da3beSAsias He 		if (r < 0)
312a67da3beSAsias He 			goto cleanup;
313a67da3beSAsias He 	}
314a67da3beSAsias He 
315a67da3beSAsias He 	return 0;
316a67da3beSAsias He cleanup:
317db927775SAlexandru Elisei 	virtio_scsi_exit(kvm);
318db927775SAlexandru Elisei 	return r;
319a67da3beSAsias He }
32049a8afd1SSasha Levin virtio_dev_init(virtio_scsi_init);
321a67da3beSAsias He 
322a67da3beSAsias He int virtio_scsi_exit(struct kvm *kvm)
323a67da3beSAsias He {
324a67da3beSAsias He 	while (!list_empty(&sdevs)) {
325a67da3beSAsias He 		struct scsi_dev *sdev;
326a67da3beSAsias He 
327a67da3beSAsias He 		sdev = list_first_entry(&sdevs, struct scsi_dev, list);
328a67da3beSAsias He 		virtio_scsi_exit_one(kvm, sdev);
329a67da3beSAsias He 	}
330a67da3beSAsias He 
331a67da3beSAsias He 	return 0;
332a67da3beSAsias He }
33349a8afd1SSasha Levin virtio_dev_exit(virtio_scsi_exit);
334