1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * virtio_pmem.c: Virtio pmem Driver 4 * 5 * Discovers persistent memory range information 6 * from host and registers the virtual pmem device 7 * with libnvdimm core. 8 */ 9 #include "virtio_pmem.h" 10 #include "nd.h" 11 12 static struct virtio_device_id id_table[] = { 13 { VIRTIO_ID_PMEM, VIRTIO_DEV_ANY_ID }, 14 { 0 }, 15 }; 16 17 /* Initialize virt queue */ 18 static int init_vq(struct virtio_pmem *vpmem) 19 { 20 /* single vq */ 21 vpmem->req_vq = virtio_find_single_vq(vpmem->vdev, 22 virtio_pmem_host_ack, "flush_queue"); 23 if (IS_ERR(vpmem->req_vq)) 24 return PTR_ERR(vpmem->req_vq); 25 26 spin_lock_init(&vpmem->pmem_lock); 27 INIT_LIST_HEAD(&vpmem->req_list); 28 29 return 0; 30 }; 31 32 static int virtio_pmem_probe(struct virtio_device *vdev) 33 { 34 struct nd_region_desc ndr_desc = {}; 35 int nid = dev_to_node(&vdev->dev); 36 struct nd_region *nd_region; 37 struct virtio_pmem *vpmem; 38 struct resource res; 39 int err = 0; 40 41 if (!vdev->config->get) { 42 dev_err(&vdev->dev, "%s failure: config access disabled\n", 43 __func__); 44 return -EINVAL; 45 } 46 47 vpmem = devm_kzalloc(&vdev->dev, sizeof(*vpmem), GFP_KERNEL); 48 if (!vpmem) { 49 err = -ENOMEM; 50 goto out_err; 51 } 52 53 vpmem->vdev = vdev; 54 vdev->priv = vpmem; 55 err = init_vq(vpmem); 56 if (err) { 57 dev_err(&vdev->dev, "failed to initialize virtio pmem vq's\n"); 58 goto out_err; 59 } 60 61 virtio_cread_le(vpmem->vdev, struct virtio_pmem_config, 62 start, &vpmem->start); 63 virtio_cread_le(vpmem->vdev, struct virtio_pmem_config, 64 size, &vpmem->size); 65 66 res.start = vpmem->start; 67 res.end = vpmem->start + vpmem->size - 1; 68 vpmem->nd_desc.provider_name = "virtio-pmem"; 69 vpmem->nd_desc.module = THIS_MODULE; 70 71 vpmem->nvdimm_bus = nvdimm_bus_register(&vdev->dev, 72 &vpmem->nd_desc); 73 if (!vpmem->nvdimm_bus) { 74 dev_err(&vdev->dev, "failed to register device with nvdimm_bus\n"); 75 err = -ENXIO; 76 goto out_vq; 77 } 78 79 dev_set_drvdata(&vdev->dev, vpmem->nvdimm_bus); 80 81 ndr_desc.res = &res; 82 ndr_desc.numa_node = nid; 83 ndr_desc.flush = async_pmem_flush; 84 set_bit(ND_REGION_PAGEMAP, &ndr_desc.flags); 85 set_bit(ND_REGION_ASYNC, &ndr_desc.flags); 86 nd_region = nvdimm_pmem_region_create(vpmem->nvdimm_bus, &ndr_desc); 87 if (!nd_region) { 88 dev_err(&vdev->dev, "failed to create nvdimm region\n"); 89 err = -ENXIO; 90 goto out_nd; 91 } 92 nd_region->provider_data = dev_to_virtio(nd_region->dev.parent->parent); 93 return 0; 94 out_nd: 95 nvdimm_bus_unregister(vpmem->nvdimm_bus); 96 out_vq: 97 vdev->config->del_vqs(vdev); 98 out_err: 99 return err; 100 } 101 102 static void virtio_pmem_remove(struct virtio_device *vdev) 103 { 104 struct nvdimm_bus *nvdimm_bus = dev_get_drvdata(&vdev->dev); 105 106 nvdimm_bus_unregister(nvdimm_bus); 107 vdev->config->del_vqs(vdev); 108 virtio_reset_device(vdev); 109 } 110 111 static struct virtio_driver virtio_pmem_driver = { 112 .driver.name = KBUILD_MODNAME, 113 .driver.owner = THIS_MODULE, 114 .id_table = id_table, 115 .probe = virtio_pmem_probe, 116 .remove = virtio_pmem_remove, 117 }; 118 119 module_virtio_driver(virtio_pmem_driver); 120 MODULE_DEVICE_TABLE(virtio, id_table); 121 MODULE_DESCRIPTION("Virtio pmem driver"); 122 MODULE_LICENSE("GPL"); 123