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-common.h"
17 #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"
28 typedef struct VirtIODeviceRequest
{
29 VirtQueueElement elem
;
33 struct virtio_pmem_req req
;
34 struct virtio_pmem_resp resp
;
35 } VirtIODeviceRequest
;
37 static int worker_cb(void *opaque
)
39 VirtIODeviceRequest
*req_data
= opaque
;
42 /* flush raw backing image */
43 err
= fsync(req_data
->fd
);
48 virtio_stw_p(req_data
->vdev
, &req_data
->resp
.ret
, err
);
53 static void done_cb(void *opaque
, int ret
)
55 VirtIODeviceRequest
*req_data
= opaque
;
56 int len
= iov_from_buf(req_data
->elem
.in_sg
, req_data
->elem
.in_num
, 0,
57 &req_data
->resp
, sizeof(struct virtio_pmem_resp
));
59 /* Callbacks are serialized, so no need to use atomic ops. */
60 virtqueue_push(req_data
->pmem
->rq_vq
, &req_data
->elem
, len
);
61 virtio_notify((VirtIODevice
*)req_data
->pmem
, req_data
->pmem
->rq_vq
);
65 static void virtio_pmem_flush(VirtIODevice
*vdev
, VirtQueue
*vq
)
67 VirtIODeviceRequest
*req_data
;
68 VirtIOPMEM
*pmem
= VIRTIO_PMEM(vdev
);
69 HostMemoryBackend
*backend
= MEMORY_BACKEND(pmem
->memdev
);
70 ThreadPool
*pool
= aio_get_thread_pool(qemu_get_aio_context());
72 req_data
= virtqueue_pop(vq
, sizeof(VirtIODeviceRequest
));
74 virtio_error(vdev
, "virtio-pmem missing request data");
78 if (req_data
->elem
.out_num
< 1 || req_data
->elem
.in_num
< 1) {
79 virtio_error(vdev
, "virtio-pmem request not proper");
80 virtqueue_detach_element(vq
, (VirtQueueElement
*)req_data
, 0);
84 req_data
->fd
= memory_region_get_fd(&backend
->mr
);
85 req_data
->pmem
= pmem
;
86 req_data
->vdev
= vdev
;
87 thread_pool_submit_aio(pool
, worker_cb
, req_data
, done_cb
, req_data
);
90 static void virtio_pmem_get_config(VirtIODevice
*vdev
, uint8_t *config
)
92 VirtIOPMEM
*pmem
= VIRTIO_PMEM(vdev
);
93 struct virtio_pmem_config
*pmemcfg
= (struct virtio_pmem_config
*) config
;
95 virtio_stq_p(vdev
, &pmemcfg
->start
, pmem
->start
);
96 virtio_stq_p(vdev
, &pmemcfg
->size
, memory_region_size(&pmem
->memdev
->mr
));
99 static uint64_t virtio_pmem_get_features(VirtIODevice
*vdev
, uint64_t features
,
105 static void virtio_pmem_realize(DeviceState
*dev
, Error
**errp
)
107 VirtIODevice
*vdev
= VIRTIO_DEVICE(dev
);
108 VirtIOPMEM
*pmem
= VIRTIO_PMEM(dev
);
111 error_setg(errp
, "virtio-pmem memdev not set");
115 if (host_memory_backend_is_mapped(pmem
->memdev
)) {
116 error_setg(errp
, "can't use already busy memdev: %s",
117 object_get_canonical_path_component(OBJECT(pmem
->memdev
)));
121 host_memory_backend_set_mapped(pmem
->memdev
, true);
122 virtio_init(vdev
, TYPE_VIRTIO_PMEM
, VIRTIO_ID_PMEM
,
123 sizeof(struct virtio_pmem_config
));
124 pmem
->rq_vq
= virtio_add_queue(vdev
, 128, virtio_pmem_flush
);
127 static void virtio_pmem_unrealize(DeviceState
*dev
)
129 VirtIODevice
*vdev
= VIRTIO_DEVICE(dev
);
130 VirtIOPMEM
*pmem
= VIRTIO_PMEM(dev
);
132 host_memory_backend_set_mapped(pmem
->memdev
, false);
133 virtio_delete_queue(pmem
->rq_vq
);
134 virtio_cleanup(vdev
);
137 static void virtio_pmem_fill_device_info(const VirtIOPMEM
*pmem
,
138 VirtioPMEMDeviceInfo
*vi
)
140 vi
->memaddr
= pmem
->start
;
141 vi
->size
= memory_region_size(&pmem
->memdev
->mr
);
142 vi
->memdev
= object_get_canonical_path(OBJECT(pmem
->memdev
));
145 static MemoryRegion
*virtio_pmem_get_memory_region(VirtIOPMEM
*pmem
,
149 error_setg(errp
, "'%s' property must be set", VIRTIO_PMEM_MEMDEV_PROP
);
153 return &pmem
->memdev
->mr
;
156 static Property virtio_pmem_properties
[] = {
157 DEFINE_PROP_UINT64(VIRTIO_PMEM_ADDR_PROP
, VirtIOPMEM
, start
, 0),
158 DEFINE_PROP_LINK(VIRTIO_PMEM_MEMDEV_PROP
, VirtIOPMEM
, memdev
,
159 TYPE_MEMORY_BACKEND
, HostMemoryBackend
*),
160 DEFINE_PROP_END_OF_LIST(),
163 static void virtio_pmem_class_init(ObjectClass
*klass
, void *data
)
165 DeviceClass
*dc
= DEVICE_CLASS(klass
);
166 VirtioDeviceClass
*vdc
= VIRTIO_DEVICE_CLASS(klass
);
167 VirtIOPMEMClass
*vpc
= VIRTIO_PMEM_CLASS(klass
);
169 device_class_set_props(dc
, virtio_pmem_properties
);
171 vdc
->realize
= virtio_pmem_realize
;
172 vdc
->unrealize
= virtio_pmem_unrealize
;
173 vdc
->get_config
= virtio_pmem_get_config
;
174 vdc
->get_features
= virtio_pmem_get_features
;
176 vpc
->fill_device_info
= virtio_pmem_fill_device_info
;
177 vpc
->get_memory_region
= virtio_pmem_get_memory_region
;
180 static TypeInfo virtio_pmem_info
= {
181 .name
= TYPE_VIRTIO_PMEM
,
182 .parent
= TYPE_VIRTIO_DEVICE
,
183 .class_size
= sizeof(VirtIOPMEMClass
),
184 .class_init
= virtio_pmem_class_init
,
185 .instance_size
= sizeof(VirtIOPMEM
),
188 static void virtio_register_types(void)
190 type_register_static(&virtio_pmem_info
);
193 type_init(virtio_register_types
)