Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | // SPDX-License-Identifier: GPL-2.0 /* * virtio_pmem.c: Virtio pmem Driver * * Discovers persistent memory range information * from host and registers the virtual pmem device * with libnvdimm core. */ #include "virtio_pmem.h" #include "nd.h" static struct virtio_device_id id_table[] = { { VIRTIO_ID_PMEM, VIRTIO_DEV_ANY_ID }, { 0 }, }; /* Initialize virt queue */ static int init_vq(struct virtio_pmem *vpmem) { /* single vq */ vpmem->req_vq = virtio_find_single_vq(vpmem->vdev, virtio_pmem_host_ack, "flush_queue"); if (IS_ERR(vpmem->req_vq)) return PTR_ERR(vpmem->req_vq); spin_lock_init(&vpmem->pmem_lock); INIT_LIST_HEAD(&vpmem->req_list); return 0; }; static int virtio_pmem_validate(struct virtio_device *vdev) { struct virtio_shm_region shm_reg; if (virtio_has_feature(vdev, VIRTIO_PMEM_F_SHMEM_REGION) && !virtio_get_shm_region(vdev, &shm_reg, (u8)VIRTIO_PMEM_SHMEM_REGION_ID) ) { dev_notice(&vdev->dev, "failed to get shared memory region %d\n", VIRTIO_PMEM_SHMEM_REGION_ID); __virtio_clear_bit(vdev, VIRTIO_PMEM_F_SHMEM_REGION); } return 0; } static int virtio_pmem_probe(struct virtio_device *vdev) { struct nd_region_desc ndr_desc = {}; struct nd_region *nd_region; struct virtio_pmem *vpmem; struct resource res; struct virtio_shm_region shm_reg; int err = 0; if (!vdev->config->get) { dev_err(&vdev->dev, "%s failure: config access disabled\n", __func__); return -EINVAL; } vpmem = devm_kzalloc(&vdev->dev, sizeof(*vpmem), GFP_KERNEL); if (!vpmem) { err = -ENOMEM; goto out_err; } vpmem->vdev = vdev; vdev->priv = vpmem; err = init_vq(vpmem); if (err) { dev_err(&vdev->dev, "failed to initialize virtio pmem vq's\n"); goto out_err; } if (virtio_has_feature(vdev, VIRTIO_PMEM_F_SHMEM_REGION)) { virtio_get_shm_region(vdev, &shm_reg, (u8)VIRTIO_PMEM_SHMEM_REGION_ID); vpmem->start = shm_reg.addr; vpmem->size = shm_reg.len; } else { virtio_cread_le(vpmem->vdev, struct virtio_pmem_config, start, &vpmem->start); virtio_cread_le(vpmem->vdev, struct virtio_pmem_config, size, &vpmem->size); } res.start = vpmem->start; res.end = vpmem->start + vpmem->size - 1; vpmem->nd_desc.provider_name = "virtio-pmem"; vpmem->nd_desc.module = THIS_MODULE; vpmem->nvdimm_bus = nvdimm_bus_register(&vdev->dev, &vpmem->nd_desc); if (!vpmem->nvdimm_bus) { dev_err(&vdev->dev, "failed to register device with nvdimm_bus\n"); err = -ENXIO; goto out_vq; } dev_set_drvdata(&vdev->dev, vpmem->nvdimm_bus); ndr_desc.res = &res; ndr_desc.numa_node = memory_add_physaddr_to_nid(res.start); ndr_desc.target_node = phys_to_target_node(res.start); if (ndr_desc.target_node == NUMA_NO_NODE) { ndr_desc.target_node = ndr_desc.numa_node; dev_dbg(&vdev->dev, "changing target node from %d to %d", NUMA_NO_NODE, ndr_desc.target_node); } ndr_desc.flush = async_pmem_flush; ndr_desc.provider_data = vdev; set_bit(ND_REGION_PAGEMAP, &ndr_desc.flags); set_bit(ND_REGION_ASYNC, &ndr_desc.flags); /* * The NVDIMM region could be available before the * virtio_device_ready() that is called by * virtio_dev_probe(), so we set device ready here. */ virtio_device_ready(vdev); nd_region = nvdimm_pmem_region_create(vpmem->nvdimm_bus, &ndr_desc); if (!nd_region) { dev_err(&vdev->dev, "failed to create nvdimm region\n"); err = -ENXIO; goto out_nd; } return 0; out_nd: virtio_reset_device(vdev); nvdimm_bus_unregister(vpmem->nvdimm_bus); out_vq: vdev->config->del_vqs(vdev); out_err: return err; } static void virtio_pmem_remove(struct virtio_device *vdev) { struct nvdimm_bus *nvdimm_bus = dev_get_drvdata(&vdev->dev); nvdimm_bus_unregister(nvdimm_bus); vdev->config->del_vqs(vdev); virtio_reset_device(vdev); } static unsigned int features[] = { VIRTIO_PMEM_F_SHMEM_REGION, }; static struct virtio_driver virtio_pmem_driver = { .feature_table = features, .feature_table_size = ARRAY_SIZE(features), .driver.name = KBUILD_MODNAME, .driver.owner = THIS_MODULE, .id_table = id_table, .validate = virtio_pmem_validate, .probe = virtio_pmem_probe, .remove = virtio_pmem_remove, }; module_virtio_driver(virtio_pmem_driver); MODULE_DEVICE_TABLE(virtio, id_table); MODULE_DESCRIPTION("Virtio pmem driver"); MODULE_LICENSE("GPL"); |