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