1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright(c) 2013-2015 Intel Corporation. All rights reserved. |
4 | */ |
5 | #include <linux/blkdev.h> |
6 | #include <linux/device.h> |
7 | #include <linux/sizes.h> |
8 | #include <linux/slab.h> |
9 | #include <linux/fs.h> |
10 | #include <linux/mm.h> |
11 | #include "nd-core.h" |
12 | #include "btt.h" |
13 | #include "nd.h" |
14 | |
15 | static void nd_btt_release(struct device *dev) |
16 | { |
17 | struct nd_region *nd_region = to_nd_region(dev: dev->parent); |
18 | struct nd_btt *nd_btt = to_nd_btt(dev); |
19 | |
20 | dev_dbg(dev, "trace\n" ); |
21 | nd_detach_ndns(dev: &nd_btt->dev, ndns: &nd_btt->ndns); |
22 | ida_simple_remove(&nd_region->btt_ida, nd_btt->id); |
23 | kfree(objp: nd_btt->uuid); |
24 | kfree(objp: nd_btt); |
25 | } |
26 | |
27 | struct nd_btt *to_nd_btt(struct device *dev) |
28 | { |
29 | struct nd_btt *nd_btt = container_of(dev, struct nd_btt, dev); |
30 | |
31 | WARN_ON(!is_nd_btt(dev)); |
32 | return nd_btt; |
33 | } |
34 | EXPORT_SYMBOL(to_nd_btt); |
35 | |
36 | static const unsigned long btt_lbasize_supported[] = { 512, 520, 528, |
37 | 4096, 4104, 4160, 4224, 0 }; |
38 | |
39 | static ssize_t sector_size_show(struct device *dev, |
40 | struct device_attribute *attr, char *buf) |
41 | { |
42 | struct nd_btt *nd_btt = to_nd_btt(dev); |
43 | |
44 | return nd_size_select_show(current_size: nd_btt->lbasize, supported: btt_lbasize_supported, buf); |
45 | } |
46 | |
47 | static ssize_t sector_size_store(struct device *dev, |
48 | struct device_attribute *attr, const char *buf, size_t len) |
49 | { |
50 | struct nd_btt *nd_btt = to_nd_btt(dev); |
51 | ssize_t rc; |
52 | |
53 | device_lock(dev); |
54 | nvdimm_bus_lock(dev); |
55 | rc = nd_size_select_store(dev, buf, current_size: &nd_btt->lbasize, |
56 | supported: btt_lbasize_supported); |
57 | dev_dbg(dev, "result: %zd wrote: %s%s" , rc, buf, |
58 | buf[len - 1] == '\n' ? "" : "\n" ); |
59 | nvdimm_bus_unlock(dev); |
60 | device_unlock(dev); |
61 | |
62 | return rc ? rc : len; |
63 | } |
64 | static DEVICE_ATTR_RW(sector_size); |
65 | |
66 | static ssize_t uuid_show(struct device *dev, |
67 | struct device_attribute *attr, char *buf) |
68 | { |
69 | struct nd_btt *nd_btt = to_nd_btt(dev); |
70 | |
71 | if (nd_btt->uuid) |
72 | return sprintf(buf, fmt: "%pUb\n" , nd_btt->uuid); |
73 | return sprintf(buf, fmt: "\n" ); |
74 | } |
75 | |
76 | static ssize_t uuid_store(struct device *dev, |
77 | struct device_attribute *attr, const char *buf, size_t len) |
78 | { |
79 | struct nd_btt *nd_btt = to_nd_btt(dev); |
80 | ssize_t rc; |
81 | |
82 | device_lock(dev); |
83 | rc = nd_uuid_store(dev, uuid_out: &nd_btt->uuid, buf, len); |
84 | dev_dbg(dev, "result: %zd wrote: %s%s" , rc, buf, |
85 | buf[len - 1] == '\n' ? "" : "\n" ); |
86 | device_unlock(dev); |
87 | |
88 | return rc ? rc : len; |
89 | } |
90 | static DEVICE_ATTR_RW(uuid); |
91 | |
92 | static ssize_t namespace_show(struct device *dev, |
93 | struct device_attribute *attr, char *buf) |
94 | { |
95 | struct nd_btt *nd_btt = to_nd_btt(dev); |
96 | ssize_t rc; |
97 | |
98 | nvdimm_bus_lock(dev); |
99 | rc = sprintf(buf, fmt: "%s\n" , nd_btt->ndns |
100 | ? dev_name(dev: &nd_btt->ndns->dev) : "" ); |
101 | nvdimm_bus_unlock(dev); |
102 | return rc; |
103 | } |
104 | |
105 | static ssize_t namespace_store(struct device *dev, |
106 | struct device_attribute *attr, const char *buf, size_t len) |
107 | { |
108 | struct nd_btt *nd_btt = to_nd_btt(dev); |
109 | ssize_t rc; |
110 | |
111 | device_lock(dev); |
112 | nvdimm_bus_lock(dev); |
113 | rc = nd_namespace_store(dev, ndns: &nd_btt->ndns, buf, len); |
114 | dev_dbg(dev, "result: %zd wrote: %s%s" , rc, buf, |
115 | buf[len - 1] == '\n' ? "" : "\n" ); |
116 | nvdimm_bus_unlock(dev); |
117 | device_unlock(dev); |
118 | |
119 | return rc; |
120 | } |
121 | static DEVICE_ATTR_RW(namespace); |
122 | |
123 | static ssize_t size_show(struct device *dev, |
124 | struct device_attribute *attr, char *buf) |
125 | { |
126 | struct nd_btt *nd_btt = to_nd_btt(dev); |
127 | ssize_t rc; |
128 | |
129 | device_lock(dev); |
130 | if (dev->driver) |
131 | rc = sprintf(buf, fmt: "%llu\n" , nd_btt->size); |
132 | else { |
133 | /* no size to convey if the btt instance is disabled */ |
134 | rc = -ENXIO; |
135 | } |
136 | device_unlock(dev); |
137 | |
138 | return rc; |
139 | } |
140 | static DEVICE_ATTR_RO(size); |
141 | |
142 | static ssize_t log_zero_flags_show(struct device *dev, |
143 | struct device_attribute *attr, char *buf) |
144 | { |
145 | return sprintf(buf, fmt: "Y\n" ); |
146 | } |
147 | static DEVICE_ATTR_RO(log_zero_flags); |
148 | |
149 | static struct attribute *nd_btt_attributes[] = { |
150 | &dev_attr_sector_size.attr, |
151 | &dev_attr_namespace.attr, |
152 | &dev_attr_uuid.attr, |
153 | &dev_attr_size.attr, |
154 | &dev_attr_log_zero_flags.attr, |
155 | NULL, |
156 | }; |
157 | |
158 | static struct attribute_group nd_btt_attribute_group = { |
159 | .attrs = nd_btt_attributes, |
160 | }; |
161 | |
162 | static const struct attribute_group *nd_btt_attribute_groups[] = { |
163 | &nd_btt_attribute_group, |
164 | &nd_device_attribute_group, |
165 | &nd_numa_attribute_group, |
166 | NULL, |
167 | }; |
168 | |
169 | static const struct device_type nd_btt_device_type = { |
170 | .name = "nd_btt" , |
171 | .release = nd_btt_release, |
172 | .groups = nd_btt_attribute_groups, |
173 | }; |
174 | |
175 | bool is_nd_btt(struct device *dev) |
176 | { |
177 | return dev->type == &nd_btt_device_type; |
178 | } |
179 | EXPORT_SYMBOL(is_nd_btt); |
180 | |
181 | static struct lock_class_key nvdimm_btt_key; |
182 | |
183 | static struct device *__nd_btt_create(struct nd_region *nd_region, |
184 | unsigned long lbasize, uuid_t *uuid, |
185 | struct nd_namespace_common *ndns) |
186 | { |
187 | struct nd_btt *nd_btt; |
188 | struct device *dev; |
189 | |
190 | nd_btt = kzalloc(size: sizeof(*nd_btt), GFP_KERNEL); |
191 | if (!nd_btt) |
192 | return NULL; |
193 | |
194 | nd_btt->id = ida_simple_get(&nd_region->btt_ida, 0, 0, GFP_KERNEL); |
195 | if (nd_btt->id < 0) |
196 | goto out_nd_btt; |
197 | |
198 | nd_btt->lbasize = lbasize; |
199 | if (uuid) { |
200 | uuid = kmemdup(p: uuid, size: 16, GFP_KERNEL); |
201 | if (!uuid) |
202 | goto out_put_id; |
203 | } |
204 | nd_btt->uuid = uuid; |
205 | dev = &nd_btt->dev; |
206 | dev_set_name(dev, name: "btt%d.%d" , nd_region->id, nd_btt->id); |
207 | dev->parent = &nd_region->dev; |
208 | dev->type = &nd_btt_device_type; |
209 | device_initialize(dev: &nd_btt->dev); |
210 | lockdep_set_class(&nd_btt->dev.mutex, &nvdimm_btt_key); |
211 | if (ndns && !__nd_attach_ndns(dev: &nd_btt->dev, attach: ndns, ndns: &nd_btt->ndns)) { |
212 | dev_dbg(&ndns->dev, "failed, already claimed by %s\n" , |
213 | dev_name(ndns->claim)); |
214 | put_device(dev); |
215 | return NULL; |
216 | } |
217 | return dev; |
218 | |
219 | out_put_id: |
220 | ida_simple_remove(&nd_region->btt_ida, nd_btt->id); |
221 | |
222 | out_nd_btt: |
223 | kfree(objp: nd_btt); |
224 | return NULL; |
225 | } |
226 | |
227 | struct device *nd_btt_create(struct nd_region *nd_region) |
228 | { |
229 | struct device *dev = __nd_btt_create(nd_region, lbasize: 0, NULL, NULL); |
230 | |
231 | nd_device_register(dev); |
232 | return dev; |
233 | } |
234 | |
235 | /** |
236 | * nd_btt_arena_is_valid - check if the metadata layout is valid |
237 | * @nd_btt: device with BTT geometry and backing device info |
238 | * @super: pointer to the arena's info block being tested |
239 | * |
240 | * Check consistency of the btt info block with itself by validating |
241 | * the checksum, and with the parent namespace by verifying the |
242 | * parent_uuid contained in the info block with the one supplied in. |
243 | * |
244 | * Returns: |
245 | * false for an invalid info block, true for a valid one |
246 | */ |
247 | bool nd_btt_arena_is_valid(struct nd_btt *nd_btt, struct btt_sb *super) |
248 | { |
249 | const uuid_t *ns_uuid = nd_dev_to_uuid(dev: &nd_btt->ndns->dev); |
250 | uuid_t parent_uuid; |
251 | u64 checksum; |
252 | |
253 | if (memcmp(p: super->signature, BTT_SIG, BTT_SIG_LEN) != 0) |
254 | return false; |
255 | |
256 | import_uuid(dst: &parent_uuid, src: super->parent_uuid); |
257 | if (!uuid_is_null(uuid: &parent_uuid)) |
258 | if (!uuid_equal(u1: &parent_uuid, u2: ns_uuid)) |
259 | return false; |
260 | |
261 | checksum = le64_to_cpu(super->checksum); |
262 | super->checksum = 0; |
263 | if (checksum != nd_sb_checksum(sb: (struct nd_gen_sb *) super)) |
264 | return false; |
265 | super->checksum = cpu_to_le64(checksum); |
266 | |
267 | /* TODO: figure out action for this */ |
268 | if ((le32_to_cpu(super->flags) & IB_FLAG_ERROR_MASK) != 0) |
269 | dev_info(&nd_btt->dev, "Found arena with an error flag\n" ); |
270 | |
271 | return true; |
272 | } |
273 | EXPORT_SYMBOL(nd_btt_arena_is_valid); |
274 | |
275 | int nd_btt_version(struct nd_btt *nd_btt, struct nd_namespace_common *ndns, |
276 | struct btt_sb *btt_sb) |
277 | { |
278 | if (ndns->claim_class == NVDIMM_CCLASS_BTT2) { |
279 | /* Probe/setup for BTT v2.0 */ |
280 | nd_btt->initial_offset = 0; |
281 | nd_btt->version_major = 2; |
282 | nd_btt->version_minor = 0; |
283 | if (nvdimm_read_bytes(ndns, offset: 0, buf: btt_sb, size: sizeof(*btt_sb), flags: 0)) |
284 | return -ENXIO; |
285 | if (!nd_btt_arena_is_valid(nd_btt, btt_sb)) |
286 | return -ENODEV; |
287 | if ((le16_to_cpu(btt_sb->version_major) != 2) || |
288 | (le16_to_cpu(btt_sb->version_minor) != 0)) |
289 | return -ENODEV; |
290 | } else { |
291 | /* |
292 | * Probe/setup for BTT v1.1 (NVDIMM_CCLASS_NONE or |
293 | * NVDIMM_CCLASS_BTT) |
294 | */ |
295 | nd_btt->initial_offset = SZ_4K; |
296 | nd_btt->version_major = 1; |
297 | nd_btt->version_minor = 1; |
298 | if (nvdimm_read_bytes(ndns, SZ_4K, buf: btt_sb, size: sizeof(*btt_sb), flags: 0)) |
299 | return -ENXIO; |
300 | if (!nd_btt_arena_is_valid(nd_btt, btt_sb)) |
301 | return -ENODEV; |
302 | if ((le16_to_cpu(btt_sb->version_major) != 1) || |
303 | (le16_to_cpu(btt_sb->version_minor) != 1)) |
304 | return -ENODEV; |
305 | } |
306 | return 0; |
307 | } |
308 | EXPORT_SYMBOL(nd_btt_version); |
309 | |
310 | static int __nd_btt_probe(struct nd_btt *nd_btt, |
311 | struct nd_namespace_common *ndns, struct btt_sb *btt_sb) |
312 | { |
313 | int rc; |
314 | |
315 | if (!btt_sb || !ndns || !nd_btt) |
316 | return -ENODEV; |
317 | |
318 | if (nvdimm_namespace_capacity(ndns) < SZ_16M) |
319 | return -ENXIO; |
320 | |
321 | rc = nd_btt_version(nd_btt, ndns, btt_sb); |
322 | if (rc < 0) |
323 | return rc; |
324 | |
325 | nd_btt->lbasize = le32_to_cpu(btt_sb->external_lbasize); |
326 | nd_btt->uuid = kmemdup(p: &btt_sb->uuid, size: sizeof(uuid_t), GFP_KERNEL); |
327 | if (!nd_btt->uuid) |
328 | return -ENOMEM; |
329 | |
330 | nd_device_register(dev: &nd_btt->dev); |
331 | |
332 | return 0; |
333 | } |
334 | |
335 | int nd_btt_probe(struct device *dev, struct nd_namespace_common *ndns) |
336 | { |
337 | int rc; |
338 | struct device *btt_dev; |
339 | struct btt_sb *btt_sb; |
340 | struct nd_region *nd_region = to_nd_region(dev: ndns->dev.parent); |
341 | |
342 | if (ndns->force_raw) |
343 | return -ENODEV; |
344 | |
345 | switch (ndns->claim_class) { |
346 | case NVDIMM_CCLASS_NONE: |
347 | case NVDIMM_CCLASS_BTT: |
348 | case NVDIMM_CCLASS_BTT2: |
349 | break; |
350 | default: |
351 | return -ENODEV; |
352 | } |
353 | |
354 | nvdimm_bus_lock(dev: &ndns->dev); |
355 | btt_dev = __nd_btt_create(nd_region, lbasize: 0, NULL, ndns); |
356 | nvdimm_bus_unlock(dev: &ndns->dev); |
357 | if (!btt_dev) |
358 | return -ENOMEM; |
359 | btt_sb = devm_kzalloc(dev, size: sizeof(*btt_sb), GFP_KERNEL); |
360 | rc = __nd_btt_probe(nd_btt: to_nd_btt(btt_dev), ndns, btt_sb); |
361 | dev_dbg(dev, "btt: %s\n" , rc == 0 ? dev_name(btt_dev) : "<none>" ); |
362 | if (rc < 0) { |
363 | struct nd_btt *nd_btt = to_nd_btt(btt_dev); |
364 | |
365 | nd_detach_ndns(dev: btt_dev, ndns: &nd_btt->ndns); |
366 | put_device(dev: btt_dev); |
367 | } |
368 | |
369 | return rc; |
370 | } |
371 | EXPORT_SYMBOL(nd_btt_probe); |
372 | |