Blame view

drivers/nvdimm/bus.c 31.3 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);
  }
e755799ae   Dan Williams   libnvdimm: Move n...
288
289
290
291
  static const struct device_type nvdimm_bus_dev_type = {
  	.release = nvdimm_bus_release,
  	.groups = nvdimm_bus_attribute_groups,
  };
87a30e1f0   Dan Williams   driver-core, libn...
292
  bool is_nvdimm_bus(struct device *dev)
18515942d   Dan Williams   libnvdimm: regist...
293
  {
e755799ae   Dan Williams   libnvdimm: Move n...
294
  	return dev->type == &nvdimm_bus_dev_type;
18515942d   Dan Williams   libnvdimm: regist...
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
  }
  
  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...
320
321
322
323
324
  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...
325
326
327
328
329
330
331
332
333
334
335
  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...
336
  	init_waitqueue_head(&nvdimm_bus->wait);
18515942d   Dan Williams   libnvdimm: regist...
337
  	nvdimm_bus->id = ida_simple_get(&nd_ida, 0, 0, GFP_KERNEL);
18515942d   Dan Williams   libnvdimm: regist...
338
339
340
341
  	if (nvdimm_bus->id < 0) {
  		kfree(nvdimm_bus);
  		return NULL;
  	}
9bf3aa446   Ocean He   libnvdimm, bus: C...
342
343
  	mutex_init(&nvdimm_bus->reconfig_mutex);
  	badrange_init(&nvdimm_bus->badrange);
18515942d   Dan Williams   libnvdimm: regist...
344
345
  	nvdimm_bus->nd_desc = nd_desc;
  	nvdimm_bus->dev.parent = parent;
e755799ae   Dan Williams   libnvdimm: Move n...
346
  	nvdimm_bus->dev.type = &nvdimm_bus_dev_type;
18515942d   Dan Williams   libnvdimm: regist...
347
348
  	nvdimm_bus->dev.groups = nd_desc->attr_groups;
  	nvdimm_bus->dev.bus = &nvdimm_bus_type;
1ff19f487   Oliver O'Halloran   libnvdimm: Add of...
349
  	nvdimm_bus->dev.of_node = nd_desc->of_node;
18515942d   Dan Williams   libnvdimm: regist...
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
378
379
380
381
  	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...
382
383
384
385
386
387
388
389
  		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...
390
  		set_bit(NVDIMM_SECURITY_FROZEN, &nvdimm->sec.flags);
7d988097c   Dave Jiang   acpi/nfit, libnvd...
391
392
393
394
395
396
397
398
  		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...
399
400
  	return 0;
  }
aa9ad44a4   Dave Jiang   libnvdimm: move p...
401
  static void free_badrange_list(struct list_head *badrange_list)
18515942d   Dan Williams   libnvdimm: regist...
402
  {
aa9ad44a4   Dave Jiang   libnvdimm: move p...
403
  	struct badrange_entry *bre, *next;
18515942d   Dan Williams   libnvdimm: regist...
404

aa9ad44a4   Dave Jiang   libnvdimm: move p...
405
406
407
  	list_for_each_entry_safe(bre, next, badrange_list, list) {
  		list_del(&bre->list);
  		kfree(bre);
18515942d   Dan Williams   libnvdimm: regist...
408
  	}
aa9ad44a4   Dave Jiang   libnvdimm: move p...
409
  	list_del_init(badrange_list);
18515942d   Dan Williams   libnvdimm: regist...
410
411
412
413
414
415
416
417
418
  }
  
  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...
419
420
  	wait_event(nvdimm_bus->wait,
  			atomic_read(&nvdimm_bus->ioctl_active) == 0);
18515942d   Dan Williams   libnvdimm: regist...
421
422
  	nd_synchronize();
  	device_for_each_child(&nvdimm_bus->dev, NULL, child_unregister);
aa9ad44a4   Dave Jiang   libnvdimm: move p...
423
424
425
  	spin_lock(&nvdimm_bus->badrange.lock);
  	free_badrange_list(&nvdimm_bus->badrange.list);
  	spin_unlock(&nvdimm_bus->badrange.lock);
18515942d   Dan Williams   libnvdimm: regist...
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
457
458
459
460
  
  	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...
461
  };
18515942d   Dan Williams   libnvdimm: regist...
462
463
464
465
466
467
468
469
470
  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...
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
  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...
489
490
  	if (dev->parent)
  		put_device(dev->parent);
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
491
492
493
494
495
  }
  
  static void nd_async_device_unregister(void *d, async_cookie_t cookie)
  {
  	struct device *dev = d;
0ba1c6348   Dan Williams   libnvdimm: write ...
496
497
498
  	/* flush bus operations before delete */
  	nvdimm_bus_lock(dev);
  	nvdimm_bus_unlock(dev);
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
499
500
501
  	device_unregister(dev);
  	put_device(dev);
  }
8c2f7e865   Dan Williams   libnvdimm: infras...
502
  void __nd_device_register(struct device *dev)
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
503
  {
cd03412a5   Dan Williams   libnvdimm, dax: i...
504
505
  	if (!dev)
  		return;
1a091d16d   Alexander Duyck   libnvdimm: Set de...
506
507
508
509
510
511
512
513
514
  
  	/*
  	 * 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...
515
  	dev->bus = &nvdimm_bus_type;
af87b9a78   Alexander Duyck   libnvdimm: Schedu...
516
  	if (dev->parent) {
b6eae0f61   Alexander Duyck   libnvdimm: Hold r...
517
  		get_device(dev->parent);
af87b9a78   Alexander Duyck   libnvdimm: Schedu...
518
519
520
  		if (dev_to_node(dev) == NUMA_NO_NODE)
  			set_dev_node(dev, dev_to_node(dev->parent));
  	}
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
521
  	get_device(dev);
af87b9a78   Alexander Duyck   libnvdimm: Schedu...
522
523
524
  
  	async_schedule_dev_domain(nd_async_device_register, dev,
  				  &nd_async_domain);
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
525
  }
8c2f7e865   Dan Williams   libnvdimm: infras...
526
527
528
529
530
531
  
  void nd_device_register(struct device *dev)
  {
  	device_initialize(dev);
  	__nd_device_register(dev);
  }
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
532
533
534
535
  EXPORT_SYMBOL(nd_device_register);
  
  void nd_device_unregister(struct device *dev, enum nd_async_mode mode)
  {
8aac0e233   Dan Williams   libnvdimm/bus: Pr...
536
  	bool killed;
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
537
538
  	switch (mode) {
  	case ND_ASYNC:
8aac0e233   Dan Williams   libnvdimm/bus: Pr...
539
540
541
542
543
544
545
546
  		/*
  		 * 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...
547
548
549
550
551
  		get_device(dev);
  		async_schedule_domain(nd_async_device_unregister, dev,
  				&nd_async_domain);
  		break;
  	case ND_SYNC:
8aac0e233   Dan Williams   libnvdimm/bus: Pr...
552
553
554
555
556
557
558
  		/*
  		 * 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...
559
  		nd_device_lock(dev);
8aac0e233   Dan Williams   libnvdimm/bus: Pr...
560
  		killed = kill_device(dev);
87a30e1f0   Dan Williams   driver-core, libn...
561
  		nd_device_unlock(dev);
8aac0e233   Dan Williams   libnvdimm/bus: Pr...
562
563
564
  
  		if (!killed)
  			return;
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
  		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 ...
584
585
  		pr_debug("driver type bitmask not set (%ps)
  ",
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
586
587
588
  				__builtin_return_address(0));
  		return -EINVAL;
  	}
6cf9c5bab   Dan Williams   libnvdimm: stop r...
589
590
591
  	if (!nd_drv->probe) {
  		pr_debug("%s ->probe() must be specified
  ", mod_name);
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
592
593
594
595
596
597
598
599
600
601
  		return -EINVAL;
  	}
  
  	drv->bus = &nvdimm_bus_type;
  	drv->owner = owner;
  	drv->mod_name = mod_name;
  
  	return driver_register(drv);
  }
  EXPORT_SYMBOL(__nd_driver_register);
32f61d675   Christoph Hellwig   nvdimm: simplify ...
602
  void nvdimm_check_and_set_ro(struct gendisk *disk)
581388209   Dan Williams   libnvdimm, nfit: ...
603
  {
52c44d93c   Dan Williams   block: remove ->d...
604
  	struct device *dev = disk_to_dev(disk)->parent;
581388209   Dan Williams   libnvdimm, nfit: ...
605
  	struct nd_region *nd_region = to_nd_region(dev->parent);
254a4cd50   Robert Elliott   linvdimm, pmem: P...
606
  	int disk_ro = get_disk_ro(disk);
581388209   Dan Williams   libnvdimm, nfit: ...
607

254a4cd50   Robert Elliott   linvdimm, pmem: P...
608
609
610
611
612
  	/*
  	 * 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)
32f61d675   Christoph Hellwig   nvdimm: simplify ...
613
  		return;
581388209   Dan Williams   libnvdimm, nfit: ...
614

254a4cd50   Robert Elliott   linvdimm, pmem: P...
615
616
617
618
  	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: ...
619
  }
32f61d675   Christoph Hellwig   nvdimm: simplify ...
620
  EXPORT_SYMBOL(nvdimm_check_and_set_ro);
581388209   Dan Williams   libnvdimm, nfit: ...
621

4d88a97aa   Dan Williams   libnvdimm, nvdimm...
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
  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...
644
  /*
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
645
646
   * nd_device_attribute_group - generic attributes for all devices on an nd bus
   */
adbb68293   Dan Williams   libnvdimm: Move n...
647
  const struct attribute_group nd_device_attribute_group = {
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
648
  	.attrs = nd_device_attributes,
e6dfb2de4   Dan Williams   libnvdimm, nfit: ...
649
  };
74ae66c3b   Toshi Kani   libnvdimm: Add sy...
650
651
652
653
654
655
656
  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);
bcba0c454   Dan Williams   libnvdimm: Export...
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
  static int nvdimm_dev_to_target_node(struct device *dev)
  {
  	struct device *parent = dev->parent;
  	struct nd_region *nd_region = NULL;
  
  	if (is_nd_region(dev))
  		nd_region = to_nd_region(dev);
  	else if (parent && is_nd_region(parent))
  		nd_region = to_nd_region(parent);
  
  	if (!nd_region)
  		return NUMA_NO_NODE;
  	return nd_region->target_node;
  }
  
  static ssize_t target_node_show(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
  	return sprintf(buf, "%d
  ", nvdimm_dev_to_target_node(dev));
  }
  static DEVICE_ATTR_RO(target_node);
74ae66c3b   Toshi Kani   libnvdimm: Add sy...
679
680
  static struct attribute *nd_numa_attributes[] = {
  	&dev_attr_numa_node.attr,
bcba0c454   Dan Williams   libnvdimm: Export...
681
  	&dev_attr_target_node.attr,
74ae66c3b   Toshi Kani   libnvdimm: Add sy...
682
683
684
685
686
687
  	NULL,
  };
  
  static umode_t nd_numa_attr_visible(struct kobject *kobj, struct attribute *a,
  		int n)
  {
bcba0c454   Dan Williams   libnvdimm: Export...
688
  	struct device *dev = container_of(kobj, typeof(*dev), kobj);
74ae66c3b   Toshi Kani   libnvdimm: Add sy...
689
690
  	if (!IS_ENABLED(CONFIG_NUMA))
  		return 0;
bcba0c454   Dan Williams   libnvdimm: Export...
691
692
693
  	if (a == &dev_attr_target_node.attr &&
  			nvdimm_dev_to_target_node(dev) == NUMA_NO_NODE)
  		return 0;
74ae66c3b   Toshi Kani   libnvdimm: Add sy...
694
695
  	return a->mode;
  }
c01dafad7   Qian Cai   libnvdimm: Fix co...
696
  /*
74ae66c3b   Toshi Kani   libnvdimm: Add sy...
697
698
   * nd_numa_attribute_group - NUMA attributes for all devices on an nd bus
   */
e2f6a0e34   Dan Williams   libnvdimm: Move n...
699
  const struct attribute_group nd_numa_attribute_group = {
74ae66c3b   Toshi Kani   libnvdimm: Add sy...
700
701
702
  	.attrs = nd_numa_attributes,
  	.is_visible = nd_numa_attr_visible,
  };
74ae66c3b   Toshi Kani   libnvdimm: Add sy...
703

45def22c1   Dan Williams   libnvdimm: contro...
704
705
706
707
708
709
710
  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...
711
  	if (IS_ERR(dev))
45def22c1   Dan Williams   libnvdimm: contro...
712
713
714
  		dev_dbg(&nvdimm_bus->dev, "failed to register ndctl%d: %ld
  ",
  				nvdimm_bus->id, PTR_ERR(dev));
425889581   Dan Williams   libnvdimm: IS_ERR...
715
  	return PTR_ERR_OR_ZERO(dev);
45def22c1   Dan Williams   libnvdimm: contro...
716
717
718
719
720
721
  }
  
  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...
722
723
724
725
  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...
726
  		.out_sizes = { 4, 128, },
62232e45f   Dan Williams   libnvdimm: contro...
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
  	},
  	[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: ...
758
759
760
761
762
763
  	[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...
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
  };
  
  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...
779
780
  		.out_num = 4,
  		.out_sizes = { 4, 4, 4, 4, },
62232e45f   Dan Williams   libnvdimm: contro...
781
782
  	},
  	[ND_CMD_ARS_START] = {
4577b0665   Dan Williams   nfit: update addr...
783
784
785
786
  		.in_num = 5,
  		.in_sizes = { 8, 8, 2, 1, 5, },
  		.out_num = 2,
  		.out_sizes = { 4, 4, },
62232e45f   Dan Williams   libnvdimm: contro...
787
788
  	},
  	[ND_CMD_ARS_STATUS] = {
747ffe11b   Dan Williams   libnvdimm, tools/...
789
790
  		.out_num = 3,
  		.out_sizes = { 4, 4, UINT_MAX, },
62232e45f   Dan Williams   libnvdimm: contro...
791
  	},
d4f323672   Dan Williams   nfit, libnvdimm: ...
792
793
794
795
796
797
  	[ND_CMD_CLEAR_ERROR] = {
  		.in_num = 2,
  		.in_sizes = { 8, 8, },
  		.out_num = 3,
  		.out_sizes = { 4, 4, 8, },
  	},
31eca76ba   Dan Williams   nfit, libnvdimm: ...
798
799
800
801
802
803
  	[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...
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
  };
  
  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: ...
831
832
833
834
  	} else if (cmd == ND_CMD_CALL) {
  		struct nd_cmd_pkg *pkg = buf;
  
  		return pkg->nd_size_in;
62232e45f   Dan Williams   libnvdimm: contro...
835
836
837
838
839
840
841
842
  	}
  
  	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...
843
  		const u32 *out_field, unsigned long remainder)
62232e45f   Dan Williams   libnvdimm: contro...
844
845
846
847
848
849
850
851
852
853
854
  {
  	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...
855
856
857
858
859
860
861
862
863
864
865
866
867
868
  	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...
869
  		if (out_field[1] - 4 == remainder)
efda1b5d8   Dan Williams   acpi, nfit, libnv...
870
  			return remainder;
286e87718   Vishal Verma   libnvdimm: fix ar...
871
  		return out_field[1] - 8;
efda1b5d8   Dan Williams   acpi, nfit, libnv...
872
  	} else if (cmd == ND_CMD_CALL) {
31eca76ba   Dan Williams   nfit, libnvdimm: ...
873
874
875
876
  		struct nd_cmd_pkg *pkg = (struct nd_cmd_pkg *) in_field;
  
  		return pkg->nd_size_out;
  	}
62232e45f   Dan Williams   libnvdimm: contro...
877
878
879
880
  
  	return UINT_MAX;
  }
  EXPORT_SYMBOL_GPL(nd_cmd_out_size);
bf9bccc14   Dan Williams   libnvdimm: pmem l...
881
  void wait_nvdimm_bus_probe_idle(struct device *dev)
eaf961536   Dan Williams   libnvdimm, nfit: ...
882
  {
bf9bccc14   Dan Williams   libnvdimm: pmem l...
883
  	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
eaf961536   Dan Williams   libnvdimm, nfit: ...
884
885
886
  	do {
  		if (nvdimm_bus->probe_active == 0)
  			break;
ca6bf264f   Dan Williams   libnvdimm/bus: Fi...
887
  		nvdimm_bus_unlock(dev);
87a30e1f0   Dan Williams   driver-core, libn...
888
  		nd_device_unlock(dev);
b70d31d05   Dan Williams   libnvdimm/bus: St...
889
  		wait_event(nvdimm_bus->wait,
eaf961536   Dan Williams   libnvdimm, nfit: ...
890
  				nvdimm_bus->probe_active == 0);
87a30e1f0   Dan Williams   driver-core, libn...
891
  		nd_device_lock(dev);
ca6bf264f   Dan Williams   libnvdimm/bus: Fi...
892
  		nvdimm_bus_lock(dev);
eaf961536   Dan Williams   libnvdimm, nfit: ...
893
894
  	} while (true);
  }
006358b35   Dave Jiang   libnvdimm: add su...
895
  static int nd_pmem_forget_poison_check(struct device *dev, void *data)
d4f323672   Dan Williams   nfit, libnvdimm: ...
896
  {
006358b35   Dave Jiang   libnvdimm: add su...
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
  	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: ...
930
  		return -EBUSY;
006358b35   Dave Jiang   libnvdimm: add su...
931

d4f323672   Dan Williams   nfit, libnvdimm: ...
932
  	return 0;
006358b35   Dave Jiang   libnvdimm: add su...
933
934
935
936
937
938
  
  }
  
  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: ...
939
  }
eaf961536   Dan Williams   libnvdimm, nfit: ...
940
  /* set_config requires an idle interleave set */
87bf572e1   Dan Williams   nfit: disable use...
941
  static int nd_cmd_clear_to_send(struct nvdimm_bus *nvdimm_bus,
006358b35   Dave Jiang   libnvdimm: add su...
942
  		struct nvdimm *nvdimm, unsigned int cmd, void *data)
eaf961536   Dan Williams   libnvdimm, nfit: ...
943
  {
87bf572e1   Dan Williams   nfit: disable use...
944
945
946
947
  	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...
948
  		int rc = nd_desc->clear_to_send(nd_desc, nvdimm, cmd, data);
87bf572e1   Dan Williams   nfit: disable use...
949
950
951
952
  
  		if (rc)
  			return rc;
  	}
eaf961536   Dan Williams   libnvdimm, nfit: ...
953

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

eaf961536   Dan Williams   libnvdimm, nfit: ...
959
960
  	if (!nvdimm || cmd != ND_CMD_SET_CONFIG_DATA)
  		return 0;
87bf572e1   Dan Williams   nfit: disable use...
961
  	/* prevent label manipulation while the kernel owns label updates */
bf9bccc14   Dan Williams   libnvdimm: pmem l...
962
  	wait_nvdimm_bus_probe_idle(&nvdimm_bus->dev);
eaf961536   Dan Williams   libnvdimm, nfit: ...
963
964
965
966
  	if (atomic_read(&nvdimm->busy))
  		return -EBUSY;
  	return 0;
  }
62232e45f   Dan Williams   libnvdimm: contro...
967
968
969
970
  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...
971
972
  	const struct nd_cmd_desc *desc = NULL;
  	unsigned int cmd = _IOC_NR(ioctl_cmd);
62232e45f   Dan Williams   libnvdimm: contro...
973
  	struct device *dev = &nvdimm_bus->dev;
58738c495   Dan Williams   libnvdimm: fix in...
974
  	void __user *p = (void __user *) arg;
6de5d06e6   Dan Williams   libnvdimm/bus: Pr...
975
  	char *out_env = NULL, *in_env = NULL;
62232e45f   Dan Williams   libnvdimm: contro...
976
  	const char *cmd_name, *dimm_name;
58738c495   Dan Williams   libnvdimm: fix in...
977
978
  	u32 in_len = 0, out_len = 0;
  	unsigned int func = cmd;
e3654eca7   Dan Williams   nfit, libnvdimm: ...
979
  	unsigned long cmd_mask;
58738c495   Dan Williams   libnvdimm: fix in...
980
  	struct nd_cmd_pkg pkg;
006358b35   Dave Jiang   libnvdimm: add su...
981
  	int rc, i, cmd_rc;
6de5d06e6   Dan Williams   libnvdimm/bus: Pr...
982
  	void *buf = NULL;
58738c495   Dan Williams   libnvdimm: fix in...
983
  	u64 buf_len = 0;
62232e45f   Dan Williams   libnvdimm: contro...
984
985
986
987
  
  	if (nvdimm) {
  		desc = nd_cmd_dimm_desc(cmd);
  		cmd_name = nvdimm_cmd_name(cmd);
e3654eca7   Dan Williams   nfit, libnvdimm: ...
988
  		cmd_mask = nvdimm->cmd_mask;
62232e45f   Dan Williams   libnvdimm: contro...
989
990
991
992
  		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: ...
993
  		cmd_mask = nd_desc->cmd_mask;
62232e45f   Dan Williams   libnvdimm: contro...
994
995
  		dimm_name = "bus";
  	}
92fe2aa85   Dan Williams   libnvdimm: Valida...
996
  	/* Validate command family support against bus declared support */
31eca76ba   Dan Williams   nfit, libnvdimm: ...
997
  	if (cmd == ND_CMD_CALL) {
92fe2aa85   Dan Williams   libnvdimm: Valida...
998
  		unsigned long *mask;
31eca76ba   Dan Williams   nfit, libnvdimm: ...
999
1000
  		if (copy_from_user(&pkg, p, sizeof(pkg)))
  			return -EFAULT;
92fe2aa85   Dan Williams   libnvdimm: Valida...
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
  
  		if (nvdimm) {
  			if (pkg.nd_family > NVDIMM_FAMILY_MAX)
  				return -EINVAL;
  			mask = &nd_desc->dimm_family_mask;
  		} else {
  			if (pkg.nd_family > NVDIMM_BUS_FAMILY_MAX)
  				return -EINVAL;
  			mask = &nd_desc->bus_family_mask;
  		}
  
  		if (!test_bit(pkg.nd_family, mask))
  			return -EINVAL;
31eca76ba   Dan Williams   nfit, libnvdimm: ...
1014
  	}
f84afbdd3   Dan Carpenter   libnvdimm: Out of...
1015
1016
1017
1018
  	if (!desc ||
  	    (desc->out_num + desc->in_num == 0) ||
  	    cmd > ND_CMD_CALL ||
  	    !test_bit(cmd, &cmd_mask))
62232e45f   Dan Williams   libnvdimm: contro...
1019
1020
1021
1022
  		return -ENOTTY;
  
  	/* fail write commands (when read-only) */
  	if (read_only)
07accfa9d   Jerry Hoemann   libnvdimm: Fix se...
1023
1024
1025
1026
  		switch (cmd) {
  		case ND_CMD_VENDOR:
  		case ND_CMD_SET_CONFIG_DATA:
  		case ND_CMD_ARS_START:
d4f323672   Dan Williams   nfit, libnvdimm: ...
1027
  		case ND_CMD_CLEAR_ERROR:
31eca76ba   Dan Williams   nfit, libnvdimm: ...
1028
  		case ND_CMD_CALL:
ca6bf264f   Dan Williams   libnvdimm/bus: Fi...
1029
1030
  			dev_dbg(dev, "'%s' command while read-only.
  ",
62232e45f   Dan Williams   libnvdimm: contro...
1031
1032
1033
1034
1035
1036
1037
1038
  					nvdimm ? nvdimm_cmd_name(cmd)
  					: nvdimm_bus_cmd_name(cmd));
  			return -EPERM;
  		default:
  			break;
  		}
  
  	/* process an input envelope */
6de5d06e6   Dan Williams   libnvdimm/bus: Pr...
1039
1040
1041
  	in_env = kzalloc(ND_CMD_MAX_ENVELOPE, GFP_KERNEL);
  	if (!in_env)
  		return -ENOMEM;
62232e45f   Dan Williams   libnvdimm: contro...
1042
1043
1044
1045
1046
1047
1048
1049
  	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...
1050
1051
  			rc = -ENXIO;
  			goto out;
62232e45f   Dan Williams   libnvdimm: contro...
1052
  		}
6de5d06e6   Dan Williams   libnvdimm/bus: Pr...
1053
1054
  		if (in_len < ND_CMD_MAX_ENVELOPE)
  			copy = min_t(u32, ND_CMD_MAX_ENVELOPE - in_len, in_size);
62232e45f   Dan Williams   libnvdimm: contro...
1055
1056
  		else
  			copy = 0;
6de5d06e6   Dan Williams   libnvdimm/bus: Pr...
1057
1058
1059
1060
  		if (copy && copy_from_user(&in_env[in_len], p + in_len, copy)) {
  			rc = -EFAULT;
  			goto out;
  		}
62232e45f   Dan Williams   libnvdimm: contro...
1061
1062
  		in_len += in_size;
  	}
31eca76ba   Dan Williams   nfit, libnvdimm: ...
1063
  	if (cmd == ND_CMD_CALL) {
53b85a449   Jerry Hoemann   libnvdimm: passth...
1064
  		func = pkg.nd_command;
426824d63   Dan Williams   libnvdimm: remove...
1065
1066
1067
  		dev_dbg(dev, "%s, idx: %llu, in: %u, out: %u, len %llu
  ",
  				dimm_name, pkg.nd_command,
31eca76ba   Dan Williams   nfit, libnvdimm: ...
1068
  				in_len, out_len, buf_len);
31eca76ba   Dan Williams   nfit, libnvdimm: ...
1069
  	}
62232e45f   Dan Williams   libnvdimm: contro...
1070
  	/* process an output envelope */
6de5d06e6   Dan Williams   libnvdimm/bus: Pr...
1071
1072
1073
1074
1075
  	out_env = kzalloc(ND_CMD_MAX_ENVELOPE, GFP_KERNEL);
  	if (!out_env) {
  		rc = -ENOMEM;
  		goto out;
  	}
62232e45f   Dan Williams   libnvdimm: contro...
1076
1077
  	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...
1078
  				(u32 *) in_env, (u32 *) out_env, 0);
62232e45f   Dan Williams   libnvdimm: contro...
1079
1080
1081
  		u32 copy;
  
  		if (out_size == UINT_MAX) {
426824d63   Dan Williams   libnvdimm: remove...
1082
1083
1084
  			dev_dbg(dev, "%s unknown output size cmd: %s field: %d
  ",
  					dimm_name, cmd_name, i);
6de5d06e6   Dan Williams   libnvdimm/bus: Pr...
1085
1086
  			rc = -EFAULT;
  			goto out;
62232e45f   Dan Williams   libnvdimm: contro...
1087
  		}
6de5d06e6   Dan Williams   libnvdimm/bus: Pr...
1088
1089
  		if (out_len < ND_CMD_MAX_ENVELOPE)
  			copy = min_t(u32, ND_CMD_MAX_ENVELOPE - out_len, out_size);
62232e45f   Dan Williams   libnvdimm: contro...
1090
1091
1092
  		else
  			copy = 0;
  		if (copy && copy_from_user(&out_env[out_len],
6de5d06e6   Dan Williams   libnvdimm/bus: Pr...
1093
1094
1095
1096
  					p + in_len + out_len, copy)) {
  			rc = -EFAULT;
  			goto out;
  		}
62232e45f   Dan Williams   libnvdimm: contro...
1097
1098
  		out_len += out_size;
  	}
58738c495   Dan Williams   libnvdimm: fix in...
1099
  	buf_len = (u64) out_len + (u64) in_len;
62232e45f   Dan Williams   libnvdimm: contro...
1100
  	if (buf_len > ND_IOCTL_MAX_BUFLEN) {
426824d63   Dan Williams   libnvdimm: remove...
1101
1102
1103
  		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...
1104
1105
  		rc = -EINVAL;
  		goto out;
62232e45f   Dan Williams   libnvdimm: contro...
1106
1107
1108
  	}
  
  	buf = vmalloc(buf_len);
6de5d06e6   Dan Williams   libnvdimm/bus: Pr...
1109
1110
1111
1112
  	if (!buf) {
  		rc = -ENOMEM;
  		goto out;
  	}
62232e45f   Dan Williams   libnvdimm: contro...
1113
1114
1115
1116
1117
  
  	if (copy_from_user(buf, p, buf_len)) {
  		rc = -EFAULT;
  		goto out;
  	}
87a30e1f0   Dan Williams   driver-core, libn...
1118
  	nd_device_lock(dev);
ca6bf264f   Dan Williams   libnvdimm/bus: Fi...
1119
  	nvdimm_bus_lock(dev);
53b85a449   Jerry Hoemann   libnvdimm: passth...
1120
  	rc = nd_cmd_clear_to_send(nvdimm_bus, nvdimm, func, buf);
eaf961536   Dan Williams   libnvdimm, nfit: ...
1121
1122
  	if (rc)
  		goto out_unlock;
006358b35   Dave Jiang   libnvdimm: add su...
1123
  	rc = nd_desc->ndctl(nd_desc, nvdimm, cmd, buf, buf_len, &cmd_rc);
62232e45f   Dan Williams   libnvdimm: contro...
1124
  	if (rc < 0)
eaf961536   Dan Williams   libnvdimm, nfit: ...
1125
  		goto out_unlock;
006358b35   Dave Jiang   libnvdimm: add su...
1126
1127
1128
  
  	if (!nvdimm && cmd == ND_CMD_CLEAR_ERROR && cmd_rc >= 0) {
  		struct nd_cmd_clear_error *clear_err = buf;
006358b35   Dave Jiang   libnvdimm: add su...
1129

23f498448   Dan Williams   libnvdimm: rework...
1130
1131
  		nvdimm_account_cleared_poison(nvdimm_bus, clear_err->address,
  				clear_err->cleared);
006358b35   Dave Jiang   libnvdimm: add su...
1132
  	}
0beb2012a   Dan Williams   libnvdimm: fix re...
1133

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

6de5d06e6   Dan Williams   libnvdimm/bus: Pr...
1137
  out_unlock:
ca6bf264f   Dan Williams   libnvdimm/bus: Fi...
1138
  	nvdimm_bus_unlock(dev);
87a30e1f0   Dan Williams   driver-core, libn...
1139
  	nd_device_unlock(dev);
6de5d06e6   Dan Williams   libnvdimm/bus: Pr...
1140
1141
1142
  out:
  	kfree(in_env);
  	kfree(out_env);
62232e45f   Dan Williams   libnvdimm: contro...
1143
1144
1145
  	vfree(buf);
  	return rc;
  }
b70d31d05   Dan Williams   libnvdimm/bus: St...
1146
1147
1148
1149
  enum nd_ioctl_mode {
  	BUS_IOCTL,
  	DIMM_IOCTL,
  };
62232e45f   Dan Williams   libnvdimm: contro...
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
  
  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...
1163
1164
  static long nd_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
  		enum nd_ioctl_mode mode)
62232e45f   Dan Williams   libnvdimm: contro...
1165
  {
b70d31d05   Dan Williams   libnvdimm/bus: St...
1166
1167
1168
1169
  	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...
1170

4dc0e7be8   Jerry Hoemann   libnvdimm: Clean-...
1171
  	ro = ((file->f_flags & O_ACCMODE) == O_RDONLY);
62232e45f   Dan Williams   libnvdimm: contro...
1172
1173
  	mutex_lock(&nvdimm_bus_list_mutex);
  	list_for_each_entry(nvdimm_bus, &nvdimm_bus_list, list) {
b70d31d05   Dan Williams   libnvdimm/bus: St...
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
  		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...
1186

b70d31d05   Dan Williams   libnvdimm/bus: St...
1187
1188
1189
1190
  		if (found) {
  			atomic_inc(&nvdimm_bus->ioctl_active);
  			break;
  		}
62232e45f   Dan Williams   libnvdimm: contro...
1191
1192
  	}
  	mutex_unlock(&nvdimm_bus_list_mutex);
b70d31d05   Dan Williams   libnvdimm/bus: St...
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
  	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...
1203
1204
  	return rc;
  }
b70d31d05   Dan Williams   libnvdimm/bus: St...
1205
1206
1207
1208
1209
1210
1211
1212
1213
  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...
1214
1215
1216
1217
1218
1219
  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...
1220
1221
1222
1223
  }
  
  static const struct file_operations nvdimm_bus_fops = {
  	.owner = THIS_MODULE,
62232e45f   Dan Williams   libnvdimm: contro...
1224
  	.open = nd_open,
b70d31d05   Dan Williams   libnvdimm/bus: St...
1225
  	.unlocked_ioctl = bus_ioctl,
1832f2d8f   Arnd Bergmann   compat_ioctl: mov...
1226
  	.compat_ioctl = compat_ptr_ioctl,
45def22c1   Dan Williams   libnvdimm: contro...
1227
1228
  	.llseek = noop_llseek,
  };
62232e45f   Dan Williams   libnvdimm: contro...
1229
1230
1231
  static const struct file_operations nvdimm_fops = {
  	.owner = THIS_MODULE,
  	.open = nd_open,
b70d31d05   Dan Williams   libnvdimm/bus: St...
1232
  	.unlocked_ioctl = dimm_ioctl,
1832f2d8f   Arnd Bergmann   compat_ioctl: mov...
1233
  	.compat_ioctl = compat_ptr_ioctl,
62232e45f   Dan Williams   libnvdimm: contro...
1234
1235
  	.llseek = noop_llseek,
  };
45def22c1   Dan Williams   libnvdimm: contro...
1236
1237
1238
  int __init nvdimm_bus_init(void)
  {
  	int rc;
e6dfb2de4   Dan Williams   libnvdimm, nfit: ...
1239
1240
1241
  	rc = bus_register(&nvdimm_bus_type);
  	if (rc)
  		return rc;
45def22c1   Dan Williams   libnvdimm: contro...
1242
1243
  	rc = register_chrdev(0, "ndctl", &nvdimm_bus_fops);
  	if (rc < 0)
62232e45f   Dan Williams   libnvdimm: contro...
1244
  		goto err_bus_chrdev;
45def22c1   Dan Williams   libnvdimm: contro...
1245
  	nvdimm_bus_major = rc;
62232e45f   Dan Williams   libnvdimm: contro...
1246
1247
1248
1249
  	rc = register_chrdev(0, "dimmctl", &nvdimm_fops);
  	if (rc < 0)
  		goto err_dimm_chrdev;
  	nvdimm_major = rc;
45def22c1   Dan Williams   libnvdimm: contro...
1250
  	nd_class = class_create(THIS_MODULE, "nd");
daa1dee40   Axel Lin   nvdimm: Fix retur...
1251
1252
  	if (IS_ERR(nd_class)) {
  		rc = PTR_ERR(nd_class);
45def22c1   Dan Williams   libnvdimm: contro...
1253
  		goto err_class;
daa1dee40   Axel Lin   nvdimm: Fix retur...
1254
  	}
45def22c1   Dan Williams   libnvdimm: contro...
1255

18515942d   Dan Williams   libnvdimm: regist...
1256
1257
1258
  	rc = driver_register(&nd_bus_driver.drv);
  	if (rc)
  		goto err_nd_bus;
45def22c1   Dan Williams   libnvdimm: contro...
1259
  	return 0;
18515942d   Dan Williams   libnvdimm: regist...
1260
1261
   err_nd_bus:
  	class_destroy(nd_class);
45def22c1   Dan Williams   libnvdimm: contro...
1262
   err_class:
62232e45f   Dan Williams   libnvdimm: contro...
1263
1264
  	unregister_chrdev(nvdimm_major, "dimmctl");
   err_dimm_chrdev:
45def22c1   Dan Williams   libnvdimm: contro...
1265
  	unregister_chrdev(nvdimm_bus_major, "ndctl");
62232e45f   Dan Williams   libnvdimm: contro...
1266
   err_bus_chrdev:
e6dfb2de4   Dan Williams   libnvdimm, nfit: ...
1267
  	bus_unregister(&nvdimm_bus_type);
45def22c1   Dan Williams   libnvdimm: contro...
1268
1269
1270
  
  	return rc;
  }
4d88a97aa   Dan Williams   libnvdimm, nvdimm...
1271
  void nvdimm_bus_exit(void)
45def22c1   Dan Williams   libnvdimm: contro...
1272
  {
18515942d   Dan Williams   libnvdimm: regist...
1273
  	driver_unregister(&nd_bus_driver.drv);
45def22c1   Dan Williams   libnvdimm: contro...
1274
1275
  	class_destroy(nd_class);
  	unregister_chrdev(nvdimm_bus_major, "ndctl");
62232e45f   Dan Williams   libnvdimm: contro...
1276
  	unregister_chrdev(nvdimm_major, "dimmctl");
e6dfb2de4   Dan Williams   libnvdimm, nfit: ...
1277
  	bus_unregister(&nvdimm_bus_type);
18515942d   Dan Williams   libnvdimm: regist...
1278
  	ida_destroy(&nd_ida);
45def22c1   Dan Williams   libnvdimm: contro...
1279
  }