blob: 5629289939afcd293ab62c490c863f9df6f1ae13 [file] [log] [blame]
Dan Williams8fdcb172021-06-15 16:18:17 -07001// SPDX-License-Identifier: GPL-2.0-only
2/* Copyright(c) 2021 Intel Corporation. All rights reserved. */
3#include <linux/libnvdimm.h>
4#include <linux/device.h>
5#include <linux/module.h>
Dan Williams21083f52021-06-15 16:36:31 -07006#include <linux/ndctl.h>
7#include <linux/async.h>
Dan Williams8fdcb172021-06-15 16:18:17 -07008#include <linux/slab.h>
Ben Widawsky5161a552021-08-02 10:29:38 -07009#include "cxlmem.h"
Dan Williams8fdcb172021-06-15 16:18:17 -070010#include "cxl.h"
11
12/*
13 * Ordered workqueue for cxl nvdimm device arrival and departure
14 * to coordinate bus rescans when a bridge arrives and trigger remove
15 * operations when the bridge is removed.
16 */
17static struct workqueue_struct *cxl_pmem_wq;
18
Dan Williams12f38562021-09-14 12:03:04 -070019static __read_mostly DECLARE_BITMAP(exclusive_cmds, CXL_MEM_COMMAND_ID_MAX);
20
21static void clear_exclusive(void *cxlm)
22{
23 clear_exclusive_cxl_commands(cxlm, exclusive_cmds);
24}
25
Dan Williams21083f52021-06-15 16:36:31 -070026static void unregister_nvdimm(void *nvdimm)
27{
28 nvdimm_delete(nvdimm);
29}
30
31static int match_nvdimm_bridge(struct device *dev, const void *data)
32{
33 return strcmp(dev_name(dev), "nvdimm-bridge") == 0;
34}
35
36static struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(void)
37{
38 struct device *dev;
39
40 dev = bus_find_device(&cxl_bus_type, NULL, NULL, match_nvdimm_bridge);
41 if (!dev)
42 return NULL;
43 return to_cxl_nvdimm_bridge(dev);
44}
45
46static int cxl_nvdimm_probe(struct device *dev)
47{
48 struct cxl_nvdimm *cxl_nvd = to_cxl_nvdimm(dev);
Dan Williams12f38562021-09-14 12:03:04 -070049 struct cxl_memdev *cxlmd = cxl_nvd->cxlmd;
50 struct cxl_mem *cxlm = cxlmd->cxlm;
Dan Williams21083f52021-06-15 16:36:31 -070051 struct cxl_nvdimm_bridge *cxl_nvb;
52 unsigned long flags = 0;
53 struct nvdimm *nvdimm;
Dan Williams12f38562021-09-14 12:03:04 -070054 int rc;
Dan Williams21083f52021-06-15 16:36:31 -070055
56 cxl_nvb = cxl_find_nvdimm_bridge();
57 if (!cxl_nvb)
58 return -ENXIO;
59
60 device_lock(&cxl_nvb->dev);
Dan Williams12f38562021-09-14 12:03:04 -070061 if (!cxl_nvb->nvdimm_bus) {
62 rc = -ENXIO;
63 goto out;
64 }
65
66 set_exclusive_cxl_commands(cxlm, exclusive_cmds);
67 rc = devm_add_action_or_reset(dev, clear_exclusive, cxlm);
68 if (rc)
Dan Williams21083f52021-06-15 16:36:31 -070069 goto out;
70
71 set_bit(NDD_LABELING, &flags);
72 nvdimm = nvdimm_create(cxl_nvb->nvdimm_bus, cxl_nvd, NULL, flags, 0, 0,
73 NULL);
Dan Williams12f38562021-09-14 12:03:04 -070074 if (!nvdimm) {
75 rc = -ENOMEM;
Dan Williams21083f52021-06-15 16:36:31 -070076 goto out;
Dan Williams12f38562021-09-14 12:03:04 -070077 }
Dan Williams21083f52021-06-15 16:36:31 -070078
Dan Williams12f38562021-09-14 12:03:04 -070079 dev_set_drvdata(dev, nvdimm);
Dan Williams21083f52021-06-15 16:36:31 -070080 rc = devm_add_action_or_reset(dev, unregister_nvdimm, nvdimm);
81out:
82 device_unlock(&cxl_nvb->dev);
83 put_device(&cxl_nvb->dev);
84
85 return rc;
86}
87
88static struct cxl_driver cxl_nvdimm_driver = {
89 .name = "cxl_nvdimm",
90 .probe = cxl_nvdimm_probe,
91 .id = CXL_DEVICE_NVDIMM,
92};
93
Dan Williams8fdcb172021-06-15 16:18:17 -070094static int cxl_pmem_ctl(struct nvdimm_bus_descriptor *nd_desc,
95 struct nvdimm *nvdimm, unsigned int cmd, void *buf,
96 unsigned int buf_len, int *cmd_rc)
97{
98 return -ENOTTY;
99}
100
101static bool online_nvdimm_bus(struct cxl_nvdimm_bridge *cxl_nvb)
102{
103 if (cxl_nvb->nvdimm_bus)
104 return true;
105 cxl_nvb->nvdimm_bus =
106 nvdimm_bus_register(&cxl_nvb->dev, &cxl_nvb->nd_desc);
107 return cxl_nvb->nvdimm_bus != NULL;
108}
109
Dan Williams21083f52021-06-15 16:36:31 -0700110static int cxl_nvdimm_release_driver(struct device *dev, void *data)
Dan Williams8fdcb172021-06-15 16:18:17 -0700111{
Dan Williams21083f52021-06-15 16:36:31 -0700112 if (!is_cxl_nvdimm(dev))
113 return 0;
114 device_release_driver(dev);
115 return 0;
116}
117
118static void offline_nvdimm_bus(struct nvdimm_bus *nvdimm_bus)
119{
120 if (!nvdimm_bus)
Dan Williams8fdcb172021-06-15 16:18:17 -0700121 return;
Dan Williams21083f52021-06-15 16:36:31 -0700122
123 /*
124 * Set the state of cxl_nvdimm devices to unbound / idle before
125 * nvdimm_bus_unregister() rips the nvdimm objects out from
126 * underneath them.
127 */
128 bus_for_each_dev(&cxl_bus_type, NULL, NULL, cxl_nvdimm_release_driver);
129 nvdimm_bus_unregister(nvdimm_bus);
Dan Williams8fdcb172021-06-15 16:18:17 -0700130}
131
132static void cxl_nvb_update_state(struct work_struct *work)
133{
134 struct cxl_nvdimm_bridge *cxl_nvb =
135 container_of(work, typeof(*cxl_nvb), state_work);
Dan Williams21083f52021-06-15 16:36:31 -0700136 struct nvdimm_bus *victim_bus = NULL;
137 bool release = false, rescan = false;
Dan Williams8fdcb172021-06-15 16:18:17 -0700138
139 device_lock(&cxl_nvb->dev);
140 switch (cxl_nvb->state) {
141 case CXL_NVB_ONLINE:
142 if (!online_nvdimm_bus(cxl_nvb)) {
143 dev_err(&cxl_nvb->dev,
144 "failed to establish nvdimm bus\n");
145 release = true;
Dan Williams21083f52021-06-15 16:36:31 -0700146 } else
147 rescan = true;
Dan Williams8fdcb172021-06-15 16:18:17 -0700148 break;
149 case CXL_NVB_OFFLINE:
150 case CXL_NVB_DEAD:
Dan Williams21083f52021-06-15 16:36:31 -0700151 victim_bus = cxl_nvb->nvdimm_bus;
152 cxl_nvb->nvdimm_bus = NULL;
Dan Williams8fdcb172021-06-15 16:18:17 -0700153 break;
154 default:
155 break;
156 }
157 device_unlock(&cxl_nvb->dev);
158
159 if (release)
160 device_release_driver(&cxl_nvb->dev);
Dan Williams21083f52021-06-15 16:36:31 -0700161 if (rescan) {
162 int rc = bus_rescan_devices(&cxl_bus_type);
163
164 dev_dbg(&cxl_nvb->dev, "rescan: %d\n", rc);
165 }
166 offline_nvdimm_bus(victim_bus);
Dan Williams8fdcb172021-06-15 16:18:17 -0700167
168 put_device(&cxl_nvb->dev);
169}
170
171static void cxl_nvdimm_bridge_remove(struct device *dev)
172{
173 struct cxl_nvdimm_bridge *cxl_nvb = to_cxl_nvdimm_bridge(dev);
174
175 if (cxl_nvb->state == CXL_NVB_ONLINE)
176 cxl_nvb->state = CXL_NVB_OFFLINE;
177 if (queue_work(cxl_pmem_wq, &cxl_nvb->state_work))
178 get_device(&cxl_nvb->dev);
179}
180
181static int cxl_nvdimm_bridge_probe(struct device *dev)
182{
183 struct cxl_nvdimm_bridge *cxl_nvb = to_cxl_nvdimm_bridge(dev);
184
185 if (cxl_nvb->state == CXL_NVB_DEAD)
186 return -ENXIO;
187
188 if (cxl_nvb->state == CXL_NVB_NEW) {
189 cxl_nvb->nd_desc = (struct nvdimm_bus_descriptor) {
190 .provider_name = "CXL",
191 .module = THIS_MODULE,
192 .ndctl = cxl_pmem_ctl,
193 };
194
195 INIT_WORK(&cxl_nvb->state_work, cxl_nvb_update_state);
196 }
197
198 cxl_nvb->state = CXL_NVB_ONLINE;
199 if (queue_work(cxl_pmem_wq, &cxl_nvb->state_work))
200 get_device(&cxl_nvb->dev);
201
202 return 0;
203}
204
205static struct cxl_driver cxl_nvdimm_bridge_driver = {
206 .name = "cxl_nvdimm_bridge",
207 .probe = cxl_nvdimm_bridge_probe,
208 .remove = cxl_nvdimm_bridge_remove,
209 .id = CXL_DEVICE_NVDIMM_BRIDGE,
210};
211
212static __init int cxl_pmem_init(void)
213{
214 int rc;
215
Dan Williams12f38562021-09-14 12:03:04 -0700216 set_bit(CXL_MEM_COMMAND_ID_SET_PARTITION_INFO, exclusive_cmds);
217 set_bit(CXL_MEM_COMMAND_ID_SET_SHUTDOWN_STATE, exclusive_cmds);
218 set_bit(CXL_MEM_COMMAND_ID_SET_LSA, exclusive_cmds);
219
Dan Williams8fdcb172021-06-15 16:18:17 -0700220 cxl_pmem_wq = alloc_ordered_workqueue("cxl_pmem", 0);
221 if (!cxl_pmem_wq)
222 return -ENXIO;
223
224 rc = cxl_driver_register(&cxl_nvdimm_bridge_driver);
225 if (rc)
Dan Williams21083f52021-06-15 16:36:31 -0700226 goto err_bridge;
227
228 rc = cxl_driver_register(&cxl_nvdimm_driver);
229 if (rc)
230 goto err_nvdimm;
Dan Williams8fdcb172021-06-15 16:18:17 -0700231
232 return 0;
233
Dan Williams21083f52021-06-15 16:36:31 -0700234err_nvdimm:
235 cxl_driver_unregister(&cxl_nvdimm_bridge_driver);
236err_bridge:
Dan Williams8fdcb172021-06-15 16:18:17 -0700237 destroy_workqueue(cxl_pmem_wq);
238 return rc;
239}
240
241static __exit void cxl_pmem_exit(void)
242{
Dan Williams21083f52021-06-15 16:36:31 -0700243 cxl_driver_unregister(&cxl_nvdimm_driver);
Dan Williams8fdcb172021-06-15 16:18:17 -0700244 cxl_driver_unregister(&cxl_nvdimm_bridge_driver);
245 destroy_workqueue(cxl_pmem_wq);
246}
247
248MODULE_LICENSE("GPL v2");
249module_init(cxl_pmem_init);
250module_exit(cxl_pmem_exit);
251MODULE_IMPORT_NS(CXL);
252MODULE_ALIAS_CXL(CXL_DEVICE_NVDIMM_BRIDGE);
Dan Williams21083f52021-06-15 16:36:31 -0700253MODULE_ALIAS_CXL(CXL_DEVICE_NVDIMM);