xref: /openbmc/qemu/hw/scsi/vhost-scsi.c (revision a9ded601)
1 /*
2  * vhost_scsi host device
3  *
4  * Copyright IBM, Corp. 2011
5  *
6  * Authors:
7  *  Stefan Hajnoczi   <stefanha@linux.vnet.ibm.com>
8  *
9  * Changes for QEMU mainline + tcm_vhost kernel upstream:
10  *  Nicholas Bellinger <nab@risingtidesystems.com>
11  *
12  * This work is licensed under the terms of the GNU LGPL, version 2 or later.
13  * See the COPYING.LIB file in the top-level directory.
14  *
15  */
16 
17 #include "qemu/osdep.h"
18 #include <linux/vhost.h>
19 #include <sys/ioctl.h>
20 #include "qapi/error.h"
21 #include "qemu/error-report.h"
22 #include "qemu/queue.h"
23 #include "monitor/monitor.h"
24 #include "migration/blocker.h"
25 #include "hw/virtio/vhost-scsi.h"
26 #include "hw/virtio/vhost.h"
27 #include "hw/virtio/virtio-scsi.h"
28 #include "hw/virtio/virtio-bus.h"
29 #include "hw/virtio/virtio-access.h"
30 #include "hw/fw-path-provider.h"
31 #include "qemu/cutils.h"
32 
33 /* Features supported by host kernel. */
34 static const int kernel_feature_bits[] = {
35     VIRTIO_F_NOTIFY_ON_EMPTY,
36     VIRTIO_RING_F_INDIRECT_DESC,
37     VIRTIO_RING_F_EVENT_IDX,
38     VIRTIO_SCSI_F_HOTPLUG,
39     VHOST_INVALID_FEATURE_BIT
40 };
41 
42 static int vhost_scsi_set_endpoint(VHostSCSI *s)
43 {
44     VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
45     VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s);
46     const VhostOps *vhost_ops = vsc->dev.vhost_ops;
47     struct vhost_scsi_target backend;
48     int ret;
49 
50     memset(&backend, 0, sizeof(backend));
51     pstrcpy(backend.vhost_wwpn, sizeof(backend.vhost_wwpn), vs->conf.wwpn);
52     ret = vhost_ops->vhost_scsi_set_endpoint(&vsc->dev, &backend);
53     if (ret < 0) {
54         return -errno;
55     }
56     return 0;
57 }
58 
59 static void vhost_scsi_clear_endpoint(VHostSCSI *s)
60 {
61     VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
62     VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s);
63     struct vhost_scsi_target backend;
64     const VhostOps *vhost_ops = vsc->dev.vhost_ops;
65 
66     memset(&backend, 0, sizeof(backend));
67     pstrcpy(backend.vhost_wwpn, sizeof(backend.vhost_wwpn), vs->conf.wwpn);
68     vhost_ops->vhost_scsi_clear_endpoint(&vsc->dev, &backend);
69 }
70 
71 static int vhost_scsi_start(VHostSCSI *s)
72 {
73     int ret, abi_version;
74     VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s);
75     const VhostOps *vhost_ops = vsc->dev.vhost_ops;
76 
77     ret = vhost_ops->vhost_scsi_get_abi_version(&vsc->dev, &abi_version);
78     if (ret < 0) {
79         return -errno;
80     }
81     if (abi_version > VHOST_SCSI_ABI_VERSION) {
82         error_report("vhost-scsi: The running tcm_vhost kernel abi_version:"
83                      " %d is greater than vhost_scsi userspace supports: %d,"
84                      " please upgrade your version of QEMU", abi_version,
85                      VHOST_SCSI_ABI_VERSION);
86         return -ENOSYS;
87     }
88 
89     ret = vhost_scsi_common_start(vsc);
90     if (ret < 0) {
91         return ret;
92     }
93 
94     ret = vhost_scsi_set_endpoint(s);
95     if (ret < 0) {
96         error_report("Error setting vhost-scsi endpoint");
97         vhost_scsi_common_stop(vsc);
98     }
99 
100     return ret;
101 }
102 
103 static void vhost_scsi_stop(VHostSCSI *s)
104 {
105     VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s);
106 
107     vhost_scsi_clear_endpoint(s);
108     vhost_scsi_common_stop(vsc);
109 }
110 
111 static void vhost_scsi_set_status(VirtIODevice *vdev, uint8_t val)
112 {
113     VHostSCSI *s = VHOST_SCSI(vdev);
114     VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s);
115     bool start = (val & VIRTIO_CONFIG_S_DRIVER_OK);
116 
117     if (vsc->dev.started == start) {
118         return;
119     }
120 
121     if (start) {
122         int ret;
123 
124         ret = vhost_scsi_start(s);
125         if (ret < 0) {
126             error_report("unable to start vhost-scsi: %s", strerror(-ret));
127             exit(1);
128         }
129     } else {
130         vhost_scsi_stop(s);
131     }
132 }
133 
134 static void vhost_dummy_handle_output(VirtIODevice *vdev, VirtQueue *vq)
135 {
136 }
137 
138 static void vhost_scsi_realize(DeviceState *dev, Error **errp)
139 {
140     VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev);
141     VHostSCSICommon *vsc = VHOST_SCSI_COMMON(dev);
142     Error *err = NULL;
143     int vhostfd = -1;
144     int ret;
145 
146     if (!vs->conf.wwpn) {
147         error_setg(errp, "vhost-scsi: missing wwpn");
148         return;
149     }
150 
151     if (vs->conf.vhostfd) {
152         vhostfd = monitor_fd_param(cur_mon, vs->conf.vhostfd, errp);
153         if (vhostfd == -1) {
154             error_prepend(errp, "vhost-scsi: unable to parse vhostfd: ");
155             return;
156         }
157     } else {
158         vhostfd = open("/dev/vhost-scsi", O_RDWR);
159         if (vhostfd < 0) {
160             error_setg(errp, "vhost-scsi: open vhost char device failed: %s",
161                        strerror(errno));
162             return;
163         }
164     }
165 
166     virtio_scsi_common_realize(dev,
167                                vhost_dummy_handle_output,
168                                vhost_dummy_handle_output,
169                                vhost_dummy_handle_output,
170                                &err);
171     if (err != NULL) {
172         error_propagate(errp, err);
173         goto close_fd;
174     }
175 
176     error_setg(&vsc->migration_blocker,
177                "vhost-scsi does not support migration");
178     migrate_add_blocker(vsc->migration_blocker, &err);
179     if (err) {
180         error_propagate(errp, err);
181         error_free(vsc->migration_blocker);
182         goto close_fd;
183     }
184 
185     vsc->dev.nvqs = VHOST_SCSI_VQ_NUM_FIXED + vs->conf.num_queues;
186     vsc->dev.vqs = g_new(struct vhost_virtqueue, vsc->dev.nvqs);
187     vsc->dev.vq_index = 0;
188     vsc->dev.backend_features = 0;
189 
190     ret = vhost_dev_init(&vsc->dev, (void *)(uintptr_t)vhostfd,
191                          VHOST_BACKEND_TYPE_KERNEL, 0);
192     if (ret < 0) {
193         error_setg(errp, "vhost-scsi: vhost initialization failed: %s",
194                    strerror(-ret));
195         goto free_vqs;
196     }
197 
198     /* At present, channel and lun both are 0 for bootable vhost-scsi disk */
199     vsc->channel = 0;
200     vsc->lun = 0;
201     /* Note: we can also get the minimum tpgt from kernel */
202     vsc->target = vs->conf.boot_tpgt;
203 
204     return;
205 
206  free_vqs:
207     migrate_del_blocker(vsc->migration_blocker);
208     g_free(vsc->dev.vqs);
209  close_fd:
210     close(vhostfd);
211     return;
212 }
213 
214 static void vhost_scsi_unrealize(DeviceState *dev, Error **errp)
215 {
216     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
217     VHostSCSICommon *vsc = VHOST_SCSI_COMMON(dev);
218 
219     migrate_del_blocker(vsc->migration_blocker);
220     error_free(vsc->migration_blocker);
221 
222     /* This will stop vhost backend. */
223     vhost_scsi_set_status(vdev, 0);
224 
225     vhost_dev_cleanup(&vsc->dev);
226     g_free(vsc->dev.vqs);
227 
228     virtio_scsi_common_unrealize(dev, errp);
229 }
230 
231 static Property vhost_scsi_properties[] = {
232     DEFINE_PROP_STRING("vhostfd", VirtIOSCSICommon, conf.vhostfd),
233     DEFINE_PROP_STRING("wwpn", VirtIOSCSICommon, conf.wwpn),
234     DEFINE_PROP_UINT32("boot_tpgt", VirtIOSCSICommon, conf.boot_tpgt, 0),
235     DEFINE_PROP_UINT32("num_queues", VirtIOSCSICommon, conf.num_queues, 1),
236     DEFINE_PROP_UINT32("max_sectors", VirtIOSCSICommon, conf.max_sectors,
237                        0xFFFF),
238     DEFINE_PROP_UINT32("cmd_per_lun", VirtIOSCSICommon, conf.cmd_per_lun, 128),
239     DEFINE_PROP_END_OF_LIST(),
240 };
241 
242 static void vhost_scsi_class_init(ObjectClass *klass, void *data)
243 {
244     DeviceClass *dc = DEVICE_CLASS(klass);
245     VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
246     FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(klass);
247 
248     dc->props = vhost_scsi_properties;
249     set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
250     vdc->realize = vhost_scsi_realize;
251     vdc->unrealize = vhost_scsi_unrealize;
252     vdc->get_features = vhost_scsi_common_get_features;
253     vdc->set_config = vhost_scsi_common_set_config;
254     vdc->set_status = vhost_scsi_set_status;
255     fwc->get_dev_path = vhost_scsi_common_get_fw_dev_path;
256 }
257 
258 static void vhost_scsi_instance_init(Object *obj)
259 {
260     VHostSCSICommon *vsc = VHOST_SCSI_COMMON(obj);
261 
262     vsc->feature_bits = kernel_feature_bits;
263 
264     device_add_bootindex_property(obj, &vsc->bootindex, "bootindex", NULL,
265                                   DEVICE(vsc), NULL);
266 }
267 
268 static const TypeInfo vhost_scsi_info = {
269     .name = TYPE_VHOST_SCSI,
270     .parent = TYPE_VHOST_SCSI_COMMON,
271     .instance_size = sizeof(VHostSCSI),
272     .class_init = vhost_scsi_class_init,
273     .instance_init = vhost_scsi_instance_init,
274     .interfaces = (InterfaceInfo[]) {
275         { TYPE_FW_PATH_PROVIDER },
276         { }
277     },
278 };
279 
280 static void virtio_register_types(void)
281 {
282     type_register_static(&vhost_scsi_info);
283 }
284 
285 type_init(virtio_register_types)
286