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 | ||
35967bdc CL |
32 | static int virtio_pmem_validate(struct virtio_device *vdev) |
33 | { | |
34 | struct virtio_shm_region shm_reg; | |
35 | ||
36 | if (virtio_has_feature(vdev, VIRTIO_PMEM_F_SHMEM_REGION) && | |
37 | !virtio_get_shm_region(vdev, &shm_reg, (u8)VIRTIO_PMEM_SHMEM_REGION_ID) | |
38 | ) { | |
39 | dev_notice(&vdev->dev, "failed to get shared memory region %d\n", | |
40 | VIRTIO_PMEM_SHMEM_REGION_ID); | |
41 | __virtio_clear_bit(vdev, VIRTIO_PMEM_F_SHMEM_REGION); | |
42 | } | |
43 | return 0; | |
44 | } | |
45 | ||
6e84200c PG |
46 | static int virtio_pmem_probe(struct virtio_device *vdev) |
47 | { | |
48 | struct nd_region_desc ndr_desc = {}; | |
6e84200c PG |
49 | struct nd_region *nd_region; |
50 | struct virtio_pmem *vpmem; | |
51 | struct resource res; | |
35967bdc | 52 | struct virtio_shm_region shm_reg; |
6e84200c PG |
53 | int err = 0; |
54 | ||
55 | if (!vdev->config->get) { | |
56 | dev_err(&vdev->dev, "%s failure: config access disabled\n", | |
57 | __func__); | |
58 | return -EINVAL; | |
59 | } | |
60 | ||
61 | vpmem = devm_kzalloc(&vdev->dev, sizeof(*vpmem), GFP_KERNEL); | |
62 | if (!vpmem) { | |
63 | err = -ENOMEM; | |
64 | goto out_err; | |
65 | } | |
66 | ||
67 | vpmem->vdev = vdev; | |
68 | vdev->priv = vpmem; | |
69 | err = init_vq(vpmem); | |
70 | if (err) { | |
71 | dev_err(&vdev->dev, "failed to initialize virtio pmem vq's\n"); | |
72 | goto out_err; | |
73 | } | |
74 | ||
35967bdc CL |
75 | if (virtio_has_feature(vdev, VIRTIO_PMEM_F_SHMEM_REGION)) { |
76 | virtio_get_shm_region(vdev, &shm_reg, (u8)VIRTIO_PMEM_SHMEM_REGION_ID); | |
77 | vpmem->start = shm_reg.addr; | |
78 | vpmem->size = shm_reg.len; | |
79 | } else { | |
80 | virtio_cread_le(vpmem->vdev, struct virtio_pmem_config, | |
81 | start, &vpmem->start); | |
82 | virtio_cread_le(vpmem->vdev, struct virtio_pmem_config, | |
83 | size, &vpmem->size); | |
84 | } | |
6e84200c PG |
85 | |
86 | res.start = vpmem->start; | |
87 | res.end = vpmem->start + vpmem->size - 1; | |
88 | vpmem->nd_desc.provider_name = "virtio-pmem"; | |
89 | vpmem->nd_desc.module = THIS_MODULE; | |
90 | ||
91 | vpmem->nvdimm_bus = nvdimm_bus_register(&vdev->dev, | |
92 | &vpmem->nd_desc); | |
93 | if (!vpmem->nvdimm_bus) { | |
94 | dev_err(&vdev->dev, "failed to register device with nvdimm_bus\n"); | |
95 | err = -ENXIO; | |
96 | goto out_vq; | |
97 | } | |
98 | ||
99 | dev_set_drvdata(&vdev->dev, vpmem->nvdimm_bus); | |
100 | ||
101 | ndr_desc.res = &res; | |
d5ff73bb MS |
102 | |
103 | ndr_desc.numa_node = memory_add_physaddr_to_nid(res.start); | |
104 | ndr_desc.target_node = phys_to_target_node(res.start); | |
105 | if (ndr_desc.target_node == NUMA_NO_NODE) { | |
106 | ndr_desc.target_node = ndr_desc.numa_node; | |
107 | dev_dbg(&vdev->dev, "changing target node from %d to %d", | |
108 | NUMA_NO_NODE, ndr_desc.target_node); | |
109 | } | |
110 | ||
6e84200c | 111 | ndr_desc.flush = async_pmem_flush; |
020e1aed | 112 | ndr_desc.provider_data = vdev; |
6e84200c PG |
113 | set_bit(ND_REGION_PAGEMAP, &ndr_desc.flags); |
114 | set_bit(ND_REGION_ASYNC, &ndr_desc.flags); | |
5d66322b JW |
115 | /* |
116 | * The NVDIMM region could be available before the | |
117 | * virtio_device_ready() that is called by | |
118 | * virtio_dev_probe(), so we set device ready here. | |
119 | */ | |
120 | virtio_device_ready(vdev); | |
6e84200c PG |
121 | nd_region = nvdimm_pmem_region_create(vpmem->nvdimm_bus, &ndr_desc); |
122 | if (!nd_region) { | |
123 | dev_err(&vdev->dev, "failed to create nvdimm region\n"); | |
124 | err = -ENXIO; | |
125 | goto out_nd; | |
126 | } | |
6e84200c PG |
127 | return 0; |
128 | out_nd: | |
5d66322b | 129 | virtio_reset_device(vdev); |
6e84200c PG |
130 | nvdimm_bus_unregister(vpmem->nvdimm_bus); |
131 | out_vq: | |
132 | vdev->config->del_vqs(vdev); | |
133 | out_err: | |
134 | return err; | |
135 | } | |
136 | ||
137 | static void virtio_pmem_remove(struct virtio_device *vdev) | |
138 | { | |
139 | struct nvdimm_bus *nvdimm_bus = dev_get_drvdata(&vdev->dev); | |
140 | ||
141 | nvdimm_bus_unregister(nvdimm_bus); | |
142 | vdev->config->del_vqs(vdev); | |
d9679d00 | 143 | virtio_reset_device(vdev); |
6e84200c PG |
144 | } |
145 | ||
35967bdc CL |
146 | static unsigned int features[] = { |
147 | VIRTIO_PMEM_F_SHMEM_REGION, | |
148 | }; | |
149 | ||
6e84200c | 150 | static struct virtio_driver virtio_pmem_driver = { |
35967bdc CL |
151 | .feature_table = features, |
152 | .feature_table_size = ARRAY_SIZE(features), | |
6e84200c | 153 | .driver.name = KBUILD_MODNAME, |
6e84200c | 154 | .id_table = id_table, |
35967bdc | 155 | .validate = virtio_pmem_validate, |
6e84200c PG |
156 | .probe = virtio_pmem_probe, |
157 | .remove = virtio_pmem_remove, | |
158 | }; | |
159 | ||
160 | module_virtio_driver(virtio_pmem_driver); | |
161 | MODULE_DEVICE_TABLE(virtio, id_table); | |
162 | MODULE_DESCRIPTION("Virtio pmem driver"); | |
163 | MODULE_LICENSE("GPL"); |