Commit | Line | Data |
---|---|---|
6e84200c PG |
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(vpmem->vdev, struct virtio_pmem_config, | |
62 | start, &vpmem->start); | |
63 | virtio_cread(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 | vdev->config->reset(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"); |