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/module.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 "hw/qdev-properties.h" |
32 | #include "qemu/cutils.h" |
33 | #include "sysemu/sysemu.h" |
34 | |
35 | /* Features supported by host kernel. */ |
36 | static const int kernel_feature_bits[] = { |
37 | VIRTIO_F_NOTIFY_ON_EMPTY, |
38 | VIRTIO_RING_F_INDIRECT_DESC, |
39 | VIRTIO_RING_F_EVENT_IDX, |
40 | VIRTIO_SCSI_F_HOTPLUG, |
41 | VHOST_INVALID_FEATURE_BIT |
42 | }; |
43 | |
44 | static int vhost_scsi_set_endpoint(VHostSCSI *s) |
45 | { |
46 | VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); |
47 | VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); |
48 | const VhostOps *vhost_ops = vsc->dev.vhost_ops; |
49 | struct vhost_scsi_target backend; |
50 | int ret; |
51 | |
52 | memset(&backend, 0, sizeof(backend)); |
53 | pstrcpy(backend.vhost_wwpn, sizeof(backend.vhost_wwpn), vs->conf.wwpn); |
54 | ret = vhost_ops->vhost_scsi_set_endpoint(&vsc->dev, &backend); |
55 | if (ret < 0) { |
56 | return -errno; |
57 | } |
58 | return 0; |
59 | } |
60 | |
61 | static void vhost_scsi_clear_endpoint(VHostSCSI *s) |
62 | { |
63 | VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); |
64 | VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); |
65 | struct vhost_scsi_target backend; |
66 | const VhostOps *vhost_ops = vsc->dev.vhost_ops; |
67 | |
68 | memset(&backend, 0, sizeof(backend)); |
69 | pstrcpy(backend.vhost_wwpn, sizeof(backend.vhost_wwpn), vs->conf.wwpn); |
70 | vhost_ops->vhost_scsi_clear_endpoint(&vsc->dev, &backend); |
71 | } |
72 | |
73 | static int vhost_scsi_start(VHostSCSI *s) |
74 | { |
75 | int ret, abi_version; |
76 | VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); |
77 | const VhostOps *vhost_ops = vsc->dev.vhost_ops; |
78 | |
79 | ret = vhost_ops->vhost_scsi_get_abi_version(&vsc->dev, &abi_version); |
80 | if (ret < 0) { |
81 | return -errno; |
82 | } |
83 | if (abi_version > VHOST_SCSI_ABI_VERSION) { |
84 | error_report("vhost-scsi: The running tcm_vhost kernel abi_version:" |
85 | " %d is greater than vhost_scsi userspace supports: %d," |
86 | " please upgrade your version of QEMU" , abi_version, |
87 | VHOST_SCSI_ABI_VERSION); |
88 | return -ENOSYS; |
89 | } |
90 | |
91 | ret = vhost_scsi_common_start(vsc); |
92 | if (ret < 0) { |
93 | return ret; |
94 | } |
95 | |
96 | ret = vhost_scsi_set_endpoint(s); |
97 | if (ret < 0) { |
98 | error_report("Error setting vhost-scsi endpoint" ); |
99 | vhost_scsi_common_stop(vsc); |
100 | } |
101 | |
102 | return ret; |
103 | } |
104 | |
105 | static void vhost_scsi_stop(VHostSCSI *s) |
106 | { |
107 | VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); |
108 | |
109 | vhost_scsi_clear_endpoint(s); |
110 | vhost_scsi_common_stop(vsc); |
111 | } |
112 | |
113 | static void vhost_scsi_set_status(VirtIODevice *vdev, uint8_t val) |
114 | { |
115 | VHostSCSI *s = VHOST_SCSI(vdev); |
116 | VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); |
117 | bool start = (val & VIRTIO_CONFIG_S_DRIVER_OK); |
118 | |
119 | if (!vdev->vm_running) { |
120 | start = false; |
121 | } |
122 | |
123 | if (vsc->dev.started == start) { |
124 | return; |
125 | } |
126 | |
127 | if (start) { |
128 | int ret; |
129 | |
130 | ret = vhost_scsi_start(s); |
131 | if (ret < 0) { |
132 | error_report("unable to start vhost-scsi: %s" , strerror(-ret)); |
133 | exit(1); |
134 | } |
135 | } else { |
136 | vhost_scsi_stop(s); |
137 | } |
138 | } |
139 | |
140 | static void vhost_dummy_handle_output(VirtIODevice *vdev, VirtQueue *vq) |
141 | { |
142 | } |
143 | |
144 | static int vhost_scsi_pre_save(void *opaque) |
145 | { |
146 | VHostSCSICommon *vsc = opaque; |
147 | |
148 | /* At this point, backend must be stopped, otherwise |
149 | * it might keep writing to memory. */ |
150 | assert(!vsc->dev.started); |
151 | |
152 | return 0; |
153 | } |
154 | |
155 | static const VMStateDescription vmstate_virtio_vhost_scsi = { |
156 | .name = "virtio-vhost_scsi" , |
157 | .minimum_version_id = 1, |
158 | .version_id = 1, |
159 | .fields = (VMStateField[]) { |
160 | VMSTATE_VIRTIO_DEVICE, |
161 | VMSTATE_END_OF_LIST() |
162 | }, |
163 | .pre_save = vhost_scsi_pre_save, |
164 | }; |
165 | |
166 | static void vhost_scsi_realize(DeviceState *dev, Error **errp) |
167 | { |
168 | VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev); |
169 | VHostSCSICommon *vsc = VHOST_SCSI_COMMON(dev); |
170 | Error *err = NULL; |
171 | int vhostfd = -1; |
172 | int ret; |
173 | |
174 | if (!vs->conf.wwpn) { |
175 | error_setg(errp, "vhost-scsi: missing wwpn" ); |
176 | return; |
177 | } |
178 | |
179 | if (vs->conf.vhostfd) { |
180 | vhostfd = monitor_fd_param(cur_mon, vs->conf.vhostfd, errp); |
181 | if (vhostfd == -1) { |
182 | error_prepend(errp, "vhost-scsi: unable to parse vhostfd: " ); |
183 | return; |
184 | } |
185 | } else { |
186 | vhostfd = open("/dev/vhost-scsi" , O_RDWR); |
187 | if (vhostfd < 0) { |
188 | error_setg(errp, "vhost-scsi: open vhost char device failed: %s" , |
189 | strerror(errno)); |
190 | return; |
191 | } |
192 | } |
193 | |
194 | virtio_scsi_common_realize(dev, |
195 | vhost_dummy_handle_output, |
196 | vhost_dummy_handle_output, |
197 | vhost_dummy_handle_output, |
198 | &err); |
199 | if (err != NULL) { |
200 | error_propagate(errp, err); |
201 | goto close_fd; |
202 | } |
203 | |
204 | if (!vsc->migratable) { |
205 | error_setg(&vsc->migration_blocker, |
206 | "vhost-scsi does not support migration in all cases. " |
207 | "When external environment supports it (Orchestrator migrates " |
208 | "target SCSI device state or use shared storage over network), " |
209 | "set 'migratable' property to true to enable migration." ); |
210 | migrate_add_blocker(vsc->migration_blocker, &err); |
211 | if (err) { |
212 | error_propagate(errp, err); |
213 | error_free(vsc->migration_blocker); |
214 | goto free_virtio; |
215 | } |
216 | } |
217 | |
218 | vsc->dev.nvqs = VHOST_SCSI_VQ_NUM_FIXED + vs->conf.num_queues; |
219 | vsc->dev.vqs = g_new0(struct vhost_virtqueue, vsc->dev.nvqs); |
220 | vsc->dev.vq_index = 0; |
221 | vsc->dev.backend_features = 0; |
222 | |
223 | ret = vhost_dev_init(&vsc->dev, (void *)(uintptr_t)vhostfd, |
224 | VHOST_BACKEND_TYPE_KERNEL, 0); |
225 | if (ret < 0) { |
226 | error_setg(errp, "vhost-scsi: vhost initialization failed: %s" , |
227 | strerror(-ret)); |
228 | goto free_vqs; |
229 | } |
230 | |
231 | /* At present, channel and lun both are 0 for bootable vhost-scsi disk */ |
232 | vsc->channel = 0; |
233 | vsc->lun = 0; |
234 | /* Note: we can also get the minimum tpgt from kernel */ |
235 | vsc->target = vs->conf.boot_tpgt; |
236 | |
237 | return; |
238 | |
239 | free_vqs: |
240 | if (!vsc->migratable) { |
241 | migrate_del_blocker(vsc->migration_blocker); |
242 | } |
243 | g_free(vsc->dev.vqs); |
244 | free_virtio: |
245 | virtio_scsi_common_unrealize(dev); |
246 | close_fd: |
247 | close(vhostfd); |
248 | return; |
249 | } |
250 | |
251 | static void vhost_scsi_unrealize(DeviceState *dev, Error **errp) |
252 | { |
253 | VirtIODevice *vdev = VIRTIO_DEVICE(dev); |
254 | VHostSCSICommon *vsc = VHOST_SCSI_COMMON(dev); |
255 | struct vhost_virtqueue *vqs = vsc->dev.vqs; |
256 | |
257 | if (!vsc->migratable) { |
258 | migrate_del_blocker(vsc->migration_blocker); |
259 | error_free(vsc->migration_blocker); |
260 | } |
261 | |
262 | /* This will stop vhost backend. */ |
263 | vhost_scsi_set_status(vdev, 0); |
264 | |
265 | vhost_dev_cleanup(&vsc->dev); |
266 | g_free(vqs); |
267 | |
268 | virtio_scsi_common_unrealize(dev); |
269 | } |
270 | |
271 | static Property vhost_scsi_properties[] = { |
272 | DEFINE_PROP_STRING("vhostfd" , VirtIOSCSICommon, conf.vhostfd), |
273 | DEFINE_PROP_STRING("wwpn" , VirtIOSCSICommon, conf.wwpn), |
274 | DEFINE_PROP_UINT32("boot_tpgt" , VirtIOSCSICommon, conf.boot_tpgt, 0), |
275 | DEFINE_PROP_UINT32("num_queues" , VirtIOSCSICommon, conf.num_queues, 1), |
276 | DEFINE_PROP_UINT32("virtqueue_size" , VirtIOSCSICommon, conf.virtqueue_size, |
277 | 128), |
278 | DEFINE_PROP_UINT32("max_sectors" , VirtIOSCSICommon, conf.max_sectors, |
279 | 0xFFFF), |
280 | DEFINE_PROP_UINT32("cmd_per_lun" , VirtIOSCSICommon, conf.cmd_per_lun, 128), |
281 | DEFINE_PROP_BIT64("t10_pi" , VHostSCSICommon, host_features, |
282 | VIRTIO_SCSI_F_T10_PI, |
283 | false), |
284 | DEFINE_PROP_BOOL("migratable" , VHostSCSICommon, migratable, false), |
285 | DEFINE_PROP_END_OF_LIST(), |
286 | }; |
287 | |
288 | static void vhost_scsi_class_init(ObjectClass *klass, void *data) |
289 | { |
290 | DeviceClass *dc = DEVICE_CLASS(klass); |
291 | VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); |
292 | FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(klass); |
293 | |
294 | dc->props = vhost_scsi_properties; |
295 | dc->vmsd = &vmstate_virtio_vhost_scsi; |
296 | set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); |
297 | vdc->realize = vhost_scsi_realize; |
298 | vdc->unrealize = vhost_scsi_unrealize; |
299 | vdc->get_features = vhost_scsi_common_get_features; |
300 | vdc->set_config = vhost_scsi_common_set_config; |
301 | vdc->set_status = vhost_scsi_set_status; |
302 | fwc->get_dev_path = vhost_scsi_common_get_fw_dev_path; |
303 | } |
304 | |
305 | static void vhost_scsi_instance_init(Object *obj) |
306 | { |
307 | VHostSCSICommon *vsc = VHOST_SCSI_COMMON(obj); |
308 | |
309 | vsc->feature_bits = kernel_feature_bits; |
310 | |
311 | device_add_bootindex_property(obj, &vsc->bootindex, "bootindex" , NULL, |
312 | DEVICE(vsc), NULL); |
313 | } |
314 | |
315 | static const TypeInfo vhost_scsi_info = { |
316 | .name = TYPE_VHOST_SCSI, |
317 | .parent = TYPE_VHOST_SCSI_COMMON, |
318 | .instance_size = sizeof(VHostSCSI), |
319 | .class_init = vhost_scsi_class_init, |
320 | .instance_init = vhost_scsi_instance_init, |
321 | .interfaces = (InterfaceInfo[]) { |
322 | { TYPE_FW_PATH_PROVIDER }, |
323 | { } |
324 | }, |
325 | }; |
326 | |
327 | static void virtio_register_types(void) |
328 | { |
329 | type_register_static(&vhost_scsi_info); |
330 | } |
331 | |
332 | type_init(virtio_register_types) |
333 | |