Blame view

drivers/nvdimm/namespace_devs.c 64.8 KB
3d88002e4   Dan Williams   libnvdimm: suppor...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  /*
   * Copyright(c) 2013-2015 Intel Corporation. All rights reserved.
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of version 2 of the GNU General Public License as
   * published by the Free Software Foundation.
   *
   * This program is distributed in the hope that it will be useful, but
   * WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * General Public License for more details.
   */
  #include <linux/module.h>
  #include <linux/device.h>
6ff3e912d   Dan Williams   libnvdimm, namesp...
15
  #include <linux/sort.h>
3d88002e4   Dan Williams   libnvdimm: suppor...
16
  #include <linux/slab.h>
ae8219f18   Dan Williams   libnvdimm, label:...
17
  #include <linux/list.h>
3d88002e4   Dan Williams   libnvdimm: suppor...
18
  #include <linux/nd.h>
bf9bccc14   Dan Williams   libnvdimm: pmem l...
19
  #include "nd-core.h"
ca6a4657e   Dan Williams   x86, libnvdimm, p...
20
  #include "pmem.h"
3d88002e4   Dan Williams   libnvdimm: suppor...
21
22
23
24
25
26
27
28
  #include "nd.h"
  
  static void namespace_io_release(struct device *dev)
  {
  	struct nd_namespace_io *nsio = to_nd_namespace_io(dev);
  
  	kfree(nsio);
  }
bf9bccc14   Dan Williams   libnvdimm: pmem l...
29
30
31
  static void namespace_pmem_release(struct device *dev)
  {
  	struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev);
0e3b0d123   Dan Williams   libnvdimm, namesp...
32
  	struct nd_region *nd_region = to_nd_region(dev->parent);
bf9bccc14   Dan Williams   libnvdimm: pmem l...
33

0e3b0d123   Dan Williams   libnvdimm, namesp...
34
35
  	if (nspm->id >= 0)
  		ida_simple_remove(&nd_region->ns_ida, nspm->id);
bf9bccc14   Dan Williams   libnvdimm: pmem l...
36
37
38
39
40
41
42
  	kfree(nspm->alt_name);
  	kfree(nspm->uuid);
  	kfree(nspm);
  }
  
  static void namespace_blk_release(struct device *dev)
  {
1b40e09a1   Dan Williams   libnvdimm: blk la...
43
44
45
46
47
48
49
50
51
  	struct nd_namespace_blk *nsblk = to_nd_namespace_blk(dev);
  	struct nd_region *nd_region = to_nd_region(dev->parent);
  
  	if (nsblk->id >= 0)
  		ida_simple_remove(&nd_region->ns_ida, nsblk->id);
  	kfree(nsblk->alt_name);
  	kfree(nsblk->uuid);
  	kfree(nsblk->res);
  	kfree(nsblk);
bf9bccc14   Dan Williams   libnvdimm: pmem l...
52
  }
970d14e39   Bhumika Goyal   nvdimm: constify ...
53
  static const struct device_type namespace_io_device_type = {
3d88002e4   Dan Williams   libnvdimm: suppor...
54
55
56
  	.name = "nd_namespace_io",
  	.release = namespace_io_release,
  };
970d14e39   Bhumika Goyal   nvdimm: constify ...
57
  static const struct device_type namespace_pmem_device_type = {
bf9bccc14   Dan Williams   libnvdimm: pmem l...
58
59
60
  	.name = "nd_namespace_pmem",
  	.release = namespace_pmem_release,
  };
970d14e39   Bhumika Goyal   nvdimm: constify ...
61
  static const struct device_type namespace_blk_device_type = {
bf9bccc14   Dan Williams   libnvdimm: pmem l...
62
63
64
  	.name = "nd_namespace_blk",
  	.release = namespace_blk_release,
  };
6ff3e912d   Dan Williams   libnvdimm, namesp...
65
  static bool is_namespace_pmem(const struct device *dev)
bf9bccc14   Dan Williams   libnvdimm: pmem l...
66
67
68
  {
  	return dev ? dev->type == &namespace_pmem_device_type : false;
  }
6ff3e912d   Dan Williams   libnvdimm, namesp...
69
  static bool is_namespace_blk(const struct device *dev)
bf9bccc14   Dan Williams   libnvdimm: pmem l...
70
71
72
  {
  	return dev ? dev->type == &namespace_blk_device_type : false;
  }
6ff3e912d   Dan Williams   libnvdimm, namesp...
73
  static bool is_namespace_io(const struct device *dev)
bf9bccc14   Dan Williams   libnvdimm: pmem l...
74
75
76
  {
  	return dev ? dev->type == &namespace_io_device_type : false;
  }
e07ecd76d   Dan Williams   libnvdimm: fix na...
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
  static int is_uuid_busy(struct device *dev, void *data)
  {
  	u8 *uuid1 = data, *uuid2 = NULL;
  
  	if (is_namespace_pmem(dev)) {
  		struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev);
  
  		uuid2 = nspm->uuid;
  	} else if (is_namespace_blk(dev)) {
  		struct nd_namespace_blk *nsblk = to_nd_namespace_blk(dev);
  
  		uuid2 = nsblk->uuid;
  	} else if (is_nd_btt(dev)) {
  		struct nd_btt *nd_btt = to_nd_btt(dev);
  
  		uuid2 = nd_btt->uuid;
  	} else if (is_nd_pfn(dev)) {
  		struct nd_pfn *nd_pfn = to_nd_pfn(dev);
  
  		uuid2 = nd_pfn->uuid;
  	}
  
  	if (uuid2 && memcmp(uuid1, uuid2, NSLABEL_UUID_LEN) == 0)
  		return -EBUSY;
  
  	return 0;
  }
  
  static int is_namespace_uuid_busy(struct device *dev, void *data)
  {
c9e582aa6   Dan Williams   libnvdimm, nfit: ...
107
  	if (is_nd_region(dev))
e07ecd76d   Dan Williams   libnvdimm: fix na...
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
  		return device_for_each_child(dev, data, is_uuid_busy);
  	return 0;
  }
  
  /**
   * nd_is_uuid_unique - verify that no other namespace has @uuid
   * @dev: any device on a nvdimm_bus
   * @uuid: uuid to check
   */
  bool nd_is_uuid_unique(struct device *dev, u8 *uuid)
  {
  	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
  
  	if (!nvdimm_bus)
  		return false;
  	WARN_ON_ONCE(!is_nvdimm_bus_locked(&nvdimm_bus->dev));
  	if (device_for_each_child(&nvdimm_bus->dev, uuid,
  				is_namespace_uuid_busy) != 0)
  		return false;
  	return true;
  }
004f1afbe   Dan Williams   libnvdimm, pmem: ...
129
130
131
  bool pmem_should_map_pages(struct device *dev)
  {
  	struct nd_region *nd_region = to_nd_region(dev->parent);
cfe30b872   Dan Williams   libnvdimm, pmem: ...
132
  	struct nd_namespace_io *nsio;
004f1afbe   Dan Williams   libnvdimm, pmem: ...
133
134
135
136
137
138
139
140
141
  
  	if (!IS_ENABLED(CONFIG_ZONE_DEVICE))
  		return false;
  
  	if (!test_bit(ND_REGION_PAGEMAP, &nd_region->flags))
  		return false;
  
  	if (is_nd_pfn(dev) || is_nd_btt(dev))
  		return false;
cfe30b872   Dan Williams   libnvdimm, pmem: ...
142
143
144
145
146
  	nsio = to_nd_namespace_io(dev);
  	if (region_intersects(nsio->res.start, resource_size(&nsio->res),
  				IORESOURCE_SYSTEM_RAM,
  				IORES_DESC_NONE) == REGION_MIXED)
  		return false;
004f1afbe   Dan Williams   libnvdimm, pmem: ...
147
  	return ARCH_MEMREMAP_PMEM == MEMREMAP_WB;
004f1afbe   Dan Williams   libnvdimm, pmem: ...
148
149
  }
  EXPORT_SYMBOL(pmem_should_map_pages);
f979b13c3   Dan Williams   libnvdimm, label:...
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
  unsigned int pmem_sector_size(struct nd_namespace_common *ndns)
  {
  	if (is_namespace_pmem(&ndns->dev)) {
  		struct nd_namespace_pmem *nspm;
  
  		nspm = to_nd_namespace_pmem(&ndns->dev);
  		if (nspm->lbasize == 0 || nspm->lbasize == 512)
  			/* default */;
  		else if (nspm->lbasize == 4096)
  			return 4096;
  		else
  			dev_WARN(&ndns->dev, "unsupported sector size: %ld
  ",
  					nspm->lbasize);
  	}
  
  	/*
  	 * There is no namespace label (is_namespace_io()), or the label
  	 * indicates the default sector size.
  	 */
  	return 512;
  }
  EXPORT_SYMBOL(pmem_sector_size);
5212e11fd   Vishal Verma   nd_btt: atomic se...
173
174
175
176
  const char *nvdimm_namespace_disk_name(struct nd_namespace_common *ndns,
  		char *name)
  {
  	struct nd_region *nd_region = to_nd_region(ndns->dev.parent);
004f1afbe   Dan Williams   libnvdimm, pmem: ...
177
  	const char *suffix = NULL;
5212e11fd   Vishal Verma   nd_btt: atomic se...
178

0731de0dd   Dan Williams   libnvdimm, pfn: m...
179
180
  	if (ndns->claim && is_nd_btt(ndns->claim))
  		suffix = "s";
5212e11fd   Vishal Verma   nd_btt: atomic se...
181

004f1afbe   Dan Williams   libnvdimm, pmem: ...
182
  	if (is_namespace_pmem(&ndns->dev) || is_namespace_io(&ndns->dev)) {
012207334   Dan Williams   libnvdimm, namesp...
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
  		int nsidx = 0;
  
  		if (is_namespace_pmem(&ndns->dev)) {
  			struct nd_namespace_pmem *nspm;
  
  			nspm = to_nd_namespace_pmem(&ndns->dev);
  			nsidx = nspm->id;
  		}
  
  		if (nsidx)
  			sprintf(name, "pmem%d.%d%s", nd_region->id, nsidx,
  					suffix ? suffix : "");
  		else
  			sprintf(name, "pmem%d%s", nd_region->id,
  					suffix ? suffix : "");
004f1afbe   Dan Williams   libnvdimm, pmem: ...
198
  	} else if (is_namespace_blk(&ndns->dev)) {
5212e11fd   Vishal Verma   nd_btt: atomic se...
199
200
201
  		struct nd_namespace_blk *nsblk;
  
  		nsblk = to_nd_namespace_blk(&ndns->dev);
004f1afbe   Dan Williams   libnvdimm, pmem: ...
202
203
  		sprintf(name, "ndblk%d.%d%s", nd_region->id, nsblk->id,
  				suffix ? suffix : "");
5212e11fd   Vishal Verma   nd_btt: atomic se...
204
205
206
207
208
209
210
  	} else {
  		return NULL;
  	}
  
  	return name;
  }
  EXPORT_SYMBOL(nvdimm_namespace_disk_name);
6ec689542   Vishal Verma   libnvdimm, btt: w...
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
  const u8 *nd_dev_to_uuid(struct device *dev)
  {
  	static const u8 null_uuid[16];
  
  	if (!dev)
  		return null_uuid;
  
  	if (is_namespace_pmem(dev)) {
  		struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev);
  
  		return nspm->uuid;
  	} else if (is_namespace_blk(dev)) {
  		struct nd_namespace_blk *nsblk = to_nd_namespace_blk(dev);
  
  		return nsblk->uuid;
  	} else
  		return null_uuid;
  }
  EXPORT_SYMBOL(nd_dev_to_uuid);
3d88002e4   Dan Williams   libnvdimm: suppor...
230
231
232
233
234
235
236
237
238
  static ssize_t nstype_show(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
  	struct nd_region *nd_region = to_nd_region(dev->parent);
  
  	return sprintf(buf, "%d
  ", nd_region_to_nstype(nd_region));
  }
  static DEVICE_ATTR_RO(nstype);
bf9bccc14   Dan Williams   libnvdimm: pmem l...
239
240
241
242
243
244
245
246
247
248
249
  static ssize_t __alt_name_store(struct device *dev, const char *buf,
  		const size_t len)
  {
  	char *input, *pos, *alt_name, **ns_altname;
  	ssize_t rc;
  
  	if (is_namespace_pmem(dev)) {
  		struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev);
  
  		ns_altname = &nspm->alt_name;
  	} else if (is_namespace_blk(dev)) {
1b40e09a1   Dan Williams   libnvdimm: blk la...
250
251
252
  		struct nd_namespace_blk *nsblk = to_nd_namespace_blk(dev);
  
  		ns_altname = &nsblk->alt_name;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
253
254
  	} else
  		return -ENXIO;
8c2f7e865   Dan Williams   libnvdimm: infras...
255
  	if (dev->driver || to_ndns(dev)->claim)
bf9bccc14   Dan Williams   libnvdimm: pmem l...
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
  		return -EBUSY;
  
  	input = kmemdup(buf, len + 1, GFP_KERNEL);
  	if (!input)
  		return -ENOMEM;
  
  	input[len] = '\0';
  	pos = strim(input);
  	if (strlen(pos) + 1 > NSLABEL_NAME_LEN) {
  		rc = -EINVAL;
  		goto out;
  	}
  
  	alt_name = kzalloc(NSLABEL_NAME_LEN, GFP_KERNEL);
  	if (!alt_name) {
  		rc = -ENOMEM;
  		goto out;
  	}
  	kfree(*ns_altname);
  	*ns_altname = alt_name;
  	sprintf(*ns_altname, "%s", pos);
  	rc = len;
  
  out:
  	kfree(input);
  	return rc;
  }
1b40e09a1   Dan Williams   libnvdimm: blk la...
283
284
  static resource_size_t nd_namespace_blk_size(struct nd_namespace_blk *nsblk)
  {
8c2f7e865   Dan Williams   libnvdimm: infras...
285
  	struct nd_region *nd_region = to_nd_region(nsblk->common.dev.parent);
1b40e09a1   Dan Williams   libnvdimm: blk la...
286
287
288
289
290
291
292
293
294
295
296
297
298
299
  	struct nd_mapping *nd_mapping = &nd_region->mapping[0];
  	struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
  	struct nd_label_id label_id;
  	resource_size_t size = 0;
  	struct resource *res;
  
  	if (!nsblk->uuid)
  		return 0;
  	nd_label_gen_id(&label_id, nsblk->uuid, NSLABEL_FLAG_LOCAL);
  	for_each_dpa_resource(ndd, res)
  		if (strcmp(res->name, label_id.id) == 0)
  			size += resource_size(res);
  	return size;
  }
047fc8a1f   Ross Zwisler   libnvdimm, nfit, ...
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
  static bool __nd_namespace_blk_validate(struct nd_namespace_blk *nsblk)
  {
  	struct nd_region *nd_region = to_nd_region(nsblk->common.dev.parent);
  	struct nd_mapping *nd_mapping = &nd_region->mapping[0];
  	struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
  	struct nd_label_id label_id;
  	struct resource *res;
  	int count, i;
  
  	if (!nsblk->uuid || !nsblk->lbasize || !ndd)
  		return false;
  
  	count = 0;
  	nd_label_gen_id(&label_id, nsblk->uuid, NSLABEL_FLAG_LOCAL);
  	for_each_dpa_resource(ndd, res) {
  		if (strcmp(res->name, label_id.id) != 0)
  			continue;
  		/*
ae551e9ca   Geert Uytterhoeven   nvdimm: Spelling ...
318
  		 * Resources with unacknowledged adjustments indicate a
047fc8a1f   Ross Zwisler   libnvdimm, nfit, ...
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
  		 * failure to update labels
  		 */
  		if (res->flags & DPA_RESOURCE_ADJUSTED)
  			return false;
  		count++;
  	}
  
  	/* These values match after a successful label update */
  	if (count != nsblk->num_resources)
  		return false;
  
  	for (i = 0; i < nsblk->num_resources; i++) {
  		struct resource *found = NULL;
  
  		for_each_dpa_resource(ndd, res)
  			if (res == nsblk->res[i]) {
  				found = res;
  				break;
  			}
  		/* stale resource */
  		if (!found)
  			return false;
  	}
  
  	return true;
  }
  
  resource_size_t nd_namespace_blk_validate(struct nd_namespace_blk *nsblk)
  {
  	resource_size_t size;
  
  	nvdimm_bus_lock(&nsblk->common.dev);
  	size = __nd_namespace_blk_validate(nsblk);
  	nvdimm_bus_unlock(&nsblk->common.dev);
  
  	return size;
  }
  EXPORT_SYMBOL(nd_namespace_blk_validate);
f524bf271   Dan Williams   libnvdimm: write ...
357
358
359
  static int nd_namespace_label_update(struct nd_region *nd_region,
  		struct device *dev)
  {
8c2f7e865   Dan Williams   libnvdimm: infras...
360
  	dev_WARN_ONCE(dev, dev->driver || to_ndns(dev)->claim,
f524bf271   Dan Williams   libnvdimm: write ...
361
362
  			"namespace must be idle during label update
  ");
8c2f7e865   Dan Williams   libnvdimm: infras...
363
  	if (dev->driver || to_ndns(dev)->claim)
f524bf271   Dan Williams   libnvdimm: write ...
364
365
366
367
368
369
370
371
  		return 0;
  
  	/*
  	 * Only allow label writes that will result in a valid namespace
  	 * or deletion of an existing namespace.
  	 */
  	if (is_namespace_pmem(dev)) {
  		struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev);
0ba1c6348   Dan Williams   libnvdimm: write ...
372
  		resource_size_t size = resource_size(&nspm->nsio.res);
f524bf271   Dan Williams   libnvdimm: write ...
373
374
375
376
377
378
379
380
  
  		if (size == 0 && nspm->uuid)
  			/* delete allocation */;
  		else if (!nspm->uuid)
  			return 0;
  
  		return nd_pmem_namespace_label_update(nd_region, nspm, size);
  	} else if (is_namespace_blk(dev)) {
0ba1c6348   Dan Williams   libnvdimm: write ...
381
382
383
384
385
386
387
388
389
  		struct nd_namespace_blk *nsblk = to_nd_namespace_blk(dev);
  		resource_size_t size = nd_namespace_blk_size(nsblk);
  
  		if (size == 0 && nsblk->uuid)
  			/* delete allocation */;
  		else if (!nsblk->uuid || !nsblk->lbasize)
  			return 0;
  
  		return nd_blk_namespace_label_update(nd_region, nsblk, size);
f524bf271   Dan Williams   libnvdimm: write ...
390
391
392
  	} else
  		return -ENXIO;
  }
bf9bccc14   Dan Williams   libnvdimm: pmem l...
393
394
395
  static ssize_t alt_name_store(struct device *dev,
  		struct device_attribute *attr, const char *buf, size_t len)
  {
f524bf271   Dan Williams   libnvdimm: write ...
396
  	struct nd_region *nd_region = to_nd_region(dev->parent);
bf9bccc14   Dan Williams   libnvdimm: pmem l...
397
398
399
400
401
402
  	ssize_t rc;
  
  	device_lock(dev);
  	nvdimm_bus_lock(dev);
  	wait_nvdimm_bus_probe_idle(dev);
  	rc = __alt_name_store(dev, buf, len);
f524bf271   Dan Williams   libnvdimm: write ...
403
404
  	if (rc >= 0)
  		rc = nd_namespace_label_update(nd_region, dev);
bf9bccc14   Dan Williams   libnvdimm: pmem l...
405
406
407
408
  	dev_dbg(dev, "%s: %s(%zd)
  ", __func__, rc < 0 ? "fail " : "", rc);
  	nvdimm_bus_unlock(dev);
  	device_unlock(dev);
f524bf271   Dan Williams   libnvdimm: write ...
409
  	return rc < 0 ? rc : len;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
410
411
412
413
414
415
416
417
418
419
420
421
  }
  
  static ssize_t alt_name_show(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
  	char *ns_altname;
  
  	if (is_namespace_pmem(dev)) {
  		struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev);
  
  		ns_altname = nspm->alt_name;
  	} else if (is_namespace_blk(dev)) {
1b40e09a1   Dan Williams   libnvdimm: blk la...
422
423
424
  		struct nd_namespace_blk *nsblk = to_nd_namespace_blk(dev);
  
  		ns_altname = nsblk->alt_name;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
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
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
  	} else
  		return -ENXIO;
  
  	return sprintf(buf, "%s
  ", ns_altname ? ns_altname : "");
  }
  static DEVICE_ATTR_RW(alt_name);
  
  static int scan_free(struct nd_region *nd_region,
  		struct nd_mapping *nd_mapping, struct nd_label_id *label_id,
  		resource_size_t n)
  {
  	bool is_blk = strncmp(label_id->id, "blk", 3) == 0;
  	struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
  	int rc = 0;
  
  	while (n) {
  		struct resource *res, *last;
  		resource_size_t new_start;
  
  		last = NULL;
  		for_each_dpa_resource(ndd, res)
  			if (strcmp(res->name, label_id->id) == 0)
  				last = res;
  		res = last;
  		if (!res)
  			return 0;
  
  		if (n >= resource_size(res)) {
  			n -= resource_size(res);
  			nd_dbg_dpa(nd_region, ndd, res, "delete %d
  ", rc);
  			nvdimm_free_dpa(ndd, res);
  			/* retry with last resource deleted */
  			continue;
  		}
  
  		/*
  		 * Keep BLK allocations relegated to high DPA as much as
  		 * possible
  		 */
  		if (is_blk)
  			new_start = res->start + n;
  		else
  			new_start = res->start;
  
  		rc = adjust_resource(res, new_start, resource_size(res) - n);
1b40e09a1   Dan Williams   libnvdimm: blk la...
472
473
  		if (rc == 0)
  			res->flags |= DPA_RESOURCE_ADJUSTED;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
  		nd_dbg_dpa(nd_region, ndd, res, "shrink %d
  ", rc);
  		break;
  	}
  
  	return rc;
  }
  
  /**
   * shrink_dpa_allocation - for each dimm in region free n bytes for label_id
   * @nd_region: the set of dimms to reclaim @n bytes from
   * @label_id: unique identifier for the namespace consuming this dpa range
   * @n: number of bytes per-dimm to release
   *
   * Assumes resources are ordered.  Starting from the end try to
   * adjust_resource() the allocation to @n, but if @n is larger than the
   * allocation delete it and find the 'new' last allocation in the label
   * set.
   */
  static int shrink_dpa_allocation(struct nd_region *nd_region,
  		struct nd_label_id *label_id, resource_size_t n)
  {
  	int i;
  
  	for (i = 0; i < nd_region->ndr_mappings; i++) {
  		struct nd_mapping *nd_mapping = &nd_region->mapping[i];
  		int rc;
  
  		rc = scan_free(nd_region, nd_mapping, label_id, n);
  		if (rc)
  			return rc;
  	}
  
  	return 0;
  }
  
  static resource_size_t init_dpa_allocation(struct nd_label_id *label_id,
  		struct nd_region *nd_region, struct nd_mapping *nd_mapping,
  		resource_size_t n)
  {
  	bool is_blk = strncmp(label_id->id, "blk", 3) == 0;
  	struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
  	resource_size_t first_dpa;
  	struct resource *res;
  	int rc = 0;
  
  	/* allocate blk from highest dpa first */
  	if (is_blk)
  		first_dpa = nd_mapping->start + nd_mapping->size - n;
  	else
  		first_dpa = nd_mapping->start;
  
  	/* first resource allocation for this label-id or dimm */
  	res = nvdimm_allocate_dpa(ndd, label_id, first_dpa, n);
  	if (!res)
  		rc = -EBUSY;
  
  	nd_dbg_dpa(nd_region, ndd, res, "init %d
  ", rc);
  	return rc ? n : 0;
  }
762d067db   Dan Williams   libnvdimm, namesp...
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
  
  /**
   * space_valid() - validate free dpa space against constraints
   * @nd_region: hosting region of the free space
   * @ndd: dimm device data for debug
   * @label_id: namespace id to allocate space
   * @prev: potential allocation that precedes free space
   * @next: allocation that follows the given free space range
   * @exist: first allocation with same id in the mapping
   * @n: range that must satisfied for pmem allocations
   * @valid: free space range to validate
   *
   * BLK-space is valid as long as it does not precede a PMEM
   * allocation in a given region. PMEM-space must be contiguous
   * and adjacent to an existing existing allocation (if one
   * exists).  If reserving PMEM any space is valid.
   */
  static void space_valid(struct nd_region *nd_region, struct nvdimm_drvdata *ndd,
  		struct nd_label_id *label_id, struct resource *prev,
  		struct resource *next, struct resource *exist,
  		resource_size_t n, struct resource *valid)
bf9bccc14   Dan Williams   libnvdimm: pmem l...
556
  {
762d067db   Dan Williams   libnvdimm, namesp...
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
  	bool is_reserve = strcmp(label_id->id, "pmem-reserve") == 0;
  	bool is_pmem = strncmp(label_id->id, "pmem", 4) == 0;
  
  	if (valid->start >= valid->end)
  		goto invalid;
  
  	if (is_reserve)
  		return;
  
  	if (!is_pmem) {
  		struct nd_mapping *nd_mapping = &nd_region->mapping[0];
  		struct nvdimm_bus *nvdimm_bus;
  		struct blk_alloc_info info = {
  			.nd_mapping = nd_mapping,
  			.available = nd_mapping->size,
  			.res = valid,
  		};
  
  		WARN_ON(!is_nd_blk(&nd_region->dev));
  		nvdimm_bus = walk_to_nvdimm_bus(&nd_region->dev);
  		device_for_each_child(&nvdimm_bus->dev, &info, alias_dpa_busy);
  		return;
  	}
  
  	/* allocation needs to be contiguous, so this is all or nothing */
  	if (resource_size(valid) < n)
  		goto invalid;
  
  	/* we've got all the space we need and no existing allocation */
  	if (!exist)
  		return;
  
  	/* allocation needs to be contiguous with the existing namespace */
  	if (valid->start == exist->end + 1
  			|| valid->end == exist->start - 1)
  		return;
  
   invalid:
  	/* truncate @valid size to 0 */
  	valid->end = valid->start - 1;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
597
598
599
600
601
602
603
604
605
606
607
608
609
  }
  
  enum alloc_loc {
  	ALLOC_ERR = 0, ALLOC_BEFORE, ALLOC_MID, ALLOC_AFTER,
  };
  
  static resource_size_t scan_allocate(struct nd_region *nd_region,
  		struct nd_mapping *nd_mapping, struct nd_label_id *label_id,
  		resource_size_t n)
  {
  	resource_size_t mapping_end = nd_mapping->start + nd_mapping->size - 1;
  	bool is_pmem = strncmp(label_id->id, "pmem", 4) == 0;
  	struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
762d067db   Dan Williams   libnvdimm, namesp...
610
  	struct resource *res, *exist = NULL, valid;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
611
  	const resource_size_t to_allocate = n;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
612
  	int first;
762d067db   Dan Williams   libnvdimm, namesp...
613
614
615
616
617
618
619
  	for_each_dpa_resource(ndd, res)
  		if (strcmp(label_id->id, res->name) == 0)
  			exist = res;
  
  	valid.start = nd_mapping->start;
  	valid.end = mapping_end;
  	valid.name = "free space";
bf9bccc14   Dan Williams   libnvdimm: pmem l...
620
621
622
   retry:
  	first = 0;
  	for_each_dpa_resource(ndd, res) {
bf9bccc14   Dan Williams   libnvdimm: pmem l...
623
  		struct resource *next = res->sibling, *new_res = NULL;
762d067db   Dan Williams   libnvdimm, namesp...
624
  		resource_size_t allocate, available = 0;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
625
626
627
628
629
630
631
632
633
634
635
636
  		enum alloc_loc loc = ALLOC_ERR;
  		const char *action;
  		int rc = 0;
  
  		/* ignore resources outside this nd_mapping */
  		if (res->start > mapping_end)
  			continue;
  		if (res->end < nd_mapping->start)
  			continue;
  
  		/* space at the beginning of the mapping */
  		if (!first++ && res->start > nd_mapping->start) {
762d067db   Dan Williams   libnvdimm, namesp...
637
638
639
640
641
642
  			valid.start = nd_mapping->start;
  			valid.end = res->start - 1;
  			space_valid(nd_region, ndd, label_id, NULL, next, exist,
  					to_allocate, &valid);
  			available = resource_size(&valid);
  			if (available)
bf9bccc14   Dan Williams   libnvdimm: pmem l...
643
644
645
646
647
  				loc = ALLOC_BEFORE;
  		}
  
  		/* space between allocations */
  		if (!loc && next) {
762d067db   Dan Williams   libnvdimm, namesp...
648
649
650
651
652
653
  			valid.start = res->start + resource_size(res);
  			valid.end = min(mapping_end, next->start - 1);
  			space_valid(nd_region, ndd, label_id, res, next, exist,
  					to_allocate, &valid);
  			available = resource_size(&valid);
  			if (available)
bf9bccc14   Dan Williams   libnvdimm: pmem l...
654
  				loc = ALLOC_MID;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
655
656
657
658
  		}
  
  		/* space at the end of the mapping */
  		if (!loc && !next) {
762d067db   Dan Williams   libnvdimm, namesp...
659
660
661
662
663
664
  			valid.start = res->start + resource_size(res);
  			valid.end = mapping_end;
  			space_valid(nd_region, ndd, label_id, res, next, exist,
  					to_allocate, &valid);
  			available = resource_size(&valid);
  			if (available)
bf9bccc14   Dan Williams   libnvdimm: pmem l...
665
  				loc = ALLOC_AFTER;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
666
667
668
669
670
671
672
673
674
  		}
  
  		if (!loc || !available)
  			continue;
  		allocate = min(available, n);
  		switch (loc) {
  		case ALLOC_BEFORE:
  			if (strcmp(res->name, label_id->id) == 0) {
  				/* adjust current resource up */
bf9bccc14   Dan Williams   libnvdimm: pmem l...
675
676
677
678
679
680
681
682
683
  				rc = adjust_resource(res, res->start - allocate,
  						resource_size(res) + allocate);
  				action = "cur grow up";
  			} else
  				action = "allocate";
  			break;
  		case ALLOC_MID:
  			if (strcmp(next->name, label_id->id) == 0) {
  				/* adjust next resource up */
bf9bccc14   Dan Williams   libnvdimm: pmem l...
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
  				rc = adjust_resource(next, next->start
  						- allocate, resource_size(next)
  						+ allocate);
  				new_res = next;
  				action = "next grow up";
  			} else if (strcmp(res->name, label_id->id) == 0) {
  				action = "grow down";
  			} else
  				action = "allocate";
  			break;
  		case ALLOC_AFTER:
  			if (strcmp(res->name, label_id->id) == 0)
  				action = "grow down";
  			else
  				action = "allocate";
  			break;
  		default:
  			return n;
  		}
  
  		if (strcmp(action, "allocate") == 0) {
  			/* BLK allocate bottom up */
  			if (!is_pmem)
762d067db   Dan Williams   libnvdimm, namesp...
707
  				valid.start += available - allocate;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
708
709
  
  			new_res = nvdimm_allocate_dpa(ndd, label_id,
762d067db   Dan Williams   libnvdimm, namesp...
710
  					valid.start, allocate);
bf9bccc14   Dan Williams   libnvdimm: pmem l...
711
712
713
714
715
716
  			if (!new_res)
  				rc = -EBUSY;
  		} else if (strcmp(action, "grow down") == 0) {
  			/* adjust current resource down */
  			rc = adjust_resource(res, res->start, resource_size(res)
  					+ allocate);
1b40e09a1   Dan Williams   libnvdimm: blk la...
717
718
  			if (rc == 0)
  				res->flags |= DPA_RESOURCE_ADJUSTED;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
  		}
  
  		if (!new_res)
  			new_res = res;
  
  		nd_dbg_dpa(nd_region, ndd, new_res, "%s(%d) %d
  ",
  				action, loc, rc);
  
  		if (rc)
  			return n;
  
  		n -= allocate;
  		if (n) {
  			/*
  			 * Retry scan with newly inserted resources.
  			 * For example, if we did an ALLOC_BEFORE
  			 * insertion there may also have been space
  			 * available for an ALLOC_AFTER insertion, so we
  			 * need to check this same resource again
  			 */
  			goto retry;
  		} else
  			return 0;
  	}
1b40e09a1   Dan Williams   libnvdimm: blk la...
744
745
746
747
748
749
  	/*
  	 * If we allocated nothing in the BLK case it may be because we are in
  	 * an initial "pmem-reserve pass".  Only do an initial BLK allocation
  	 * when none of the DPA space is reserved.
  	 */
  	if ((is_pmem || !ndd->dpa.child) && n == to_allocate)
bf9bccc14   Dan Williams   libnvdimm: pmem l...
750
751
752
  		return init_dpa_allocation(label_id, nd_region, nd_mapping, n);
  	return n;
  }
1b40e09a1   Dan Williams   libnvdimm: blk la...
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
  static int merge_dpa(struct nd_region *nd_region,
  		struct nd_mapping *nd_mapping, struct nd_label_id *label_id)
  {
  	struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
  	struct resource *res;
  
  	if (strncmp("pmem", label_id->id, 4) == 0)
  		return 0;
   retry:
  	for_each_dpa_resource(ndd, res) {
  		int rc;
  		struct resource *next = res->sibling;
  		resource_size_t end = res->start + resource_size(res);
  
  		if (!next || strcmp(res->name, label_id->id) != 0
  				|| strcmp(next->name, label_id->id) != 0
  				|| end != next->start)
  			continue;
  		end += resource_size(next);
  		nvdimm_free_dpa(ndd, next);
  		rc = adjust_resource(res, res->start, end - res->start);
  		nd_dbg_dpa(nd_region, ndd, res, "merge %d
  ", rc);
  		if (rc)
  			return rc;
  		res->flags |= DPA_RESOURCE_ADJUSTED;
  		goto retry;
  	}
  
  	return 0;
  }
  
  static int __reserve_free_pmem(struct device *dev, void *data)
  {
  	struct nvdimm *nvdimm = data;
  	struct nd_region *nd_region;
  	struct nd_label_id label_id;
  	int i;
c9e582aa6   Dan Williams   libnvdimm, nfit: ...
791
  	if (!is_memory(dev))
1b40e09a1   Dan Williams   libnvdimm: blk la...
792
793
794
795
796
797
798
799
800
801
802
803
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
831
832
833
834
835
836
837
838
839
840
841
842
843
844
  		return 0;
  
  	nd_region = to_nd_region(dev);
  	if (nd_region->ndr_mappings == 0)
  		return 0;
  
  	memset(&label_id, 0, sizeof(label_id));
  	strcat(label_id.id, "pmem-reserve");
  	for (i = 0; i < nd_region->ndr_mappings; i++) {
  		struct nd_mapping *nd_mapping = &nd_region->mapping[i];
  		resource_size_t n, rem = 0;
  
  		if (nd_mapping->nvdimm != nvdimm)
  			continue;
  
  		n = nd_pmem_available_dpa(nd_region, nd_mapping, &rem);
  		if (n == 0)
  			return 0;
  		rem = scan_allocate(nd_region, nd_mapping, &label_id, n);
  		dev_WARN_ONCE(&nd_region->dev, rem,
  				"pmem reserve underrun: %#llx of %#llx bytes
  ",
  				(unsigned long long) n - rem,
  				(unsigned long long) n);
  		return rem ? -ENXIO : 0;
  	}
  
  	return 0;
  }
  
  static void release_free_pmem(struct nvdimm_bus *nvdimm_bus,
  		struct nd_mapping *nd_mapping)
  {
  	struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
  	struct resource *res, *_res;
  
  	for_each_dpa_resource_safe(ndd, res, _res)
  		if (strcmp(res->name, "pmem-reserve") == 0)
  			nvdimm_free_dpa(ndd, res);
  }
  
  static int reserve_free_pmem(struct nvdimm_bus *nvdimm_bus,
  		struct nd_mapping *nd_mapping)
  {
  	struct nvdimm *nvdimm = nd_mapping->nvdimm;
  	int rc;
  
  	rc = device_for_each_child(&nvdimm_bus->dev, nvdimm,
  			__reserve_free_pmem);
  	if (rc)
  		release_free_pmem(nvdimm_bus, nd_mapping);
  	return rc;
  }
bf9bccc14   Dan Williams   libnvdimm: pmem l...
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
  /**
   * grow_dpa_allocation - for each dimm allocate n bytes for @label_id
   * @nd_region: the set of dimms to allocate @n more bytes from
   * @label_id: unique identifier for the namespace consuming this dpa range
   * @n: number of bytes per-dimm to add to the existing allocation
   *
   * Assumes resources are ordered.  For BLK regions, first consume
   * BLK-only available DPA free space, then consume PMEM-aliased DPA
   * space starting at the highest DPA.  For PMEM regions start
   * allocations from the start of an interleave set and end at the first
   * BLK allocation or the end of the interleave set, whichever comes
   * first.
   */
  static int grow_dpa_allocation(struct nd_region *nd_region,
  		struct nd_label_id *label_id, resource_size_t n)
  {
1b40e09a1   Dan Williams   libnvdimm: blk la...
861
862
  	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(&nd_region->dev);
  	bool is_pmem = strncmp(label_id->id, "pmem", 4) == 0;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
863
864
865
866
  	int i;
  
  	for (i = 0; i < nd_region->ndr_mappings; i++) {
  		struct nd_mapping *nd_mapping = &nd_region->mapping[i];
1b40e09a1   Dan Williams   libnvdimm: blk la...
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
  		resource_size_t rem = n;
  		int rc, j;
  
  		/*
  		 * In the BLK case try once with all unallocated PMEM
  		 * reserved, and once without
  		 */
  		for (j = is_pmem; j < 2; j++) {
  			bool blk_only = j == 0;
  
  			if (blk_only) {
  				rc = reserve_free_pmem(nvdimm_bus, nd_mapping);
  				if (rc)
  					return rc;
  			}
  			rem = scan_allocate(nd_region, nd_mapping,
  					label_id, rem);
  			if (blk_only)
  				release_free_pmem(nvdimm_bus, nd_mapping);
bf9bccc14   Dan Williams   libnvdimm: pmem l...
886

1b40e09a1   Dan Williams   libnvdimm: blk la...
887
888
889
890
891
892
893
894
895
896
897
898
899
900
  			/* try again and allow encroachments into PMEM */
  			if (rem == 0)
  				break;
  		}
  
  		dev_WARN_ONCE(&nd_region->dev, rem,
  				"allocation underrun: %#llx of %#llx bytes
  ",
  				(unsigned long long) n - rem,
  				(unsigned long long) n);
  		if (rem)
  			return -ENXIO;
  
  		rc = merge_dpa(nd_region, nd_mapping, label_id);
bf9bccc14   Dan Williams   libnvdimm: pmem l...
901
902
903
904
905
906
  		if (rc)
  			return rc;
  	}
  
  	return 0;
  }
0e3b0d123   Dan Williams   libnvdimm, namesp...
907
  static void nd_namespace_pmem_set_resource(struct nd_region *nd_region,
bf9bccc14   Dan Williams   libnvdimm: pmem l...
908
909
910
  		struct nd_namespace_pmem *nspm, resource_size_t size)
  {
  	struct resource *res = &nspm->nsio.res;
0e3b0d123   Dan Williams   libnvdimm, namesp...
911
  	resource_size_t offset = 0;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
912

0e3b0d123   Dan Williams   libnvdimm, namesp...
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
  	if (size && !nspm->uuid) {
  		WARN_ON_ONCE(1);
  		size = 0;
  	}
  
  	if (size && nspm->uuid) {
  		struct nd_mapping *nd_mapping = &nd_region->mapping[0];
  		struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
  		struct nd_label_id label_id;
  		struct resource *res;
  
  		if (!ndd) {
  			size = 0;
  			goto out;
  		}
  
  		nd_label_gen_id(&label_id, nspm->uuid, 0);
  
  		/* calculate a spa offset from the dpa allocation offset */
  		for_each_dpa_resource(ndd, res)
  			if (strcmp(res->name, label_id.id) == 0) {
  				offset = (res->start - nd_mapping->start)
  					* nd_region->ndr_mappings;
  				goto out;
  			}
  
  		WARN_ON_ONCE(1);
  		size = 0;
  	}
  
   out:
  	res->start = nd_region->ndr_start + offset;
  	res->end = res->start + size - 1;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
946
  }
bd26d0d0c   Dmitry Krivenok   nvdimm: improve d...
947
948
949
950
951
952
953
954
955
  static bool uuid_not_set(const u8 *uuid, struct device *dev, const char *where)
  {
  	if (!uuid) {
  		dev_dbg(dev, "%s: uuid not set
  ", where);
  		return true;
  	}
  	return false;
  }
bf9bccc14   Dan Williams   libnvdimm: pmem l...
956
957
958
959
  static ssize_t __size_store(struct device *dev, unsigned long long val)
  {
  	resource_size_t allocated = 0, available = 0;
  	struct nd_region *nd_region = to_nd_region(dev->parent);
1f19b983a   Dan Williams   libnvdimm, namesp...
960
  	struct nd_namespace_common *ndns = to_ndns(dev);
bf9bccc14   Dan Williams   libnvdimm: pmem l...
961
962
963
964
  	struct nd_mapping *nd_mapping;
  	struct nvdimm_drvdata *ndd;
  	struct nd_label_id label_id;
  	u32 flags = 0, remainder;
9d032f420   Dan Williams   libnvdimm, namesp...
965
  	int rc, i, id = -1;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
966
  	u8 *uuid = NULL;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
967

1f19b983a   Dan Williams   libnvdimm, namesp...
968
  	if (dev->driver || ndns->claim)
bf9bccc14   Dan Williams   libnvdimm: pmem l...
969
970
971
972
973
974
  		return -EBUSY;
  
  	if (is_namespace_pmem(dev)) {
  		struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev);
  
  		uuid = nspm->uuid;
9d032f420   Dan Williams   libnvdimm, namesp...
975
  		id = nspm->id;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
976
  	} else if (is_namespace_blk(dev)) {
1b40e09a1   Dan Williams   libnvdimm: blk la...
977
978
979
980
  		struct nd_namespace_blk *nsblk = to_nd_namespace_blk(dev);
  
  		uuid = nsblk->uuid;
  		flags = NSLABEL_FLAG_LOCAL;
9d032f420   Dan Williams   libnvdimm, namesp...
981
  		id = nsblk->id;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
982
983
984
985
986
987
  	}
  
  	/*
  	 * We need a uuid for the allocation-label and dimm(s) on which
  	 * to store the label.
  	 */
bd26d0d0c   Dmitry Krivenok   nvdimm: improve d...
988
  	if (uuid_not_set(uuid, dev, __func__))
bf9bccc14   Dan Williams   libnvdimm: pmem l...
989
  		return -ENXIO;
bd26d0d0c   Dmitry Krivenok   nvdimm: improve d...
990
991
992
993
994
  	if (nd_region->ndr_mappings == 0) {
  		dev_dbg(dev, "%s: not associated with dimm(s)
  ", __func__);
  		return -ENXIO;
  	}
bf9bccc14   Dan Williams   libnvdimm: pmem l...
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
  
  	div_u64_rem(val, SZ_4K * nd_region->ndr_mappings, &remainder);
  	if (remainder) {
  		dev_dbg(dev, "%llu is not %dK aligned
  ", val,
  				(SZ_4K * nd_region->ndr_mappings) / SZ_1K);
  		return -EINVAL;
  	}
  
  	nd_label_gen_id(&label_id, uuid, flags);
  	for (i = 0; i < nd_region->ndr_mappings; i++) {
  		nd_mapping = &nd_region->mapping[i];
  		ndd = to_ndd(nd_mapping);
  
  		/*
  		 * All dimms in an interleave set, or the base dimm for a blk
  		 * region, need to be enabled for the size to be changed.
  		 */
  		if (!ndd)
  			return -ENXIO;
  
  		allocated += nvdimm_allocated_dpa(ndd, &label_id);
  	}
  	available = nd_region_available_dpa(nd_region);
  
  	if (val > available + allocated)
  		return -ENOSPC;
  
  	if (val == allocated)
  		return 0;
  
  	val = div_u64(val, nd_region->ndr_mappings);
  	allocated = div_u64(allocated, nd_region->ndr_mappings);
  	if (val < allocated)
  		rc = shrink_dpa_allocation(nd_region, &label_id,
  				allocated - val);
  	else
  		rc = grow_dpa_allocation(nd_region, &label_id, val - allocated);
  
  	if (rc)
  		return rc;
  
  	if (is_namespace_pmem(dev)) {
  		struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev);
0e3b0d123   Dan Williams   libnvdimm, namesp...
1039
  		nd_namespace_pmem_set_resource(nd_region, nspm,
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1040
1041
  				val * nd_region->ndr_mappings);
  	}
1f19b983a   Dan Williams   libnvdimm, namesp...
1042
1043
  	/*
  	 * Try to delete the namespace if we deleted all of its
9d032f420   Dan Williams   libnvdimm, namesp...
1044
1045
1046
  	 * allocation, this is not the seed or 0th device for the
  	 * region, and it is not actively claimed by a btt, pfn, or dax
  	 * instance.
1f19b983a   Dan Williams   libnvdimm, namesp...
1047
  	 */
9d032f420   Dan Williams   libnvdimm, namesp...
1048
  	if (val == 0 && id != 0 && nd_region->ns_seed != dev && !ndns->claim)
1f19b983a   Dan Williams   libnvdimm, namesp...
1049
  		nd_device_unregister(dev, ND_ASYNC);
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1050
1051
1052
1053
1054
1055
  	return rc;
  }
  
  static ssize_t size_store(struct device *dev,
  		struct device_attribute *attr, const char *buf, size_t len)
  {
f524bf271   Dan Williams   libnvdimm: write ...
1056
  	struct nd_region *nd_region = to_nd_region(dev->parent);
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
  	unsigned long long val;
  	u8 **uuid = NULL;
  	int rc;
  
  	rc = kstrtoull(buf, 0, &val);
  	if (rc)
  		return rc;
  
  	device_lock(dev);
  	nvdimm_bus_lock(dev);
  	wait_nvdimm_bus_probe_idle(dev);
  	rc = __size_store(dev, val);
f524bf271   Dan Williams   libnvdimm: write ...
1069
1070
  	if (rc >= 0)
  		rc = nd_namespace_label_update(nd_region, dev);
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1071
1072
1073
1074
1075
1076
  
  	if (is_namespace_pmem(dev)) {
  		struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev);
  
  		uuid = &nspm->uuid;
  	} else if (is_namespace_blk(dev)) {
1b40e09a1   Dan Williams   libnvdimm: blk la...
1077
1078
1079
  		struct nd_namespace_blk *nsblk = to_nd_namespace_blk(dev);
  
  		uuid = &nsblk->uuid;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
  	}
  
  	if (rc == 0 && val == 0 && uuid) {
  		/* setting size zero == 'delete namespace' */
  		kfree(*uuid);
  		*uuid = NULL;
  	}
  
  	dev_dbg(dev, "%s: %llx %s (%d)
  ", __func__, val, rc < 0
  			? "fail" : "success", rc);
  
  	nvdimm_bus_unlock(dev);
  	device_unlock(dev);
f524bf271   Dan Williams   libnvdimm: write ...
1094
  	return rc < 0 ? rc : len;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1095
  }
8c2f7e865   Dan Williams   libnvdimm: infras...
1096
  resource_size_t __nvdimm_namespace_capacity(struct nd_namespace_common *ndns)
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1097
  {
8c2f7e865   Dan Williams   libnvdimm: infras...
1098
  	struct device *dev = &ndns->dev;
1b40e09a1   Dan Williams   libnvdimm: blk la...
1099

bf9bccc14   Dan Williams   libnvdimm: pmem l...
1100
1101
  	if (is_namespace_pmem(dev)) {
  		struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev);
8c2f7e865   Dan Williams   libnvdimm: infras...
1102
  		return resource_size(&nspm->nsio.res);
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1103
  	} else if (is_namespace_blk(dev)) {
8c2f7e865   Dan Williams   libnvdimm: infras...
1104
  		return nd_namespace_blk_size(to_nd_namespace_blk(dev));
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1105
1106
  	} else if (is_namespace_io(dev)) {
  		struct nd_namespace_io *nsio = to_nd_namespace_io(dev);
8c2f7e865   Dan Williams   libnvdimm: infras...
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
  		return resource_size(&nsio->res);
  	} else
  		WARN_ONCE(1, "unknown namespace type
  ");
  	return 0;
  }
  
  resource_size_t nvdimm_namespace_capacity(struct nd_namespace_common *ndns)
  {
  	resource_size_t size;
1b40e09a1   Dan Williams   libnvdimm: blk la...
1117

8c2f7e865   Dan Williams   libnvdimm: infras...
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
  	nvdimm_bus_lock(&ndns->dev);
  	size = __nvdimm_namespace_capacity(ndns);
  	nvdimm_bus_unlock(&ndns->dev);
  
  	return size;
  }
  EXPORT_SYMBOL(nvdimm_namespace_capacity);
  
  static ssize_t size_show(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
  	return sprintf(buf, "%llu
  ", (unsigned long long)
  			nvdimm_namespace_capacity(to_ndns(dev)));
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1132
  }
b44fe7604   Fabian Frederick   libnvdimm, namesp...
1133
  static DEVICE_ATTR(size, 0444, size_show, size_store);
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1134

f95b4bca9   Dan Williams   libnvdimm, namesp...
1135
  static u8 *namespace_to_uuid(struct device *dev)
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1136
  {
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1137
1138
  	if (is_namespace_pmem(dev)) {
  		struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev);
f95b4bca9   Dan Williams   libnvdimm, namesp...
1139
  		return nspm->uuid;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1140
  	} else if (is_namespace_blk(dev)) {
1b40e09a1   Dan Williams   libnvdimm: blk la...
1141
  		struct nd_namespace_blk *nsblk = to_nd_namespace_blk(dev);
f95b4bca9   Dan Williams   libnvdimm, namesp...
1142
  		return nsblk->uuid;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1143
  	} else
f95b4bca9   Dan Williams   libnvdimm, namesp...
1144
1145
1146
1147
1148
1149
1150
  		return ERR_PTR(-ENXIO);
  }
  
  static ssize_t uuid_show(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
  	u8 *uuid = namespace_to_uuid(dev);
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1151

f95b4bca9   Dan Williams   libnvdimm, namesp...
1152
1153
  	if (IS_ERR(uuid))
  		return PTR_ERR(uuid);
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
  	if (uuid)
  		return sprintf(buf, "%pUb
  ", uuid);
  	return sprintf(buf, "
  ");
  }
  
  /**
   * namespace_update_uuid - check for a unique uuid and whether we're "renaming"
   * @nd_region: parent region so we can updates all dimms in the set
   * @dev: namespace type for generating label_id
   * @new_uuid: incoming uuid
   * @old_uuid: reference to the uuid storage location in the namespace object
   */
  static int namespace_update_uuid(struct nd_region *nd_region,
  		struct device *dev, u8 *new_uuid, u8 **old_uuid)
  {
  	u32 flags = is_namespace_blk(dev) ? NSLABEL_FLAG_LOCAL : 0;
  	struct nd_label_id old_label_id;
  	struct nd_label_id new_label_id;
f524bf271   Dan Williams   libnvdimm: write ...
1174
  	int i;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1175

f524bf271   Dan Williams   libnvdimm: write ...
1176
1177
  	if (!nd_is_uuid_unique(dev, new_uuid))
  		return -EINVAL;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1178
1179
1180
  
  	if (*old_uuid == NULL)
  		goto out;
f524bf271   Dan Williams   libnvdimm: write ...
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
  	/*
  	 * If we've already written a label with this uuid, then it's
  	 * too late to rename because we can't reliably update the uuid
  	 * without losing the old namespace.  Userspace must delete this
  	 * namespace to abandon the old uuid.
  	 */
  	for (i = 0; i < nd_region->ndr_mappings; i++) {
  		struct nd_mapping *nd_mapping = &nd_region->mapping[i];
  
  		/*
  		 * This check by itself is sufficient because old_uuid
  		 * would be NULL above if this uuid did not exist in the
  		 * currently written set.
  		 *
  		 * FIXME: can we delete uuid with zero dpa allocated?
  		 */
ae8219f18   Dan Williams   libnvdimm, label:...
1197
  		if (list_empty(&nd_mapping->labels))
f524bf271   Dan Williams   libnvdimm: write ...
1198
1199
  			return -EBUSY;
  	}
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
  	nd_label_gen_id(&old_label_id, *old_uuid, flags);
  	nd_label_gen_id(&new_label_id, new_uuid, flags);
  	for (i = 0; i < nd_region->ndr_mappings; i++) {
  		struct nd_mapping *nd_mapping = &nd_region->mapping[i];
  		struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
  		struct resource *res;
  
  		for_each_dpa_resource(ndd, res)
  			if (strcmp(res->name, old_label_id.id) == 0)
  				sprintf((void *) res->name, "%s",
  						new_label_id.id);
  	}
  	kfree(*old_uuid);
   out:
  	*old_uuid = new_uuid;
  	return 0;
  }
  
  static ssize_t uuid_store(struct device *dev,
  		struct device_attribute *attr, const char *buf, size_t len)
  {
  	struct nd_region *nd_region = to_nd_region(dev->parent);
  	u8 *uuid = NULL;
8c2f7e865   Dan Williams   libnvdimm: infras...
1223
  	ssize_t rc = 0;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1224
  	u8 **ns_uuid;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1225
1226
1227
1228
1229
1230
  
  	if (is_namespace_pmem(dev)) {
  		struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev);
  
  		ns_uuid = &nspm->uuid;
  	} else if (is_namespace_blk(dev)) {
1b40e09a1   Dan Williams   libnvdimm: blk la...
1231
1232
1233
  		struct nd_namespace_blk *nsblk = to_nd_namespace_blk(dev);
  
  		ns_uuid = &nsblk->uuid;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1234
1235
1236
1237
1238
1239
  	} else
  		return -ENXIO;
  
  	device_lock(dev);
  	nvdimm_bus_lock(dev);
  	wait_nvdimm_bus_probe_idle(dev);
8c2f7e865   Dan Williams   libnvdimm: infras...
1240
1241
1242
1243
  	if (to_ndns(dev)->claim)
  		rc = -EBUSY;
  	if (rc >= 0)
  		rc = nd_uuid_store(dev, &uuid, buf, len);
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1244
1245
  	if (rc >= 0)
  		rc = namespace_update_uuid(nd_region, dev, uuid, ns_uuid);
f524bf271   Dan Williams   libnvdimm: write ...
1246
1247
1248
1249
  	if (rc >= 0)
  		rc = nd_namespace_label_update(nd_region, dev);
  	else
  		kfree(uuid);
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1250
1251
1252
1253
1254
1255
  	dev_dbg(dev, "%s: result: %zd wrote: %s%s", __func__,
  			rc, buf, buf[len - 1] == '
  ' ? "" : "
  ");
  	nvdimm_bus_unlock(dev);
  	device_unlock(dev);
f524bf271   Dan Williams   libnvdimm: write ...
1256
  	return rc < 0 ? rc : len;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
  }
  static DEVICE_ATTR_RW(uuid);
  
  static ssize_t resource_show(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
  	struct resource *res;
  
  	if (is_namespace_pmem(dev)) {
  		struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev);
  
  		res = &nspm->nsio.res;
  	} else if (is_namespace_io(dev)) {
  		struct nd_namespace_io *nsio = to_nd_namespace_io(dev);
  
  		res = &nsio->res;
  	} else
  		return -ENXIO;
  
  	/* no address to convey if the namespace has no allocation */
  	if (resource_size(res) == 0)
  		return -ENXIO;
  	return sprintf(buf, "%#llx
  ", (unsigned long long) res->start);
  }
  static DEVICE_ATTR_RO(resource);
f979b13c3   Dan Williams   libnvdimm, label:...
1283
  static const unsigned long blk_lbasize_supported[] = { 512, 520, 528,
fcae69573   Vishal Verma   libnvdimm, blk: a...
1284
  	4096, 4104, 4160, 4224, 0 };
1b40e09a1   Dan Williams   libnvdimm: blk la...
1285

f979b13c3   Dan Williams   libnvdimm, label:...
1286
  static const unsigned long pmem_lbasize_supported[] = { 512, 4096, 0 };
1b40e09a1   Dan Williams   libnvdimm: blk la...
1287
1288
1289
  static ssize_t sector_size_show(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
f979b13c3   Dan Williams   libnvdimm, label:...
1290
1291
  	if (is_namespace_blk(dev)) {
  		struct nd_namespace_blk *nsblk = to_nd_namespace_blk(dev);
1b40e09a1   Dan Williams   libnvdimm: blk la...
1292

b2c48f9f9   Dan Williams   libnvdimm: rename...
1293
  		return nd_size_select_show(nsblk->lbasize,
f979b13c3   Dan Williams   libnvdimm, label:...
1294
1295
  				blk_lbasize_supported, buf);
  	}
1b40e09a1   Dan Williams   libnvdimm: blk la...
1296

f979b13c3   Dan Williams   libnvdimm, label:...
1297
1298
  	if (is_namespace_pmem(dev)) {
  		struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev);
b2c48f9f9   Dan Williams   libnvdimm: rename...
1299
  		return nd_size_select_show(nspm->lbasize,
f979b13c3   Dan Williams   libnvdimm, label:...
1300
1301
1302
  				pmem_lbasize_supported, buf);
  	}
  	return -ENXIO;
1b40e09a1   Dan Williams   libnvdimm: blk la...
1303
1304
1305
1306
1307
  }
  
  static ssize_t sector_size_store(struct device *dev,
  		struct device_attribute *attr, const char *buf, size_t len)
  {
f524bf271   Dan Williams   libnvdimm: write ...
1308
  	struct nd_region *nd_region = to_nd_region(dev->parent);
f979b13c3   Dan Williams   libnvdimm, label:...
1309
1310
  	const unsigned long *supported;
  	unsigned long *lbasize;
8c2f7e865   Dan Williams   libnvdimm: infras...
1311
  	ssize_t rc = 0;
1b40e09a1   Dan Williams   libnvdimm: blk la...
1312

f979b13c3   Dan Williams   libnvdimm, label:...
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
  	if (is_namespace_blk(dev)) {
  		struct nd_namespace_blk *nsblk = to_nd_namespace_blk(dev);
  
  		lbasize = &nsblk->lbasize;
  		supported = blk_lbasize_supported;
  	} else if (is_namespace_pmem(dev)) {
  		struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev);
  
  		lbasize = &nspm->lbasize;
  		supported = pmem_lbasize_supported;
  	} else
1b40e09a1   Dan Williams   libnvdimm: blk la...
1324
1325
1326
1327
  		return -ENXIO;
  
  	device_lock(dev);
  	nvdimm_bus_lock(dev);
8c2f7e865   Dan Williams   libnvdimm: infras...
1328
1329
1330
  	if (to_ndns(dev)->claim)
  		rc = -EBUSY;
  	if (rc >= 0)
b2c48f9f9   Dan Williams   libnvdimm: rename...
1331
  		rc = nd_size_select_store(dev, buf, lbasize, supported);
f524bf271   Dan Williams   libnvdimm: write ...
1332
1333
1334
1335
1336
1337
1338
  	if (rc >= 0)
  		rc = nd_namespace_label_update(nd_region, dev);
  	dev_dbg(dev, "%s: result: %zd %s: %s%s", __func__,
  			rc, rc < 0 ? "tried" : "wrote", buf,
  			buf[len - 1] == '
  ' ? "" : "
  ");
1b40e09a1   Dan Williams   libnvdimm: blk la...
1339
1340
1341
1342
1343
1344
  	nvdimm_bus_unlock(dev);
  	device_unlock(dev);
  
  	return rc ? rc : len;
  }
  static DEVICE_ATTR_RW(sector_size);
0ba1c6348   Dan Williams   libnvdimm: write ...
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
  static ssize_t dpa_extents_show(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
  	struct nd_region *nd_region = to_nd_region(dev->parent);
  	struct nd_label_id label_id;
  	int count = 0, i;
  	u8 *uuid = NULL;
  	u32 flags = 0;
  
  	nvdimm_bus_lock(dev);
  	if (is_namespace_pmem(dev)) {
  		struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev);
  
  		uuid = nspm->uuid;
  		flags = 0;
  	} else if (is_namespace_blk(dev)) {
  		struct nd_namespace_blk *nsblk = to_nd_namespace_blk(dev);
  
  		uuid = nsblk->uuid;
  		flags = NSLABEL_FLAG_LOCAL;
  	}
  
  	if (!uuid)
  		goto out;
  
  	nd_label_gen_id(&label_id, uuid, flags);
  	for (i = 0; i < nd_region->ndr_mappings; i++) {
  		struct nd_mapping *nd_mapping = &nd_region->mapping[i];
  		struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
  		struct resource *res;
  
  		for_each_dpa_resource(ndd, res)
  			if (strcmp(res->name, label_id.id) == 0)
  				count++;
  	}
   out:
  	nvdimm_bus_unlock(dev);
  
  	return sprintf(buf, "%d
  ", count);
  }
  static DEVICE_ATTR_RO(dpa_extents);
14e494542   Vishal Verma   libnvdimm, btt: B...
1387
1388
1389
1390
1391
1392
1393
1394
1395
  static int btt_claim_class(struct device *dev)
  {
  	struct nd_region *nd_region = to_nd_region(dev->parent);
  	int i, loop_bitmask = 0;
  
  	for (i = 0; i < nd_region->ndr_mappings; i++) {
  		struct nd_mapping *nd_mapping = &nd_region->mapping[i];
  		struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
  		struct nd_namespace_index *nsindex;
33a560867   Dan Williams   libnvdimm, namesp...
1396
1397
1398
1399
1400
1401
1402
1403
  		/*
  		 * If any of the DIMMs do not support labels the only
  		 * possible BTT format is v1.
  		 */
  		if (!ndd) {
  			loop_bitmask = 0;
  			break;
  		}
14e494542   Vishal Verma   libnvdimm, btt: B...
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
  		nsindex = to_namespace_index(ndd, ndd->ns_current);
  		if (nsindex == NULL)
  			loop_bitmask |= 1;
  		else {
  			/* check whether existing labels are v1.1 or v1.2 */
  			if (__le16_to_cpu(nsindex->major) == 1
  					&& __le16_to_cpu(nsindex->minor) == 1)
  				loop_bitmask |= 2;
  			else
  				loop_bitmask |= 4;
  		}
  	}
  	/*
  	 * If nsindex is null loop_bitmask's bit 0 will be set, and if an index
  	 * block is found, a v1.1 label for any mapping will set bit 1, and a
  	 * v1.2 label will set bit 2.
  	 *
  	 * At the end of the loop, at most one of the three bits must be set.
  	 * If multiple bits were set, it means the different mappings disagree
  	 * about their labels, and this must be cleaned up first.
  	 *
  	 * If all the label index blocks are found to agree, nsindex of NULL
  	 * implies labels haven't been initialized yet, and when they will,
  	 * they will be of the 1.2 format, so we can assume BTT2.0
  	 *
  	 * If 1.1 labels are found, we enforce BTT1.1, and if 1.2 labels are
  	 * found, we enforce BTT2.0
  	 *
  	 * If the loop was never entered, default to BTT1.1 (legacy namespaces)
  	 */
  	switch (loop_bitmask) {
  	case 0:
  	case 2:
  		return NVDIMM_CCLASS_BTT;
  	case 1:
  	case 4:
  		return NVDIMM_CCLASS_BTT2;
  	default:
  		return -ENXIO;
  	}
  }
8c2f7e865   Dan Williams   libnvdimm: infras...
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
  static ssize_t holder_show(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
  	struct nd_namespace_common *ndns = to_ndns(dev);
  	ssize_t rc;
  
  	device_lock(dev);
  	rc = sprintf(buf, "%s
  ", ndns->claim ? dev_name(ndns->claim) : "");
  	device_unlock(dev);
  
  	return rc;
  }
  static DEVICE_ATTR_RO(holder);
b3fde74ea   Dan Williams   libnvdimm, label:...
1459
1460
1461
1462
1463
1464
1465
1466
1467
  static ssize_t __holder_class_store(struct device *dev, const char *buf)
  {
  	struct nd_namespace_common *ndns = to_ndns(dev);
  
  	if (dev->driver || ndns->claim)
  		return -EBUSY;
  
  	if (strcmp(buf, "btt") == 0 || strcmp(buf, "btt
  ") == 0)
14e494542   Vishal Verma   libnvdimm, btt: B...
1468
  		ndns->claim_class = btt_claim_class(dev);
b3fde74ea   Dan Williams   libnvdimm, label:...
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
  	else if (strcmp(buf, "pfn") == 0 || strcmp(buf, "pfn
  ") == 0)
  		ndns->claim_class = NVDIMM_CCLASS_PFN;
  	else if (strcmp(buf, "dax") == 0 || strcmp(buf, "dax
  ") == 0)
  		ndns->claim_class = NVDIMM_CCLASS_DAX;
  	else if (strcmp(buf, "") == 0 || strcmp(buf, "
  ") == 0)
  		ndns->claim_class = NVDIMM_CCLASS_NONE;
  	else
  		return -EINVAL;
14e494542   Vishal Verma   libnvdimm, btt: B...
1480
1481
1482
  	/* btt_claim_class() could've returned an error */
  	if (ndns->claim_class < 0)
  		return ndns->claim_class;
b3fde74ea   Dan Williams   libnvdimm, label:...
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
  	return 0;
  }
  
  static ssize_t holder_class_store(struct device *dev,
  		struct device_attribute *attr, const char *buf, size_t len)
  {
  	struct nd_region *nd_region = to_nd_region(dev->parent);
  	ssize_t rc;
  
  	device_lock(dev);
  	nvdimm_bus_lock(dev);
  	wait_nvdimm_bus_probe_idle(dev);
  	rc = __holder_class_store(dev, buf);
  	if (rc >= 0)
  		rc = nd_namespace_label_update(nd_region, dev);
  	dev_dbg(dev, "%s: %s(%zd)
  ", __func__, rc < 0 ? "fail " : "", rc);
  	nvdimm_bus_unlock(dev);
  	device_unlock(dev);
  
  	return rc < 0 ? rc : len;
  }
  
  static ssize_t holder_class_show(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
  	struct nd_namespace_common *ndns = to_ndns(dev);
  	ssize_t rc;
  
  	device_lock(dev);
  	if (ndns->claim_class == NVDIMM_CCLASS_NONE)
  		rc = sprintf(buf, "
  ");
14e494542   Vishal Verma   libnvdimm, btt: B...
1516
1517
  	else if ((ndns->claim_class == NVDIMM_CCLASS_BTT) ||
  			(ndns->claim_class == NVDIMM_CCLASS_BTT2))
b3fde74ea   Dan Williams   libnvdimm, label:...
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
  		rc = sprintf(buf, "btt
  ");
  	else if (ndns->claim_class == NVDIMM_CCLASS_PFN)
  		rc = sprintf(buf, "pfn
  ");
  	else if (ndns->claim_class == NVDIMM_CCLASS_DAX)
  		rc = sprintf(buf, "dax
  ");
  	else
  		rc = sprintf(buf, "<unknown>
  ");
  	device_unlock(dev);
  
  	return rc;
  }
  static DEVICE_ATTR_RW(holder_class);
0731de0dd   Dan Williams   libnvdimm, pfn: m...
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
  static ssize_t mode_show(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
  	struct nd_namespace_common *ndns = to_ndns(dev);
  	struct device *claim;
  	char *mode;
  	ssize_t rc;
  
  	device_lock(dev);
  	claim = ndns->claim;
9c4124281   Dan Williams   libnvdimm: fix mo...
1544
  	if (claim && is_nd_btt(claim))
0731de0dd   Dan Williams   libnvdimm, pfn: m...
1545
  		mode = "safe";
9c4124281   Dan Williams   libnvdimm: fix mo...
1546
1547
  	else if (claim && is_nd_pfn(claim))
  		mode = "memory";
cd03412a5   Dan Williams   libnvdimm, dax: i...
1548
1549
  	else if (claim && is_nd_dax(claim))
  		mode = "dax";
9c4124281   Dan Williams   libnvdimm: fix mo...
1550
1551
  	else if (!claim && pmem_should_map_pages(dev))
  		mode = "memory";
0731de0dd   Dan Williams   libnvdimm, pfn: m...
1552
1553
1554
1555
1556
1557
1558
1559
1560
  	else
  		mode = "raw";
  	rc = sprintf(buf, "%s
  ", mode);
  	device_unlock(dev);
  
  	return rc;
  }
  static DEVICE_ATTR_RO(mode);
8c2f7e865   Dan Williams   libnvdimm: infras...
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
  static ssize_t force_raw_store(struct device *dev,
  		struct device_attribute *attr, const char *buf, size_t len)
  {
  	bool force_raw;
  	int rc = strtobool(buf, &force_raw);
  
  	if (rc)
  		return rc;
  
  	to_ndns(dev)->force_raw = force_raw;
  	return len;
  }
  
  static ssize_t force_raw_show(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
  	return sprintf(buf, "%d
  ", to_ndns(dev)->force_raw);
  }
  static DEVICE_ATTR_RW(force_raw);
3d88002e4   Dan Williams   libnvdimm: suppor...
1581
1582
  static struct attribute *nd_namespace_attributes[] = {
  	&dev_attr_nstype.attr,
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1583
  	&dev_attr_size.attr,
0731de0dd   Dan Williams   libnvdimm, pfn: m...
1584
  	&dev_attr_mode.attr,
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1585
  	&dev_attr_uuid.attr,
8c2f7e865   Dan Williams   libnvdimm: infras...
1586
  	&dev_attr_holder.attr,
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1587
1588
  	&dev_attr_resource.attr,
  	&dev_attr_alt_name.attr,
8c2f7e865   Dan Williams   libnvdimm: infras...
1589
  	&dev_attr_force_raw.attr,
1b40e09a1   Dan Williams   libnvdimm: blk la...
1590
  	&dev_attr_sector_size.attr,
0ba1c6348   Dan Williams   libnvdimm: write ...
1591
  	&dev_attr_dpa_extents.attr,
b3fde74ea   Dan Williams   libnvdimm, label:...
1592
  	&dev_attr_holder_class.attr,
3d88002e4   Dan Williams   libnvdimm: suppor...
1593
1594
  	NULL,
  };
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1595
1596
1597
1598
1599
1600
1601
1602
  static umode_t namespace_visible(struct kobject *kobj,
  		struct attribute *a, int n)
  {
  	struct device *dev = container_of(kobj, struct device, kobj);
  
  	if (a == &dev_attr_resource.attr) {
  		if (is_namespace_blk(dev))
  			return 0;
65551fb50   Dan Williams   libnvdimm, namesp...
1603
  		return 0400;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1604
1605
1606
1607
  	}
  
  	if (is_namespace_pmem(dev) || is_namespace_blk(dev)) {
  		if (a == &dev_attr_size.attr)
b44fe7604   Fabian Frederick   libnvdimm, namesp...
1608
  			return 0644;
1b40e09a1   Dan Williams   libnvdimm: blk la...
1609

bf9bccc14   Dan Williams   libnvdimm: pmem l...
1610
1611
  		return a->mode;
  	}
8c2f7e865   Dan Williams   libnvdimm: infras...
1612
1613
  	if (a == &dev_attr_nstype.attr || a == &dev_attr_size.attr
  			|| a == &dev_attr_holder.attr
b3fde74ea   Dan Williams   libnvdimm, label:...
1614
  			|| a == &dev_attr_holder_class.attr
0731de0dd   Dan Williams   libnvdimm, pfn: m...
1615
1616
  			|| a == &dev_attr_force_raw.attr
  			|| a == &dev_attr_mode.attr)
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1617
1618
1619
1620
  		return a->mode;
  
  	return 0;
  }
3d88002e4   Dan Williams   libnvdimm: suppor...
1621
1622
  static struct attribute_group nd_namespace_attribute_group = {
  	.attrs = nd_namespace_attributes,
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1623
  	.is_visible = namespace_visible,
3d88002e4   Dan Williams   libnvdimm: suppor...
1624
1625
1626
1627
1628
  };
  
  static const struct attribute_group *nd_namespace_attribute_groups[] = {
  	&nd_device_attribute_group,
  	&nd_namespace_attribute_group,
74ae66c3b   Toshi Kani   libnvdimm: Add sy...
1629
  	&nd_numa_attribute_group,
3d88002e4   Dan Williams   libnvdimm: suppor...
1630
1631
  	NULL,
  };
8c2f7e865   Dan Williams   libnvdimm: infras...
1632
1633
1634
  struct nd_namespace_common *nvdimm_namespace_common_probe(struct device *dev)
  {
  	struct nd_btt *nd_btt = is_nd_btt(dev) ? to_nd_btt(dev) : NULL;
e1455744b   Dan Williams   libnvdimm, pfn: '...
1635
  	struct nd_pfn *nd_pfn = is_nd_pfn(dev) ? to_nd_pfn(dev) : NULL;
cd03412a5   Dan Williams   libnvdimm, dax: i...
1636
  	struct nd_dax *nd_dax = is_nd_dax(dev) ? to_nd_dax(dev) : NULL;
0bfb8dd3e   Dan Williams   libnvdimm: cleanu...
1637
  	struct nd_namespace_common *ndns = NULL;
8c2f7e865   Dan Williams   libnvdimm: infras...
1638
  	resource_size_t size;
cd03412a5   Dan Williams   libnvdimm, dax: i...
1639
  	if (nd_btt || nd_pfn || nd_dax) {
0bfb8dd3e   Dan Williams   libnvdimm: cleanu...
1640
  		if (nd_btt)
e1455744b   Dan Williams   libnvdimm, pfn: '...
1641
  			ndns = nd_btt->ndns;
0bfb8dd3e   Dan Williams   libnvdimm: cleanu...
1642
  		else if (nd_pfn)
e1455744b   Dan Williams   libnvdimm, pfn: '...
1643
  			ndns = nd_pfn->ndns;
cd03412a5   Dan Williams   libnvdimm, dax: i...
1644
1645
  		else if (nd_dax)
  			ndns = nd_dax->nd_pfn.ndns;
e1455744b   Dan Williams   libnvdimm, pfn: '...
1646

0bfb8dd3e   Dan Williams   libnvdimm: cleanu...
1647
  		if (!ndns)
8c2f7e865   Dan Williams   libnvdimm: infras...
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
  			return ERR_PTR(-ENODEV);
  
  		/*
  		 * Flush any in-progess probes / removals in the driver
  		 * for the raw personality of this namespace.
  		 */
  		device_lock(&ndns->dev);
  		device_unlock(&ndns->dev);
  		if (ndns->dev.driver) {
  			dev_dbg(&ndns->dev, "is active, can't bind %s
  ",
0bfb8dd3e   Dan Williams   libnvdimm: cleanu...
1659
  					dev_name(dev));
8c2f7e865   Dan Williams   libnvdimm: infras...
1660
1661
  			return ERR_PTR(-EBUSY);
  		}
0bfb8dd3e   Dan Williams   libnvdimm: cleanu...
1662
  		if (dev_WARN_ONCE(&ndns->dev, ndns->claim != dev,
8c2f7e865   Dan Williams   libnvdimm: infras...
1663
1664
  					"host (%s) vs claim (%s) mismatch
  ",
0bfb8dd3e   Dan Williams   libnvdimm: cleanu...
1665
  					dev_name(dev),
8c2f7e865   Dan Williams   libnvdimm: infras...
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
  					dev_name(ndns->claim)))
  			return ERR_PTR(-ENXIO);
  	} else {
  		ndns = to_ndns(dev);
  		if (ndns->claim) {
  			dev_dbg(dev, "claimed by %s, failing probe
  ",
  				dev_name(ndns->claim));
  
  			return ERR_PTR(-ENXIO);
  		}
  	}
  
  	size = nvdimm_namespace_capacity(ndns);
  	if (size < ND_MIN_NAMESPACE_SIZE) {
  		dev_dbg(&ndns->dev, "%pa, too small must be at least %#x
  ",
  				&size, ND_MIN_NAMESPACE_SIZE);
  		return ERR_PTR(-ENODEV);
  	}
  
  	if (is_namespace_pmem(&ndns->dev)) {
  		struct nd_namespace_pmem *nspm;
  
  		nspm = to_nd_namespace_pmem(&ndns->dev);
bd26d0d0c   Dmitry Krivenok   nvdimm: improve d...
1691
  		if (uuid_not_set(nspm->uuid, &ndns->dev, __func__))
8c2f7e865   Dan Williams   libnvdimm: infras...
1692
  			return ERR_PTR(-ENODEV);
8c2f7e865   Dan Williams   libnvdimm: infras...
1693
  	} else if (is_namespace_blk(&ndns->dev)) {
047fc8a1f   Ross Zwisler   libnvdimm, nfit, ...
1694
1695
1696
  		struct nd_namespace_blk *nsblk;
  
  		nsblk = to_nd_namespace_blk(&ndns->dev);
bd26d0d0c   Dmitry Krivenok   nvdimm: improve d...
1697
1698
1699
1700
1701
1702
1703
1704
  		if (uuid_not_set(nsblk->uuid, &ndns->dev, __func__))
  			return ERR_PTR(-ENODEV);
  		if (!nsblk->lbasize) {
  			dev_dbg(&ndns->dev, "%s: sector size not set
  ",
  				__func__);
  			return ERR_PTR(-ENODEV);
  		}
047fc8a1f   Ross Zwisler   libnvdimm, nfit, ...
1705
1706
  		if (!nd_namespace_blk_validate(nsblk))
  			return ERR_PTR(-ENODEV);
8c2f7e865   Dan Williams   libnvdimm: infras...
1707
1708
1709
1710
1711
  	}
  
  	return ndns;
  }
  EXPORT_SYMBOL(nvdimm_namespace_common_probe);
3d88002e4   Dan Williams   libnvdimm: suppor...
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
  static struct device **create_namespace_io(struct nd_region *nd_region)
  {
  	struct nd_namespace_io *nsio;
  	struct device *dev, **devs;
  	struct resource *res;
  
  	nsio = kzalloc(sizeof(*nsio), GFP_KERNEL);
  	if (!nsio)
  		return NULL;
  
  	devs = kcalloc(2, sizeof(struct device *), GFP_KERNEL);
  	if (!devs) {
  		kfree(nsio);
  		return NULL;
  	}
8c2f7e865   Dan Williams   libnvdimm: infras...
1727
  	dev = &nsio->common.dev;
3d88002e4   Dan Williams   libnvdimm: suppor...
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
  	dev->type = &namespace_io_device_type;
  	dev->parent = &nd_region->dev;
  	res = &nsio->res;
  	res->name = dev_name(&nd_region->dev);
  	res->flags = IORESOURCE_MEM;
  	res->start = nd_region->ndr_start;
  	res->end = res->start + nd_region->ndr_size - 1;
  
  	devs[0] = dev;
  	return devs;
  }
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1739
1740
1741
1742
1743
1744
1745
1746
  static bool has_uuid_at_pos(struct nd_region *nd_region, u8 *uuid,
  		u64 cookie, u16 pos)
  {
  	struct nd_namespace_label *found = NULL;
  	int i;
  
  	for (i = 0; i < nd_region->ndr_mappings; i++) {
  		struct nd_mapping *nd_mapping = &nd_region->mapping[i];
faec6f8a1   Dan Williams   libnvdimm, label:...
1747
1748
  		struct nd_interleave_set *nd_set = nd_region->nd_set;
  		struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
ae8219f18   Dan Williams   libnvdimm, label:...
1749
  		struct nd_label_ent *label_ent;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1750
  		bool found_uuid = false;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1751

ae8219f18   Dan Williams   libnvdimm, label:...
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
  		list_for_each_entry(label_ent, &nd_mapping->labels, list) {
  			struct nd_namespace_label *nd_label = label_ent->label;
  			u16 position, nlabel;
  			u64 isetcookie;
  
  			if (!nd_label)
  				continue;
  			isetcookie = __le64_to_cpu(nd_label->isetcookie);
  			position = __le16_to_cpu(nd_label->position);
  			nlabel = __le16_to_cpu(nd_label->nlabel);
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1762
1763
1764
1765
1766
1767
  
  			if (isetcookie != cookie)
  				continue;
  
  			if (memcmp(nd_label->uuid, uuid, NSLABEL_UUID_LEN) != 0)
  				continue;
faec6f8a1   Dan Williams   libnvdimm, label:...
1768
1769
1770
1771
1772
1773
1774
1775
1776
  			if (namespace_label_has(ndd, type_guid)
  					&& !guid_equal(&nd_set->type_guid,
  						&nd_label->type_guid)) {
  				dev_dbg(ndd->dev, "expect type_guid %pUb got %pUb
  ",
  						nd_set->type_guid.b,
  						nd_label->type_guid.b);
  				continue;
  			}
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1777
  			if (found_uuid) {
faec6f8a1   Dan Williams   libnvdimm, label:...
1778
  				dev_dbg(ndd->dev,
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
  						"%s duplicate entry for uuid
  ",
  						__func__);
  				return false;
  			}
  			found_uuid = true;
  			if (nlabel != nd_region->ndr_mappings)
  				continue;
  			if (position != pos)
  				continue;
  			found = nd_label;
  			break;
  		}
  		if (found)
  			break;
  	}
  	return found != NULL;
  }
  
  static int select_pmem_id(struct nd_region *nd_region, u8 *pmem_id)
  {
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1800
1801
1802
1803
1804
1805
1806
  	int i;
  
  	if (!pmem_id)
  		return -ENODEV;
  
  	for (i = 0; i < nd_region->ndr_mappings; i++) {
  		struct nd_mapping *nd_mapping = &nd_region->mapping[i];
0e3b0d123   Dan Williams   libnvdimm, namesp...
1807
  		struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
ae8219f18   Dan Williams   libnvdimm, label:...
1808
  		struct nd_namespace_label *nd_label = NULL;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1809
  		u64 hw_start, hw_end, pmem_start, pmem_end;
ae8219f18   Dan Williams   libnvdimm, label:...
1810
  		struct nd_label_ent *label_ent;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1811

9cf8bd529   Dan Williams   libnvdimm: replac...
1812
  		lockdep_assert_held(&nd_mapping->lock);
ae8219f18   Dan Williams   libnvdimm, label:...
1813
1814
1815
1816
  		list_for_each_entry(label_ent, &nd_mapping->labels, list) {
  			nd_label = label_ent->label;
  			if (!nd_label)
  				continue;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1817
1818
  			if (memcmp(nd_label->uuid, pmem_id, NSLABEL_UUID_LEN) == 0)
  				break;
ae8219f18   Dan Williams   libnvdimm, label:...
1819
1820
  			nd_label = NULL;
  		}
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1821
1822
1823
1824
1825
  
  		if (!nd_label) {
  			WARN_ON(1);
  			return -EINVAL;
  		}
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1826
1827
1828
1829
1830
1831
  		/*
  		 * Check that this label is compliant with the dpa
  		 * range published in NFIT
  		 */
  		hw_start = nd_mapping->start;
  		hw_end = hw_start + nd_mapping->size;
ae8219f18   Dan Williams   libnvdimm, label:...
1832
1833
  		pmem_start = __le64_to_cpu(nd_label->dpa);
  		pmem_end = pmem_start + __le64_to_cpu(nd_label->rawsize);
0e3b0d123   Dan Williams   libnvdimm, namesp...
1834
1835
  		if (pmem_start >= hw_start && pmem_start < hw_end
  				&& pmem_end <= hw_end && pmem_end > hw_start)
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1836
  			/* pass */;
0e3b0d123   Dan Williams   libnvdimm, namesp...
1837
1838
1839
1840
  		else {
  			dev_dbg(&nd_region->dev, "%s invalid label for %pUb
  ",
  					dev_name(ndd->dev), nd_label->uuid);
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1841
  			return -EINVAL;
0e3b0d123   Dan Williams   libnvdimm, namesp...
1842
  		}
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1843

8a5f50d3b   Dan Williams   libnvdimm, namesp...
1844
1845
  		/* move recently validated label to the front of the list */
  		list_move(&label_ent->list, &nd_mapping->labels);
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1846
1847
1848
1849
1850
  	}
  	return 0;
  }
  
  /**
8a5f50d3b   Dan Williams   libnvdimm, namesp...
1851
   * create_namespace_pmem - validate interleave set labelling, retrieve label0
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1852
   * @nd_region: region with mappings to validate
8a5f50d3b   Dan Williams   libnvdimm, namesp...
1853
1854
   * @nspm: target namespace to create
   * @nd_label: target pmem namespace label to evaluate
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1855
   */
8a5f50d3b   Dan Williams   libnvdimm, namesp...
1856
  struct device *create_namespace_pmem(struct nd_region *nd_region,
c12c48ce8   Dan Williams   libnvdimm, label:...
1857
  		struct nd_namespace_index *nsindex,
8a5f50d3b   Dan Williams   libnvdimm, namesp...
1858
  		struct nd_namespace_label *nd_label)
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1859
  {
c12c48ce8   Dan Williams   libnvdimm, label:...
1860
  	u64 cookie = nd_region_interleave_set_cookie(nd_region, nsindex);
86ef58a4e   Dan Williams   nfit, libnvdimm: ...
1861
  	u64 altcookie = nd_region_interleave_set_altcookie(nd_region);
ae8219f18   Dan Williams   libnvdimm, label:...
1862
  	struct nd_label_ent *label_ent;
8a5f50d3b   Dan Williams   libnvdimm, namesp...
1863
  	struct nd_namespace_pmem *nspm;
ae8219f18   Dan Williams   libnvdimm, label:...
1864
  	struct nd_mapping *nd_mapping;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1865
  	resource_size_t size = 0;
8a5f50d3b   Dan Williams   libnvdimm, namesp...
1866
1867
  	struct resource *res;
  	struct device *dev;
ae8219f18   Dan Williams   libnvdimm, label:...
1868
  	int rc = 0;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1869
  	u16 i;
4765218db   Dan Williams   libnvdimm, namesp...
1870
1871
1872
  	if (cookie == 0) {
  		dev_dbg(&nd_region->dev, "invalid interleave-set-cookie
  ");
8a5f50d3b   Dan Williams   libnvdimm, namesp...
1873
  		return ERR_PTR(-ENXIO);
4765218db   Dan Williams   libnvdimm, namesp...
1874
  	}
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1875

8a5f50d3b   Dan Williams   libnvdimm, namesp...
1876
1877
1878
1879
  	if (__le64_to_cpu(nd_label->isetcookie) != cookie) {
  		dev_dbg(&nd_region->dev, "invalid cookie in label: %pUb
  ",
  				nd_label->uuid);
86ef58a4e   Dan Williams   nfit, libnvdimm: ...
1880
1881
1882
1883
1884
1885
  		if (__le64_to_cpu(nd_label->isetcookie) != altcookie)
  			return ERR_PTR(-EAGAIN);
  
  		dev_dbg(&nd_region->dev, "valid altcookie in label: %pUb
  ",
  				nd_label->uuid);
ae8219f18   Dan Williams   libnvdimm, label:...
1886
  	}
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1887

8a5f50d3b   Dan Williams   libnvdimm, namesp...
1888
1889
1890
  	nspm = kzalloc(sizeof(*nspm), GFP_KERNEL);
  	if (!nspm)
  		return ERR_PTR(-ENOMEM);
ae8219f18   Dan Williams   libnvdimm, label:...
1891

0e3b0d123   Dan Williams   libnvdimm, namesp...
1892
  	nspm->id = -1;
8a5f50d3b   Dan Williams   libnvdimm, namesp...
1893
1894
1895
1896
1897
1898
  	dev = &nspm->nsio.common.dev;
  	dev->type = &namespace_pmem_device_type;
  	dev->parent = &nd_region->dev;
  	res = &nspm->nsio.res;
  	res->name = dev_name(&nd_region->dev);
  	res->flags = IORESOURCE_MEM;
ae8219f18   Dan Williams   libnvdimm, label:...
1899

86ef58a4e   Dan Williams   nfit, libnvdimm: ...
1900
1901
1902
1903
1904
1905
1906
  	for (i = 0; i < nd_region->ndr_mappings; i++) {
  		if (has_uuid_at_pos(nd_region, nd_label->uuid, cookie, i))
  			continue;
  		if (has_uuid_at_pos(nd_region, nd_label->uuid, altcookie, i))
  			continue;
  		break;
  	}
8a5f50d3b   Dan Williams   libnvdimm, namesp...
1907
  	if (i < nd_region->ndr_mappings) {
b68b77c93   Dan Williams   libnvdimm, namesp...
1908
  		struct nvdimm *nvdimm = nd_region->mapping[i].nvdimm;
0e3b0d123   Dan Williams   libnvdimm, namesp...
1909

8a5f50d3b   Dan Williams   libnvdimm, namesp...
1910
1911
1912
1913
1914
  		/*
  		 * Give up if we don't find an instance of a uuid at each
  		 * position (from 0 to nd_region->ndr_mappings - 1), or if we
  		 * find a dimm with two instances of the same uuid.
  		 */
0e3b0d123   Dan Williams   libnvdimm, namesp...
1915
1916
  		dev_err(&nd_region->dev, "%s missing label for %pUb
  ",
b68b77c93   Dan Williams   libnvdimm, namesp...
1917
  				nvdimm_name(nvdimm), nd_label->uuid);
8a5f50d3b   Dan Williams   libnvdimm, namesp...
1918
  		rc = -EINVAL;
ae8219f18   Dan Williams   libnvdimm, label:...
1919
  		goto err;
8a5f50d3b   Dan Williams   libnvdimm, namesp...
1920
  	}
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1921
1922
1923
1924
1925
1926
1927
1928
1929
  
  	/*
  	 * Fix up each mapping's 'labels' to have the validated pmem label for
  	 * that position at labels[0], and NULL at labels[1].  In the process,
  	 * check that the namespace aligns with interleave-set.  We know
  	 * that it does not overlap with any blk namespaces by virtue of
  	 * the dimm being enabled (i.e. nd_label_reserve_dpa()
  	 * succeeded).
  	 */
8a5f50d3b   Dan Williams   libnvdimm, namesp...
1930
  	rc = select_pmem_id(nd_region, nd_label->uuid);
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1931
1932
1933
1934
1935
  	if (rc)
  		goto err;
  
  	/* Calculate total size and populate namespace properties from label0 */
  	for (i = 0; i < nd_region->ndr_mappings; i++) {
ae8219f18   Dan Williams   libnvdimm, label:...
1936
  		struct nd_namespace_label *label0;
b3fde74ea   Dan Williams   libnvdimm, label:...
1937
  		struct nvdimm_drvdata *ndd;
ae8219f18   Dan Williams   libnvdimm, label:...
1938
1939
  
  		nd_mapping = &nd_region->mapping[i];
ae8219f18   Dan Williams   libnvdimm, label:...
1940
1941
1942
  		label_ent = list_first_entry_or_null(&nd_mapping->labels,
  				typeof(*label_ent), list);
  		label0 = label_ent ? label_ent->label : 0;
ae8219f18   Dan Williams   libnvdimm, label:...
1943
1944
1945
1946
1947
  
  		if (!label0) {
  			WARN_ON(1);
  			continue;
  		}
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1948
1949
1950
1951
1952
1953
1954
1955
1956
  
  		size += __le64_to_cpu(label0->rawsize);
  		if (__le16_to_cpu(label0->position) != 0)
  			continue;
  		WARN_ON(nspm->alt_name || nspm->uuid);
  		nspm->alt_name = kmemdup((void __force *) label0->name,
  				NSLABEL_NAME_LEN, GFP_KERNEL);
  		nspm->uuid = kmemdup((void __force *) label0->uuid,
  				NSLABEL_UUID_LEN, GFP_KERNEL);
f979b13c3   Dan Williams   libnvdimm, label:...
1957
  		nspm->lbasize = __le64_to_cpu(label0->lbasize);
b3fde74ea   Dan Williams   libnvdimm, label:...
1958
1959
1960
1961
  		ndd = to_ndd(nd_mapping);
  		if (namespace_label_has(ndd, abstraction_guid))
  			nspm->nsio.common.claim_class
  				= to_nvdimm_cclass(&label0->abstraction_guid);
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1962
1963
1964
1965
1966
1967
  	}
  
  	if (!nspm->alt_name || !nspm->uuid) {
  		rc = -ENOMEM;
  		goto err;
  	}
0e3b0d123   Dan Williams   libnvdimm, namesp...
1968
  	nd_namespace_pmem_set_resource(nd_region, nspm, size);
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1969

8a5f50d3b   Dan Williams   libnvdimm, namesp...
1970
  	return dev;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1971
   err:
8a5f50d3b   Dan Williams   libnvdimm, namesp...
1972
  	namespace_pmem_release(dev);
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
  	switch (rc) {
  	case -EINVAL:
  		dev_dbg(&nd_region->dev, "%s: invalid label(s)
  ", __func__);
  		break;
  	case -ENODEV:
  		dev_dbg(&nd_region->dev, "%s: label not found
  ", __func__);
  		break;
  	default:
  		dev_dbg(&nd_region->dev, "%s: unexpected err: %d
  ",
  				__func__, rc);
  		break;
  	}
8a5f50d3b   Dan Williams   libnvdimm, namesp...
1988
  	return ERR_PTR(rc);
bf9bccc14   Dan Williams   libnvdimm: pmem l...
1989
  }
1b40e09a1   Dan Williams   libnvdimm: blk la...
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
  struct resource *nsblk_add_resource(struct nd_region *nd_region,
  		struct nvdimm_drvdata *ndd, struct nd_namespace_blk *nsblk,
  		resource_size_t start)
  {
  	struct nd_label_id label_id;
  	struct resource *res;
  
  	nd_label_gen_id(&label_id, nsblk->uuid, NSLABEL_FLAG_LOCAL);
  	res = krealloc(nsblk->res,
  			sizeof(void *) * (nsblk->num_resources + 1),
  			GFP_KERNEL);
  	if (!res)
  		return NULL;
  	nsblk->res = (struct resource **) res;
  	for_each_dpa_resource(ndd, res)
  		if (strcmp(res->name, label_id.id) == 0
  				&& res->start == start) {
  			nsblk->res[nsblk->num_resources++] = res;
  			return res;
  		}
  	return NULL;
  }
  
  static struct device *nd_namespace_blk_create(struct nd_region *nd_region)
  {
  	struct nd_namespace_blk *nsblk;
  	struct device *dev;
  
  	if (!is_nd_blk(&nd_region->dev))
  		return NULL;
  
  	nsblk = kzalloc(sizeof(*nsblk), GFP_KERNEL);
  	if (!nsblk)
  		return NULL;
8c2f7e865   Dan Williams   libnvdimm: infras...
2024
  	dev = &nsblk->common.dev;
1b40e09a1   Dan Williams   libnvdimm: blk la...
2025
2026
2027
2028
2029
2030
2031
2032
2033
  	dev->type = &namespace_blk_device_type;
  	nsblk->id = ida_simple_get(&nd_region->ns_ida, 0, 0, GFP_KERNEL);
  	if (nsblk->id < 0) {
  		kfree(nsblk);
  		return NULL;
  	}
  	dev_set_name(dev, "namespace%d.%d", nd_region->id, nsblk->id);
  	dev->parent = &nd_region->dev;
  	dev->groups = nd_namespace_attribute_groups;
8c2f7e865   Dan Williams   libnvdimm: infras...
2034
  	return &nsblk->common.dev;
1b40e09a1   Dan Williams   libnvdimm: blk la...
2035
  }
98a29c39d   Dan Williams   libnvdimm, namesp...
2036
2037
2038
2039
2040
  static struct device *nd_namespace_pmem_create(struct nd_region *nd_region)
  {
  	struct nd_namespace_pmem *nspm;
  	struct resource *res;
  	struct device *dev;
c9e582aa6   Dan Williams   libnvdimm, nfit: ...
2041
  	if (!is_memory(&nd_region->dev))
98a29c39d   Dan Williams   libnvdimm, namesp...
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
  		return NULL;
  
  	nspm = kzalloc(sizeof(*nspm), GFP_KERNEL);
  	if (!nspm)
  		return NULL;
  
  	dev = &nspm->nsio.common.dev;
  	dev->type = &namespace_pmem_device_type;
  	dev->parent = &nd_region->dev;
  	res = &nspm->nsio.res;
  	res->name = dev_name(&nd_region->dev);
  	res->flags = IORESOURCE_MEM;
  
  	nspm->id = ida_simple_get(&nd_region->ns_ida, 0, 0, GFP_KERNEL);
  	if (nspm->id < 0) {
  		kfree(nspm);
  		return NULL;
  	}
  	dev_set_name(dev, "namespace%d.%d", nd_region->id, nspm->id);
  	dev->parent = &nd_region->dev;
  	dev->groups = nd_namespace_attribute_groups;
  	nd_namespace_pmem_set_resource(nd_region, nspm, 0);
  
  	return dev;
  }
  
  void nd_region_create_ns_seed(struct nd_region *nd_region)
1b40e09a1   Dan Williams   libnvdimm: blk la...
2069
2070
  {
  	WARN_ON(!is_nvdimm_bus_locked(&nd_region->dev));
98a29c39d   Dan Williams   libnvdimm, namesp...
2071
2072
2073
2074
2075
2076
2077
2078
  
  	if (nd_region_to_nstype(nd_region) == ND_DEVICE_NAMESPACE_IO)
  		return;
  
  	if (is_nd_blk(&nd_region->dev))
  		nd_region->ns_seed = nd_namespace_blk_create(nd_region);
  	else
  		nd_region->ns_seed = nd_namespace_pmem_create(nd_region);
1b40e09a1   Dan Williams   libnvdimm: blk la...
2079
2080
2081
2082
2083
  	/*
  	 * Seed creation failures are not fatal, provisioning is simply
  	 * disabled until memory becomes available
  	 */
  	if (!nd_region->ns_seed)
98a29c39d   Dan Williams   libnvdimm, namesp...
2084
2085
2086
  		dev_err(&nd_region->dev, "failed to create %s namespace
  ",
  				is_nd_blk(&nd_region->dev) ? "blk" : "pmem");
1b40e09a1   Dan Williams   libnvdimm: blk la...
2087
2088
2089
  	else
  		nd_device_register(nd_region->ns_seed);
  }
cd03412a5   Dan Williams   libnvdimm, dax: i...
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
  void nd_region_create_dax_seed(struct nd_region *nd_region)
  {
  	WARN_ON(!is_nvdimm_bus_locked(&nd_region->dev));
  	nd_region->dax_seed = nd_dax_create(nd_region);
  	/*
  	 * Seed creation failures are not fatal, provisioning is simply
  	 * disabled until memory becomes available
  	 */
  	if (!nd_region->dax_seed)
  		dev_err(&nd_region->dev, "failed to create dax namespace
  ");
  }
2dc43331e   Dan Williams   libnvdimm, pfn: f...
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
  void nd_region_create_pfn_seed(struct nd_region *nd_region)
  {
  	WARN_ON(!is_nvdimm_bus_locked(&nd_region->dev));
  	nd_region->pfn_seed = nd_pfn_create(nd_region);
  	/*
  	 * Seed creation failures are not fatal, provisioning is simply
  	 * disabled until memory becomes available
  	 */
  	if (!nd_region->pfn_seed)
  		dev_err(&nd_region->dev, "failed to create pfn namespace
  ");
  }
8c2f7e865   Dan Williams   libnvdimm: infras...
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
  void nd_region_create_btt_seed(struct nd_region *nd_region)
  {
  	WARN_ON(!is_nvdimm_bus_locked(&nd_region->dev));
  	nd_region->btt_seed = nd_btt_create(nd_region);
  	/*
  	 * Seed creation failures are not fatal, provisioning is simply
  	 * disabled until memory becomes available
  	 */
  	if (!nd_region->btt_seed)
  		dev_err(&nd_region->dev, "failed to create btt namespace
  ");
  }
8a5f50d3b   Dan Williams   libnvdimm, namesp...
2126
2127
2128
  static int add_namespace_resource(struct nd_region *nd_region,
  		struct nd_namespace_label *nd_label, struct device **devs,
  		int count)
1b40e09a1   Dan Williams   libnvdimm: blk la...
2129
  {
8a5f50d3b   Dan Williams   libnvdimm, namesp...
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
  	struct nd_mapping *nd_mapping = &nd_region->mapping[0];
  	struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
  	int i;
  
  	for (i = 0; i < count; i++) {
  		u8 *uuid = namespace_to_uuid(devs[i]);
  		struct resource *res;
  
  		if (IS_ERR_OR_NULL(uuid)) {
  			WARN_ON(1);
  			continue;
  		}
  
  		if (memcmp(uuid, nd_label->uuid, NSLABEL_UUID_LEN) != 0)
  			continue;
  		if (is_namespace_blk(devs[i])) {
  			res = nsblk_add_resource(nd_region, ndd,
  					to_nd_namespace_blk(devs[i]),
  					__le64_to_cpu(nd_label->dpa));
  			if (!res)
  				return -ENXIO;
  			nd_dbg_dpa(nd_region, ndd, res, "%d assign
  ", count);
  		} else {
  			dev_err(&nd_region->dev,
  					"error: conflicting extents for uuid: %pUb
  ",
  					nd_label->uuid);
  			return -ENXIO;
  		}
  		break;
  	}
  
  	return i;
  }
  
  struct device *create_namespace_blk(struct nd_region *nd_region,
  		struct nd_namespace_label *nd_label, int count)
  {
  
  	struct nd_mapping *nd_mapping = &nd_region->mapping[0];
faec6f8a1   Dan Williams   libnvdimm, label:...
2171
  	struct nd_interleave_set *nd_set = nd_region->nd_set;
ae8219f18   Dan Williams   libnvdimm, label:...
2172
  	struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
1b40e09a1   Dan Williams   libnvdimm: blk la...
2173
  	struct nd_namespace_blk *nsblk;
238b323a6   Nicolas Iooss   libnvdimm, namesp...
2174
  	char name[NSLABEL_NAME_LEN];
8a5f50d3b   Dan Williams   libnvdimm, namesp...
2175
2176
  	struct device *dev = NULL;
  	struct resource *res;
8f2bc2430   Dan Williams   libnvdimm, label:...
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
  	if (namespace_label_has(ndd, type_guid)) {
  		if (!guid_equal(&nd_set->type_guid, &nd_label->type_guid)) {
  			dev_dbg(ndd->dev, "expect type_guid %pUb got %pUb
  ",
  					nd_set->type_guid.b,
  					nd_label->type_guid.b);
  			return ERR_PTR(-EAGAIN);
  		}
  
  		if (nd_label->isetcookie != __cpu_to_le64(nd_set->cookie2)) {
  			dev_dbg(ndd->dev, "expect cookie %#llx got %#llx
  ",
  					nd_set->cookie2,
  					__le64_to_cpu(nd_label->isetcookie));
  			return ERR_PTR(-EAGAIN);
  		}
faec6f8a1   Dan Williams   libnvdimm, label:...
2193
  	}
8a5f50d3b   Dan Williams   libnvdimm, namesp...
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
  	nsblk = kzalloc(sizeof(*nsblk), GFP_KERNEL);
  	if (!nsblk)
  		return ERR_PTR(-ENOMEM);
  	dev = &nsblk->common.dev;
  	dev->type = &namespace_blk_device_type;
  	dev->parent = &nd_region->dev;
  	nsblk->id = -1;
  	nsblk->lbasize = __le64_to_cpu(nd_label->lbasize);
  	nsblk->uuid = kmemdup(nd_label->uuid, NSLABEL_UUID_LEN,
  			GFP_KERNEL);
b3fde74ea   Dan Williams   libnvdimm, label:...
2204
2205
2206
  	if (namespace_label_has(ndd, abstraction_guid))
  		nsblk->common.claim_class
  			= to_nvdimm_cclass(&nd_label->abstraction_guid);
8a5f50d3b   Dan Williams   libnvdimm, namesp...
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
  	if (!nsblk->uuid)
  		goto blk_err;
  	memcpy(name, nd_label->name, NSLABEL_NAME_LEN);
  	if (name[0])
  		nsblk->alt_name = kmemdup(name, NSLABEL_NAME_LEN,
  				GFP_KERNEL);
  	res = nsblk_add_resource(nd_region, ndd, nsblk,
  			__le64_to_cpu(nd_label->dpa));
  	if (!res)
  		goto blk_err;
  	nd_dbg_dpa(nd_region, ndd, res, "%d: assign
  ", count);
  	return dev;
   blk_err:
  	namespace_blk_release(dev);
  	return ERR_PTR(-ENXIO);
  }
6ff3e912d   Dan Williams   libnvdimm, namesp...
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
  static int cmp_dpa(const void *a, const void *b)
  {
  	const struct device *dev_a = *(const struct device **) a;
  	const struct device *dev_b = *(const struct device **) b;
  	struct nd_namespace_blk *nsblk_a, *nsblk_b;
  	struct nd_namespace_pmem *nspm_a, *nspm_b;
  
  	if (is_namespace_io(dev_a))
  		return 0;
  
  	if (is_namespace_blk(dev_a)) {
  		nsblk_a = to_nd_namespace_blk(dev_a);
  		nsblk_b = to_nd_namespace_blk(dev_b);
  
  		return memcmp(&nsblk_a->res[0]->start, &nsblk_b->res[0]->start,
  				sizeof(resource_size_t));
  	}
  
  	nspm_a = to_nd_namespace_pmem(dev_a);
  	nspm_b = to_nd_namespace_pmem(dev_b);
  
  	return memcmp(&nspm_a->nsio.res.start, &nspm_b->nsio.res.start,
  			sizeof(resource_size_t));
  }
8a5f50d3b   Dan Williams   libnvdimm, namesp...
2248
2249
  static struct device **scan_labels(struct nd_region *nd_region)
  {
c969e24c1   Dan Williams   libnvdimm, namesp...
2250
  	int i, count = 0;
8a5f50d3b   Dan Williams   libnvdimm, namesp...
2251
2252
  	struct device *dev, **devs = NULL;
  	struct nd_label_ent *label_ent, *e;
c969e24c1   Dan Williams   libnvdimm, namesp...
2253
2254
  	struct nd_mapping *nd_mapping = &nd_region->mapping[0];
  	resource_size_t map_end = nd_mapping->start + nd_mapping->size - 1;
1b40e09a1   Dan Williams   libnvdimm: blk la...
2255

8a5f50d3b   Dan Williams   libnvdimm, namesp...
2256
2257
  	/* "safe" because create_namespace_pmem() might list_move() label_ent */
  	list_for_each_entry_safe(label_ent, e, &nd_mapping->labels, list) {
ae8219f18   Dan Williams   libnvdimm, label:...
2258
  		struct nd_namespace_label *nd_label = label_ent->label;
1b40e09a1   Dan Williams   libnvdimm: blk la...
2259
  		struct device **__devs;
ae8219f18   Dan Williams   libnvdimm, label:...
2260
  		u32 flags;
1b40e09a1   Dan Williams   libnvdimm: blk la...
2261

ae8219f18   Dan Williams   libnvdimm, label:...
2262
2263
2264
  		if (!nd_label)
  			continue;
  		flags = __le32_to_cpu(nd_label->flags);
8a5f50d3b   Dan Williams   libnvdimm, namesp...
2265
2266
2267
  		if (is_nd_blk(&nd_region->dev)
  				== !!(flags & NSLABEL_FLAG_LOCAL))
  			/* pass, region matches label type */;
1b40e09a1   Dan Williams   libnvdimm: blk la...
2268
2269
  		else
  			continue;
c969e24c1   Dan Williams   libnvdimm, namesp...
2270
2271
2272
  		/* skip labels that describe extents outside of the region */
  		if (nd_label->dpa < nd_mapping->start || nd_label->dpa > map_end)
  			continue;
8a5f50d3b   Dan Williams   libnvdimm, namesp...
2273
2274
2275
  		i = add_namespace_resource(nd_region, nd_label, devs, count);
  		if (i < 0)
  			goto err;
1b40e09a1   Dan Williams   libnvdimm: blk la...
2276
2277
2278
2279
2280
2281
2282
2283
  		if (i < count)
  			continue;
  		__devs = kcalloc(count + 2, sizeof(dev), GFP_KERNEL);
  		if (!__devs)
  			goto err;
  		memcpy(__devs, devs, sizeof(dev) * count);
  		kfree(devs);
  		devs = __devs;
faec6f8a1   Dan Williams   libnvdimm, label:...
2284
  		if (is_nd_blk(&nd_region->dev))
8a5f50d3b   Dan Williams   libnvdimm, namesp...
2285
  			dev = create_namespace_blk(nd_region, nd_label, count);
faec6f8a1   Dan Williams   libnvdimm, label:...
2286
  		else {
c12c48ce8   Dan Williams   libnvdimm, label:...
2287
2288
2289
2290
2291
  			struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
  			struct nd_namespace_index *nsindex;
  
  			nsindex = to_namespace_index(ndd, ndd->ns_current);
  			dev = create_namespace_pmem(nd_region, nsindex, nd_label);
8a5f50d3b   Dan Williams   libnvdimm, namesp...
2292
  		}
faec6f8a1   Dan Williams   libnvdimm, label:...
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
  
  		if (IS_ERR(dev)) {
  			switch (PTR_ERR(dev)) {
  			case -EAGAIN:
  				/* skip invalid labels */
  				continue;
  			case -ENODEV:
  				/* fallthrough to seed creation */
  				break;
  			default:
  				goto err;
  			}
  		} else
  			devs[count++] = dev;
1b40e09a1   Dan Williams   libnvdimm: blk la...
2307
  	}
8a5f50d3b   Dan Williams   libnvdimm, namesp...
2308
2309
2310
2311
  	dev_dbg(&nd_region->dev, "%s: discovered %d %s namespace%s
  ",
  			__func__, count, is_nd_blk(&nd_region->dev)
  			? "blk" : "pmem", count == 1 ? "" : "s");
1b40e09a1   Dan Williams   libnvdimm: blk la...
2312
2313
2314
  
  	if (count == 0) {
  		/* Publish a zero-sized namespace for userspace to configure. */
ae8219f18   Dan Williams   libnvdimm, label:...
2315
  		nd_mapping_free_labels(nd_mapping);
1b40e09a1   Dan Williams   libnvdimm: blk la...
2316
2317
2318
2319
  
  		devs = kcalloc(2, sizeof(dev), GFP_KERNEL);
  		if (!devs)
  			goto err;
8a5f50d3b   Dan Williams   libnvdimm, namesp...
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
  		if (is_nd_blk(&nd_region->dev)) {
  			struct nd_namespace_blk *nsblk;
  
  			nsblk = kzalloc(sizeof(*nsblk), GFP_KERNEL);
  			if (!nsblk)
  				goto err;
  			dev = &nsblk->common.dev;
  			dev->type = &namespace_blk_device_type;
  		} else {
  			struct nd_namespace_pmem *nspm;
  
  			nspm = kzalloc(sizeof(*nspm), GFP_KERNEL);
  			if (!nspm)
  				goto err;
  			dev = &nspm->nsio.common.dev;
  			dev->type = &namespace_pmem_device_type;
0e3b0d123   Dan Williams   libnvdimm, namesp...
2336
  			nd_namespace_pmem_set_resource(nd_region, nspm, 0);
8a5f50d3b   Dan Williams   libnvdimm, namesp...
2337
  		}
1b40e09a1   Dan Williams   libnvdimm: blk la...
2338
2339
  		dev->parent = &nd_region->dev;
  		devs[count++] = dev;
c9e582aa6   Dan Williams   libnvdimm, nfit: ...
2340
  	} else if (is_memory(&nd_region->dev)) {
8a5f50d3b   Dan Williams   libnvdimm, namesp...
2341
2342
  		/* clean unselected labels */
  		for (i = 0; i < nd_region->ndr_mappings; i++) {
0e3b0d123   Dan Williams   libnvdimm, namesp...
2343
2344
2345
  			struct list_head *l, *e;
  			LIST_HEAD(list);
  			int j;
8a5f50d3b   Dan Williams   libnvdimm, namesp...
2346
2347
2348
2349
2350
  			nd_mapping = &nd_region->mapping[i];
  			if (list_empty(&nd_mapping->labels)) {
  				WARN_ON(1);
  				continue;
  			}
0e3b0d123   Dan Williams   libnvdimm, namesp...
2351
2352
2353
2354
2355
2356
2357
  
  			j = count;
  			list_for_each_safe(l, e, &nd_mapping->labels) {
  				if (!j--)
  					break;
  				list_move_tail(l, &list);
  			}
8a5f50d3b   Dan Williams   libnvdimm, namesp...
2358
  			nd_mapping_free_labels(nd_mapping);
0e3b0d123   Dan Williams   libnvdimm, namesp...
2359
  			list_splice_init(&list, &nd_mapping->labels);
8a5f50d3b   Dan Williams   libnvdimm, namesp...
2360
  		}
1b40e09a1   Dan Williams   libnvdimm: blk la...
2361
  	}
6ff3e912d   Dan Williams   libnvdimm, namesp...
2362
2363
  	if (count > 1)
  		sort(devs, count, sizeof(struct device *), cmp_dpa, NULL);
1b40e09a1   Dan Williams   libnvdimm: blk la...
2364
  	return devs;
ae8219f18   Dan Williams   libnvdimm, label:...
2365
   err:
75d29713b   Dan Carpenter   libnvdimm, namesp...
2366
2367
2368
2369
2370
2371
2372
2373
  	if (devs) {
  		for (i = 0; devs[i]; i++)
  			if (is_nd_blk(&nd_region->dev))
  				namespace_blk_release(devs[i]);
  			else
  				namespace_pmem_release(devs[i]);
  		kfree(devs);
  	}
1b40e09a1   Dan Williams   libnvdimm: blk la...
2374
2375
  	return NULL;
  }
8a5f50d3b   Dan Williams   libnvdimm, namesp...
2376
  static struct device **create_namespaces(struct nd_region *nd_region)
ae8219f18   Dan Williams   libnvdimm, label:...
2377
2378
2379
  {
  	struct nd_mapping *nd_mapping = &nd_region->mapping[0];
  	struct device **devs;
8a5f50d3b   Dan Williams   libnvdimm, namesp...
2380
  	int i;
ae8219f18   Dan Williams   libnvdimm, label:...
2381
2382
2383
  
  	if (nd_region->ndr_mappings == 0)
  		return NULL;
8a5f50d3b   Dan Williams   libnvdimm, namesp...
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
  	/* lock down all mappings while we scan labels */
  	for (i = 0; i < nd_region->ndr_mappings; i++) {
  		nd_mapping = &nd_region->mapping[i];
  		mutex_lock_nested(&nd_mapping->lock, i);
  	}
  
  	devs = scan_labels(nd_region);
  
  	for (i = 0; i < nd_region->ndr_mappings; i++) {
  		int reverse = nd_region->ndr_mappings - 1 - i;
  
  		nd_mapping = &nd_region->mapping[reverse];
  		mutex_unlock(&nd_mapping->lock);
  	}
ae8219f18   Dan Williams   libnvdimm, label:...
2398
2399
2400
  
  	return devs;
  }
bf9bccc14   Dan Williams   libnvdimm: pmem l...
2401
2402
2403
2404
2405
2406
2407
2408
  static int init_active_labels(struct nd_region *nd_region)
  {
  	int i;
  
  	for (i = 0; i < nd_region->ndr_mappings; i++) {
  		struct nd_mapping *nd_mapping = &nd_region->mapping[i];
  		struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
  		struct nvdimm *nvdimm = nd_mapping->nvdimm;
ae8219f18   Dan Williams   libnvdimm, label:...
2409
  		struct nd_label_ent *label_ent;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
2410
2411
2412
  		int count, j;
  
  		/*
9d62ed965   Dan Williams   libnvdimm: handle...
2413
2414
  		 * If the dimm is disabled then we may need to prevent
  		 * the region from being activated.
bf9bccc14   Dan Williams   libnvdimm: pmem l...
2415
2416
  		 */
  		if (!ndd) {
9d62ed965   Dan Williams   libnvdimm: handle...
2417
2418
2419
2420
2421
  			if (test_bit(NDD_LOCKED, &nvdimm->flags))
  				/* fail, label data may be unreadable */;
  			else if (test_bit(NDD_ALIASING, &nvdimm->flags))
  				/* fail, labels needed to disambiguate dpa */;
  			else
bf9bccc14   Dan Williams   libnvdimm: pmem l...
2422
  				return 0;
9d62ed965   Dan Williams   libnvdimm: handle...
2423
2424
2425
2426
2427
2428
  
  			dev_err(&nd_region->dev, "%s: is %s, failing probe
  ",
  					dev_name(&nd_mapping->nvdimm->dev),
  					test_bit(NDD_LOCKED, &nvdimm->flags)
  					? "locked" : "disabled");
bf9bccc14   Dan Williams   libnvdimm: pmem l...
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
  			return -ENXIO;
  		}
  		nd_mapping->ndd = ndd;
  		atomic_inc(&nvdimm->busy);
  		get_ndd(ndd);
  
  		count = nd_label_active_count(ndd);
  		dev_dbg(ndd->dev, "%s: %d
  ", __func__, count);
  		if (!count)
  			continue;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
2440
2441
  		for (j = 0; j < count; j++) {
  			struct nd_namespace_label *label;
ae8219f18   Dan Williams   libnvdimm, label:...
2442
2443
2444
  			label_ent = kzalloc(sizeof(*label_ent), GFP_KERNEL);
  			if (!label_ent)
  				break;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
2445
  			label = nd_label_active(ndd, j);
ae8219f18   Dan Williams   libnvdimm, label:...
2446
2447
2448
2449
2450
  			label_ent->label = label;
  
  			mutex_lock(&nd_mapping->lock);
  			list_add_tail(&label_ent->list, &nd_mapping->labels);
  			mutex_unlock(&nd_mapping->lock);
bf9bccc14   Dan Williams   libnvdimm: pmem l...
2451
  		}
ae8219f18   Dan Williams   libnvdimm, label:...
2452
2453
2454
2455
2456
2457
2458
2459
  
  		if (j >= count)
  			continue;
  
  		mutex_lock(&nd_mapping->lock);
  		nd_mapping_free_labels(nd_mapping);
  		mutex_unlock(&nd_mapping->lock);
  		return -ENOMEM;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
2460
2461
2462
2463
  	}
  
  	return 0;
  }
3d88002e4   Dan Williams   libnvdimm: suppor...
2464
2465
2466
  int nd_region_register_namespaces(struct nd_region *nd_region, int *err)
  {
  	struct device **devs = NULL;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
2467
  	int i, rc = 0, type;
3d88002e4   Dan Williams   libnvdimm: suppor...
2468
2469
  
  	*err = 0;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
2470
2471
2472
2473
2474
2475
2476
2477
2478
  	nvdimm_bus_lock(&nd_region->dev);
  	rc = init_active_labels(nd_region);
  	if (rc) {
  		nvdimm_bus_unlock(&nd_region->dev);
  		return rc;
  	}
  
  	type = nd_region_to_nstype(nd_region);
  	switch (type) {
3d88002e4   Dan Williams   libnvdimm: suppor...
2479
2480
2481
  	case ND_DEVICE_NAMESPACE_IO:
  		devs = create_namespace_io(nd_region);
  		break;
bf9bccc14   Dan Williams   libnvdimm: pmem l...
2482
  	case ND_DEVICE_NAMESPACE_PMEM:
1b40e09a1   Dan Williams   libnvdimm: blk la...
2483
  	case ND_DEVICE_NAMESPACE_BLK:
8a5f50d3b   Dan Williams   libnvdimm, namesp...
2484
  		devs = create_namespaces(nd_region);
1b40e09a1   Dan Williams   libnvdimm: blk la...
2485
  		break;
3d88002e4   Dan Williams   libnvdimm: suppor...
2486
2487
2488
  	default:
  		break;
  	}
bf9bccc14   Dan Williams   libnvdimm: pmem l...
2489
  	nvdimm_bus_unlock(&nd_region->dev);
3d88002e4   Dan Williams   libnvdimm: suppor...
2490
2491
2492
2493
2494
2495
  
  	if (!devs)
  		return -ENODEV;
  
  	for (i = 0; devs[i]; i++) {
  		struct device *dev = devs[i];
1b40e09a1   Dan Williams   libnvdimm: blk la...
2496
  		int id;
3d88002e4   Dan Williams   libnvdimm: suppor...
2497

1b40e09a1   Dan Williams   libnvdimm: blk la...
2498
2499
2500
2501
2502
2503
2504
  		if (type == ND_DEVICE_NAMESPACE_BLK) {
  			struct nd_namespace_blk *nsblk;
  
  			nsblk = to_nd_namespace_blk(dev);
  			id = ida_simple_get(&nd_region->ns_ida, 0, 0,
  					GFP_KERNEL);
  			nsblk->id = id;
0e3b0d123   Dan Williams   libnvdimm, namesp...
2505
2506
2507
2508
2509
2510
2511
  		} else if (type == ND_DEVICE_NAMESPACE_PMEM) {
  			struct nd_namespace_pmem *nspm;
  
  			nspm = to_nd_namespace_pmem(dev);
  			id = ida_simple_get(&nd_region->ns_ida, 0, 0,
  					GFP_KERNEL);
  			nspm->id = id;
1b40e09a1   Dan Williams   libnvdimm: blk la...
2512
2513
2514
2515
2516
2517
  		} else
  			id = i;
  
  		if (id < 0)
  			break;
  		dev_set_name(dev, "namespace%d.%d", nd_region->id, id);
3d88002e4   Dan Williams   libnvdimm: suppor...
2518
2519
2520
  		dev->groups = nd_namespace_attribute_groups;
  		nd_device_register(dev);
  	}
1b40e09a1   Dan Williams   libnvdimm: blk la...
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
  	if (i)
  		nd_region->ns_seed = devs[0];
  
  	if (devs[i]) {
  		int j;
  
  		for (j = i; devs[j]; j++) {
  			struct device *dev = devs[j];
  
  			device_initialize(dev);
  			put_device(dev);
  		}
  		*err = j - i;
  		/*
  		 * All of the namespaces we tried to register failed, so
  		 * fail region activation.
  		 */
  		if (*err == 0)
  			rc = -ENODEV;
  	}
3d88002e4   Dan Williams   libnvdimm: suppor...
2541
  	kfree(devs);
1b40e09a1   Dan Williams   libnvdimm: blk la...
2542
2543
  	if (rc == -ENODEV)
  		return rc;
3d88002e4   Dan Williams   libnvdimm: suppor...
2544
2545
  	return i;
  }