]>
Commit | Line | Data |
---|---|---|
f12c1ebd FF |
1 | /* |
2 | * vhost-user-scsi host device | |
3 | * | |
4 | * Copyright (c) 2016 Nutanix Inc. All rights reserved. | |
5 | * | |
6 | * Author: | |
7 | * Felipe Franciosi <[email protected]> | |
8 | * | |
9 | * This work is largely based on the "vhost-scsi" implementation by: | |
10 | * Stefan Hajnoczi <[email protected]> | |
11 | * Nicholas Bellinger <[email protected]> | |
12 | * | |
13 | * This work is licensed under the terms of the GNU LGPL, version 2 or later. | |
14 | * See the COPYING.LIB file in the top-level directory. | |
15 | * | |
16 | */ | |
17 | ||
18 | #include "qemu/osdep.h" | |
19 | #include "qapi/error.h" | |
20 | #include "qemu/error-report.h" | |
f12c1ebd FF |
21 | #include "qom/object.h" |
22 | #include "hw/fw-path-provider.h" | |
23 | #include "hw/qdev-core.h" | |
a27bd6c7 | 24 | #include "hw/qdev-properties.h" |
f12c1ebd FF |
25 | #include "hw/virtio/vhost.h" |
26 | #include "hw/virtio/vhost-backend.h" | |
27 | #include "hw/virtio/vhost-user-scsi.h" | |
28 | #include "hw/virtio/virtio.h" | |
29 | #include "hw/virtio/virtio-access.h" | |
30 | #include "chardev/char-fe.h" | |
2f780b6a | 31 | #include "sysemu/sysemu.h" |
f12c1ebd FF |
32 | |
33 | /* Features supported by the host application */ | |
34 | static const int user_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 | ||
f0472439 RN |
42 | enum VhostUserProtocolFeature { |
43 | VHOST_USER_PROTOCOL_F_RESET_DEVICE = 13, | |
44 | }; | |
45 | ||
f12c1ebd FF |
46 | static void vhost_user_scsi_set_status(VirtIODevice *vdev, uint8_t status) |
47 | { | |
48 | VHostUserSCSI *s = (VHostUserSCSI *)vdev; | |
49 | VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); | |
50 | bool start = (status & VIRTIO_CONFIG_S_DRIVER_OK) && vdev->vm_running; | |
51 | ||
52 | if (vsc->dev.started == start) { | |
53 | return; | |
54 | } | |
55 | ||
56 | if (start) { | |
57 | int ret; | |
58 | ||
59 | ret = vhost_scsi_common_start(vsc); | |
60 | if (ret < 0) { | |
61 | error_report("unable to start vhost-user-scsi: %s", strerror(-ret)); | |
62 | exit(1); | |
63 | } | |
64 | } else { | |
65 | vhost_scsi_common_stop(vsc); | |
66 | } | |
67 | } | |
68 | ||
f0472439 RN |
69 | static void vhost_user_scsi_reset(VirtIODevice *vdev) |
70 | { | |
71 | VHostSCSICommon *vsc = VHOST_SCSI_COMMON(vdev); | |
72 | struct vhost_dev *dev = &vsc->dev; | |
73 | ||
74 | /* | |
75 | * Historically, reset was not implemented so only reset devices | |
76 | * that are expecting it. | |
77 | */ | |
78 | if (!virtio_has_feature(dev->protocol_features, | |
79 | VHOST_USER_PROTOCOL_F_RESET_DEVICE)) { | |
80 | return; | |
81 | } | |
82 | ||
83 | if (dev->vhost_ops->vhost_reset_device) { | |
84 | dev->vhost_ops->vhost_reset_device(dev); | |
85 | } | |
86 | } | |
87 | ||
f12c1ebd FF |
88 | static void vhost_dummy_handle_output(VirtIODevice *vdev, VirtQueue *vq) |
89 | { | |
90 | } | |
91 | ||
92 | static void vhost_user_scsi_realize(DeviceState *dev, Error **errp) | |
93 | { | |
94 | VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev); | |
95 | VHostUserSCSI *s = VHOST_USER_SCSI(dev); | |
96 | VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); | |
386cff49 | 97 | struct vhost_virtqueue *vqs = NULL; |
f12c1ebd FF |
98 | Error *err = NULL; |
99 | int ret; | |
100 | ||
101 | if (!vs->conf.chardev.chr) { | |
102 | error_setg(errp, "vhost-user-scsi: missing chardev"); | |
103 | return; | |
104 | } | |
105 | ||
106 | virtio_scsi_common_realize(dev, vhost_dummy_handle_output, | |
107 | vhost_dummy_handle_output, | |
108 | vhost_dummy_handle_output, &err); | |
109 | if (err != NULL) { | |
110 | error_propagate(errp, err); | |
111 | return; | |
112 | } | |
113 | ||
0b99f224 | 114 | if (!vhost_user_init(&s->vhost_user, &vs->conf.chardev, errp)) { |
68fa7ca0 | 115 | goto free_virtio; |
4d0cf552 | 116 | } |
4d0cf552 | 117 | |
f12c1ebd | 118 | vsc->dev.nvqs = 2 + vs->conf.num_queues; |
5d4c1ed3 | 119 | vsc->dev.vqs = g_new0(struct vhost_virtqueue, vsc->dev.nvqs); |
f12c1ebd FF |
120 | vsc->dev.vq_index = 0; |
121 | vsc->dev.backend_features = 0; | |
386cff49 | 122 | vqs = vsc->dev.vqs; |
f12c1ebd | 123 | |
0b99f224 | 124 | ret = vhost_dev_init(&vsc->dev, &s->vhost_user, |
f12c1ebd FF |
125 | VHOST_BACKEND_TYPE_USER, 0); |
126 | if (ret < 0) { | |
127 | error_setg(errp, "vhost-user-scsi: vhost initialization failed: %s", | |
128 | strerror(-ret)); | |
68fa7ca0 | 129 | goto free_vhost; |
f12c1ebd FF |
130 | } |
131 | ||
132 | /* Channel and lun both are 0 for bootable vhost-user-scsi disk */ | |
133 | vsc->channel = 0; | |
134 | vsc->lun = 0; | |
135 | vsc->target = vs->conf.boot_tpgt; | |
68fa7ca0 XY |
136 | |
137 | return; | |
138 | ||
139 | free_vhost: | |
140 | vhost_user_cleanup(&s->vhost_user); | |
141 | g_free(vqs); | |
142 | free_virtio: | |
143 | virtio_scsi_common_unrealize(dev); | |
f12c1ebd FF |
144 | } |
145 | ||
b69c3c21 | 146 | static void vhost_user_scsi_unrealize(DeviceState *dev) |
f12c1ebd FF |
147 | { |
148 | VirtIODevice *vdev = VIRTIO_DEVICE(dev); | |
149 | VHostUserSCSI *s = VHOST_USER_SCSI(dev); | |
150 | VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); | |
a5390d93 | 151 | struct vhost_virtqueue *vqs = vsc->dev.vqs; |
f12c1ebd FF |
152 | |
153 | /* This will stop the vhost backend. */ | |
154 | vhost_user_scsi_set_status(vdev, 0); | |
155 | ||
156 | vhost_dev_cleanup(&vsc->dev); | |
a5390d93 | 157 | g_free(vqs); |
f12c1ebd | 158 | |
12e1dc49 | 159 | virtio_scsi_common_unrealize(dev); |
0b99f224 | 160 | vhost_user_cleanup(&s->vhost_user); |
f12c1ebd FF |
161 | } |
162 | ||
f12c1ebd FF |
163 | static Property vhost_user_scsi_properties[] = { |
164 | DEFINE_PROP_CHR("chardev", VirtIOSCSICommon, conf.chardev), | |
165 | DEFINE_PROP_UINT32("boot_tpgt", VirtIOSCSICommon, conf.boot_tpgt, 0), | |
166 | DEFINE_PROP_UINT32("num_queues", VirtIOSCSICommon, conf.num_queues, 1), | |
92003610 DS |
167 | DEFINE_PROP_UINT32("virtqueue_size", VirtIOSCSICommon, conf.virtqueue_size, |
168 | 128), | |
f12c1ebd FF |
169 | DEFINE_PROP_UINT32("max_sectors", VirtIOSCSICommon, conf.max_sectors, |
170 | 0xFFFF), | |
171 | DEFINE_PROP_UINT32("cmd_per_lun", VirtIOSCSICommon, conf.cmd_per_lun, 128), | |
eb5757fc GE |
172 | DEFINE_PROP_BIT64("hotplug", VHostSCSICommon, host_features, |
173 | VIRTIO_SCSI_F_HOTPLUG, | |
174 | true), | |
175 | DEFINE_PROP_BIT64("param_change", VHostSCSICommon, host_features, | |
176 | VIRTIO_SCSI_F_CHANGE, | |
177 | true), | |
f287fdd9 GE |
178 | DEFINE_PROP_BIT64("t10_pi", VHostSCSICommon, host_features, |
179 | VIRTIO_SCSI_F_T10_PI, | |
180 | false), | |
f12c1ebd FF |
181 | DEFINE_PROP_END_OF_LIST(), |
182 | }; | |
183 | ||
184 | static const VMStateDescription vmstate_vhost_scsi = { | |
185 | .name = "virtio-scsi", | |
186 | .minimum_version_id = 1, | |
187 | .version_id = 1, | |
188 | .fields = (VMStateField[]) { | |
189 | VMSTATE_VIRTIO_DEVICE, | |
190 | VMSTATE_END_OF_LIST() | |
191 | }, | |
192 | }; | |
193 | ||
194 | static void vhost_user_scsi_class_init(ObjectClass *klass, void *data) | |
195 | { | |
196 | DeviceClass *dc = DEVICE_CLASS(klass); | |
197 | VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); | |
198 | FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(klass); | |
199 | ||
4f67d30b | 200 | device_class_set_props(dc, vhost_user_scsi_properties); |
f12c1ebd FF |
201 | dc->vmsd = &vmstate_vhost_scsi; |
202 | set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); | |
203 | vdc->realize = vhost_user_scsi_realize; | |
204 | vdc->unrealize = vhost_user_scsi_unrealize; | |
b1110d83 | 205 | vdc->get_features = vhost_scsi_common_get_features; |
f12c1ebd FF |
206 | vdc->set_config = vhost_scsi_common_set_config; |
207 | vdc->set_status = vhost_user_scsi_set_status; | |
f0472439 | 208 | vdc->reset = vhost_user_scsi_reset; |
f12c1ebd FF |
209 | fwc->get_dev_path = vhost_scsi_common_get_fw_dev_path; |
210 | } | |
211 | ||
212 | static void vhost_user_scsi_instance_init(Object *obj) | |
213 | { | |
214 | VHostSCSICommon *vsc = VHOST_SCSI_COMMON(obj); | |
215 | ||
216 | vsc->feature_bits = user_feature_bits; | |
217 | ||
218 | /* Add the bootindex property for this object */ | |
219 | device_add_bootindex_property(obj, &vsc->bootindex, "bootindex", NULL, | |
40c2281c | 220 | DEVICE(vsc)); |
f12c1ebd FF |
221 | } |
222 | ||
223 | static const TypeInfo vhost_user_scsi_info = { | |
224 | .name = TYPE_VHOST_USER_SCSI, | |
225 | .parent = TYPE_VHOST_SCSI_COMMON, | |
226 | .instance_size = sizeof(VHostUserSCSI), | |
227 | .class_init = vhost_user_scsi_class_init, | |
228 | .instance_init = vhost_user_scsi_instance_init, | |
229 | .interfaces = (InterfaceInfo[]) { | |
230 | { TYPE_FW_PATH_PROVIDER }, | |
231 | { } | |
232 | }, | |
233 | }; | |
234 | ||
235 | static void virtio_register_types(void) | |
236 | { | |
237 | type_register_static(&vhost_user_scsi_info); | |
238 | } | |
239 | ||
240 | type_init(virtio_register_types) |