Linux Audio

Check our new training course

In-person Linux kernel drivers training

Jun 16-20, 2025
Register
Loading...
Note: File does not exist in v3.1.
  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
 12static struct virtio_device_id id_table[] = {
 13	{ VIRTIO_ID_PMEM, VIRTIO_DEV_ANY_ID },
 14	{ 0 },
 15};
 16
 17 /* Initialize virt queue */
 18static 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
 32static 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;
 94out_nd:
 95	nvdimm_bus_unregister(vpmem->nvdimm_bus);
 96out_vq:
 97	vdev->config->del_vqs(vdev);
 98out_err:
 99	return err;
100}
101
102static 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	vdev->config->reset(vdev);
109}
110
111static 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
119module_virtio_driver(virtio_pmem_driver);
120MODULE_DEVICE_TABLE(virtio, id_table);
121MODULE_DESCRIPTION("Virtio pmem driver");
122MODULE_LICENSE("GPL");