xref: /kvmtool/virtio/scsi.c (revision 13ea439a1f48a19cc31e5c39abb8079aa8deedef)
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 	int				vhost_fd;
28a67da3beSAsias He 	struct virtio_device		vdev;
29a67da3beSAsias He 	struct list_head		list;
30a67da3beSAsias He 	struct kvm			*kvm;
31a67da3beSAsias He };
32a67da3beSAsias He 
33c5ae742bSSasha Levin static u8 *get_config(struct kvm *kvm, void *dev)
34a67da3beSAsias He {
35a67da3beSAsias He 	struct scsi_dev *sdev = dev;
36a67da3beSAsias He 
37c5ae742bSSasha Levin 	return ((u8 *)(&sdev->config));
38a67da3beSAsias He }
39a67da3beSAsias He 
40e4730284SMartin Radev static size_t get_config_size(struct kvm *kvm, void *dev)
41e4730284SMartin Radev {
42e4730284SMartin Radev 	struct scsi_dev *sdev = dev;
43e4730284SMartin Radev 
44e4730284SMartin Radev 	return sizeof(sdev->config);
45e4730284SMartin Radev }
46e4730284SMartin Radev 
473c8f82b8SJean-Philippe Brucker static u64 get_host_features(struct kvm *kvm, void *dev)
48a67da3beSAsias He {
49*13ea439aSJean-Philippe Brucker 	int r;
50*13ea439aSJean-Philippe Brucker 	u64 features;
51*13ea439aSJean-Philippe Brucker 	struct scsi_dev *sdev = dev;
52*13ea439aSJean-Philippe Brucker 
53*13ea439aSJean-Philippe Brucker 	r = ioctl(sdev->vhost_fd, VHOST_GET_FEATURES, &features);
54*13ea439aSJean-Philippe Brucker 	if (r != 0)
55*13ea439aSJean-Philippe Brucker 		die_perror("VHOST_GET_FEATURES failed");
56*13ea439aSJean-Philippe Brucker 
57*13ea439aSJean-Philippe Brucker 	return features &
58*13ea439aSJean-Philippe Brucker 		(1ULL << VIRTIO_RING_F_EVENT_IDX |	\
59*13ea439aSJean-Philippe Brucker 		 1ULL << VIRTIO_RING_F_INDIRECT_DESC |	\
60*13ea439aSJean-Philippe Brucker 		 1ULL << VIRTIO_F_ANY_LAYOUT);
61a67da3beSAsias He }
62a67da3beSAsias He 
6395242e44SJean-Philippe Brucker static void notify_status(struct kvm *kvm, void *dev, u32 status)
6495242e44SJean-Philippe Brucker {
65*13ea439aSJean-Philippe Brucker 	int r;
66867b15ccSJean-Philippe Brucker 	struct scsi_dev *sdev = dev;
67867b15ccSJean-Philippe Brucker 	struct virtio_device *vdev = &sdev->vdev;
68867b15ccSJean-Philippe Brucker 	struct virtio_scsi_config *conf = &sdev->config;
69b17552eeSAndre Przywara 	u16 endian = vdev->endian;
70867b15ccSJean-Philippe Brucker 
7113e7d626SJean-Philippe Brucker 	if (status & VIRTIO__STATUS_START) {
72*13ea439aSJean-Philippe Brucker 		r = ioctl(sdev->vhost_fd, VHOST_SET_FEATURES,
73*13ea439aSJean-Philippe Brucker 			  &sdev->vdev.features);
74*13ea439aSJean-Philippe Brucker 		if (r != 0)
75*13ea439aSJean-Philippe Brucker 			die_perror("VHOST_SET_FEATURES failed");
76*13ea439aSJean-Philippe Brucker 
77*13ea439aSJean-Philippe Brucker 		r = ioctl(sdev->vhost_fd, VHOST_SCSI_SET_ENDPOINT,
7813e7d626SJean-Philippe Brucker 			      &sdev->target);
7913e7d626SJean-Philippe Brucker 		if (r != 0)
8013e7d626SJean-Philippe Brucker 			die("VHOST_SCSI_SET_ENDPOINT failed %d", errno);
8113e7d626SJean-Philippe Brucker 	}
8213e7d626SJean-Philippe Brucker 
83867b15ccSJean-Philippe Brucker 	if (!(status & VIRTIO__STATUS_CONFIG))
84867b15ccSJean-Philippe Brucker 		return;
85867b15ccSJean-Philippe Brucker 
86b17552eeSAndre Przywara 	conf->num_queues = virtio_host_to_guest_u32(endian, NUM_VIRT_QUEUES - 2);
87b17552eeSAndre Przywara 	conf->seg_max = virtio_host_to_guest_u32(endian, VIRTIO_SCSI_CDB_SIZE - 2);
88b17552eeSAndre Przywara 	conf->max_sectors = virtio_host_to_guest_u32(endian, 65535);
89b17552eeSAndre Przywara 	conf->cmd_per_lun = virtio_host_to_guest_u32(endian, 128);
90b17552eeSAndre Przywara 	conf->sense_size = virtio_host_to_guest_u32(endian, VIRTIO_SCSI_SENSE_SIZE);
91b17552eeSAndre Przywara 	conf->cdb_size = virtio_host_to_guest_u32(endian, VIRTIO_SCSI_CDB_SIZE);
92b8420e8dSJean-Philippe Brucker 	conf->max_target = virtio_host_to_guest_u16(endian, 255);
93b17552eeSAndre Przywara 	conf->max_lun = virtio_host_to_guest_u32(endian, 16383);
94b17552eeSAndre Przywara 	conf->event_info_size = virtio_host_to_guest_u32(endian, sizeof(struct virtio_scsi_event));
9595242e44SJean-Philippe Brucker }
9695242e44SJean-Philippe Brucker 
97609ee906SJean-Philippe Brucker static int init_vq(struct kvm *kvm, void *dev, u32 vq)
98a67da3beSAsias He {
99a67da3beSAsias He 	struct scsi_dev *sdev = dev;
100a67da3beSAsias He 	struct virt_queue *queue;
101a67da3beSAsias He 
102a67da3beSAsias He 	compat__remove_message(compat_id);
103a67da3beSAsias He 
104a67da3beSAsias He 	queue		= &sdev->vqs[vq];
105a67da3beSAsias He 
106609ee906SJean-Philippe Brucker 	virtio_init_device_vq(kvm, &sdev->vdev, queue, VIRTIO_SCSI_QUEUE_SIZE);
107a67da3beSAsias He 
108a67da3beSAsias He 	if (sdev->vhost_fd == 0)
109a67da3beSAsias He 		return 0;
110a67da3beSAsias He 
111745221e5SJean-Philippe Brucker 	virtio_vhost_set_vring(kvm, sdev->vhost_fd, vq, queue);
112a67da3beSAsias He 	return 0;
113a67da3beSAsias He }
114a67da3beSAsias He 
115a67da3beSAsias He static void notify_vq_gsi(struct kvm *kvm, void *dev, u32 vq, u32 gsi)
116a67da3beSAsias He {
117a67da3beSAsias He 	struct scsi_dev *sdev = dev;
118a67da3beSAsias He 
119a67da3beSAsias He 	if (sdev->vhost_fd == 0)
120a67da3beSAsias He 		return;
121a67da3beSAsias He 
122029cd2bbSJean-Philippe Brucker 	virtio_vhost_set_vring_call(kvm, sdev->vhost_fd, vq, gsi,
123029cd2bbSJean-Philippe Brucker 				    &sdev->vqs[vq]);
124a67da3beSAsias He }
125a67da3beSAsias He 
126a67da3beSAsias He static void notify_vq_eventfd(struct kvm *kvm, void *dev, u32 vq, u32 efd)
127a67da3beSAsias He {
128a67da3beSAsias He 	struct scsi_dev *sdev = dev;
129a67da3beSAsias He 
130a67da3beSAsias He 	if (sdev->vhost_fd == 0)
131a67da3beSAsias He 		return;
132a67da3beSAsias He 
133676c0c8aSJean-Philippe Brucker 	virtio_vhost_set_vring_kick(kvm, sdev->vhost_fd, vq, efd);
134a67da3beSAsias He }
135a67da3beSAsias He 
136a67da3beSAsias He static int notify_vq(struct kvm *kvm, void *dev, u32 vq)
137a67da3beSAsias He {
138a67da3beSAsias He 	return 0;
139a67da3beSAsias He }
140a67da3beSAsias He 
14153fbb17bSJean-Philippe Brucker static struct virt_queue *get_vq(struct kvm *kvm, void *dev, u32 vq)
142a67da3beSAsias He {
143a67da3beSAsias He 	struct scsi_dev *sdev = dev;
144a67da3beSAsias He 
14553fbb17bSJean-Philippe Brucker 	return &sdev->vqs[vq];
146a67da3beSAsias He }
147a67da3beSAsias He 
148a67da3beSAsias He static int get_size_vq(struct kvm *kvm, void *dev, u32 vq)
149a67da3beSAsias He {
150a67da3beSAsias He 	return VIRTIO_SCSI_QUEUE_SIZE;
151a67da3beSAsias He }
152a67da3beSAsias He 
153a67da3beSAsias He static int set_size_vq(struct kvm *kvm, void *dev, u32 vq, int size)
154a67da3beSAsias He {
155a67da3beSAsias He 	return size;
156a67da3beSAsias He }
157a67da3beSAsias He 
15831e0eaccSMartin Radev static unsigned int get_vq_count(struct kvm *kvm, void *dev)
159b98ac591SJean-Philippe Brucker {
160b98ac591SJean-Philippe Brucker 	return NUM_VIRT_QUEUES;
161b98ac591SJean-Philippe Brucker }
162b98ac591SJean-Philippe Brucker 
16315542babSAndre Przywara static struct virtio_ops scsi_dev_virtio_ops = {
164a67da3beSAsias He 	.get_config		= get_config,
165e4730284SMartin Radev 	.get_config_size	= get_config_size,
166a67da3beSAsias He 	.get_host_features	= get_host_features,
167a67da3beSAsias He 	.init_vq		= init_vq,
16853fbb17bSJean-Philippe Brucker 	.get_vq			= get_vq,
169a67da3beSAsias He 	.get_size_vq		= get_size_vq,
170a67da3beSAsias He 	.set_size_vq		= set_size_vq,
17195242e44SJean-Philippe Brucker 	.notify_status		= notify_status,
172a67da3beSAsias He 	.notify_vq		= notify_vq,
173a67da3beSAsias He 	.notify_vq_gsi		= notify_vq_gsi,
174a67da3beSAsias He 	.notify_vq_eventfd	= notify_vq_eventfd,
175b98ac591SJean-Philippe Brucker 	.get_vq_count		= get_vq_count,
176a67da3beSAsias He };
177a67da3beSAsias He 
178a67da3beSAsias He static void virtio_scsi_vhost_init(struct kvm *kvm, struct scsi_dev *sdev)
179a67da3beSAsias He {
180a67da3beSAsias He 	sdev->vhost_fd = open("/dev/vhost-scsi", O_RDWR);
181a67da3beSAsias He 	if (sdev->vhost_fd < 0)
182a67da3beSAsias He 		die_perror("Failed openning vhost-scsi device");
183a67da3beSAsias He 
184f84ab9ebSJean-Philippe Brucker 	virtio_vhost_init(kvm, sdev->vhost_fd);
185a67da3beSAsias He 
186a67da3beSAsias He 	sdev->vdev.use_vhost = true;
187a67da3beSAsias He }
188a67da3beSAsias He 
189a67da3beSAsias He 
190a67da3beSAsias He static int virtio_scsi_init_one(struct kvm *kvm, struct disk_image *disk)
191a67da3beSAsias He {
192a67da3beSAsias He 	struct scsi_dev *sdev;
193db927775SAlexandru Elisei 	int r;
194a67da3beSAsias He 
195a67da3beSAsias He 	if (!disk)
196a67da3beSAsias He 		return -EINVAL;
197a67da3beSAsias He 
198a67da3beSAsias He 	sdev = calloc(1, sizeof(struct scsi_dev));
199a67da3beSAsias He 	if (sdev == NULL)
200a67da3beSAsias He 		return -ENOMEM;
201a67da3beSAsias He 
202a67da3beSAsias He 	*sdev = (struct scsi_dev) {
203a67da3beSAsias He 		.kvm			= kvm,
204a67da3beSAsias He 	};
20505755b29SAndre Przywara 	strlcpy((char *)&sdev->target.vhost_wwpn, disk->wwpn, sizeof(sdev->target.vhost_wwpn));
206145a86feSJean-Philippe Brucker 	sdev->target.abi_version = VHOST_SCSI_ABI_VERSION;
207a67da3beSAsias He 
208db927775SAlexandru Elisei 	list_add_tail(&sdev->list, &sdevs);
209db927775SAlexandru Elisei 
210db927775SAlexandru Elisei 	r = virtio_init(kvm, sdev, &sdev->vdev, &scsi_dev_virtio_ops,
2119b46ebc5SRajnesh Kanwal 			kvm->cfg.virtio_transport, PCI_DEVICE_ID_VIRTIO_SCSI,
212ae06ce71SWill Deacon 			VIRTIO_ID_SCSI, PCI_CLASS_BLK);
213db927775SAlexandru Elisei 	if (r < 0)
214db927775SAlexandru Elisei 		return r;
215a67da3beSAsias He 
216a67da3beSAsias He 	virtio_scsi_vhost_init(kvm, sdev);
217a67da3beSAsias He 
218a67da3beSAsias He 	if (compat_id == -1)
219a67da3beSAsias He 		compat_id = virtio_compat_add_message("virtio-scsi", "CONFIG_VIRTIO_SCSI");
220a67da3beSAsias He 
221a67da3beSAsias He 	return 0;
222a67da3beSAsias He }
223a67da3beSAsias He 
224a67da3beSAsias He static int virtio_scsi_exit_one(struct kvm *kvm, struct scsi_dev *sdev)
225a67da3beSAsias He {
226a67da3beSAsias He 	int r;
227a67da3beSAsias He 
228a67da3beSAsias He 	r = ioctl(sdev->vhost_fd, VHOST_SCSI_CLEAR_ENDPOINT, &sdev->target);
229a67da3beSAsias He 	if (r != 0)
230a67da3beSAsias He 		die("VHOST_SCSI_CLEAR_ENDPOINT failed %d", errno);
231a67da3beSAsias He 
232a67da3beSAsias He 	list_del(&sdev->list);
233a67da3beSAsias He 	free(sdev);
234a67da3beSAsias He 
235a67da3beSAsias He 	return 0;
236a67da3beSAsias He }
237a67da3beSAsias He 
238a67da3beSAsias He int virtio_scsi_init(struct kvm *kvm)
239a67da3beSAsias He {
240a67da3beSAsias He 	int i, r = 0;
241a67da3beSAsias He 
242a67da3beSAsias He 	for (i = 0; i < kvm->nr_disks; i++) {
243a67da3beSAsias He 		if (!kvm->disks[i]->wwpn)
244a67da3beSAsias He 			continue;
245a67da3beSAsias He 		r = virtio_scsi_init_one(kvm, kvm->disks[i]);
246a67da3beSAsias He 		if (r < 0)
247a67da3beSAsias He 			goto cleanup;
248a67da3beSAsias He 	}
249a67da3beSAsias He 
250a67da3beSAsias He 	return 0;
251a67da3beSAsias He cleanup:
252db927775SAlexandru Elisei 	virtio_scsi_exit(kvm);
253db927775SAlexandru Elisei 	return r;
254a67da3beSAsias He }
25549a8afd1SSasha Levin virtio_dev_init(virtio_scsi_init);
256a67da3beSAsias He 
257a67da3beSAsias He int virtio_scsi_exit(struct kvm *kvm)
258a67da3beSAsias He {
259a67da3beSAsias He 	while (!list_empty(&sdevs)) {
260a67da3beSAsias He 		struct scsi_dev *sdev;
261a67da3beSAsias He 
262a67da3beSAsias He 		sdev = list_first_entry(&sdevs, struct scsi_dev, list);
263a67da3beSAsias He 		virtio_scsi_exit_one(kvm, sdev);
264a67da3beSAsias He 	}
265a67da3beSAsias He 
266a67da3beSAsias He 	return 0;
267a67da3beSAsias He }
26849a8afd1SSasha Levin virtio_dev_exit(virtio_scsi_exit);
269