Blame view

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

452bae0ae   Dan Williams   libnvdimm: fix nv...
349
  		nd_detach_ndns(btt_dev, &nd_btt->ndns);
e32bc729a   Dan Williams   libnvdimm, btt, c...
350
  		put_device(btt_dev);
8c2f7e865   Dan Williams   libnvdimm: infras...
351
352
353
354
355
  	}
  
  	return rc;
  }
  EXPORT_SYMBOL(nd_btt_probe);