4 * Copyright (C) 2018-2019 Red Hat, Inc.
7 * Pankaj Gupta <pagupta@redhat.com>
8 * David Hildenbrand <david@redhat.com>
10 * This work is licensed under the terms of the GNU GPL, version 2.
11 * See the COPYING file in the top-level directory.
14 #include "qemu/osdep.h"
15 #include "qapi/error.h"
16 #include "qemu/error-report.h"
18 #include "qemu/main-loop.h"
19 #include "hw/virtio/virtio-pmem.h"
20 #include "hw/qdev-properties.h"
21 #include "hw/virtio/virtio-access.h"
22 #include "standard-headers/linux/virtio_ids.h"
23 #include "standard-headers/linux/virtio_pmem.h"
24 #include "sysemu/hostmem.h"
25 #include "block/aio.h"
26 #include "block/thread-pool.h"
29 typedef struct VirtIODeviceRequest
{
30 VirtQueueElement elem
;
34 struct virtio_pmem_req req
;
35 struct virtio_pmem_resp resp
;
36 } VirtIODeviceRequest
;
38 static int worker_cb(void *opaque
)
40 VirtIODeviceRequest
*req_data
= opaque
;
43 /* flush raw backing image */
44 err
= fsync(req_data
->fd
);
45 trace_virtio_pmem_flush_done(err
);
50 virtio_stl_p(req_data
->vdev
, &req_data
->resp
.ret
, err
);
55 static void done_cb(void *opaque
, int ret
)
57 VirtIODeviceRequest
*req_data
= opaque
;
58 int len
= iov_from_buf(req_data
->elem
.in_sg
, req_data
->elem
.in_num
, 0,
59 &req_data
->resp
, sizeof(struct virtio_pmem_resp
));
61 /* Callbacks are serialized, so no need to use atomic ops. */
62 virtqueue_push(req_data
->pmem
->rq_vq
, &req_data
->elem
, len
);
63 virtio_notify((VirtIODevice
*)req_data
->pmem
, req_data
->pmem
->rq_vq
);
64 trace_virtio_pmem_response();
68 static void virtio_pmem_flush(VirtIODevice
*vdev
, VirtQueue
*vq
)
70 VirtIODeviceRequest
*req_data
;
71 VirtIOPMEM
*pmem
= VIRTIO_PMEM(vdev
);
72 HostMemoryBackend
*backend
= MEMORY_BACKEND(pmem
->memdev
);
74 trace_virtio_pmem_flush_request();
75 req_data
= virtqueue_pop(vq
, sizeof(VirtIODeviceRequest
));
77 virtio_error(vdev
, "virtio-pmem missing request data");
81 if (req_data
->elem
.out_num
< 1 || req_data
->elem
.in_num
< 1) {
82 virtio_error(vdev
, "virtio-pmem request not proper");
83 virtqueue_detach_element(vq
, (VirtQueueElement
*)req_data
, 0);
87 req_data
->fd
= memory_region_get_fd(&backend
->mr
);
88 req_data
->pmem
= pmem
;
89 req_data
->vdev
= vdev
;
90 thread_pool_submit_aio(worker_cb
, req_data
, done_cb
, req_data
);
93 static void virtio_pmem_get_config(VirtIODevice
*vdev
, uint8_t *config
)
95 VirtIOPMEM
*pmem
= VIRTIO_PMEM(vdev
);
96 struct virtio_pmem_config
*pmemcfg
= (struct virtio_pmem_config
*) config
;
98 virtio_stq_p(vdev
, &pmemcfg
->start
, pmem
->start
);
99 virtio_stq_p(vdev
, &pmemcfg
->size
, memory_region_size(&pmem
->memdev
->mr
));
102 static uint64_t virtio_pmem_get_features(VirtIODevice
*vdev
, uint64_t features
,
108 static void virtio_pmem_realize(DeviceState
*dev
, Error
**errp
)
110 VirtIODevice
*vdev
= VIRTIO_DEVICE(dev
);
111 VirtIOPMEM
*pmem
= VIRTIO_PMEM(dev
);
114 error_setg(errp
, "virtio-pmem memdev not set");
118 if (host_memory_backend_is_mapped(pmem
->memdev
)) {
119 error_setg(errp
, "can't use already busy memdev: %s",
120 object_get_canonical_path_component(OBJECT(pmem
->memdev
)));
124 host_memory_backend_set_mapped(pmem
->memdev
, true);
125 virtio_init(vdev
, VIRTIO_ID_PMEM
, sizeof(struct virtio_pmem_config
));
126 pmem
->rq_vq
= virtio_add_queue(vdev
, 128, virtio_pmem_flush
);
129 static void virtio_pmem_unrealize(DeviceState
*dev
)
131 VirtIODevice
*vdev
= VIRTIO_DEVICE(dev
);
132 VirtIOPMEM
*pmem
= VIRTIO_PMEM(dev
);
134 host_memory_backend_set_mapped(pmem
->memdev
, false);
135 virtio_delete_queue(pmem
->rq_vq
);
136 virtio_cleanup(vdev
);
139 static void virtio_pmem_fill_device_info(const VirtIOPMEM
*pmem
,
140 VirtioPMEMDeviceInfo
*vi
)
142 vi
->memaddr
= pmem
->start
;
143 vi
->size
= memory_region_size(&pmem
->memdev
->mr
);
144 vi
->memdev
= object_get_canonical_path(OBJECT(pmem
->memdev
));
147 static MemoryRegion
*virtio_pmem_get_memory_region(VirtIOPMEM
*pmem
,
151 error_setg(errp
, "'%s' property must be set", VIRTIO_PMEM_MEMDEV_PROP
);
155 return &pmem
->memdev
->mr
;
158 static Property virtio_pmem_properties
[] = {
159 DEFINE_PROP_UINT64(VIRTIO_PMEM_ADDR_PROP
, VirtIOPMEM
, start
, 0),
160 DEFINE_PROP_LINK(VIRTIO_PMEM_MEMDEV_PROP
, VirtIOPMEM
, memdev
,
161 TYPE_MEMORY_BACKEND
, HostMemoryBackend
*),
162 DEFINE_PROP_END_OF_LIST(),
165 static void virtio_pmem_class_init(ObjectClass
*klass
, void *data
)
167 DeviceClass
*dc
= DEVICE_CLASS(klass
);
168 VirtioDeviceClass
*vdc
= VIRTIO_DEVICE_CLASS(klass
);
169 VirtIOPMEMClass
*vpc
= VIRTIO_PMEM_CLASS(klass
);
171 device_class_set_props(dc
, virtio_pmem_properties
);
173 vdc
->realize
= virtio_pmem_realize
;
174 vdc
->unrealize
= virtio_pmem_unrealize
;
175 vdc
->get_config
= virtio_pmem_get_config
;
176 vdc
->get_features
= virtio_pmem_get_features
;
178 vpc
->fill_device_info
= virtio_pmem_fill_device_info
;
179 vpc
->get_memory_region
= virtio_pmem_get_memory_region
;
180 set_bit(DEVICE_CATEGORY_STORAGE
, dc
->categories
);
183 static const TypeInfo virtio_pmem_info
= {
184 .name
= TYPE_VIRTIO_PMEM
,
185 .parent
= TYPE_VIRTIO_DEVICE
,
186 .class_size
= sizeof(VirtIOPMEMClass
),
187 .class_init
= virtio_pmem_class_init
,
188 .instance_size
= sizeof(VirtIOPMEM
),
191 static void virtio_register_types(void)
193 type_register_static(&virtio_pmem_info
);
196 type_init(virtio_register_types
)