xref: /openbmc/qemu/hw/virtio/virtio-pmem.c (revision 17735e93719cbb44010aa517a24527b13e5f70d8)
15f503cd9SPankaj Gupta /*
25f503cd9SPankaj Gupta  * Virtio PMEM device
35f503cd9SPankaj Gupta  *
45f503cd9SPankaj Gupta  * Copyright (C) 2018-2019 Red Hat, Inc.
55f503cd9SPankaj Gupta  *
65f503cd9SPankaj Gupta  * Authors:
75f503cd9SPankaj Gupta  *  Pankaj Gupta <pagupta@redhat.com>
85f503cd9SPankaj Gupta  *  David Hildenbrand <david@redhat.com>
95f503cd9SPankaj Gupta  *
105f503cd9SPankaj Gupta  * This work is licensed under the terms of the GNU GPL, version 2.
115f503cd9SPankaj Gupta  * See the COPYING file in the top-level directory.
125f503cd9SPankaj Gupta  */
135f503cd9SPankaj Gupta 
145f503cd9SPankaj Gupta #include "qemu/osdep.h"
155f503cd9SPankaj Gupta #include "qapi/error.h"
165f503cd9SPankaj Gupta #include "qemu/error-report.h"
17e2c1c34fSMarkus Armbruster #include "qemu/iov.h"
18db725815SMarkus Armbruster #include "qemu/main-loop.h"
195f503cd9SPankaj Gupta #include "hw/virtio/virtio-pmem.h"
20a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
215f503cd9SPankaj Gupta #include "hw/virtio/virtio-access.h"
225f503cd9SPankaj Gupta #include "standard-headers/linux/virtio_ids.h"
235f503cd9SPankaj Gupta #include "standard-headers/linux/virtio_pmem.h"
247969dd91SMarkus Armbruster #include "sysemu/hostmem.h"
255f503cd9SPankaj Gupta #include "block/aio.h"
265f503cd9SPankaj Gupta #include "block/thread-pool.h"
2773b12307SPankaj Gupta #include "trace.h"
285f503cd9SPankaj Gupta 
295f503cd9SPankaj Gupta typedef struct VirtIODeviceRequest {
305f503cd9SPankaj Gupta     VirtQueueElement elem;
315f503cd9SPankaj Gupta     int fd;
325f503cd9SPankaj Gupta     VirtIOPMEM *pmem;
335f503cd9SPankaj Gupta     VirtIODevice *vdev;
345f503cd9SPankaj Gupta     struct virtio_pmem_req req;
355f503cd9SPankaj Gupta     struct virtio_pmem_resp resp;
365f503cd9SPankaj Gupta } VirtIODeviceRequest;
375f503cd9SPankaj Gupta 
worker_cb(void * opaque)385f503cd9SPankaj Gupta static int worker_cb(void *opaque)
395f503cd9SPankaj Gupta {
405f503cd9SPankaj Gupta     VirtIODeviceRequest *req_data = opaque;
415f503cd9SPankaj Gupta     int err = 0;
425f503cd9SPankaj Gupta 
435f503cd9SPankaj Gupta     /* flush raw backing image */
445f503cd9SPankaj Gupta     err = fsync(req_data->fd);
4573b12307SPankaj Gupta     trace_virtio_pmem_flush_done(err);
465f503cd9SPankaj Gupta     if (err != 0) {
475f503cd9SPankaj Gupta         err = 1;
485f503cd9SPankaj Gupta     }
495f503cd9SPankaj Gupta 
50d2adda34SWang Liang     virtio_stl_p(req_data->vdev, &req_data->resp.ret, err);
515f503cd9SPankaj Gupta 
525f503cd9SPankaj Gupta     return 0;
535f503cd9SPankaj Gupta }
545f503cd9SPankaj Gupta 
done_cb(void * opaque,int ret)555f503cd9SPankaj Gupta static void done_cb(void *opaque, int ret)
565f503cd9SPankaj Gupta {
575f503cd9SPankaj Gupta     VirtIODeviceRequest *req_data = opaque;
585f503cd9SPankaj Gupta     int len = iov_from_buf(req_data->elem.in_sg, req_data->elem.in_num, 0,
595f503cd9SPankaj Gupta                               &req_data->resp, sizeof(struct virtio_pmem_resp));
605f503cd9SPankaj Gupta 
615f503cd9SPankaj Gupta     /* Callbacks are serialized, so no need to use atomic ops. */
625f503cd9SPankaj Gupta     virtqueue_push(req_data->pmem->rq_vq, &req_data->elem, len);
635f503cd9SPankaj Gupta     virtio_notify((VirtIODevice *)req_data->pmem, req_data->pmem->rq_vq);
6473b12307SPankaj Gupta     trace_virtio_pmem_response();
655f503cd9SPankaj Gupta     g_free(req_data);
665f503cd9SPankaj Gupta }
675f503cd9SPankaj Gupta 
virtio_pmem_flush(VirtIODevice * vdev,VirtQueue * vq)685f503cd9SPankaj Gupta static void virtio_pmem_flush(VirtIODevice *vdev, VirtQueue *vq)
695f503cd9SPankaj Gupta {
705f503cd9SPankaj Gupta     VirtIODeviceRequest *req_data;
715f503cd9SPankaj Gupta     VirtIOPMEM *pmem = VIRTIO_PMEM(vdev);
725f503cd9SPankaj Gupta     HostMemoryBackend *backend = MEMORY_BACKEND(pmem->memdev);
735f503cd9SPankaj Gupta 
7473b12307SPankaj Gupta     trace_virtio_pmem_flush_request();
755f503cd9SPankaj Gupta     req_data = virtqueue_pop(vq, sizeof(VirtIODeviceRequest));
765f503cd9SPankaj Gupta     if (!req_data) {
775f503cd9SPankaj Gupta         virtio_error(vdev, "virtio-pmem missing request data");
785f503cd9SPankaj Gupta         return;
795f503cd9SPankaj Gupta     }
805f503cd9SPankaj Gupta 
815f503cd9SPankaj Gupta     if (req_data->elem.out_num < 1 || req_data->elem.in_num < 1) {
825f503cd9SPankaj Gupta         virtio_error(vdev, "virtio-pmem request not proper");
832bc9e0daSLi Qiang         virtqueue_detach_element(vq, (VirtQueueElement *)req_data, 0);
845f503cd9SPankaj Gupta         g_free(req_data);
855f503cd9SPankaj Gupta         return;
865f503cd9SPankaj Gupta     }
875f503cd9SPankaj Gupta     req_data->fd   = memory_region_get_fd(&backend->mr);
885f503cd9SPankaj Gupta     req_data->pmem = pmem;
895f503cd9SPankaj Gupta     req_data->vdev = vdev;
90aef04fc7SEmanuele Giuseppe Esposito     thread_pool_submit_aio(worker_cb, req_data, done_cb, req_data);
915f503cd9SPankaj Gupta }
925f503cd9SPankaj Gupta 
virtio_pmem_get_config(VirtIODevice * vdev,uint8_t * config)935f503cd9SPankaj Gupta static void virtio_pmem_get_config(VirtIODevice *vdev, uint8_t *config)
945f503cd9SPankaj Gupta {
955f503cd9SPankaj Gupta     VirtIOPMEM *pmem = VIRTIO_PMEM(vdev);
965f503cd9SPankaj Gupta     struct virtio_pmem_config *pmemcfg = (struct virtio_pmem_config *) config;
975f503cd9SPankaj Gupta 
985f503cd9SPankaj Gupta     virtio_stq_p(vdev, &pmemcfg->start, pmem->start);
995f503cd9SPankaj Gupta     virtio_stq_p(vdev, &pmemcfg->size, memory_region_size(&pmem->memdev->mr));
1005f503cd9SPankaj Gupta }
1015f503cd9SPankaj Gupta 
virtio_pmem_get_features(VirtIODevice * vdev,uint64_t features,Error ** errp)1025f503cd9SPankaj Gupta static uint64_t virtio_pmem_get_features(VirtIODevice *vdev, uint64_t features,
1035f503cd9SPankaj Gupta                                         Error **errp)
1045f503cd9SPankaj Gupta {
1055f503cd9SPankaj Gupta     return features;
1065f503cd9SPankaj Gupta }
1075f503cd9SPankaj Gupta 
virtio_pmem_realize(DeviceState * dev,Error ** errp)1085f503cd9SPankaj Gupta static void virtio_pmem_realize(DeviceState *dev, Error **errp)
1095f503cd9SPankaj Gupta {
1105f503cd9SPankaj Gupta     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
1115f503cd9SPankaj Gupta     VirtIOPMEM *pmem = VIRTIO_PMEM(dev);
1125f503cd9SPankaj Gupta 
1135f503cd9SPankaj Gupta     if (!pmem->memdev) {
1145f503cd9SPankaj Gupta         error_setg(errp, "virtio-pmem memdev not set");
1155f503cd9SPankaj Gupta         return;
1165f503cd9SPankaj Gupta     }
1175f503cd9SPankaj Gupta 
1185f503cd9SPankaj Gupta     if (host_memory_backend_is_mapped(pmem->memdev)) {
1197a309cc9SMarkus Armbruster         error_setg(errp, "can't use already busy memdev: %s",
1207a309cc9SMarkus Armbruster                    object_get_canonical_path_component(OBJECT(pmem->memdev)));
1215f503cd9SPankaj Gupta         return;
1225f503cd9SPankaj Gupta     }
1235f503cd9SPankaj Gupta 
1245f503cd9SPankaj Gupta     host_memory_backend_set_mapped(pmem->memdev, true);
1253857cd5cSJonah Palmer     virtio_init(vdev, VIRTIO_ID_PMEM, sizeof(struct virtio_pmem_config));
1265f503cd9SPankaj Gupta     pmem->rq_vq = virtio_add_queue(vdev, 128, virtio_pmem_flush);
1275f503cd9SPankaj Gupta }
1285f503cd9SPankaj Gupta 
virtio_pmem_unrealize(DeviceState * dev)129b69c3c21SMarkus Armbruster static void virtio_pmem_unrealize(DeviceState *dev)
1305f503cd9SPankaj Gupta {
1315f503cd9SPankaj Gupta     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
1325f503cd9SPankaj Gupta     VirtIOPMEM *pmem = VIRTIO_PMEM(dev);
1335f503cd9SPankaj Gupta 
1345f503cd9SPankaj Gupta     host_memory_backend_set_mapped(pmem->memdev, false);
1359861546eSPan Nengyuan     virtio_delete_queue(pmem->rq_vq);
1365f503cd9SPankaj Gupta     virtio_cleanup(vdev);
1375f503cd9SPankaj Gupta }
1385f503cd9SPankaj Gupta 
virtio_pmem_fill_device_info(const VirtIOPMEM * pmem,VirtioPMEMDeviceInfo * vi)1395f503cd9SPankaj Gupta static void virtio_pmem_fill_device_info(const VirtIOPMEM *pmem,
1405f503cd9SPankaj Gupta                                          VirtioPMEMDeviceInfo *vi)
1415f503cd9SPankaj Gupta {
1425f503cd9SPankaj Gupta     vi->memaddr = pmem->start;
1437b8a8474SPankaj Gupta     vi->size    = memory_region_size(&pmem->memdev->mr);
1445f503cd9SPankaj Gupta     vi->memdev  = object_get_canonical_path(OBJECT(pmem->memdev));
1455f503cd9SPankaj Gupta }
1465f503cd9SPankaj Gupta 
virtio_pmem_get_memory_region(VirtIOPMEM * pmem,Error ** errp)1475f503cd9SPankaj Gupta static MemoryRegion *virtio_pmem_get_memory_region(VirtIOPMEM *pmem,
1485f503cd9SPankaj Gupta                                                    Error **errp)
1495f503cd9SPankaj Gupta {
150*2d7f1081SMaciej S. Szmigiero     if (!pmem->memdev) {
151*2d7f1081SMaciej S. Szmigiero         error_setg(errp, "'%s' property must be set", VIRTIO_PMEM_MEMDEV_PROP);
152*2d7f1081SMaciej S. Szmigiero         return NULL;
153*2d7f1081SMaciej S. Szmigiero     }
1545f503cd9SPankaj Gupta 
1555f503cd9SPankaj Gupta     return &pmem->memdev->mr;
1565f503cd9SPankaj Gupta }
1575f503cd9SPankaj Gupta 
1585f503cd9SPankaj Gupta static Property virtio_pmem_properties[] = {
1595f503cd9SPankaj Gupta     DEFINE_PROP_UINT64(VIRTIO_PMEM_ADDR_PROP, VirtIOPMEM, start, 0),
1605f503cd9SPankaj Gupta     DEFINE_PROP_LINK(VIRTIO_PMEM_MEMDEV_PROP, VirtIOPMEM, memdev,
1615f503cd9SPankaj Gupta                      TYPE_MEMORY_BACKEND, HostMemoryBackend *),
1625f503cd9SPankaj Gupta     DEFINE_PROP_END_OF_LIST(),
1635f503cd9SPankaj Gupta };
1645f503cd9SPankaj Gupta 
virtio_pmem_class_init(ObjectClass * klass,void * data)1655f503cd9SPankaj Gupta static void virtio_pmem_class_init(ObjectClass *klass, void *data)
1665f503cd9SPankaj Gupta {
1675f503cd9SPankaj Gupta     DeviceClass *dc = DEVICE_CLASS(klass);
1685f503cd9SPankaj Gupta     VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
1695f503cd9SPankaj Gupta     VirtIOPMEMClass *vpc = VIRTIO_PMEM_CLASS(klass);
1705f503cd9SPankaj Gupta 
1714f67d30bSMarc-André Lureau     device_class_set_props(dc, virtio_pmem_properties);
1725f503cd9SPankaj Gupta 
1735f503cd9SPankaj Gupta     vdc->realize = virtio_pmem_realize;
1745f503cd9SPankaj Gupta     vdc->unrealize = virtio_pmem_unrealize;
1755f503cd9SPankaj Gupta     vdc->get_config = virtio_pmem_get_config;
1765f503cd9SPankaj Gupta     vdc->get_features = virtio_pmem_get_features;
1775f503cd9SPankaj Gupta 
1785f503cd9SPankaj Gupta     vpc->fill_device_info = virtio_pmem_fill_device_info;
1795f503cd9SPankaj Gupta     vpc->get_memory_region = virtio_pmem_get_memory_region;
180d3649bfcSGan Qixin     set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
1815f503cd9SPankaj Gupta }
1825f503cd9SPankaj Gupta 
1835e78c98bSBernhard Beschow static const TypeInfo virtio_pmem_info = {
1845f503cd9SPankaj Gupta     .name          = TYPE_VIRTIO_PMEM,
1855f503cd9SPankaj Gupta     .parent        = TYPE_VIRTIO_DEVICE,
1865f503cd9SPankaj Gupta     .class_size    = sizeof(VirtIOPMEMClass),
1875f503cd9SPankaj Gupta     .class_init    = virtio_pmem_class_init,
1885f503cd9SPankaj Gupta     .instance_size = sizeof(VirtIOPMEM),
1895f503cd9SPankaj Gupta };
1905f503cd9SPankaj Gupta 
virtio_register_types(void)1915f503cd9SPankaj Gupta static void virtio_register_types(void)
1925f503cd9SPankaj Gupta {
1935f503cd9SPankaj Gupta     type_register_static(&virtio_pmem_info);
1945f503cd9SPankaj Gupta }
1955f503cd9SPankaj Gupta 
1965f503cd9SPankaj Gupta type_init(virtio_register_types)
197