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
get_config(struct kvm * kvm,void * dev)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
get_config_size(struct kvm * kvm,void * dev)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
get_host_features(struct kvm * kvm,void * dev)473c8f82b8SJean-Philippe Brucker static u64 get_host_features(struct kvm *kvm, void *dev)
48a67da3beSAsias He {
4913ea439aSJean-Philippe Brucker int r;
5013ea439aSJean-Philippe Brucker u64 features;
5113ea439aSJean-Philippe Brucker struct scsi_dev *sdev = dev;
5213ea439aSJean-Philippe Brucker
5313ea439aSJean-Philippe Brucker r = ioctl(sdev->vhost_fd, VHOST_GET_FEATURES, &features);
5413ea439aSJean-Philippe Brucker if (r != 0)
5513ea439aSJean-Philippe Brucker die_perror("VHOST_GET_FEATURES failed");
5613ea439aSJean-Philippe Brucker
5713ea439aSJean-Philippe Brucker return features &
5813ea439aSJean-Philippe Brucker (1ULL << VIRTIO_RING_F_EVENT_IDX | \
5913ea439aSJean-Philippe Brucker 1ULL << VIRTIO_RING_F_INDIRECT_DESC | \
6013ea439aSJean-Philippe Brucker 1ULL << VIRTIO_F_ANY_LAYOUT);
61a67da3beSAsias He }
62a67da3beSAsias He
notify_status(struct kvm * kvm,void * dev,u32 status)6395242e44SJean-Philippe Brucker static void notify_status(struct kvm *kvm, void *dev, u32 status)
6495242e44SJean-Philippe Brucker {
6513ea439aSJean-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*3b1cdcf9SJean-Philippe Brucker r = virtio_vhost_set_features(sdev->vhost_fd, sdev->vdev.features);
7313ea439aSJean-Philippe Brucker if (r != 0)
7413ea439aSJean-Philippe Brucker die_perror("VHOST_SET_FEATURES failed");
7513ea439aSJean-Philippe Brucker
7613ea439aSJean-Philippe Brucker r = ioctl(sdev->vhost_fd, VHOST_SCSI_SET_ENDPOINT,
7713e7d626SJean-Philippe Brucker &sdev->target);
7813e7d626SJean-Philippe Brucker if (r != 0)
7913e7d626SJean-Philippe Brucker die("VHOST_SCSI_SET_ENDPOINT failed %d", errno);
8013e7d626SJean-Philippe Brucker }
8113e7d626SJean-Philippe Brucker
82867b15ccSJean-Philippe Brucker if (!(status & VIRTIO__STATUS_CONFIG))
83867b15ccSJean-Philippe Brucker return;
84867b15ccSJean-Philippe Brucker
85b17552eeSAndre Przywara conf->num_queues = virtio_host_to_guest_u32(endian, NUM_VIRT_QUEUES - 2);
86b17552eeSAndre Przywara conf->seg_max = virtio_host_to_guest_u32(endian, VIRTIO_SCSI_CDB_SIZE - 2);
87b17552eeSAndre Przywara conf->max_sectors = virtio_host_to_guest_u32(endian, 65535);
88b17552eeSAndre Przywara conf->cmd_per_lun = virtio_host_to_guest_u32(endian, 128);
89b17552eeSAndre Przywara conf->sense_size = virtio_host_to_guest_u32(endian, VIRTIO_SCSI_SENSE_SIZE);
90b17552eeSAndre Przywara conf->cdb_size = virtio_host_to_guest_u32(endian, VIRTIO_SCSI_CDB_SIZE);
91b8420e8dSJean-Philippe Brucker conf->max_target = virtio_host_to_guest_u16(endian, 255);
92b17552eeSAndre Przywara conf->max_lun = virtio_host_to_guest_u32(endian, 16383);
93b17552eeSAndre Przywara conf->event_info_size = virtio_host_to_guest_u32(endian, sizeof(struct virtio_scsi_event));
9495242e44SJean-Philippe Brucker }
9595242e44SJean-Philippe Brucker
init_vq(struct kvm * kvm,void * dev,u32 vq)96609ee906SJean-Philippe Brucker static int init_vq(struct kvm *kvm, void *dev, u32 vq)
97a67da3beSAsias He {
98a67da3beSAsias He struct scsi_dev *sdev = dev;
99a67da3beSAsias He struct virt_queue *queue;
100a67da3beSAsias He
101a67da3beSAsias He compat__remove_message(compat_id);
102a67da3beSAsias He
103a67da3beSAsias He queue = &sdev->vqs[vq];
104a67da3beSAsias He
105609ee906SJean-Philippe Brucker virtio_init_device_vq(kvm, &sdev->vdev, queue, VIRTIO_SCSI_QUEUE_SIZE);
106a67da3beSAsias He
107a67da3beSAsias He if (sdev->vhost_fd == 0)
108a67da3beSAsias He return 0;
109a67da3beSAsias He
110745221e5SJean-Philippe Brucker virtio_vhost_set_vring(kvm, sdev->vhost_fd, vq, queue);
111a67da3beSAsias He return 0;
112a67da3beSAsias He }
113a67da3beSAsias He
notify_vq_gsi(struct kvm * kvm,void * dev,u32 vq,u32 gsi)114a67da3beSAsias He static void notify_vq_gsi(struct kvm *kvm, void *dev, u32 vq, u32 gsi)
115a67da3beSAsias He {
116a67da3beSAsias He struct scsi_dev *sdev = dev;
117a67da3beSAsias He
118a67da3beSAsias He if (sdev->vhost_fd == 0)
119a67da3beSAsias He return;
120a67da3beSAsias He
12146aaf3b8SJean-Philippe Brucker virtio_vhost_set_vring_irqfd(kvm, gsi, &sdev->vqs[vq]);
122a67da3beSAsias He }
123a67da3beSAsias He
notify_vq_eventfd(struct kvm * kvm,void * dev,u32 vq,u32 efd)124a67da3beSAsias He static void notify_vq_eventfd(struct kvm *kvm, void *dev, u32 vq, u32 efd)
125a67da3beSAsias He {
126a67da3beSAsias He struct scsi_dev *sdev = dev;
127a67da3beSAsias He
128a67da3beSAsias He if (sdev->vhost_fd == 0)
129a67da3beSAsias He return;
130a67da3beSAsias He
131676c0c8aSJean-Philippe Brucker virtio_vhost_set_vring_kick(kvm, sdev->vhost_fd, vq, efd);
132a67da3beSAsias He }
133a67da3beSAsias He
notify_vq(struct kvm * kvm,void * dev,u32 vq)134a67da3beSAsias He static int notify_vq(struct kvm *kvm, void *dev, u32 vq)
135a67da3beSAsias He {
136a67da3beSAsias He return 0;
137a67da3beSAsias He }
138a67da3beSAsias He
get_vq(struct kvm * kvm,void * dev,u32 vq)13953fbb17bSJean-Philippe Brucker static struct virt_queue *get_vq(struct kvm *kvm, void *dev, u32 vq)
140a67da3beSAsias He {
141a67da3beSAsias He struct scsi_dev *sdev = dev;
142a67da3beSAsias He
14353fbb17bSJean-Philippe Brucker return &sdev->vqs[vq];
144a67da3beSAsias He }
145a67da3beSAsias He
get_size_vq(struct kvm * kvm,void * dev,u32 vq)146a67da3beSAsias He static int get_size_vq(struct kvm *kvm, void *dev, u32 vq)
147a67da3beSAsias He {
148a67da3beSAsias He return VIRTIO_SCSI_QUEUE_SIZE;
149a67da3beSAsias He }
150a67da3beSAsias He
set_size_vq(struct kvm * kvm,void * dev,u32 vq,int size)151a67da3beSAsias He static int set_size_vq(struct kvm *kvm, void *dev, u32 vq, int size)
152a67da3beSAsias He {
153a67da3beSAsias He return size;
154a67da3beSAsias He }
155a67da3beSAsias He
get_vq_count(struct kvm * kvm,void * dev)15631e0eaccSMartin Radev static unsigned int get_vq_count(struct kvm *kvm, void *dev)
157b98ac591SJean-Philippe Brucker {
158b98ac591SJean-Philippe Brucker return NUM_VIRT_QUEUES;
159b98ac591SJean-Philippe Brucker }
160b98ac591SJean-Philippe Brucker
16115542babSAndre Przywara static struct virtio_ops scsi_dev_virtio_ops = {
162a67da3beSAsias He .get_config = get_config,
163e4730284SMartin Radev .get_config_size = get_config_size,
164a67da3beSAsias He .get_host_features = get_host_features,
165a67da3beSAsias He .init_vq = init_vq,
16653fbb17bSJean-Philippe Brucker .get_vq = get_vq,
167a67da3beSAsias He .get_size_vq = get_size_vq,
168a67da3beSAsias He .set_size_vq = set_size_vq,
16995242e44SJean-Philippe Brucker .notify_status = notify_status,
170a67da3beSAsias He .notify_vq = notify_vq,
171a67da3beSAsias He .notify_vq_gsi = notify_vq_gsi,
172a67da3beSAsias He .notify_vq_eventfd = notify_vq_eventfd,
173b98ac591SJean-Philippe Brucker .get_vq_count = get_vq_count,
174a67da3beSAsias He };
175a67da3beSAsias He
virtio_scsi_vhost_init(struct kvm * kvm,struct scsi_dev * sdev)176a67da3beSAsias He static void virtio_scsi_vhost_init(struct kvm *kvm, struct scsi_dev *sdev)
177a67da3beSAsias He {
178a67da3beSAsias He sdev->vhost_fd = open("/dev/vhost-scsi", O_RDWR);
179a67da3beSAsias He if (sdev->vhost_fd < 0)
180a67da3beSAsias He die_perror("Failed openning vhost-scsi device");
181a67da3beSAsias He
182f84ab9ebSJean-Philippe Brucker virtio_vhost_init(kvm, sdev->vhost_fd);
183a67da3beSAsias He
184a67da3beSAsias He sdev->vdev.use_vhost = true;
185a67da3beSAsias He }
186a67da3beSAsias He
187a67da3beSAsias He
virtio_scsi_init_one(struct kvm * kvm,struct disk_image * disk)188a67da3beSAsias He static int virtio_scsi_init_one(struct kvm *kvm, struct disk_image *disk)
189a67da3beSAsias He {
190a67da3beSAsias He struct scsi_dev *sdev;
191db927775SAlexandru Elisei int r;
192a67da3beSAsias He
193a67da3beSAsias He if (!disk)
194a67da3beSAsias He return -EINVAL;
195a67da3beSAsias He
196a67da3beSAsias He sdev = calloc(1, sizeof(struct scsi_dev));
197a67da3beSAsias He if (sdev == NULL)
198a67da3beSAsias He return -ENOMEM;
199a67da3beSAsias He
200a67da3beSAsias He *sdev = (struct scsi_dev) {
201a67da3beSAsias He .kvm = kvm,
202a67da3beSAsias He };
20305755b29SAndre Przywara strlcpy((char *)&sdev->target.vhost_wwpn, disk->wwpn, sizeof(sdev->target.vhost_wwpn));
204145a86feSJean-Philippe Brucker sdev->target.abi_version = VHOST_SCSI_ABI_VERSION;
205a67da3beSAsias He
206db927775SAlexandru Elisei list_add_tail(&sdev->list, &sdevs);
207db927775SAlexandru Elisei
208db927775SAlexandru Elisei r = virtio_init(kvm, sdev, &sdev->vdev, &scsi_dev_virtio_ops,
2099b46ebc5SRajnesh Kanwal kvm->cfg.virtio_transport, PCI_DEVICE_ID_VIRTIO_SCSI,
210ae06ce71SWill Deacon VIRTIO_ID_SCSI, PCI_CLASS_BLK);
211db927775SAlexandru Elisei if (r < 0)
212db927775SAlexandru Elisei return r;
213a67da3beSAsias He
214a67da3beSAsias He virtio_scsi_vhost_init(kvm, sdev);
215a67da3beSAsias He
216a67da3beSAsias He if (compat_id == -1)
21733e026a7SJean-Philippe Brucker compat_id = virtio_compat_add_message("virtio-scsi", "CONFIG_SCSI_VIRTIO");
218a67da3beSAsias He
219a67da3beSAsias He return 0;
220a67da3beSAsias He }
221a67da3beSAsias He
virtio_scsi_exit_one(struct kvm * kvm,struct scsi_dev * sdev)222a67da3beSAsias He static int virtio_scsi_exit_one(struct kvm *kvm, struct scsi_dev *sdev)
223a67da3beSAsias He {
224a67da3beSAsias He int r;
225a67da3beSAsias He
226a67da3beSAsias He r = ioctl(sdev->vhost_fd, VHOST_SCSI_CLEAR_ENDPOINT, &sdev->target);
227a67da3beSAsias He if (r != 0)
228a67da3beSAsias He die("VHOST_SCSI_CLEAR_ENDPOINT failed %d", errno);
229a67da3beSAsias He
230a67da3beSAsias He list_del(&sdev->list);
231a67da3beSAsias He free(sdev);
232a67da3beSAsias He
233a67da3beSAsias He return 0;
234a67da3beSAsias He }
235a67da3beSAsias He
virtio_scsi_init(struct kvm * kvm)236a67da3beSAsias He int virtio_scsi_init(struct kvm *kvm)
237a67da3beSAsias He {
238a67da3beSAsias He int i, r = 0;
239a67da3beSAsias He
240a67da3beSAsias He for (i = 0; i < kvm->nr_disks; i++) {
241a67da3beSAsias He if (!kvm->disks[i]->wwpn)
242a67da3beSAsias He continue;
243a67da3beSAsias He r = virtio_scsi_init_one(kvm, kvm->disks[i]);
244a67da3beSAsias He if (r < 0)
245a67da3beSAsias He goto cleanup;
246a67da3beSAsias He }
247a67da3beSAsias He
248a67da3beSAsias He return 0;
249a67da3beSAsias He cleanup:
250db927775SAlexandru Elisei virtio_scsi_exit(kvm);
251db927775SAlexandru Elisei return r;
252a67da3beSAsias He }
25349a8afd1SSasha Levin virtio_dev_init(virtio_scsi_init);
254a67da3beSAsias He
virtio_scsi_exit(struct kvm * kvm)255a67da3beSAsias He int virtio_scsi_exit(struct kvm *kvm)
256a67da3beSAsias He {
257a67da3beSAsias He while (!list_empty(&sdevs)) {
258a67da3beSAsias He struct scsi_dev *sdev;
259a67da3beSAsias He
260a67da3beSAsias He sdev = list_first_entry(&sdevs, struct scsi_dev, list);
261a67da3beSAsias He virtio_scsi_exit_one(kvm, sdev);
262a67da3beSAsias He }
263a67da3beSAsias He
264a67da3beSAsias He return 0;
265a67da3beSAsias He }
26649a8afd1SSasha Levin virtio_dev_exit(virtio_scsi_exit);
267