Blame view

drivers/nvdimm/bus.c 30.1 KB
5b497af42   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
45def22c1   Dan Williams   libnvdimm: contro...
2
3
  /*
   * Copyright(c) 2013-2015 Intel Corporation. All rights reserved.
45def22c1   Dan Williams   libnvdimm: contro...
4
5
   */
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
aa9ad44a4   Dave Jiang   libnvdimm: move p...
6
  #include <linux/libnvdimm.h>
0930a750c   Vishal Verma   libnvdimm: fix po...
7
  #include <linux/sched/mm.h>
62232e45f   Dan Williams   libnvdimm: contro...
8
  #include <linux/vmalloc.h>
45def22c1   Dan Williams   libnvdimm: contro...
9
  #include <linux/uaccess.h>
3d88002e4   Dan Williams   libnvdimm: suppor...
10
  #include <linux/module.h>
8c2f7e865   Dan Williams   libnvdimm: infras...
11
  #include <linux/blkdev.h>
45def22c1   Dan Williams   libnvdimm: contro...
12
  #include <linux/fcntl.h>
e6dfb2de4   Dan Williams   libnvdimm, nfit: ...
13
  #include <linux/async.h>
8c2f7e865   Dan Williams   libnvdimm: infras...
14
  #include <linux/genhd.h>
62232e45f   Dan Williams   libnvdimm: contro...
15
  #include <linux/ndctl.h>
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
16
  #include <linux/sched.h>
45def22c1   Dan Williams   libnvdimm: contro...
17
  #include <linux/slab.h>
af87b9a78   Alexander Duyck   libnvdimm: Schedu...
18
  #include <linux/cpu.h>
45def22c1   Dan Williams   libnvdimm: contro...
19
20
  #include <linux/fs.h>
  #include <linux/io.h>
62232e45f   Dan Williams   libnvdimm: contro...
21
  #include <linux/mm.h>
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
22
  #include <linux/nd.h>
45def22c1   Dan Williams   libnvdimm: contro...
23
  #include "nd-core.h"
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
24
  #include "nd.h"
006358b35   Dave Jiang   libnvdimm: add su...
25
  #include "pfn.h"
45def22c1   Dan Williams   libnvdimm: contro...
26

62232e45f   Dan Williams   libnvdimm: contro...
27
  int nvdimm_major;
45def22c1   Dan Williams   libnvdimm: contro...
28
  static int nvdimm_bus_major;
87a30e1f0   Dan Williams   driver-core, libn...
29
  struct class *nd_class;
18515942d   Dan Williams   libnvdimm: regist...
30
  static DEFINE_IDA(nd_ida);
45def22c1   Dan Williams   libnvdimm: contro...
31

4d88a97aa   Dan Williams   libnvdimm, nvdimm...
32
33
34
35
  static int to_nd_device_type(struct device *dev)
  {
  	if (is_nvdimm(dev))
  		return ND_DEVICE_DIMM;
c9e582aa6   Dan Williams   libnvdimm, nfit: ...
36
  	else if (is_memory(dev))
3d88002e4   Dan Williams   libnvdimm: suppor...
37
38
39
  		return ND_DEVICE_REGION_PMEM;
  	else if (is_nd_blk(dev))
  		return ND_DEVICE_REGION_BLK;
cd03412a5   Dan Williams   libnvdimm, dax: i...
40
41
  	else if (is_nd_dax(dev))
  		return ND_DEVICE_DAX_PMEM;
c9e582aa6   Dan Williams   libnvdimm, nfit: ...
42
  	else if (is_nd_region(dev->parent))
3d88002e4   Dan Williams   libnvdimm: suppor...
43
  		return nd_region_to_nstype(to_nd_region(dev->parent));
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
44
45
46
47
48
49
50
51
52
  
  	return 0;
  }
  
  static int nvdimm_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
  {
  	return add_uevent_var(env, "MODALIAS=" ND_DEVICE_MODALIAS_FMT,
  			to_nd_device_type(dev));
  }
3d88002e4   Dan Williams   libnvdimm: suppor...
53
54
55
  static struct module *to_bus_provider(struct device *dev)
  {
  	/* pin bus providers while regions are enabled */
c9e582aa6   Dan Williams   libnvdimm, nfit: ...
56
  	if (is_nd_region(dev)) {
3d88002e4   Dan Williams   libnvdimm: suppor...
57
  		struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
bc9775d86   Dan Williams   libnvdimm: move -...
58
  		return nvdimm_bus->nd_desc->module;
3d88002e4   Dan Williams   libnvdimm: suppor...
59
60
61
  	}
  	return NULL;
  }
eaf961536   Dan Williams   libnvdimm, nfit: ...
62
63
64
65
66
67
68
69
70
71
72
  static void nvdimm_bus_probe_start(struct nvdimm_bus *nvdimm_bus)
  {
  	nvdimm_bus_lock(&nvdimm_bus->dev);
  	nvdimm_bus->probe_active++;
  	nvdimm_bus_unlock(&nvdimm_bus->dev);
  }
  
  static void nvdimm_bus_probe_end(struct nvdimm_bus *nvdimm_bus)
  {
  	nvdimm_bus_lock(&nvdimm_bus->dev);
  	if (--nvdimm_bus->probe_active == 0)
b70d31d05   Dan Williams   libnvdimm/bus: St...
73
  		wake_up(&nvdimm_bus->wait);
eaf961536   Dan Williams   libnvdimm, nfit: ...
74
75
  	nvdimm_bus_unlock(&nvdimm_bus->dev);
  }
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
76
77
78
  static int nvdimm_bus_probe(struct device *dev)
  {
  	struct nd_device_driver *nd_drv = to_nd_device_driver(dev->driver);
3d88002e4   Dan Williams   libnvdimm: suppor...
79
  	struct module *provider = to_bus_provider(dev);
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
80
81
  	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
  	int rc;
3d88002e4   Dan Williams   libnvdimm: suppor...
82
83
  	if (!try_module_get(provider))
  		return -ENXIO;
3f46833df   Dan Williams   libnvdimm: Debug ...
84
85
86
  	dev_dbg(&nvdimm_bus->dev, "START: %s.probe(%s)
  ",
  			dev->driver->name, dev_name(dev));
eaf961536   Dan Williams   libnvdimm, nfit: ...
87
  	nvdimm_bus_probe_start(nvdimm_bus);
87a30e1f0   Dan Williams   driver-core, libn...
88
  	debug_nvdimm_lock(dev);
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
89
  	rc = nd_drv->probe(dev);
87a30e1f0   Dan Williams   driver-core, libn...
90
  	debug_nvdimm_unlock(dev);
1c97afa71   Aneesh Kumar K.V   libnvdimm/pmem: A...
91
92
  	if ((rc == 0 || rc == -EOPNOTSUPP) &&
  			dev->parent && is_nd_region(dev->parent))
a2d1c7a61   Dan Williams   libnvdimm/region:...
93
  		nd_region_advance_seeds(to_nd_region(dev->parent), dev);
eaf961536   Dan Williams   libnvdimm, nfit: ...
94
  	nvdimm_bus_probe_end(nvdimm_bus);
3f46833df   Dan Williams   libnvdimm: Debug ...
95
96
  	dev_dbg(&nvdimm_bus->dev, "END: %s.probe(%s) = %d
  ", dev->driver->name,
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
97
  			dev_name(dev), rc);
8c2f7e865   Dan Williams   libnvdimm: infras...
98

3d88002e4   Dan Williams   libnvdimm: suppor...
99
100
  	if (rc != 0)
  		module_put(provider);
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
101
102
103
104
105
106
  	return rc;
  }
  
  static int nvdimm_bus_remove(struct device *dev)
  {
  	struct nd_device_driver *nd_drv = to_nd_device_driver(dev->driver);
3d88002e4   Dan Williams   libnvdimm: suppor...
107
  	struct module *provider = to_bus_provider(dev);
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
108
  	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
6cf9c5bab   Dan Williams   libnvdimm: stop r...
109
  	int rc = 0;
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
110

87a30e1f0   Dan Williams   driver-core, libn...
111
112
  	if (nd_drv->remove) {
  		debug_nvdimm_lock(dev);
6cf9c5bab   Dan Williams   libnvdimm: stop r...
113
  		rc = nd_drv->remove(dev);
87a30e1f0   Dan Williams   driver-core, libn...
114
115
  		debug_nvdimm_unlock(dev);
  	}
eaf961536   Dan Williams   libnvdimm, nfit: ...
116

4d88a97aa   Dan Williams   libnvdimm, nvdimm...
117
118
119
  	dev_dbg(&nvdimm_bus->dev, "%s.remove(%s) = %d
  ", dev->driver->name,
  			dev_name(dev), rc);
3d88002e4   Dan Williams   libnvdimm: suppor...
120
  	module_put(provider);
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
121
122
  	return rc;
  }
476f848aa   Dan Williams   libnvdimm, pmem: ...
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
  static void nvdimm_bus_shutdown(struct device *dev)
  {
  	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
  	struct nd_device_driver *nd_drv = NULL;
  
  	if (dev->driver)
  		nd_drv = to_nd_device_driver(dev->driver);
  
  	if (nd_drv && nd_drv->shutdown) {
  		nd_drv->shutdown(dev);
  		dev_dbg(&nvdimm_bus->dev, "%s.shutdown(%s)
  ",
  				dev->driver->name, dev_name(dev));
  	}
  }
719994660   Dan Williams   libnvdimm: async ...
138
139
  void nd_device_notify(struct device *dev, enum nvdimm_event event)
  {
87a30e1f0   Dan Williams   driver-core, libn...
140
  	nd_device_lock(dev);
719994660   Dan Williams   libnvdimm: async ...
141
142
143
144
145
146
147
  	if (dev->driver) {
  		struct nd_device_driver *nd_drv;
  
  		nd_drv = to_nd_device_driver(dev->driver);
  		if (nd_drv->notify)
  			nd_drv->notify(dev, event);
  	}
87a30e1f0   Dan Williams   driver-core, libn...
148
  	nd_device_unlock(dev);
719994660   Dan Williams   libnvdimm: async ...
149
150
151
152
153
154
155
156
157
158
159
160
161
162
  }
  EXPORT_SYMBOL(nd_device_notify);
  
  void nvdimm_region_notify(struct nd_region *nd_region, enum nvdimm_event event)
  {
  	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(&nd_region->dev);
  
  	if (!nvdimm_bus)
  		return;
  
  	/* caller is responsible for holding a reference on the device */
  	nd_device_notify(&nd_region->dev, event);
  }
  EXPORT_SYMBOL_GPL(nvdimm_region_notify);
23f498448   Dan Williams   libnvdimm: rework...
163
164
165
166
167
168
169
170
171
172
173
174
  struct clear_badblocks_context {
  	resource_size_t phys, cleared;
  };
  
  static int nvdimm_clear_badblocks_region(struct device *dev, void *data)
  {
  	struct clear_badblocks_context *ctx = data;
  	struct nd_region *nd_region;
  	resource_size_t ndr_end;
  	sector_t sector;
  
  	/* make sure device is a region */
c42adf87e   Aneesh Kumar K.V   libnvdimm/region:...
175
  	if (!is_memory(dev))
23f498448   Dan Williams   libnvdimm: rework...
176
177
178
179
180
181
182
183
184
185
186
187
  		return 0;
  
  	nd_region = to_nd_region(dev);
  	ndr_end = nd_region->ndr_start + nd_region->ndr_size - 1;
  
  	/* make sure we are in the region */
  	if (ctx->phys < nd_region->ndr_start
  			|| (ctx->phys + ctx->cleared) > ndr_end)
  		return 0;
  
  	sector = (ctx->phys - nd_region->ndr_start) / 512;
  	badblocks_clear(&nd_region->bb, sector, ctx->cleared / 512);
975750a98   Toshi Kani   libnvdimm, pmem: ...
188
189
  	if (nd_region->bb_state)
  		sysfs_notify_dirent(nd_region->bb_state);
23f498448   Dan Williams   libnvdimm: rework...
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
  	return 0;
  }
  
  static void nvdimm_clear_badblocks_regions(struct nvdimm_bus *nvdimm_bus,
  		phys_addr_t phys, u64 cleared)
  {
  	struct clear_badblocks_context ctx = {
  		.phys = phys,
  		.cleared = cleared,
  	};
  
  	device_for_each_child(&nvdimm_bus->dev, &ctx,
  			nvdimm_clear_badblocks_region);
  }
  
  static void nvdimm_account_cleared_poison(struct nvdimm_bus *nvdimm_bus,
  		phys_addr_t phys, u64 cleared)
  {
  	if (cleared > 0)
aa9ad44a4   Dave Jiang   libnvdimm: move p...
209
  		badrange_forget(&nvdimm_bus->badrange, phys, cleared);
23f498448   Dan Williams   libnvdimm: rework...
210
211
212
213
  
  	if (cleared > 0 && cleared / 512)
  		nvdimm_clear_badblocks_regions(nvdimm_bus, phys, cleared);
  }
59e647398   Dan Williams   libnvdimm, pmem: ...
214
215
216
217
218
219
220
221
  long nvdimm_clear_poison(struct device *dev, phys_addr_t phys,
  		unsigned int len)
  {
  	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
  	struct nvdimm_bus_descriptor *nd_desc;
  	struct nd_cmd_clear_error clear_err;
  	struct nd_cmd_ars_cap ars_cap;
  	u32 clear_err_unit, mask;
0930a750c   Vishal Verma   libnvdimm: fix po...
222
  	unsigned int noio_flag;
59e647398   Dan Williams   libnvdimm, pmem: ...
223
224
225
226
227
228
  	int cmd_rc, rc;
  
  	if (!nvdimm_bus)
  		return -ENXIO;
  
  	nd_desc = nvdimm_bus->nd_desc;
1e8b8d961   Dave Jiang   libnvdimm: allow ...
229
230
231
232
  	/*
  	 * if ndctl does not exist, it's PMEM_LEGACY and
  	 * we want to just pretend everything is handled.
  	 */
59e647398   Dan Williams   libnvdimm, pmem: ...
233
  	if (!nd_desc->ndctl)
1e8b8d961   Dave Jiang   libnvdimm: allow ...
234
  		return len;
59e647398   Dan Williams   libnvdimm, pmem: ...
235
236
237
238
  
  	memset(&ars_cap, 0, sizeof(ars_cap));
  	ars_cap.address = phys;
  	ars_cap.length = len;
0930a750c   Vishal Verma   libnvdimm: fix po...
239
  	noio_flag = memalloc_noio_save();
59e647398   Dan Williams   libnvdimm, pmem: ...
240
241
  	rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_ARS_CAP, &ars_cap,
  			sizeof(ars_cap), &cmd_rc);
0930a750c   Vishal Verma   libnvdimm: fix po...
242
  	memalloc_noio_restore(noio_flag);
59e647398   Dan Williams   libnvdimm, pmem: ...
243
244
245
246
247
248
249
250
251
252
253
254
255
256
  	if (rc < 0)
  		return rc;
  	if (cmd_rc < 0)
  		return cmd_rc;
  	clear_err_unit = ars_cap.clear_err_unit;
  	if (!clear_err_unit || !is_power_of_2(clear_err_unit))
  		return -ENXIO;
  
  	mask = clear_err_unit - 1;
  	if ((phys | len) & mask)
  		return -ENXIO;
  	memset(&clear_err, 0, sizeof(clear_err));
  	clear_err.address = phys;
  	clear_err.length = len;
0930a750c   Vishal Verma   libnvdimm: fix po...
257
  	noio_flag = memalloc_noio_save();
59e647398   Dan Williams   libnvdimm, pmem: ...
258
259
  	rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_CLEAR_ERROR, &clear_err,
  			sizeof(clear_err), &cmd_rc);
0930a750c   Vishal Verma   libnvdimm: fix po...
260
  	memalloc_noio_restore(noio_flag);
59e647398   Dan Williams   libnvdimm, pmem: ...
261
262
263
264
  	if (rc < 0)
  		return rc;
  	if (cmd_rc < 0)
  		return cmd_rc;
e046114af   Vishal Verma   libnvdimm: clear ...
265

23f498448   Dan Williams   libnvdimm: rework...
266
  	nvdimm_account_cleared_poison(nvdimm_bus, phys, clear_err.cleared);
8d13c0290   Toshi Kani   libnvdimm: fix cl...
267

59e647398   Dan Williams   libnvdimm, pmem: ...
268
269
270
  	return clear_err.cleared;
  }
  EXPORT_SYMBOL_GPL(nvdimm_clear_poison);
18515942d   Dan Williams   libnvdimm: regist...
271
  static int nvdimm_bus_match(struct device *dev, struct device_driver *drv);
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
272
  static struct bus_type nvdimm_bus_type = {
e6dfb2de4   Dan Williams   libnvdimm, nfit: ...
273
  	.name = "nd",
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
274
275
276
277
  	.uevent = nvdimm_bus_uevent,
  	.match = nvdimm_bus_match,
  	.probe = nvdimm_bus_probe,
  	.remove = nvdimm_bus_remove,
476f848aa   Dan Williams   libnvdimm, pmem: ...
278
  	.shutdown = nvdimm_bus_shutdown,
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
279
  };
18515942d   Dan Williams   libnvdimm: regist...
280
281
282
283
284
285
286
287
  static void nvdimm_bus_release(struct device *dev)
  {
  	struct nvdimm_bus *nvdimm_bus;
  
  	nvdimm_bus = container_of(dev, struct nvdimm_bus, dev);
  	ida_simple_remove(&nd_ida, nvdimm_bus->id);
  	kfree(nvdimm_bus);
  }
87a30e1f0   Dan Williams   driver-core, libn...
288
  bool is_nvdimm_bus(struct device *dev)
18515942d   Dan Williams   libnvdimm: regist...
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
  {
  	return dev->release == nvdimm_bus_release;
  }
  
  struct nvdimm_bus *walk_to_nvdimm_bus(struct device *nd_dev)
  {
  	struct device *dev;
  
  	for (dev = nd_dev; dev; dev = dev->parent)
  		if (is_nvdimm_bus(dev))
  			break;
  	dev_WARN_ONCE(nd_dev, !dev, "invalid dev, not on nd bus
  ");
  	if (dev)
  		return to_nvdimm_bus(dev);
  	return NULL;
  }
  
  struct nvdimm_bus *to_nvdimm_bus(struct device *dev)
  {
  	struct nvdimm_bus *nvdimm_bus;
  
  	nvdimm_bus = container_of(dev, struct nvdimm_bus, dev);
  	WARN_ON(!is_nvdimm_bus(dev));
  	return nvdimm_bus;
  }
  EXPORT_SYMBOL_GPL(to_nvdimm_bus);
f29893965   Dave Jiang   acpi/nfit, libnvd...
316
317
318
319
320
  struct nvdimm_bus *nvdimm_to_bus(struct nvdimm *nvdimm)
  {
  	return to_nvdimm_bus(nvdimm->dev.parent);
  }
  EXPORT_SYMBOL_GPL(nvdimm_to_bus);
18515942d   Dan Williams   libnvdimm: regist...
321
322
323
324
325
326
327
328
329
330
331
  struct nvdimm_bus *nvdimm_bus_register(struct device *parent,
  		struct nvdimm_bus_descriptor *nd_desc)
  {
  	struct nvdimm_bus *nvdimm_bus;
  	int rc;
  
  	nvdimm_bus = kzalloc(sizeof(*nvdimm_bus), GFP_KERNEL);
  	if (!nvdimm_bus)
  		return NULL;
  	INIT_LIST_HEAD(&nvdimm_bus->list);
  	INIT_LIST_HEAD(&nvdimm_bus->mapping_list);
b70d31d05   Dan Williams   libnvdimm/bus: St...
332
  	init_waitqueue_head(&nvdimm_bus->wait);
18515942d   Dan Williams   libnvdimm: regist...
333
  	nvdimm_bus->id = ida_simple_get(&nd_ida, 0, 0, GFP_KERNEL);
18515942d   Dan Williams   libnvdimm: regist...
334
335
336
337
  	if (nvdimm_bus->id < 0) {
  		kfree(nvdimm_bus);
  		return NULL;
  	}
9bf3aa446   Ocean He   libnvdimm, bus: C...
338
339
  	mutex_init(&nvdimm_bus->reconfig_mutex);
  	badrange_init(&nvdimm_bus->badrange);
18515942d   Dan Williams   libnvdimm: regist...
340
341
342
343
344
  	nvdimm_bus->nd_desc = nd_desc;
  	nvdimm_bus->dev.parent = parent;
  	nvdimm_bus->dev.release = nvdimm_bus_release;
  	nvdimm_bus->dev.groups = nd_desc->attr_groups;
  	nvdimm_bus->dev.bus = &nvdimm_bus_type;
1ff19f487   Oliver O'Halloran   libnvdimm: Add of...
345
  	nvdimm_bus->dev.of_node = nd_desc->of_node;
18515942d   Dan Williams   libnvdimm: regist...
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
  	dev_set_name(&nvdimm_bus->dev, "ndbus%d", nvdimm_bus->id);
  	rc = device_register(&nvdimm_bus->dev);
  	if (rc) {
  		dev_dbg(&nvdimm_bus->dev, "registration failed: %d
  ", rc);
  		goto err;
  	}
  
  	return nvdimm_bus;
   err:
  	put_device(&nvdimm_bus->dev);
  	return NULL;
  }
  EXPORT_SYMBOL_GPL(nvdimm_bus_register);
  
  void nvdimm_bus_unregister(struct nvdimm_bus *nvdimm_bus)
  {
  	if (!nvdimm_bus)
  		return;
  	device_unregister(&nvdimm_bus->dev);
  }
  EXPORT_SYMBOL_GPL(nvdimm_bus_unregister);
  
  static int child_unregister(struct device *dev, void *data)
  {
  	/*
  	 * the singular ndctl class device per bus needs to be
  	 * "device_destroy"ed, so skip it here
  	 *
  	 * i.e. remove classless children
  	 */
  	if (dev->class)
7d988097c   Dave Jiang   acpi/nfit, libnvd...
378
379
380
381
382
383
384
385
  		return 0;
  
  	if (is_nvdimm(dev)) {
  		struct nvdimm *nvdimm = to_nvdimm(dev);
  		bool dev_put = false;
  
  		/* We are shutting down. Make state frozen artificially. */
  		nvdimm_bus_lock(dev);
d78c620a2   Dan Williams   libnvdimm/securit...
386
  		set_bit(NVDIMM_SECURITY_FROZEN, &nvdimm->sec.flags);
7d988097c   Dave Jiang   acpi/nfit, libnvd...
387
388
389
390
391
392
393
394
  		if (test_and_clear_bit(NDD_WORK_PENDING, &nvdimm->flags))
  			dev_put = true;
  		nvdimm_bus_unlock(dev);
  		cancel_delayed_work_sync(&nvdimm->dwork);
  		if (dev_put)
  			put_device(dev);
  	}
  	nd_device_unregister(dev, ND_SYNC);
18515942d   Dan Williams   libnvdimm: regist...
395
396
  	return 0;
  }
aa9ad44a4   Dave Jiang   libnvdimm: move p...
397
  static void free_badrange_list(struct list_head *badrange_list)
18515942d   Dan Williams   libnvdimm: regist...
398
  {
aa9ad44a4   Dave Jiang   libnvdimm: move p...
399
  	struct badrange_entry *bre, *next;
18515942d   Dan Williams   libnvdimm: regist...
400

aa9ad44a4   Dave Jiang   libnvdimm: move p...
401
402
403
  	list_for_each_entry_safe(bre, next, badrange_list, list) {
  		list_del(&bre->list);
  		kfree(bre);
18515942d   Dan Williams   libnvdimm: regist...
404
  	}
aa9ad44a4   Dave Jiang   libnvdimm: move p...
405
  	list_del_init(badrange_list);
18515942d   Dan Williams   libnvdimm: regist...
406
407
408
409
410
411
412
413
414
  }
  
  static int nd_bus_remove(struct device *dev)
  {
  	struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
  
  	mutex_lock(&nvdimm_bus_list_mutex);
  	list_del_init(&nvdimm_bus->list);
  	mutex_unlock(&nvdimm_bus_list_mutex);
b70d31d05   Dan Williams   libnvdimm/bus: St...
415
416
  	wait_event(nvdimm_bus->wait,
  			atomic_read(&nvdimm_bus->ioctl_active) == 0);
18515942d   Dan Williams   libnvdimm: regist...
417
418
  	nd_synchronize();
  	device_for_each_child(&nvdimm_bus->dev, NULL, child_unregister);
aa9ad44a4   Dave Jiang   libnvdimm: move p...
419
420
421
  	spin_lock(&nvdimm_bus->badrange.lock);
  	free_badrange_list(&nvdimm_bus->badrange.list);
  	spin_unlock(&nvdimm_bus->badrange.lock);
18515942d   Dan Williams   libnvdimm: regist...
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
  
  	nvdimm_bus_destroy_ndctl(nvdimm_bus);
  
  	return 0;
  }
  
  static int nd_bus_probe(struct device *dev)
  {
  	struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
  	int rc;
  
  	rc = nvdimm_bus_create_ndctl(nvdimm_bus);
  	if (rc)
  		return rc;
  
  	mutex_lock(&nvdimm_bus_list_mutex);
  	list_add_tail(&nvdimm_bus->list, &nvdimm_bus_list);
  	mutex_unlock(&nvdimm_bus_list_mutex);
  
  	/* enable bus provider attributes to look up their local context */
  	dev_set_drvdata(dev, nvdimm_bus->nd_desc);
  
  	return 0;
  }
  
  static struct nd_device_driver nd_bus_driver = {
  	.probe = nd_bus_probe,
  	.remove = nd_bus_remove,
  	.drv = {
  		.name = "nd_bus",
  		.suppress_bind_attrs = true,
  		.bus = &nvdimm_bus_type,
  		.owner = THIS_MODULE,
  		.mod_name = KBUILD_MODNAME,
  	},
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
457
  };
18515942d   Dan Williams   libnvdimm: regist...
458
459
460
461
462
463
464
465
466
  static int nvdimm_bus_match(struct device *dev, struct device_driver *drv)
  {
  	struct nd_device_driver *nd_drv = to_nd_device_driver(drv);
  
  	if (is_nvdimm_bus(dev) && nd_drv == &nd_bus_driver)
  		return true;
  
  	return !!test_bit(to_nd_device_type(dev), &nd_drv->type);
  }
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
  static ASYNC_DOMAIN_EXCLUSIVE(nd_async_domain);
  
  void nd_synchronize(void)
  {
  	async_synchronize_full_domain(&nd_async_domain);
  }
  EXPORT_SYMBOL_GPL(nd_synchronize);
  
  static void nd_async_device_register(void *d, async_cookie_t cookie)
  {
  	struct device *dev = d;
  
  	if (device_add(dev) != 0) {
  		dev_err(dev, "%s: failed
  ", __func__);
  		put_device(dev);
  	}
  	put_device(dev);
b6eae0f61   Alexander Duyck   libnvdimm: Hold r...
485
486
  	if (dev->parent)
  		put_device(dev->parent);
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
487
488
489
490
491
  }
  
  static void nd_async_device_unregister(void *d, async_cookie_t cookie)
  {
  	struct device *dev = d;
0ba1c6348   Dan Williams   libnvdimm: write ...
492
493
494
  	/* flush bus operations before delete */
  	nvdimm_bus_lock(dev);
  	nvdimm_bus_unlock(dev);
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
495
496
497
  	device_unregister(dev);
  	put_device(dev);
  }
8c2f7e865   Dan Williams   libnvdimm: infras...
498
  void __nd_device_register(struct device *dev)
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
499
  {
cd03412a5   Dan Williams   libnvdimm, dax: i...
500
501
  	if (!dev)
  		return;
1a091d16d   Alexander Duyck   libnvdimm: Set de...
502
503
504
505
506
507
508
509
510
  
  	/*
  	 * Ensure that region devices always have their NUMA node set as
  	 * early as possible. This way we are able to make certain that
  	 * any memory associated with the creation and the creation
  	 * itself of the region is associated with the correct node.
  	 */
  	if (is_nd_region(dev))
  		set_dev_node(dev, to_nd_region(dev)->numa_node);
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
511
  	dev->bus = &nvdimm_bus_type;
af87b9a78   Alexander Duyck   libnvdimm: Schedu...
512
  	if (dev->parent) {
b6eae0f61   Alexander Duyck   libnvdimm: Hold r...
513
  		get_device(dev->parent);
af87b9a78   Alexander Duyck   libnvdimm: Schedu...
514
515
516
  		if (dev_to_node(dev) == NUMA_NO_NODE)
  			set_dev_node(dev, dev_to_node(dev->parent));
  	}
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
517
  	get_device(dev);
af87b9a78   Alexander Duyck   libnvdimm: Schedu...
518
519
520
  
  	async_schedule_dev_domain(nd_async_device_register, dev,
  				  &nd_async_domain);
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
521
  }
8c2f7e865   Dan Williams   libnvdimm: infras...
522
523
524
525
526
527
  
  void nd_device_register(struct device *dev)
  {
  	device_initialize(dev);
  	__nd_device_register(dev);
  }
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
528
529
530
531
  EXPORT_SYMBOL(nd_device_register);
  
  void nd_device_unregister(struct device *dev, enum nd_async_mode mode)
  {
8aac0e233   Dan Williams   libnvdimm/bus: Pr...
532
  	bool killed;
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
533
534
  	switch (mode) {
  	case ND_ASYNC:
8aac0e233   Dan Williams   libnvdimm/bus: Pr...
535
536
537
538
539
540
541
542
  		/*
  		 * In the async case this is being triggered with the
  		 * device lock held and the unregistration work needs to
  		 * be moved out of line iff this is thread has won the
  		 * race to schedule the deletion.
  		 */
  		if (!kill_device(dev))
  			return;
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
543
544
545
546
547
  		get_device(dev);
  		async_schedule_domain(nd_async_device_unregister, dev,
  				&nd_async_domain);
  		break;
  	case ND_SYNC:
8aac0e233   Dan Williams   libnvdimm/bus: Pr...
548
549
550
551
552
553
554
  		/*
  		 * In the sync case the device is being unregistered due
  		 * to a state change of the parent. Claim the kill state
  		 * to synchronize against other unregistration requests,
  		 * or otherwise let the async path handle it if the
  		 * unregistration was already queued.
  		 */
87a30e1f0   Dan Williams   driver-core, libn...
555
  		nd_device_lock(dev);
8aac0e233   Dan Williams   libnvdimm/bus: Pr...
556
  		killed = kill_device(dev);
87a30e1f0   Dan Williams   driver-core, libn...
557
  		nd_device_unlock(dev);
8aac0e233   Dan Williams   libnvdimm/bus: Pr...
558
559
560
  
  		if (!killed)
  			return;
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
  		nd_synchronize();
  		device_unregister(dev);
  		break;
  	}
  }
  EXPORT_SYMBOL(nd_device_unregister);
  
  /**
   * __nd_driver_register() - register a region or a namespace driver
   * @nd_drv: driver to register
   * @owner: automatically set by nd_driver_register() macro
   * @mod_name: automatically set by nd_driver_register() macro
   */
  int __nd_driver_register(struct nd_device_driver *nd_drv, struct module *owner,
  		const char *mod_name)
  {
  	struct device_driver *drv = &nd_drv->drv;
  
  	if (!nd_drv->type) {
d75f773c8   Sakari Ailus   treewide: Switch ...
580
581
  		pr_debug("driver type bitmask not set (%ps)
  ",
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
582
583
584
  				__builtin_return_address(0));
  		return -EINVAL;
  	}
6cf9c5bab   Dan Williams   libnvdimm: stop r...
585
586
587
  	if (!nd_drv->probe) {
  		pr_debug("%s ->probe() must be specified
  ", mod_name);
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
588
589
590
591
592
593
594
595
596
597
  		return -EINVAL;
  	}
  
  	drv->bus = &nvdimm_bus_type;
  	drv->owner = owner;
  	drv->mod_name = mod_name;
  
  	return driver_register(drv);
  }
  EXPORT_SYMBOL(__nd_driver_register);
581388209   Dan Williams   libnvdimm, nfit: ...
598
599
  int nvdimm_revalidate_disk(struct gendisk *disk)
  {
52c44d93c   Dan Williams   block: remove ->d...
600
  	struct device *dev = disk_to_dev(disk)->parent;
581388209   Dan Williams   libnvdimm, nfit: ...
601
  	struct nd_region *nd_region = to_nd_region(dev->parent);
254a4cd50   Robert Elliott   linvdimm, pmem: P...
602
  	int disk_ro = get_disk_ro(disk);
581388209   Dan Williams   libnvdimm, nfit: ...
603

254a4cd50   Robert Elliott   linvdimm, pmem: P...
604
605
606
607
608
  	/*
  	 * Upgrade to read-only if the region is read-only preserve as
  	 * read-only if the disk is already read-only.
  	 */
  	if (disk_ro || nd_region->ro == disk_ro)
581388209   Dan Williams   libnvdimm, nfit: ...
609
  		return 0;
254a4cd50   Robert Elliott   linvdimm, pmem: P...
610
611
612
613
  	dev_info(dev, "%s read-only, marking %s read-only
  ",
  			dev_name(&nd_region->dev), disk->disk_name);
  	set_disk_ro(disk, 1);
581388209   Dan Williams   libnvdimm, nfit: ...
614
615
616
617
618
  
  	return 0;
  
  }
  EXPORT_SYMBOL(nvdimm_revalidate_disk);
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
  static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
  		char *buf)
  {
  	return sprintf(buf, ND_DEVICE_MODALIAS_FMT "
  ",
  			to_nd_device_type(dev));
  }
  static DEVICE_ATTR_RO(modalias);
  
  static ssize_t devtype_show(struct device *dev, struct device_attribute *attr,
  		char *buf)
  {
  	return sprintf(buf, "%s
  ", dev->type->name);
  }
  static DEVICE_ATTR_RO(devtype);
  
  static struct attribute *nd_device_attributes[] = {
  	&dev_attr_modalias.attr,
  	&dev_attr_devtype.attr,
  	NULL,
  };
c01dafad7   Qian Cai   libnvdimm: Fix co...
641
  /*
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
642
643
644
645
   * nd_device_attribute_group - generic attributes for all devices on an nd bus
   */
  struct attribute_group nd_device_attribute_group = {
  	.attrs = nd_device_attributes,
e6dfb2de4   Dan Williams   libnvdimm, nfit: ...
646
  };
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
647
  EXPORT_SYMBOL_GPL(nd_device_attribute_group);
e6dfb2de4   Dan Williams   libnvdimm, nfit: ...
648

74ae66c3b   Toshi Kani   libnvdimm: Add sy...
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
  static ssize_t numa_node_show(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
  	return sprintf(buf, "%d
  ", dev_to_node(dev));
  }
  static DEVICE_ATTR_RO(numa_node);
  
  static struct attribute *nd_numa_attributes[] = {
  	&dev_attr_numa_node.attr,
  	NULL,
  };
  
  static umode_t nd_numa_attr_visible(struct kobject *kobj, struct attribute *a,
  		int n)
  {
  	if (!IS_ENABLED(CONFIG_NUMA))
  		return 0;
  
  	return a->mode;
  }
c01dafad7   Qian Cai   libnvdimm: Fix co...
670
  /*
74ae66c3b   Toshi Kani   libnvdimm: Add sy...
671
672
673
674
675
676
677
   * nd_numa_attribute_group - NUMA attributes for all devices on an nd bus
   */
  struct attribute_group nd_numa_attribute_group = {
  	.attrs = nd_numa_attributes,
  	.is_visible = nd_numa_attr_visible,
  };
  EXPORT_SYMBOL_GPL(nd_numa_attribute_group);
45def22c1   Dan Williams   libnvdimm: contro...
678
679
680
681
682
683
684
  int nvdimm_bus_create_ndctl(struct nvdimm_bus *nvdimm_bus)
  {
  	dev_t devt = MKDEV(nvdimm_bus_major, nvdimm_bus->id);
  	struct device *dev;
  
  	dev = device_create(nd_class, &nvdimm_bus->dev, devt, nvdimm_bus,
  			"ndctl%d", nvdimm_bus->id);
425889581   Dan Williams   libnvdimm: IS_ERR...
685
  	if (IS_ERR(dev))
45def22c1   Dan Williams   libnvdimm: contro...
686
687
688
  		dev_dbg(&nvdimm_bus->dev, "failed to register ndctl%d: %ld
  ",
  				nvdimm_bus->id, PTR_ERR(dev));
425889581   Dan Williams   libnvdimm: IS_ERR...
689
  	return PTR_ERR_OR_ZERO(dev);
45def22c1   Dan Williams   libnvdimm: contro...
690
691
692
693
694
695
  }
  
  void nvdimm_bus_destroy_ndctl(struct nvdimm_bus *nvdimm_bus)
  {
  	device_destroy(nd_class, MKDEV(nvdimm_bus_major, nvdimm_bus->id));
  }
62232e45f   Dan Williams   libnvdimm: contro...
696
697
698
699
  static const struct nd_cmd_desc __nd_cmd_dimm_descs[] = {
  	[ND_CMD_IMPLEMENTED] = { },
  	[ND_CMD_SMART] = {
  		.out_num = 2,
211291126   Dan Williams   libnvdimm: fix sm...
700
  		.out_sizes = { 4, 128, },
62232e45f   Dan Williams   libnvdimm: contro...
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
  	},
  	[ND_CMD_SMART_THRESHOLD] = {
  		.out_num = 2,
  		.out_sizes = { 4, 8, },
  	},
  	[ND_CMD_DIMM_FLAGS] = {
  		.out_num = 2,
  		.out_sizes = { 4, 4 },
  	},
  	[ND_CMD_GET_CONFIG_SIZE] = {
  		.out_num = 3,
  		.out_sizes = { 4, 4, 4, },
  	},
  	[ND_CMD_GET_CONFIG_DATA] = {
  		.in_num = 2,
  		.in_sizes = { 4, 4, },
  		.out_num = 2,
  		.out_sizes = { 4, UINT_MAX, },
  	},
  	[ND_CMD_SET_CONFIG_DATA] = {
  		.in_num = 3,
  		.in_sizes = { 4, 4, UINT_MAX, },
  		.out_num = 1,
  		.out_sizes = { 4, },
  	},
  	[ND_CMD_VENDOR] = {
  		.in_num = 3,
  		.in_sizes = { 4, 4, UINT_MAX, },
  		.out_num = 3,
  		.out_sizes = { 4, 4, UINT_MAX, },
  	},
31eca76ba   Dan Williams   nfit, libnvdimm: ...
732
733
734
735
736
737
  	[ND_CMD_CALL] = {
  		.in_num = 2,
  		.in_sizes = { sizeof(struct nd_cmd_pkg), UINT_MAX, },
  		.out_num = 1,
  		.out_sizes = { UINT_MAX, },
  	},
62232e45f   Dan Williams   libnvdimm: contro...
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
  };
  
  const struct nd_cmd_desc *nd_cmd_dimm_desc(int cmd)
  {
  	if (cmd < ARRAY_SIZE(__nd_cmd_dimm_descs))
  		return &__nd_cmd_dimm_descs[cmd];
  	return NULL;
  }
  EXPORT_SYMBOL_GPL(nd_cmd_dimm_desc);
  
  static const struct nd_cmd_desc __nd_cmd_bus_descs[] = {
  	[ND_CMD_IMPLEMENTED] = { },
  	[ND_CMD_ARS_CAP] = {
  		.in_num = 2,
  		.in_sizes = { 8, 8, },
4577b0665   Dan Williams   nfit: update addr...
753
754
  		.out_num = 4,
  		.out_sizes = { 4, 4, 4, 4, },
62232e45f   Dan Williams   libnvdimm: contro...
755
756
  	},
  	[ND_CMD_ARS_START] = {
4577b0665   Dan Williams   nfit: update addr...
757
758
759
760
  		.in_num = 5,
  		.in_sizes = { 8, 8, 2, 1, 5, },
  		.out_num = 2,
  		.out_sizes = { 4, 4, },
62232e45f   Dan Williams   libnvdimm: contro...
761
762
  	},
  	[ND_CMD_ARS_STATUS] = {
747ffe11b   Dan Williams   libnvdimm, tools/...
763
764
  		.out_num = 3,
  		.out_sizes = { 4, 4, UINT_MAX, },
62232e45f   Dan Williams   libnvdimm: contro...
765
  	},
d4f323672   Dan Williams   nfit, libnvdimm: ...
766
767
768
769
770
771
  	[ND_CMD_CLEAR_ERROR] = {
  		.in_num = 2,
  		.in_sizes = { 8, 8, },
  		.out_num = 3,
  		.out_sizes = { 4, 4, 8, },
  	},
31eca76ba   Dan Williams   nfit, libnvdimm: ...
772
773
774
775
776
777
  	[ND_CMD_CALL] = {
  		.in_num = 2,
  		.in_sizes = { sizeof(struct nd_cmd_pkg), UINT_MAX, },
  		.out_num = 1,
  		.out_sizes = { UINT_MAX, },
  	},
62232e45f   Dan Williams   libnvdimm: contro...
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
  };
  
  const struct nd_cmd_desc *nd_cmd_bus_desc(int cmd)
  {
  	if (cmd < ARRAY_SIZE(__nd_cmd_bus_descs))
  		return &__nd_cmd_bus_descs[cmd];
  	return NULL;
  }
  EXPORT_SYMBOL_GPL(nd_cmd_bus_desc);
  
  u32 nd_cmd_in_size(struct nvdimm *nvdimm, int cmd,
  		const struct nd_cmd_desc *desc, int idx, void *buf)
  {
  	if (idx >= desc->in_num)
  		return UINT_MAX;
  
  	if (desc->in_sizes[idx] < UINT_MAX)
  		return desc->in_sizes[idx];
  
  	if (nvdimm && cmd == ND_CMD_SET_CONFIG_DATA && idx == 2) {
  		struct nd_cmd_set_config_hdr *hdr = buf;
  
  		return hdr->in_length;
  	} else if (nvdimm && cmd == ND_CMD_VENDOR && idx == 2) {
  		struct nd_cmd_vendor_hdr *hdr = buf;
  
  		return hdr->in_length;
31eca76ba   Dan Williams   nfit, libnvdimm: ...
805
806
807
808
  	} else if (cmd == ND_CMD_CALL) {
  		struct nd_cmd_pkg *pkg = buf;
  
  		return pkg->nd_size_in;
62232e45f   Dan Williams   libnvdimm: contro...
809
810
811
812
813
814
815
816
  	}
  
  	return UINT_MAX;
  }
  EXPORT_SYMBOL_GPL(nd_cmd_in_size);
  
  u32 nd_cmd_out_size(struct nvdimm *nvdimm, int cmd,
  		const struct nd_cmd_desc *desc, int idx, const u32 *in_field,
efda1b5d8   Dan Williams   acpi, nfit, libnv...
817
  		const u32 *out_field, unsigned long remainder)
62232e45f   Dan Williams   libnvdimm: contro...
818
819
820
821
822
823
824
825
826
827
828
  {
  	if (idx >= desc->out_num)
  		return UINT_MAX;
  
  	if (desc->out_sizes[idx] < UINT_MAX)
  		return desc->out_sizes[idx];
  
  	if (nvdimm && cmd == ND_CMD_GET_CONFIG_DATA && idx == 1)
  		return in_field[1];
  	else if (nvdimm && cmd == ND_CMD_VENDOR && idx == 2)
  		return out_field[1];
efda1b5d8   Dan Williams   acpi, nfit, libnv...
829
830
831
832
833
834
835
836
837
838
839
840
841
842
  	else if (!nvdimm && cmd == ND_CMD_ARS_STATUS && idx == 2) {
  		/*
  		 * Per table 9-276 ARS Data in ACPI 6.1, out_field[1] is
  		 * "Size of Output Buffer in bytes, including this
  		 * field."
  		 */
  		if (out_field[1] < 4)
  			return 0;
  		/*
  		 * ACPI 6.1 is ambiguous if 'status' is included in the
  		 * output size. If we encounter an output size that
  		 * overshoots the remainder by 4 bytes, assume it was
  		 * including 'status'.
  		 */
286e87718   Vishal Verma   libnvdimm: fix ar...
843
  		if (out_field[1] - 4 == remainder)
efda1b5d8   Dan Williams   acpi, nfit, libnv...
844
  			return remainder;
286e87718   Vishal Verma   libnvdimm: fix ar...
845
  		return out_field[1] - 8;
efda1b5d8   Dan Williams   acpi, nfit, libnv...
846
  	} else if (cmd == ND_CMD_CALL) {
31eca76ba   Dan Williams   nfit, libnvdimm: ...
847
848
849
850
  		struct nd_cmd_pkg *pkg = (struct nd_cmd_pkg *) in_field;
  
  		return pkg->nd_size_out;
  	}
62232e45f   Dan Williams   libnvdimm: contro...
851
852
853
854
  
  	return UINT_MAX;
  }
  EXPORT_SYMBOL_GPL(nd_cmd_out_size);
bf9bccc14   Dan Williams   libnvdimm: pmem l...
855
  void wait_nvdimm_bus_probe_idle(struct device *dev)
eaf961536   Dan Williams   libnvdimm, nfit: ...
856
  {
bf9bccc14   Dan Williams   libnvdimm: pmem l...
857
  	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
eaf961536   Dan Williams   libnvdimm, nfit: ...
858
859
860
  	do {
  		if (nvdimm_bus->probe_active == 0)
  			break;
ca6bf264f   Dan Williams   libnvdimm/bus: Fi...
861
  		nvdimm_bus_unlock(dev);
87a30e1f0   Dan Williams   driver-core, libn...
862
  		nd_device_unlock(dev);
b70d31d05   Dan Williams   libnvdimm/bus: St...
863
  		wait_event(nvdimm_bus->wait,
eaf961536   Dan Williams   libnvdimm, nfit: ...
864
  				nvdimm_bus->probe_active == 0);
87a30e1f0   Dan Williams   driver-core, libn...
865
  		nd_device_lock(dev);
ca6bf264f   Dan Williams   libnvdimm/bus: Fi...
866
  		nvdimm_bus_lock(dev);
eaf961536   Dan Williams   libnvdimm, nfit: ...
867
868
  	} while (true);
  }
006358b35   Dave Jiang   libnvdimm: add su...
869
  static int nd_pmem_forget_poison_check(struct device *dev, void *data)
d4f323672   Dan Williams   nfit, libnvdimm: ...
870
  {
006358b35   Dave Jiang   libnvdimm: add su...
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
  	struct nd_cmd_clear_error *clear_err =
  		(struct nd_cmd_clear_error *)data;
  	struct nd_btt *nd_btt = is_nd_btt(dev) ? to_nd_btt(dev) : NULL;
  	struct nd_pfn *nd_pfn = is_nd_pfn(dev) ? to_nd_pfn(dev) : NULL;
  	struct nd_dax *nd_dax = is_nd_dax(dev) ? to_nd_dax(dev) : NULL;
  	struct nd_namespace_common *ndns = NULL;
  	struct nd_namespace_io *nsio;
  	resource_size_t offset = 0, end_trunc = 0, start, end, pstart, pend;
  
  	if (nd_dax || !dev->driver)
  		return 0;
  
  	start = clear_err->address;
  	end = clear_err->address + clear_err->cleared - 1;
  
  	if (nd_btt || nd_pfn || nd_dax) {
  		if (nd_btt)
  			ndns = nd_btt->ndns;
  		else if (nd_pfn)
  			ndns = nd_pfn->ndns;
  		else if (nd_dax)
  			ndns = nd_dax->nd_pfn.ndns;
  
  		if (!ndns)
  			return 0;
  	} else
  		ndns = to_ndns(dev);
  
  	nsio = to_nd_namespace_io(&ndns->dev);
  	pstart = nsio->res.start + offset;
  	pend = nsio->res.end - end_trunc;
  
  	if ((pstart >= start) && (pend <= end))
d4f323672   Dan Williams   nfit, libnvdimm: ...
904
  		return -EBUSY;
006358b35   Dave Jiang   libnvdimm: add su...
905

d4f323672   Dan Williams   nfit, libnvdimm: ...
906
  	return 0;
006358b35   Dave Jiang   libnvdimm: add su...
907
908
909
910
911
912
  
  }
  
  static int nd_ns_forget_poison_check(struct device *dev, void *data)
  {
  	return device_for_each_child(dev, data, nd_pmem_forget_poison_check);
d4f323672   Dan Williams   nfit, libnvdimm: ...
913
  }
eaf961536   Dan Williams   libnvdimm, nfit: ...
914
  /* set_config requires an idle interleave set */
87bf572e1   Dan Williams   nfit: disable use...
915
  static int nd_cmd_clear_to_send(struct nvdimm_bus *nvdimm_bus,
006358b35   Dave Jiang   libnvdimm: add su...
916
  		struct nvdimm *nvdimm, unsigned int cmd, void *data)
eaf961536   Dan Williams   libnvdimm, nfit: ...
917
  {
87bf572e1   Dan Williams   nfit: disable use...
918
919
920
921
  	struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc;
  
  	/* ask the bus provider if it would like to block this request */
  	if (nd_desc->clear_to_send) {
b3ed2ce02   Dave Jiang   acpi/nfit: Add su...
922
  		int rc = nd_desc->clear_to_send(nd_desc, nvdimm, cmd, data);
87bf572e1   Dan Williams   nfit: disable use...
923
924
925
926
  
  		if (rc)
  			return rc;
  	}
eaf961536   Dan Williams   libnvdimm, nfit: ...
927

d4f323672   Dan Williams   nfit, libnvdimm: ...
928
929
  	/* require clear error to go through the pmem driver */
  	if (!nvdimm && cmd == ND_CMD_CLEAR_ERROR)
006358b35   Dave Jiang   libnvdimm: add su...
930
931
  		return device_for_each_child(&nvdimm_bus->dev, data,
  				nd_ns_forget_poison_check);
d4f323672   Dan Williams   nfit, libnvdimm: ...
932

eaf961536   Dan Williams   libnvdimm, nfit: ...
933
934
  	if (!nvdimm || cmd != ND_CMD_SET_CONFIG_DATA)
  		return 0;
87bf572e1   Dan Williams   nfit: disable use...
935
  	/* prevent label manipulation while the kernel owns label updates */
bf9bccc14   Dan Williams   libnvdimm: pmem l...
936
  	wait_nvdimm_bus_probe_idle(&nvdimm_bus->dev);
eaf961536   Dan Williams   libnvdimm, nfit: ...
937
938
939
940
  	if (atomic_read(&nvdimm->busy))
  		return -EBUSY;
  	return 0;
  }
62232e45f   Dan Williams   libnvdimm: contro...
941
942
943
944
  static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm,
  		int read_only, unsigned int ioctl_cmd, unsigned long arg)
  {
  	struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc;
62232e45f   Dan Williams   libnvdimm: contro...
945
946
  	const struct nd_cmd_desc *desc = NULL;
  	unsigned int cmd = _IOC_NR(ioctl_cmd);
62232e45f   Dan Williams   libnvdimm: contro...
947
  	struct device *dev = &nvdimm_bus->dev;
58738c495   Dan Williams   libnvdimm: fix in...
948
  	void __user *p = (void __user *) arg;
6de5d06e6   Dan Williams   libnvdimm/bus: Pr...
949
  	char *out_env = NULL, *in_env = NULL;
62232e45f   Dan Williams   libnvdimm: contro...
950
  	const char *cmd_name, *dimm_name;
58738c495   Dan Williams   libnvdimm: fix in...
951
952
  	u32 in_len = 0, out_len = 0;
  	unsigned int func = cmd;
e3654eca7   Dan Williams   nfit, libnvdimm: ...
953
  	unsigned long cmd_mask;
58738c495   Dan Williams   libnvdimm: fix in...
954
  	struct nd_cmd_pkg pkg;
006358b35   Dave Jiang   libnvdimm: add su...
955
  	int rc, i, cmd_rc;
6de5d06e6   Dan Williams   libnvdimm/bus: Pr...
956
  	void *buf = NULL;
58738c495   Dan Williams   libnvdimm: fix in...
957
  	u64 buf_len = 0;
62232e45f   Dan Williams   libnvdimm: contro...
958
959
960
961
  
  	if (nvdimm) {
  		desc = nd_cmd_dimm_desc(cmd);
  		cmd_name = nvdimm_cmd_name(cmd);
e3654eca7   Dan Williams   nfit, libnvdimm: ...
962
  		cmd_mask = nvdimm->cmd_mask;
62232e45f   Dan Williams   libnvdimm: contro...
963
964
965
966
  		dimm_name = dev_name(&nvdimm->dev);
  	} else {
  		desc = nd_cmd_bus_desc(cmd);
  		cmd_name = nvdimm_bus_cmd_name(cmd);
e3654eca7   Dan Williams   nfit, libnvdimm: ...
967
  		cmd_mask = nd_desc->cmd_mask;
62232e45f   Dan Williams   libnvdimm: contro...
968
969
  		dimm_name = "bus";
  	}
31eca76ba   Dan Williams   nfit, libnvdimm: ...
970
971
972
973
  	if (cmd == ND_CMD_CALL) {
  		if (copy_from_user(&pkg, p, sizeof(pkg)))
  			return -EFAULT;
  	}
62232e45f   Dan Williams   libnvdimm: contro...
974
  	if (!desc || (desc->out_num + desc->in_num == 0) ||
e3654eca7   Dan Williams   nfit, libnvdimm: ...
975
  			!test_bit(cmd, &cmd_mask))
62232e45f   Dan Williams   libnvdimm: contro...
976
977
978
979
  		return -ENOTTY;
  
  	/* fail write commands (when read-only) */
  	if (read_only)
07accfa9d   Jerry Hoemann   libnvdimm: Fix se...
980
981
982
983
  		switch (cmd) {
  		case ND_CMD_VENDOR:
  		case ND_CMD_SET_CONFIG_DATA:
  		case ND_CMD_ARS_START:
d4f323672   Dan Williams   nfit, libnvdimm: ...
984
  		case ND_CMD_CLEAR_ERROR:
31eca76ba   Dan Williams   nfit, libnvdimm: ...
985
  		case ND_CMD_CALL:
ca6bf264f   Dan Williams   libnvdimm/bus: Fi...
986
987
  			dev_dbg(dev, "'%s' command while read-only.
  ",
62232e45f   Dan Williams   libnvdimm: contro...
988
989
990
991
992
993
994
995
  					nvdimm ? nvdimm_cmd_name(cmd)
  					: nvdimm_bus_cmd_name(cmd));
  			return -EPERM;
  		default:
  			break;
  		}
  
  	/* process an input envelope */
6de5d06e6   Dan Williams   libnvdimm/bus: Pr...
996
997
998
  	in_env = kzalloc(ND_CMD_MAX_ENVELOPE, GFP_KERNEL);
  	if (!in_env)
  		return -ENOMEM;
62232e45f   Dan Williams   libnvdimm: contro...
999
1000
1001
1002
1003
1004
1005
1006
  	for (i = 0; i < desc->in_num; i++) {
  		u32 in_size, copy;
  
  		in_size = nd_cmd_in_size(nvdimm, cmd, desc, i, in_env);
  		if (in_size == UINT_MAX) {
  			dev_err(dev, "%s:%s unknown input size cmd: %s field: %d
  ",
  					__func__, dimm_name, cmd_name, i);
6de5d06e6   Dan Williams   libnvdimm/bus: Pr...
1007
1008
  			rc = -ENXIO;
  			goto out;
62232e45f   Dan Williams   libnvdimm: contro...
1009
  		}
6de5d06e6   Dan Williams   libnvdimm/bus: Pr...
1010
1011
  		if (in_len < ND_CMD_MAX_ENVELOPE)
  			copy = min_t(u32, ND_CMD_MAX_ENVELOPE - in_len, in_size);
62232e45f   Dan Williams   libnvdimm: contro...
1012
1013
  		else
  			copy = 0;
6de5d06e6   Dan Williams   libnvdimm/bus: Pr...
1014
1015
1016
1017
  		if (copy && copy_from_user(&in_env[in_len], p + in_len, copy)) {
  			rc = -EFAULT;
  			goto out;
  		}
62232e45f   Dan Williams   libnvdimm: contro...
1018
1019
  		in_len += in_size;
  	}
31eca76ba   Dan Williams   nfit, libnvdimm: ...
1020
  	if (cmd == ND_CMD_CALL) {
53b85a449   Jerry Hoemann   libnvdimm: passth...
1021
  		func = pkg.nd_command;
426824d63   Dan Williams   libnvdimm: remove...
1022
1023
1024
  		dev_dbg(dev, "%s, idx: %llu, in: %u, out: %u, len %llu
  ",
  				dimm_name, pkg.nd_command,
31eca76ba   Dan Williams   nfit, libnvdimm: ...
1025
  				in_len, out_len, buf_len);
31eca76ba   Dan Williams   nfit, libnvdimm: ...
1026
  	}
62232e45f   Dan Williams   libnvdimm: contro...
1027
  	/* process an output envelope */
6de5d06e6   Dan Williams   libnvdimm/bus: Pr...
1028
1029
1030
1031
1032
  	out_env = kzalloc(ND_CMD_MAX_ENVELOPE, GFP_KERNEL);
  	if (!out_env) {
  		rc = -ENOMEM;
  		goto out;
  	}
62232e45f   Dan Williams   libnvdimm: contro...
1033
1034
  	for (i = 0; i < desc->out_num; i++) {
  		u32 out_size = nd_cmd_out_size(nvdimm, cmd, desc, i,
efda1b5d8   Dan Williams   acpi, nfit, libnv...
1035
  				(u32 *) in_env, (u32 *) out_env, 0);
62232e45f   Dan Williams   libnvdimm: contro...
1036
1037
1038
  		u32 copy;
  
  		if (out_size == UINT_MAX) {
426824d63   Dan Williams   libnvdimm: remove...
1039
1040
1041
  			dev_dbg(dev, "%s unknown output size cmd: %s field: %d
  ",
  					dimm_name, cmd_name, i);
6de5d06e6   Dan Williams   libnvdimm/bus: Pr...
1042
1043
  			rc = -EFAULT;
  			goto out;
62232e45f   Dan Williams   libnvdimm: contro...
1044
  		}
6de5d06e6   Dan Williams   libnvdimm/bus: Pr...
1045
1046
  		if (out_len < ND_CMD_MAX_ENVELOPE)
  			copy = min_t(u32, ND_CMD_MAX_ENVELOPE - out_len, out_size);
62232e45f   Dan Williams   libnvdimm: contro...
1047
1048
1049
  		else
  			copy = 0;
  		if (copy && copy_from_user(&out_env[out_len],
6de5d06e6   Dan Williams   libnvdimm/bus: Pr...
1050
1051
1052
1053
  					p + in_len + out_len, copy)) {
  			rc = -EFAULT;
  			goto out;
  		}
62232e45f   Dan Williams   libnvdimm: contro...
1054
1055
  		out_len += out_size;
  	}
58738c495   Dan Williams   libnvdimm: fix in...
1056
  	buf_len = (u64) out_len + (u64) in_len;
62232e45f   Dan Williams   libnvdimm: contro...
1057
  	if (buf_len > ND_IOCTL_MAX_BUFLEN) {
426824d63   Dan Williams   libnvdimm: remove...
1058
1059
1060
  		dev_dbg(dev, "%s cmd: %s buf_len: %llu > %d
  ", dimm_name,
  				cmd_name, buf_len, ND_IOCTL_MAX_BUFLEN);
6de5d06e6   Dan Williams   libnvdimm/bus: Pr...
1061
1062
  		rc = -EINVAL;
  		goto out;
62232e45f   Dan Williams   libnvdimm: contro...
1063
1064
1065
  	}
  
  	buf = vmalloc(buf_len);
6de5d06e6   Dan Williams   libnvdimm/bus: Pr...
1066
1067
1068
1069
  	if (!buf) {
  		rc = -ENOMEM;
  		goto out;
  	}
62232e45f   Dan Williams   libnvdimm: contro...
1070
1071
1072
1073
1074
  
  	if (copy_from_user(buf, p, buf_len)) {
  		rc = -EFAULT;
  		goto out;
  	}
87a30e1f0   Dan Williams   driver-core, libn...
1075
  	nd_device_lock(dev);
ca6bf264f   Dan Williams   libnvdimm/bus: Fi...
1076
  	nvdimm_bus_lock(dev);
53b85a449   Jerry Hoemann   libnvdimm: passth...
1077
  	rc = nd_cmd_clear_to_send(nvdimm_bus, nvdimm, func, buf);
eaf961536   Dan Williams   libnvdimm, nfit: ...
1078
1079
  	if (rc)
  		goto out_unlock;
006358b35   Dave Jiang   libnvdimm: add su...
1080
  	rc = nd_desc->ndctl(nd_desc, nvdimm, cmd, buf, buf_len, &cmd_rc);
62232e45f   Dan Williams   libnvdimm: contro...
1081
  	if (rc < 0)
eaf961536   Dan Williams   libnvdimm, nfit: ...
1082
  		goto out_unlock;
006358b35   Dave Jiang   libnvdimm: add su...
1083
1084
1085
  
  	if (!nvdimm && cmd == ND_CMD_CLEAR_ERROR && cmd_rc >= 0) {
  		struct nd_cmd_clear_error *clear_err = buf;
006358b35   Dave Jiang   libnvdimm: add su...
1086

23f498448   Dan Williams   libnvdimm: rework...
1087
1088
  		nvdimm_account_cleared_poison(nvdimm_bus, clear_err->address,
  				clear_err->cleared);
006358b35   Dave Jiang   libnvdimm: add su...
1089
  	}
0beb2012a   Dan Williams   libnvdimm: fix re...
1090

62232e45f   Dan Williams   libnvdimm: contro...
1091
1092
  	if (copy_to_user(p, buf, buf_len))
  		rc = -EFAULT;
0beb2012a   Dan Williams   libnvdimm: fix re...
1093

6de5d06e6   Dan Williams   libnvdimm/bus: Pr...
1094
  out_unlock:
ca6bf264f   Dan Williams   libnvdimm/bus: Fi...
1095
  	nvdimm_bus_unlock(dev);
87a30e1f0   Dan Williams   driver-core, libn...
1096
  	nd_device_unlock(dev);
6de5d06e6   Dan Williams   libnvdimm/bus: Pr...
1097
1098
1099
  out:
  	kfree(in_env);
  	kfree(out_env);
62232e45f   Dan Williams   libnvdimm: contro...
1100
1101
1102
  	vfree(buf);
  	return rc;
  }
b70d31d05   Dan Williams   libnvdimm/bus: St...
1103
1104
1105
1106
  enum nd_ioctl_mode {
  	BUS_IOCTL,
  	DIMM_IOCTL,
  };
62232e45f   Dan Williams   libnvdimm: contro...
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
  
  static int match_dimm(struct device *dev, void *data)
  {
  	long id = (long) data;
  
  	if (is_nvdimm(dev)) {
  		struct nvdimm *nvdimm = to_nvdimm(dev);
  
  		return nvdimm->id == id;
  	}
  
  	return 0;
  }
b70d31d05   Dan Williams   libnvdimm/bus: St...
1120
1121
  static long nd_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
  		enum nd_ioctl_mode mode)
62232e45f   Dan Williams   libnvdimm: contro...
1122
  {
b70d31d05   Dan Williams   libnvdimm/bus: St...
1123
1124
1125
1126
  	struct nvdimm_bus *nvdimm_bus, *found = NULL;
  	long id = (long) file->private_data;
  	struct nvdimm *nvdimm = NULL;
  	int rc, ro;
62232e45f   Dan Williams   libnvdimm: contro...
1127

4dc0e7be8   Jerry Hoemann   libnvdimm: Clean-...
1128
  	ro = ((file->f_flags & O_ACCMODE) == O_RDONLY);
62232e45f   Dan Williams   libnvdimm: contro...
1129
1130
  	mutex_lock(&nvdimm_bus_list_mutex);
  	list_for_each_entry(nvdimm_bus, &nvdimm_bus_list, list) {
b70d31d05   Dan Williams   libnvdimm/bus: St...
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
  		if (mode == DIMM_IOCTL) {
  			struct device *dev;
  
  			dev = device_find_child(&nvdimm_bus->dev,
  					file->private_data, match_dimm);
  			if (!dev)
  				continue;
  			nvdimm = to_nvdimm(dev);
  			found = nvdimm_bus;
  		} else if (nvdimm_bus->id == id) {
  			found = nvdimm_bus;
  		}
62232e45f   Dan Williams   libnvdimm: contro...
1143

b70d31d05   Dan Williams   libnvdimm/bus: St...
1144
1145
1146
1147
  		if (found) {
  			atomic_inc(&nvdimm_bus->ioctl_active);
  			break;
  		}
62232e45f   Dan Williams   libnvdimm: contro...
1148
1149
  	}
  	mutex_unlock(&nvdimm_bus_list_mutex);
b70d31d05   Dan Williams   libnvdimm/bus: St...
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
  	if (!found)
  		return -ENXIO;
  
  	nvdimm_bus = found;
  	rc = __nd_ioctl(nvdimm_bus, nvdimm, ro, cmd, arg);
  
  	if (nvdimm)
  		put_device(&nvdimm->dev);
  	if (atomic_dec_and_test(&nvdimm_bus->ioctl_active))
  		wake_up(&nvdimm_bus->wait);
62232e45f   Dan Williams   libnvdimm: contro...
1160
1161
  	return rc;
  }
b70d31d05   Dan Williams   libnvdimm/bus: St...
1162
1163
1164
1165
1166
1167
1168
1169
1170
  static long bus_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
  {
  	return nd_ioctl(file, cmd, arg, BUS_IOCTL);
  }
  
  static long dimm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
  {
  	return nd_ioctl(file, cmd, arg, DIMM_IOCTL);
  }
62232e45f   Dan Williams   libnvdimm: contro...
1171
1172
1173
1174
1175
1176
  static int nd_open(struct inode *inode, struct file *file)
  {
  	long minor = iminor(inode);
  
  	file->private_data = (void *) minor;
  	return 0;
45def22c1   Dan Williams   libnvdimm: contro...
1177
1178
1179
1180
  }
  
  static const struct file_operations nvdimm_bus_fops = {
  	.owner = THIS_MODULE,
62232e45f   Dan Williams   libnvdimm: contro...
1181
  	.open = nd_open,
b70d31d05   Dan Williams   libnvdimm/bus: St...
1182
1183
  	.unlocked_ioctl = bus_ioctl,
  	.compat_ioctl = bus_ioctl,
45def22c1   Dan Williams   libnvdimm: contro...
1184
1185
  	.llseek = noop_llseek,
  };
62232e45f   Dan Williams   libnvdimm: contro...
1186
1187
1188
  static const struct file_operations nvdimm_fops = {
  	.owner = THIS_MODULE,
  	.open = nd_open,
b70d31d05   Dan Williams   libnvdimm/bus: St...
1189
1190
  	.unlocked_ioctl = dimm_ioctl,
  	.compat_ioctl = dimm_ioctl,
62232e45f   Dan Williams   libnvdimm: contro...
1191
1192
  	.llseek = noop_llseek,
  };
45def22c1   Dan Williams   libnvdimm: contro...
1193
1194
1195
  int __init nvdimm_bus_init(void)
  {
  	int rc;
e6dfb2de4   Dan Williams   libnvdimm, nfit: ...
1196
1197
1198
  	rc = bus_register(&nvdimm_bus_type);
  	if (rc)
  		return rc;
45def22c1   Dan Williams   libnvdimm: contro...
1199
1200
  	rc = register_chrdev(0, "ndctl", &nvdimm_bus_fops);
  	if (rc < 0)
62232e45f   Dan Williams   libnvdimm: contro...
1201
  		goto err_bus_chrdev;
45def22c1   Dan Williams   libnvdimm: contro...
1202
  	nvdimm_bus_major = rc;
62232e45f   Dan Williams   libnvdimm: contro...
1203
1204
1205
1206
  	rc = register_chrdev(0, "dimmctl", &nvdimm_fops);
  	if (rc < 0)
  		goto err_dimm_chrdev;
  	nvdimm_major = rc;
45def22c1   Dan Williams   libnvdimm: contro...
1207
  	nd_class = class_create(THIS_MODULE, "nd");
daa1dee40   Axel Lin   nvdimm: Fix retur...
1208
1209
  	if (IS_ERR(nd_class)) {
  		rc = PTR_ERR(nd_class);
45def22c1   Dan Williams   libnvdimm: contro...
1210
  		goto err_class;
daa1dee40   Axel Lin   nvdimm: Fix retur...
1211
  	}
45def22c1   Dan Williams   libnvdimm: contro...
1212

18515942d   Dan Williams   libnvdimm: regist...
1213
1214
1215
  	rc = driver_register(&nd_bus_driver.drv);
  	if (rc)
  		goto err_nd_bus;
45def22c1   Dan Williams   libnvdimm: contro...
1216
  	return 0;
18515942d   Dan Williams   libnvdimm: regist...
1217
1218
   err_nd_bus:
  	class_destroy(nd_class);
45def22c1   Dan Williams   libnvdimm: contro...
1219
   err_class:
62232e45f   Dan Williams   libnvdimm: contro...
1220
1221
  	unregister_chrdev(nvdimm_major, "dimmctl");
   err_dimm_chrdev:
45def22c1   Dan Williams   libnvdimm: contro...
1222
  	unregister_chrdev(nvdimm_bus_major, "ndctl");
62232e45f   Dan Williams   libnvdimm: contro...
1223
   err_bus_chrdev:
e6dfb2de4   Dan Williams   libnvdimm, nfit: ...
1224
  	bus_unregister(&nvdimm_bus_type);
45def22c1   Dan Williams   libnvdimm: contro...
1225
1226
1227
  
  	return rc;
  }
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
1228
  void nvdimm_bus_exit(void)
45def22c1   Dan Williams   libnvdimm: contro...
1229
  {
18515942d   Dan Williams   libnvdimm: regist...
1230
  	driver_unregister(&nd_bus_driver.drv);
45def22c1   Dan Williams   libnvdimm: contro...
1231
1232
  	class_destroy(nd_class);
  	unregister_chrdev(nvdimm_bus_major, "ndctl");
62232e45f   Dan Williams   libnvdimm: contro...
1233
  	unregister_chrdev(nvdimm_major, "dimmctl");
e6dfb2de4   Dan Williams   libnvdimm, nfit: ...
1234
  	bus_unregister(&nvdimm_bus_type);
18515942d   Dan Williams   libnvdimm: regist...
1235
  	ida_destroy(&nd_ida);
45def22c1   Dan Williams   libnvdimm: contro...
1236
  }