1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright(c) 2013-2016 Intel Corporation. All rights reserved. |
4 | */ |
5 | #include <linux/device.h> |
6 | #include <linux/sizes.h> |
7 | #include <linux/slab.h> |
8 | #include <linux/mm.h> |
9 | #include "nd-core.h" |
10 | #include "pfn.h" |
11 | #include "nd.h" |
12 | |
13 | static void nd_dax_release(struct device *dev) |
14 | { |
15 | struct nd_region *nd_region = to_nd_region(dev: dev->parent); |
16 | struct nd_dax *nd_dax = to_nd_dax(dev); |
17 | struct nd_pfn *nd_pfn = &nd_dax->nd_pfn; |
18 | |
19 | dev_dbg(dev, "trace\n" ); |
20 | nd_detach_ndns(dev, ndns: &nd_pfn->ndns); |
21 | ida_simple_remove(&nd_region->dax_ida, nd_pfn->id); |
22 | kfree(objp: nd_pfn->uuid); |
23 | kfree(objp: nd_dax); |
24 | } |
25 | |
26 | struct nd_dax *to_nd_dax(struct device *dev) |
27 | { |
28 | struct nd_dax *nd_dax = container_of(dev, struct nd_dax, nd_pfn.dev); |
29 | |
30 | WARN_ON(!is_nd_dax(dev)); |
31 | return nd_dax; |
32 | } |
33 | EXPORT_SYMBOL(to_nd_dax); |
34 | |
35 | static const struct device_type nd_dax_device_type = { |
36 | .name = "nd_dax" , |
37 | .release = nd_dax_release, |
38 | .groups = nd_pfn_attribute_groups, |
39 | }; |
40 | |
41 | bool is_nd_dax(const struct device *dev) |
42 | { |
43 | return dev ? dev->type == &nd_dax_device_type : false; |
44 | } |
45 | EXPORT_SYMBOL(is_nd_dax); |
46 | |
47 | static struct nd_dax *nd_dax_alloc(struct nd_region *nd_region) |
48 | { |
49 | struct nd_pfn *nd_pfn; |
50 | struct nd_dax *nd_dax; |
51 | struct device *dev; |
52 | |
53 | nd_dax = kzalloc(size: sizeof(*nd_dax), GFP_KERNEL); |
54 | if (!nd_dax) |
55 | return NULL; |
56 | |
57 | nd_pfn = &nd_dax->nd_pfn; |
58 | nd_pfn->id = ida_simple_get(&nd_region->dax_ida, 0, 0, GFP_KERNEL); |
59 | if (nd_pfn->id < 0) { |
60 | kfree(objp: nd_dax); |
61 | return NULL; |
62 | } |
63 | |
64 | dev = &nd_pfn->dev; |
65 | dev_set_name(dev, name: "dax%d.%d" , nd_region->id, nd_pfn->id); |
66 | dev->type = &nd_dax_device_type; |
67 | dev->parent = &nd_region->dev; |
68 | |
69 | return nd_dax; |
70 | } |
71 | |
72 | struct device *nd_dax_create(struct nd_region *nd_region) |
73 | { |
74 | struct device *dev = NULL; |
75 | struct nd_dax *nd_dax; |
76 | |
77 | if (!is_memory(dev: &nd_region->dev)) |
78 | return NULL; |
79 | |
80 | nd_dax = nd_dax_alloc(nd_region); |
81 | if (nd_dax) |
82 | dev = nd_pfn_devinit(nd_pfn: &nd_dax->nd_pfn, NULL); |
83 | nd_device_register(dev); |
84 | return dev; |
85 | } |
86 | |
87 | int nd_dax_probe(struct device *dev, struct nd_namespace_common *ndns) |
88 | { |
89 | int rc; |
90 | struct nd_dax *nd_dax; |
91 | struct device *dax_dev; |
92 | struct nd_pfn *nd_pfn; |
93 | struct nd_pfn_sb *pfn_sb; |
94 | struct nd_region *nd_region = to_nd_region(dev: ndns->dev.parent); |
95 | |
96 | if (ndns->force_raw) |
97 | return -ENODEV; |
98 | |
99 | switch (ndns->claim_class) { |
100 | case NVDIMM_CCLASS_NONE: |
101 | case NVDIMM_CCLASS_DAX: |
102 | break; |
103 | default: |
104 | return -ENODEV; |
105 | } |
106 | |
107 | nvdimm_bus_lock(dev: &ndns->dev); |
108 | nd_dax = nd_dax_alloc(nd_region); |
109 | nd_pfn = &nd_dax->nd_pfn; |
110 | dax_dev = nd_pfn_devinit(nd_pfn, ndns); |
111 | nvdimm_bus_unlock(dev: &ndns->dev); |
112 | if (!dax_dev) |
113 | return -ENOMEM; |
114 | pfn_sb = devm_kmalloc(dev, size: sizeof(*pfn_sb), GFP_KERNEL); |
115 | nd_pfn->pfn_sb = pfn_sb; |
116 | rc = nd_pfn_validate(nd_pfn, DAX_SIG); |
117 | dev_dbg(dev, "dax: %s\n" , rc == 0 ? dev_name(dax_dev) : "<none>" ); |
118 | if (rc < 0) { |
119 | nd_detach_ndns(dev: dax_dev, ndns: &nd_pfn->ndns); |
120 | put_device(dev: dax_dev); |
121 | } else |
122 | nd_device_register(dev: dax_dev); |
123 | |
124 | return rc; |
125 | } |
126 | EXPORT_SYMBOL(nd_dax_probe); |
127 | |