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
);
73 ThreadPool
*pool
= aio_get_thread_pool(qemu_get_aio_context());
75 trace_virtio_pmem_flush_request();
76 req_data
= virtqueue_pop(vq
, sizeof(VirtIODeviceRequest
));
78 virtio_error(vdev
, "virtio-pmem missing request data");
82 if (req_data
->elem
.out_num
< 1 || req_data
->elem
.in_num
< 1) {
83 virtio_error(vdev
, "virtio-pmem request not proper");
84 virtqueue_detach_element(vq
, (VirtQueueElement
*)req_data
, 0);
88 req_data
->fd
= memory_region_get_fd(&backend
->mr
);
89 req_data
->pmem
= pmem
;
90 req_data
->vdev
= vdev
;
91 thread_pool_submit_aio(pool
, worker_cb
, req_data
, done_cb
, req_data
);
94 static void virtio_pmem_get_config(VirtIODevice
*vdev
, uint8_t *config
)
96 VirtIOPMEM
*pmem
= VIRTIO_PMEM(vdev
);
97 struct virtio_pmem_config
*pmemcfg
= (struct virtio_pmem_config
*) config
;
99 virtio_stq_p(vdev
, &pmemcfg
->start
, pmem
->start
);
100 virtio_stq_p(vdev
, &pmemcfg
->size
, memory_region_size(&pmem
->memdev
->mr
));
103 static uint64_t virtio_pmem_get_features(VirtIODevice
*vdev
, uint64_t features
,
109 static void virtio_pmem_realize(DeviceState
*dev
, Error
**errp
)
111 VirtIODevice
*vdev
= VIRTIO_DEVICE(dev
);
112 VirtIOPMEM
*pmem
= VIRTIO_PMEM(dev
);
115 error_setg(errp
, "virtio-pmem memdev not set");
119 if (host_memory_backend_is_mapped(pmem
->memdev
)) {
120 error_setg(errp
, "can't use already busy memdev: %s",
121 object_get_canonical_path_component(OBJECT(pmem
->memdev
)));
125 host_memory_backend_set_mapped(pmem
->memdev
, true);
126 virtio_init(vdev
, VIRTIO_ID_PMEM
, sizeof(struct virtio_pmem_config
));
127 pmem
->rq_vq
= virtio_add_queue(vdev
, 128, virtio_pmem_flush
);
130 static void virtio_pmem_unrealize(DeviceState
*dev
)
132 VirtIODevice
*vdev
= VIRTIO_DEVICE(dev
);
133 VirtIOPMEM
*pmem
= VIRTIO_PMEM(dev
);
135 host_memory_backend_set_mapped(pmem
->memdev
, false);
136 virtio_delete_queue(pmem
->rq_vq
);
137 virtio_cleanup(vdev
);
140 static void virtio_pmem_fill_device_info(const VirtIOPMEM
*pmem
,
141 VirtioPMEMDeviceInfo
*vi
)
143 vi
->memaddr
= pmem
->start
;
144 vi
->size
= memory_region_size(&pmem
->memdev
->mr
);
145 vi
->memdev
= object_get_canonical_path(OBJECT(pmem
->memdev
));
148 static MemoryRegion
*virtio_pmem_get_memory_region(VirtIOPMEM
*pmem
,
152 error_setg(errp
, "'%s' property must be set", VIRTIO_PMEM_MEMDEV_PROP
);
156 return &pmem
->memdev
->mr
;
159 static Property virtio_pmem_properties
[] = {
160 DEFINE_PROP_UINT64(VIRTIO_PMEM_ADDR_PROP
, VirtIOPMEM
, start
, 0),
161 DEFINE_PROP_LINK(VIRTIO_PMEM_MEMDEV_PROP
, VirtIOPMEM
, memdev
,
162 TYPE_MEMORY_BACKEND
, HostMemoryBackend
*),
163 DEFINE_PROP_END_OF_LIST(),
166 static void virtio_pmem_class_init(ObjectClass
*klass
, void *data
)
168 DeviceClass
*dc
= DEVICE_CLASS(klass
);
169 VirtioDeviceClass
*vdc
= VIRTIO_DEVICE_CLASS(klass
);
170 VirtIOPMEMClass
*vpc
= VIRTIO_PMEM_CLASS(klass
);
172 device_class_set_props(dc
, virtio_pmem_properties
);
174 vdc
->realize
= virtio_pmem_realize
;
175 vdc
->unrealize
= virtio_pmem_unrealize
;
176 vdc
->get_config
= virtio_pmem_get_config
;
177 vdc
->get_features
= virtio_pmem_get_features
;
179 vpc
->fill_device_info
= virtio_pmem_fill_device_info
;
180 vpc
->get_memory_region
= virtio_pmem_get_memory_region
;
181 set_bit(DEVICE_CATEGORY_STORAGE
, dc
->categories
);
184 static const TypeInfo virtio_pmem_info
= {
185 .name
= TYPE_VIRTIO_PMEM
,
186 .parent
= TYPE_VIRTIO_DEVICE
,
187 .class_size
= sizeof(VirtIOPMEMClass
),
188 .class_init
= virtio_pmem_class_init
,
189 .instance_size
= sizeof(VirtIOPMEM
),
192 static void virtio_register_types(void)
194 type_register_static(&virtio_pmem_info
);
197 type_init(virtio_register_types
)