18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * virtio_pmem.c: Virtio pmem Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Discovers persistent memory range information 68c2ecf20Sopenharmony_ci * from host and registers the virtual pmem device 78c2ecf20Sopenharmony_ci * with libnvdimm core. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci#include "virtio_pmem.h" 108c2ecf20Sopenharmony_ci#include "nd.h" 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_cistatic struct virtio_device_id id_table[] = { 138c2ecf20Sopenharmony_ci { VIRTIO_ID_PMEM, VIRTIO_DEV_ANY_ID }, 148c2ecf20Sopenharmony_ci { 0 }, 158c2ecf20Sopenharmony_ci}; 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci /* Initialize virt queue */ 188c2ecf20Sopenharmony_cistatic int init_vq(struct virtio_pmem *vpmem) 198c2ecf20Sopenharmony_ci{ 208c2ecf20Sopenharmony_ci /* single vq */ 218c2ecf20Sopenharmony_ci vpmem->req_vq = virtio_find_single_vq(vpmem->vdev, 228c2ecf20Sopenharmony_ci virtio_pmem_host_ack, "flush_queue"); 238c2ecf20Sopenharmony_ci if (IS_ERR(vpmem->req_vq)) 248c2ecf20Sopenharmony_ci return PTR_ERR(vpmem->req_vq); 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci spin_lock_init(&vpmem->pmem_lock); 278c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&vpmem->req_list); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci return 0; 308c2ecf20Sopenharmony_ci}; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic int virtio_pmem_probe(struct virtio_device *vdev) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci struct nd_region_desc ndr_desc = {}; 358c2ecf20Sopenharmony_ci int nid = dev_to_node(&vdev->dev); 368c2ecf20Sopenharmony_ci struct nd_region *nd_region; 378c2ecf20Sopenharmony_ci struct virtio_pmem *vpmem; 388c2ecf20Sopenharmony_ci struct resource res; 398c2ecf20Sopenharmony_ci int err = 0; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci if (!vdev->config->get) { 428c2ecf20Sopenharmony_ci dev_err(&vdev->dev, "%s failure: config access disabled\n", 438c2ecf20Sopenharmony_ci __func__); 448c2ecf20Sopenharmony_ci return -EINVAL; 458c2ecf20Sopenharmony_ci } 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci vpmem = devm_kzalloc(&vdev->dev, sizeof(*vpmem), GFP_KERNEL); 488c2ecf20Sopenharmony_ci if (!vpmem) { 498c2ecf20Sopenharmony_ci err = -ENOMEM; 508c2ecf20Sopenharmony_ci goto out_err; 518c2ecf20Sopenharmony_ci } 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci vpmem->vdev = vdev; 548c2ecf20Sopenharmony_ci vdev->priv = vpmem; 558c2ecf20Sopenharmony_ci err = init_vq(vpmem); 568c2ecf20Sopenharmony_ci if (err) { 578c2ecf20Sopenharmony_ci dev_err(&vdev->dev, "failed to initialize virtio pmem vq's\n"); 588c2ecf20Sopenharmony_ci goto out_err; 598c2ecf20Sopenharmony_ci } 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci virtio_cread_le(vpmem->vdev, struct virtio_pmem_config, 628c2ecf20Sopenharmony_ci start, &vpmem->start); 638c2ecf20Sopenharmony_ci virtio_cread_le(vpmem->vdev, struct virtio_pmem_config, 648c2ecf20Sopenharmony_ci size, &vpmem->size); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci res.start = vpmem->start; 678c2ecf20Sopenharmony_ci res.end = vpmem->start + vpmem->size - 1; 688c2ecf20Sopenharmony_ci vpmem->nd_desc.provider_name = "virtio-pmem"; 698c2ecf20Sopenharmony_ci vpmem->nd_desc.module = THIS_MODULE; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci vpmem->nvdimm_bus = nvdimm_bus_register(&vdev->dev, 728c2ecf20Sopenharmony_ci &vpmem->nd_desc); 738c2ecf20Sopenharmony_ci if (!vpmem->nvdimm_bus) { 748c2ecf20Sopenharmony_ci dev_err(&vdev->dev, "failed to register device with nvdimm_bus\n"); 758c2ecf20Sopenharmony_ci err = -ENXIO; 768c2ecf20Sopenharmony_ci goto out_vq; 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci dev_set_drvdata(&vdev->dev, vpmem->nvdimm_bus); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci ndr_desc.res = &res; 828c2ecf20Sopenharmony_ci ndr_desc.numa_node = nid; 838c2ecf20Sopenharmony_ci ndr_desc.flush = async_pmem_flush; 848c2ecf20Sopenharmony_ci set_bit(ND_REGION_PAGEMAP, &ndr_desc.flags); 858c2ecf20Sopenharmony_ci set_bit(ND_REGION_ASYNC, &ndr_desc.flags); 868c2ecf20Sopenharmony_ci nd_region = nvdimm_pmem_region_create(vpmem->nvdimm_bus, &ndr_desc); 878c2ecf20Sopenharmony_ci if (!nd_region) { 888c2ecf20Sopenharmony_ci dev_err(&vdev->dev, "failed to create nvdimm region\n"); 898c2ecf20Sopenharmony_ci err = -ENXIO; 908c2ecf20Sopenharmony_ci goto out_nd; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci nd_region->provider_data = dev_to_virtio(nd_region->dev.parent->parent); 938c2ecf20Sopenharmony_ci return 0; 948c2ecf20Sopenharmony_ciout_nd: 958c2ecf20Sopenharmony_ci nvdimm_bus_unregister(vpmem->nvdimm_bus); 968c2ecf20Sopenharmony_ciout_vq: 978c2ecf20Sopenharmony_ci vdev->config->del_vqs(vdev); 988c2ecf20Sopenharmony_ciout_err: 998c2ecf20Sopenharmony_ci return err; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic void virtio_pmem_remove(struct virtio_device *vdev) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci struct nvdimm_bus *nvdimm_bus = dev_get_drvdata(&vdev->dev); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci nvdimm_bus_unregister(nvdimm_bus); 1078c2ecf20Sopenharmony_ci vdev->config->del_vqs(vdev); 1088c2ecf20Sopenharmony_ci vdev->config->reset(vdev); 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic struct virtio_driver virtio_pmem_driver = { 1128c2ecf20Sopenharmony_ci .driver.name = KBUILD_MODNAME, 1138c2ecf20Sopenharmony_ci .driver.owner = THIS_MODULE, 1148c2ecf20Sopenharmony_ci .id_table = id_table, 1158c2ecf20Sopenharmony_ci .probe = virtio_pmem_probe, 1168c2ecf20Sopenharmony_ci .remove = virtio_pmem_remove, 1178c2ecf20Sopenharmony_ci}; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cimodule_virtio_driver(virtio_pmem_driver); 1208c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(virtio, id_table); 1218c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Virtio pmem driver"); 1228c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 123