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 "hw/virtio/virtio-pmem.h"
19 #include "hw/virtio/virtio-access.h"
20 #include "standard-headers/linux/virtio_ids.h"
21 #include "standard-headers/linux/virtio_pmem.h"
22 #include "block/aio.h"
23 #include "block/thread-pool.h"
25 typedef struct VirtIODeviceRequest
{
26 VirtQueueElement elem
;
30 struct virtio_pmem_req req
;
31 struct virtio_pmem_resp resp
;
32 } VirtIODeviceRequest
;
34 static int worker_cb(void *opaque
)
36 VirtIODeviceRequest
*req_data
= opaque
;
39 /* flush raw backing image */
40 err
= fsync(req_data
->fd
);
45 virtio_stw_p(req_data
->vdev
, &req_data
->resp
.ret
, err
);
50 static void done_cb(void *opaque
, int ret
)
52 VirtIODeviceRequest
*req_data
= opaque
;
53 int len
= iov_from_buf(req_data
->elem
.in_sg
, req_data
->elem
.in_num
, 0,
54 &req_data
->resp
, sizeof(struct virtio_pmem_resp
));
56 /* Callbacks are serialized, so no need to use atomic ops. */
57 virtqueue_push(req_data
->pmem
->rq_vq
, &req_data
->elem
, len
);
58 virtio_notify((VirtIODevice
*)req_data
->pmem
, req_data
->pmem
->rq_vq
);
62 static void virtio_pmem_flush(VirtIODevice
*vdev
, VirtQueue
*vq
)
64 VirtIODeviceRequest
*req_data
;
65 VirtIOPMEM
*pmem
= VIRTIO_PMEM(vdev
);
66 HostMemoryBackend
*backend
= MEMORY_BACKEND(pmem
->memdev
);
67 ThreadPool
*pool
= aio_get_thread_pool(qemu_get_aio_context());
69 req_data
= virtqueue_pop(vq
, sizeof(VirtIODeviceRequest
));
71 virtio_error(vdev
, "virtio-pmem missing request data");
75 if (req_data
->elem
.out_num
< 1 || req_data
->elem
.in_num
< 1) {
76 virtio_error(vdev
, "virtio-pmem request not proper");
80 req_data
->fd
= memory_region_get_fd(&backend
->mr
);
81 req_data
->pmem
= pmem
;
82 req_data
->vdev
= vdev
;
83 thread_pool_submit_aio(pool
, worker_cb
, req_data
, done_cb
, req_data
);
86 static void virtio_pmem_get_config(VirtIODevice
*vdev
, uint8_t *config
)
88 VirtIOPMEM
*pmem
= VIRTIO_PMEM(vdev
);
89 struct virtio_pmem_config
*pmemcfg
= (struct virtio_pmem_config
*) config
;
91 virtio_stq_p(vdev
, &pmemcfg
->start
, pmem
->start
);
92 virtio_stq_p(vdev
, &pmemcfg
->size
, memory_region_size(&pmem
->memdev
->mr
));
95 static uint64_t virtio_pmem_get_features(VirtIODevice
*vdev
, uint64_t features
,
101 static void virtio_pmem_realize(DeviceState
*dev
, Error
**errp
)
103 VirtIODevice
*vdev
= VIRTIO_DEVICE(dev
);
104 VirtIOPMEM
*pmem
= VIRTIO_PMEM(dev
);
107 error_setg(errp
, "virtio-pmem memdev not set");
111 if (host_memory_backend_is_mapped(pmem
->memdev
)) {
112 char *path
= object_get_canonical_path_component(OBJECT(pmem
->memdev
));
113 error_setg(errp
, "can't use already busy memdev: %s", path
);
118 host_memory_backend_set_mapped(pmem
->memdev
, true);
119 virtio_init(vdev
, TYPE_VIRTIO_PMEM
, VIRTIO_ID_PMEM
,
120 sizeof(struct virtio_pmem_config
));
121 pmem
->rq_vq
= virtio_add_queue(vdev
, 128, virtio_pmem_flush
);
124 static void virtio_pmem_unrealize(DeviceState
*dev
, Error
**errp
)
126 VirtIODevice
*vdev
= VIRTIO_DEVICE(dev
);
127 VirtIOPMEM
*pmem
= VIRTIO_PMEM(dev
);
129 host_memory_backend_set_mapped(pmem
->memdev
, false);
130 virtio_cleanup(vdev
);
133 static void virtio_pmem_fill_device_info(const VirtIOPMEM
*pmem
,
134 VirtioPMEMDeviceInfo
*vi
)
136 vi
->memaddr
= pmem
->start
;
137 vi
->size
= pmem
->memdev
? memory_region_size(&pmem
->memdev
->mr
) : 0;
138 vi
->memdev
= object_get_canonical_path(OBJECT(pmem
->memdev
));
141 static MemoryRegion
*virtio_pmem_get_memory_region(VirtIOPMEM
*pmem
,
145 error_setg(errp
, "'%s' property must be set", VIRTIO_PMEM_MEMDEV_PROP
);
149 return &pmem
->memdev
->mr
;
152 static Property virtio_pmem_properties
[] = {
153 DEFINE_PROP_UINT64(VIRTIO_PMEM_ADDR_PROP
, VirtIOPMEM
, start
, 0),
154 DEFINE_PROP_LINK(VIRTIO_PMEM_MEMDEV_PROP
, VirtIOPMEM
, memdev
,
155 TYPE_MEMORY_BACKEND
, HostMemoryBackend
*),
156 DEFINE_PROP_END_OF_LIST(),
159 static void virtio_pmem_class_init(ObjectClass
*klass
, void *data
)
161 DeviceClass
*dc
= DEVICE_CLASS(klass
);
162 VirtioDeviceClass
*vdc
= VIRTIO_DEVICE_CLASS(klass
);
163 VirtIOPMEMClass
*vpc
= VIRTIO_PMEM_CLASS(klass
);
165 dc
->props
= virtio_pmem_properties
;
167 vdc
->realize
= virtio_pmem_realize
;
168 vdc
->unrealize
= virtio_pmem_unrealize
;
169 vdc
->get_config
= virtio_pmem_get_config
;
170 vdc
->get_features
= virtio_pmem_get_features
;
172 vpc
->fill_device_info
= virtio_pmem_fill_device_info
;
173 vpc
->get_memory_region
= virtio_pmem_get_memory_region
;
176 static TypeInfo virtio_pmem_info
= {
177 .name
= TYPE_VIRTIO_PMEM
,
178 .parent
= TYPE_VIRTIO_DEVICE
,
179 .class_size
= sizeof(VirtIOPMEMClass
),
180 .class_init
= virtio_pmem_class_init
,
181 .instance_size
= sizeof(VirtIOPMEM
),
184 static void virtio_register_types(void)
186 type_register_static(&virtio_pmem_info
);
189 type_init(virtio_register_types
)