1 /* 2 * Virtio PMEM device 3 * 4 * Copyright (C) 2018-2019 Red Hat, Inc. 5 * 6 * Authors: 7 * Pankaj Gupta <pagupta@redhat.com> 8 * David Hildenbrand <david@redhat.com> 9 * 10 * This work is licensed under the terms of the GNU GPL, version 2. 11 * See the COPYING file in the top-level directory. 12 */ 13 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" 24 25 typedef struct VirtIODeviceRequest { 26 VirtQueueElement elem; 27 int fd; 28 VirtIOPMEM *pmem; 29 VirtIODevice *vdev; 30 struct virtio_pmem_req req; 31 struct virtio_pmem_resp resp; 32 } VirtIODeviceRequest; 33 34 static int worker_cb(void *opaque) 35 { 36 VirtIODeviceRequest *req_data = opaque; 37 int err = 0; 38 39 /* flush raw backing image */ 40 err = fsync(req_data->fd); 41 if (err != 0) { 42 err = 1; 43 } 44 45 virtio_stw_p(req_data->vdev, &req_data->resp.ret, err); 46 47 return 0; 48 } 49 50 static void done_cb(void *opaque, int ret) 51 { 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)); 55 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); 59 g_free(req_data); 60 } 61 62 static void virtio_pmem_flush(VirtIODevice *vdev, VirtQueue *vq) 63 { 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()); 68 69 req_data = virtqueue_pop(vq, sizeof(VirtIODeviceRequest)); 70 if (!req_data) { 71 virtio_error(vdev, "virtio-pmem missing request data"); 72 return; 73 } 74 75 if (req_data->elem.out_num < 1 || req_data->elem.in_num < 1) { 76 virtio_error(vdev, "virtio-pmem request not proper"); 77 g_free(req_data); 78 return; 79 } 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); 84 } 85 86 static void virtio_pmem_get_config(VirtIODevice *vdev, uint8_t *config) 87 { 88 VirtIOPMEM *pmem = VIRTIO_PMEM(vdev); 89 struct virtio_pmem_config *pmemcfg = (struct virtio_pmem_config *) config; 90 91 virtio_stq_p(vdev, &pmemcfg->start, pmem->start); 92 virtio_stq_p(vdev, &pmemcfg->size, memory_region_size(&pmem->memdev->mr)); 93 } 94 95 static uint64_t virtio_pmem_get_features(VirtIODevice *vdev, uint64_t features, 96 Error **errp) 97 { 98 return features; 99 } 100 101 static void virtio_pmem_realize(DeviceState *dev, Error **errp) 102 { 103 VirtIODevice *vdev = VIRTIO_DEVICE(dev); 104 VirtIOPMEM *pmem = VIRTIO_PMEM(dev); 105 106 if (!pmem->memdev) { 107 error_setg(errp, "virtio-pmem memdev not set"); 108 return; 109 } 110 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); 114 g_free(path); 115 return; 116 } 117 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); 122 } 123 124 static void virtio_pmem_unrealize(DeviceState *dev, Error **errp) 125 { 126 VirtIODevice *vdev = VIRTIO_DEVICE(dev); 127 VirtIOPMEM *pmem = VIRTIO_PMEM(dev); 128 129 host_memory_backend_set_mapped(pmem->memdev, false); 130 virtio_cleanup(vdev); 131 } 132 133 static void virtio_pmem_fill_device_info(const VirtIOPMEM *pmem, 134 VirtioPMEMDeviceInfo *vi) 135 { 136 vi->memaddr = pmem->start; 137 vi->size = memory_region_size(&pmem->memdev->mr); 138 vi->memdev = object_get_canonical_path(OBJECT(pmem->memdev)); 139 } 140 141 static MemoryRegion *virtio_pmem_get_memory_region(VirtIOPMEM *pmem, 142 Error **errp) 143 { 144 if (!pmem->memdev) { 145 error_setg(errp, "'%s' property must be set", VIRTIO_PMEM_MEMDEV_PROP); 146 return NULL; 147 } 148 149 return &pmem->memdev->mr; 150 } 151 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(), 157 }; 158 159 static void virtio_pmem_class_init(ObjectClass *klass, void *data) 160 { 161 DeviceClass *dc = DEVICE_CLASS(klass); 162 VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); 163 VirtIOPMEMClass *vpc = VIRTIO_PMEM_CLASS(klass); 164 165 dc->props = virtio_pmem_properties; 166 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; 171 172 vpc->fill_device_info = virtio_pmem_fill_device_info; 173 vpc->get_memory_region = virtio_pmem_get_memory_region; 174 } 175 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), 182 }; 183 184 static void virtio_register_types(void) 185 { 186 type_register_static(&virtio_pmem_info); 187 } 188 189 type_init(virtio_register_types) 190