]>
Commit | Line | Data |
---|---|---|
5e9be92d NB |
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 | ||
a4ab4792 | 17 | #include "qemu/osdep.h" |
a9c94277 | 18 | #include <linux/vhost.h> |
5e9be92d | 19 | #include <sys/ioctl.h> |
a9c94277 | 20 | #include "qapi/error.h" |
d49b6836 | 21 | #include "qemu/error-report.h" |
5e9be92d NB |
22 | #include "qemu/queue.h" |
23 | #include "monitor/monitor.h" | |
795c40b8 | 24 | #include "migration/blocker.h" |
5e9be92d NB |
25 | #include "hw/virtio/vhost-scsi.h" |
26 | #include "hw/virtio/vhost.h" | |
27 | #include "hw/virtio/virtio-scsi.h" | |
1c819449 | 28 | #include "hw/virtio/virtio-bus.h" |
7ce04255 | 29 | #include "hw/virtio/virtio-access.h" |
1956cf6f | 30 | #include "hw/fw-path-provider.h" |
f348b6d1 | 31 | #include "qemu/cutils.h" |
5e9be92d | 32 | |
2e6d46d7 NN |
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 | ||
5e9be92d NB |
42 | static int vhost_scsi_set_endpoint(VHostSCSI *s) |
43 | { | |
44 | VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); | |
95615ce5 FF |
45 | VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); |
46 | const VhostOps *vhost_ops = vsc->dev.vhost_ops; | |
5e9be92d NB |
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); | |
95615ce5 | 52 | ret = vhost_ops->vhost_scsi_set_endpoint(&vsc->dev, &backend); |
5e9be92d NB |
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); | |
95615ce5 | 62 | VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); |
5e9be92d | 63 | struct vhost_scsi_target backend; |
95615ce5 | 64 | const VhostOps *vhost_ops = vsc->dev.vhost_ops; |
5e9be92d NB |
65 | |
66 | memset(&backend, 0, sizeof(backend)); | |
67 | pstrcpy(backend.vhost_wwpn, sizeof(backend.vhost_wwpn), vs->conf.wwpn); | |
95615ce5 | 68 | vhost_ops->vhost_scsi_clear_endpoint(&vsc->dev, &backend); |
5e9be92d NB |
69 | } |
70 | ||
71 | static int vhost_scsi_start(VHostSCSI *s) | |
72 | { | |
95615ce5 FF |
73 | int ret, abi_version; |
74 | VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); | |
75 | const VhostOps *vhost_ops = vsc->dev.vhost_ops; | |
5e9be92d | 76 | |
95615ce5 | 77 | ret = vhost_ops->vhost_scsi_get_abi_version(&vsc->dev, &abi_version); |
5e9be92d NB |
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:" | |
95615ce5 FF |
83 | " %d is greater than vhost_scsi userspace supports: %d," |
84 | " please upgrade your version of QEMU", abi_version, | |
5e9be92d NB |
85 | VHOST_SCSI_ABI_VERSION); |
86 | return -ENOSYS; | |
87 | } | |
88 | ||
95615ce5 | 89 | ret = vhost_scsi_common_start(vsc); |
5e9be92d NB |
90 | if (ret < 0) { |
91 | return ret; | |
92 | } | |
93 | ||
5e9be92d NB |
94 | ret = vhost_scsi_set_endpoint(s); |
95 | if (ret < 0) { | |
95615ce5 FF |
96 | error_report("Error setting vhost-scsi endpoint"); |
97 | vhost_scsi_common_stop(vsc); | |
5e9be92d NB |
98 | } |
99 | ||
100 | return ret; | |
5e9be92d NB |
101 | } |
102 | ||
103 | static void vhost_scsi_stop(VHostSCSI *s) | |
104 | { | |
95615ce5 | 105 | VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); |
5e9be92d NB |
106 | |
107 | vhost_scsi_clear_endpoint(s); | |
95615ce5 | 108 | vhost_scsi_common_stop(vsc); |
5e9be92d NB |
109 | } |
110 | ||
111 | static void vhost_scsi_set_status(VirtIODevice *vdev, uint8_t val) | |
112 | { | |
95615ce5 FF |
113 | VHostSCSI *s = VHOST_SCSI(vdev); |
114 | VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); | |
5e9be92d NB |
115 | bool start = (val & VIRTIO_CONFIG_S_DRIVER_OK); |
116 | ||
95615ce5 | 117 | if (vsc->dev.started == start) { |
5e9be92d NB |
118 | return; |
119 | } | |
120 | ||
121 | if (start) { | |
122 | int ret; | |
123 | ||
124 | ret = vhost_scsi_start(s); | |
125 | if (ret < 0) { | |
95615ce5 | 126 | error_report("unable to start vhost-scsi: %s", strerror(-ret)); |
5e9be92d NB |
127 | exit(1); |
128 | } | |
129 | } else { | |
130 | vhost_scsi_stop(s); | |
131 | } | |
132 | } | |
133 | ||
91d670fb ML |
134 | static void vhost_dummy_handle_output(VirtIODevice *vdev, VirtQueue *vq) |
135 | { | |
136 | } | |
137 | ||
71a6520b | 138 | static void vhost_scsi_realize(DeviceState *dev, Error **errp) |
5e9be92d | 139 | { |
71a6520b | 140 | VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev); |
95615ce5 | 141 | VHostSCSICommon *vsc = VHOST_SCSI_COMMON(dev); |
71a6520b | 142 | Error *err = NULL; |
5e9be92d NB |
143 | int vhostfd = -1; |
144 | int ret; | |
145 | ||
146 | if (!vs->conf.wwpn) { | |
71a6520b AF |
147 | error_setg(errp, "vhost-scsi: missing wwpn"); |
148 | return; | |
5e9be92d NB |
149 | } |
150 | ||
151 | if (vs->conf.vhostfd) { | |
e43bfd9c | 152 | vhostfd = monitor_fd_param(cur_mon, vs->conf.vhostfd, errp); |
5e9be92d | 153 | if (vhostfd == -1) { |
e43bfd9c | 154 | error_prepend(errp, "vhost-scsi: unable to parse vhostfd: "); |
71a6520b | 155 | return; |
5e9be92d | 156 | } |
81647a65 NN |
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 | } | |
5e9be92d NB |
164 | } |
165 | ||
bf46e67d | 166 | virtio_scsi_common_realize(dev, |
91d670fb | 167 | vhost_dummy_handle_output, |
bf46e67d FZ |
168 | vhost_dummy_handle_output, |
169 | vhost_dummy_handle_output, | |
170 | &err); | |
71a6520b AF |
171 | if (err != NULL) { |
172 | error_propagate(errp, err); | |
fe44dc91 AA |
173 | goto close_fd; |
174 | } | |
175 | ||
95615ce5 | 176 | error_setg(&vsc->migration_blocker, |
fe44dc91 | 177 | "vhost-scsi does not support migration"); |
95615ce5 | 178 | migrate_add_blocker(vsc->migration_blocker, &err); |
fe44dc91 AA |
179 | if (err) { |
180 | error_propagate(errp, err); | |
95615ce5 | 181 | error_free(vsc->migration_blocker); |
fe44dc91 | 182 | goto close_fd; |
5e9be92d NB |
183 | } |
184 | ||
95615ce5 FF |
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; | |
5e9be92d | 189 | |
95615ce5 | 190 | ret = vhost_dev_init(&vsc->dev, (void *)(uintptr_t)vhostfd, |
69e87b32 | 191 | VHOST_BACKEND_TYPE_KERNEL, 0); |
5e9be92d | 192 | if (ret < 0) { |
71a6520b AF |
193 | error_setg(errp, "vhost-scsi: vhost initialization failed: %s", |
194 | strerror(-ret)); | |
fe44dc91 | 195 | goto free_vqs; |
5e9be92d | 196 | } |
5e9be92d | 197 | |
444c7e0d | 198 | /* At present, channel and lun both are 0 for bootable vhost-scsi disk */ |
95615ce5 FF |
199 | vsc->channel = 0; |
200 | vsc->lun = 0; | |
444c7e0d | 201 | /* Note: we can also get the minimum tpgt from kernel */ |
95615ce5 | 202 | vsc->target = vs->conf.boot_tpgt; |
444c7e0d | 203 | |
fe44dc91 AA |
204 | return; |
205 | ||
206 | free_vqs: | |
95615ce5 FF |
207 | migrate_del_blocker(vsc->migration_blocker); |
208 | g_free(vsc->dev.vqs); | |
fe44dc91 AA |
209 | close_fd: |
210 | close(vhostfd); | |
211 | return; | |
5e9be92d NB |
212 | } |
213 | ||
306ec6c3 | 214 | static void vhost_scsi_unrealize(DeviceState *dev, Error **errp) |
5e9be92d | 215 | { |
306ec6c3 | 216 | VirtIODevice *vdev = VIRTIO_DEVICE(dev); |
95615ce5 | 217 | VHostSCSICommon *vsc = VHOST_SCSI_COMMON(dev); |
5e9be92d | 218 | |
95615ce5 FF |
219 | migrate_del_blocker(vsc->migration_blocker); |
220 | error_free(vsc->migration_blocker); | |
5e9be92d NB |
221 | |
222 | /* This will stop vhost backend. */ | |
223 | vhost_scsi_set_status(vdev, 0); | |
224 | ||
95615ce5 FF |
225 | vhost_dev_cleanup(&vsc->dev); |
226 | g_free(vsc->dev.vqs); | |
306ec6c3 AF |
227 | |
228 | virtio_scsi_common_unrealize(dev, errp); | |
5e9be92d NB |
229 | } |
230 | ||
231 | static Property vhost_scsi_properties[] = { | |
95615ce5 FF |
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), | |
5e9be92d NB |
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); | |
1956cf6f | 246 | FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(klass); |
71a6520b | 247 | |
5e9be92d | 248 | dc->props = vhost_scsi_properties; |
125ee0ed | 249 | set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); |
71a6520b | 250 | vdc->realize = vhost_scsi_realize; |
306ec6c3 | 251 | vdc->unrealize = vhost_scsi_unrealize; |
95615ce5 FF |
252 | vdc->get_features = vhost_scsi_common_get_features; |
253 | vdc->set_config = vhost_scsi_common_set_config; | |
5e9be92d | 254 | vdc->set_status = vhost_scsi_set_status; |
95615ce5 | 255 | fwc->get_dev_path = vhost_scsi_common_get_fw_dev_path; |
5e9be92d NB |
256 | } |
257 | ||
d4433f32 GA |
258 | static void vhost_scsi_instance_init(Object *obj) |
259 | { | |
95615ce5 FF |
260 | VHostSCSICommon *vsc = VHOST_SCSI_COMMON(obj); |
261 | ||
262 | vsc->feature_bits = kernel_feature_bits; | |
d4433f32 | 263 | |
95615ce5 FF |
264 | device_add_bootindex_property(obj, &vsc->bootindex, "bootindex", NULL, |
265 | DEVICE(vsc), NULL); | |
d4433f32 GA |
266 | } |
267 | ||
5e9be92d NB |
268 | static const TypeInfo vhost_scsi_info = { |
269 | .name = TYPE_VHOST_SCSI, | |
95615ce5 | 270 | .parent = TYPE_VHOST_SCSI_COMMON, |
5e9be92d NB |
271 | .instance_size = sizeof(VHostSCSI), |
272 | .class_init = vhost_scsi_class_init, | |
d4433f32 | 273 | .instance_init = vhost_scsi_instance_init, |
1956cf6f GA |
274 | .interfaces = (InterfaceInfo[]) { |
275 | { TYPE_FW_PATH_PROVIDER }, | |
276 | { } | |
277 | }, | |
5e9be92d NB |
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) |