Blame view

drivers/md/dm-thin-metadata.c 47 KB
991d9fa02   Joe Thornber   dm: add thin prov...
1
  /*
da105ed5f   Joe Thornber   dm thin metadata:...
2
   * Copyright (C) 2011-2012 Red Hat, Inc.
991d9fa02   Joe Thornber   dm: add thin prov...
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
   *
   * This file is released under the GPL.
   */
  
  #include "dm-thin-metadata.h"
  #include "persistent-data/dm-btree.h"
  #include "persistent-data/dm-space-map.h"
  #include "persistent-data/dm-space-map-disk.h"
  #include "persistent-data/dm-transaction-manager.h"
  
  #include <linux/list.h>
  #include <linux/device-mapper.h>
  #include <linux/workqueue.h>
  
  /*--------------------------------------------------------------------------
   * As far as the metadata goes, there is:
   *
   * - A superblock in block zero, taking up fewer than 512 bytes for
   *   atomic writes.
   *
   * - A space map managing the metadata blocks.
   *
   * - A space map managing the data blocks.
   *
   * - A btree mapping our internal thin dev ids onto struct disk_device_details.
   *
   * - A hierarchical btree, with 2 levels which effectively maps (thin
   *   dev id, virtual block) -> block_time.  Block time is a 64-bit
   *   field holding the time in the low 24 bits, and block in the top 48
   *   bits.
   *
   * BTrees consist solely of btree_nodes, that fill a block.  Some are
   * internal nodes, as such their values are a __le64 pointing to other
   * nodes.  Leaf nodes can store data of any reasonable size (ie. much
   * smaller than the block size).  The nodes consist of the header,
   * followed by an array of keys, followed by an array of values.  We have
   * to binary search on the keys so they're all held together to help the
   * cpu cache.
   *
   * Space maps have 2 btrees:
   *
   * - One maps a uint64_t onto a struct index_entry.  Which points to a
   *   bitmap block, and has some details about how many free entries there
   *   are etc.
   *
   * - The bitmap blocks have a header (for the checksum).  Then the rest
   *   of the block is pairs of bits.  With the meaning being:
   *
   *   0 - ref count is 0
   *   1 - ref count is 1
   *   2 - ref count is 2
   *   3 - ref count is higher than 2
   *
   * - If the count is higher than 2 then the ref count is entered in a
   *   second btree that directly maps the block_address to a uint32_t ref
   *   count.
   *
   * The space map metadata variant doesn't have a bitmaps btree.  Instead
   * it has one single blocks worth of index_entries.  This avoids
   * recursive issues with the bitmap btree needing to allocate space in
   * order to insert.  With a small data block size such as 64k the
   * metadata support data devices that are hundreds of terrabytes.
   *
   * The space maps allocate space linearly from front to back.  Space that
   * is freed in a transaction is never recycled within that transaction.
   * To try and avoid fragmenting _free_ space the allocator always goes
   * back and fills in gaps.
   *
   * All metadata io is in THIN_METADATA_BLOCK_SIZE sized/aligned chunks
   * from the block manager.
   *--------------------------------------------------------------------------*/
  
  #define DM_MSG_PREFIX   "thin metadata"
  
  #define THIN_SUPERBLOCK_MAGIC 27022010
  #define THIN_SUPERBLOCK_LOCATION 0
07f2b6e03   Mike Snitzer   dm thin: ensure u...
79
  #define THIN_VERSION 2
991d9fa02   Joe Thornber   dm: add thin prov...
80
  #define SECTOR_TO_BLOCK_SHIFT 3
8c971178a   Joe Thornber   dm thin metadata:...
81
  /*
490ae017f   Dennis Yang   dm thin metadata:...
82
   * For btree insert:
8c971178a   Joe Thornber   dm thin metadata:...
83
84
   *  3 for btree insert +
   *  2 for btree lookup used within space map
490ae017f   Dennis Yang   dm thin metadata:...
85
86
87
   * For btree remove:
   *  2 for shadow spine +
   *  4 for rebalance 3 child node
8c971178a   Joe Thornber   dm thin metadata:...
88
   */
490ae017f   Dennis Yang   dm thin metadata:...
89
  #define THIN_MAX_CONCURRENT_LOCKS 6
8c971178a   Joe Thornber   dm thin metadata:...
90

991d9fa02   Joe Thornber   dm: add thin prov...
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
  /* This should be plenty */
  #define SPACE_MAP_ROOT_SIZE 128
  
  /*
   * Little endian on-disk superblock and device details.
   */
  struct thin_disk_superblock {
  	__le32 csum;	/* Checksum of superblock except for this field. */
  	__le32 flags;
  	__le64 blocknr;	/* This block number, dm_block_t. */
  
  	__u8 uuid[16];
  	__le64 magic;
  	__le32 version;
  	__le32 time;
  
  	__le64 trans_id;
  
  	/*
  	 * Root held by userspace transactions.
  	 */
  	__le64 held_root;
  
  	__u8 data_space_map_root[SPACE_MAP_ROOT_SIZE];
  	__u8 metadata_space_map_root[SPACE_MAP_ROOT_SIZE];
  
  	/*
  	 * 2-level btree mapping (dev_id, (dev block, time)) -> data block
  	 */
  	__le64 data_mapping_root;
  
  	/*
  	 * Device detail root mapping dev_id -> device_details
  	 */
  	__le64 device_details_root;
  
  	__le32 data_block_size;		/* In 512-byte sectors. */
  
  	__le32 metadata_block_size;	/* In 512-byte sectors. */
  	__le64 metadata_nr_blocks;
  
  	__le32 compat_flags;
  	__le32 compat_ro_flags;
  	__le32 incompat_flags;
  } __packed;
  
  struct disk_device_details {
  	__le64 mapped_blocks;
  	__le64 transaction_id;		/* When created. */
  	__le32 creation_time;
  	__le32 snapshotted_time;
  } __packed;
  
  struct dm_pool_metadata {
  	struct hlist_node hash;
  
  	struct block_device *bdev;
  	struct dm_block_manager *bm;
  	struct dm_space_map *metadata_sm;
  	struct dm_space_map *data_sm;
  	struct dm_transaction_manager *tm;
  	struct dm_transaction_manager *nb_tm;
  
  	/*
  	 * Two-level btree.
  	 * First level holds thin_dev_t.
  	 * Second level holds mappings.
  	 */
  	struct dm_btree_info info;
  
  	/*
  	 * Non-blocking version of the above.
  	 */
  	struct dm_btree_info nb_info;
  
  	/*
  	 * Just the top level for deleting whole devices.
  	 */
  	struct dm_btree_info tl_info;
  
  	/*
  	 * Just the bottom level for creating new devices.
  	 */
  	struct dm_btree_info bl_info;
  
  	/*
  	 * Describes the device details btree.
  	 */
  	struct dm_btree_info details_info;
  
  	struct rw_semaphore root_lock;
  	uint32_t time;
991d9fa02   Joe Thornber   dm: add thin prov...
183
184
185
186
187
188
  	dm_block_t root;
  	dm_block_t details_root;
  	struct list_head thin_devices;
  	uint64_t trans_id;
  	unsigned long flags;
  	sector_t data_block_size;
da105ed5f   Joe Thornber   dm thin metadata:...
189
190
  
  	/*
d2688d36c   Nikos Tsironis   dm thin metadata:...
191
192
193
194
195
196
197
198
199
  	 * Pre-commit callback.
  	 *
  	 * This allows the thin provisioning target to run a callback before
  	 * the metadata are committed.
  	 */
  	dm_pool_pre_commit_fn pre_commit_fn;
  	void *pre_commit_context;
  
  	/*
3ab918281   Joe Thornber   dm thin metadata:...
200
201
202
203
204
205
  	 * We reserve a section of the metadata for commit overhead.
  	 * All reported space does *not* include this.
  	 */
  	dm_block_t metadata_reserve;
  
  	/*
da105ed5f   Joe Thornber   dm thin metadata:...
206
207
208
209
210
  	 * Set if a transaction has to be aborted but the attempt to roll back
  	 * to the previous (good) transaction failed.  The only pool metadata
  	 * operation possible in this state is the closing of the device.
  	 */
  	bool fail_io:1;
5a32083d0   Joe Thornber   dm: take care to ...
211
212
  
  	/*
873f258be   Mike Snitzer   dm thin metadata:...
213
214
215
216
217
218
219
  	 * Set once a thin-pool has been accessed through one of the interfaces
  	 * that imply the pool is in-service (e.g. thin devices created/deleted,
  	 * thin-pool message, metadata snapshots, etc).
  	 */
  	bool in_service:1;
  
  	/*
5a32083d0   Joe Thornber   dm: take care to ...
220
221
222
223
224
  	 * Reading the space map roots can fail, so we read it into these
  	 * buffers before the superblock is locked and updated.
  	 */
  	__u8 data_space_map_root[SPACE_MAP_ROOT_SIZE];
  	__u8 metadata_space_map_root[SPACE_MAP_ROOT_SIZE];
991d9fa02   Joe Thornber   dm: add thin prov...
225
226
227
228
229
230
231
232
  };
  
  struct dm_thin_device {
  	struct list_head list;
  	struct dm_pool_metadata *pmd;
  	dm_thin_id id;
  
  	int open_count;
da105ed5f   Joe Thornber   dm thin metadata:...
233
234
  	bool changed:1;
  	bool aborted_with_changes:1;
991d9fa02   Joe Thornber   dm: add thin prov...
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
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
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
  	uint64_t mapped_blocks;
  	uint64_t transaction_id;
  	uint32_t creation_time;
  	uint32_t snapshotted_time;
  };
  
  /*----------------------------------------------------------------
   * superblock validator
   *--------------------------------------------------------------*/
  
  #define SUPERBLOCK_CSUM_XOR 160774
  
  static void sb_prepare_for_write(struct dm_block_validator *v,
  				 struct dm_block *b,
  				 size_t block_size)
  {
  	struct thin_disk_superblock *disk_super = dm_block_data(b);
  
  	disk_super->blocknr = cpu_to_le64(dm_block_location(b));
  	disk_super->csum = cpu_to_le32(dm_bm_checksum(&disk_super->flags,
  						      block_size - sizeof(__le32),
  						      SUPERBLOCK_CSUM_XOR));
  }
  
  static int sb_check(struct dm_block_validator *v,
  		    struct dm_block *b,
  		    size_t block_size)
  {
  	struct thin_disk_superblock *disk_super = dm_block_data(b);
  	__le32 csum_le;
  
  	if (dm_block_location(b) != le64_to_cpu(disk_super->blocknr)) {
  		DMERR("sb_check failed: blocknr %llu: "
  		      "wanted %llu", le64_to_cpu(disk_super->blocknr),
  		      (unsigned long long)dm_block_location(b));
  		return -ENOTBLK;
  	}
  
  	if (le64_to_cpu(disk_super->magic) != THIN_SUPERBLOCK_MAGIC) {
  		DMERR("sb_check failed: magic %llu: "
  		      "wanted %llu", le64_to_cpu(disk_super->magic),
  		      (unsigned long long)THIN_SUPERBLOCK_MAGIC);
  		return -EILSEQ;
  	}
  
  	csum_le = cpu_to_le32(dm_bm_checksum(&disk_super->flags,
  					     block_size - sizeof(__le32),
  					     SUPERBLOCK_CSUM_XOR));
  	if (csum_le != disk_super->csum) {
  		DMERR("sb_check failed: csum %u: wanted %u",
  		      le32_to_cpu(csum_le), le32_to_cpu(disk_super->csum));
  		return -EILSEQ;
  	}
  
  	return 0;
  }
  
  static struct dm_block_validator sb_validator = {
  	.name = "superblock",
  	.prepare_for_write = sb_prepare_for_write,
  	.check = sb_check
  };
  
  /*----------------------------------------------------------------
   * Methods for the btree value types
   *--------------------------------------------------------------*/
  
  static uint64_t pack_block_time(dm_block_t b, uint32_t t)
  {
  	return (b << 24) | t;
  }
  
  static void unpack_block_time(uint64_t v, dm_block_t *b, uint32_t *t)
  {
  	*b = v >> 24;
  	*t = v & ((1 << 24) - 1);
  }
018cede93   Mike Snitzer   dm persistent dat...
312
  static void data_block_inc(void *context, const void *value_le)
991d9fa02   Joe Thornber   dm: add thin prov...
313
314
315
316
317
318
319
320
321
322
  {
  	struct dm_space_map *sm = context;
  	__le64 v_le;
  	uint64_t b;
  	uint32_t t;
  
  	memcpy(&v_le, value_le, sizeof(v_le));
  	unpack_block_time(le64_to_cpu(v_le), &b, &t);
  	dm_sm_inc_block(sm, b);
  }
018cede93   Mike Snitzer   dm persistent dat...
323
  static void data_block_dec(void *context, const void *value_le)
991d9fa02   Joe Thornber   dm: add thin prov...
324
325
326
327
328
329
330
331
332
333
  {
  	struct dm_space_map *sm = context;
  	__le64 v_le;
  	uint64_t b;
  	uint32_t t;
  
  	memcpy(&v_le, value_le, sizeof(v_le));
  	unpack_block_time(le64_to_cpu(v_le), &b, &t);
  	dm_sm_dec_block(sm, b);
  }
018cede93   Mike Snitzer   dm persistent dat...
334
  static int data_block_equal(void *context, const void *value1_le, const void *value2_le)
991d9fa02   Joe Thornber   dm: add thin prov...
335
336
337
338
339
340
341
342
343
344
345
346
  {
  	__le64 v1_le, v2_le;
  	uint64_t b1, b2;
  	uint32_t t;
  
  	memcpy(&v1_le, value1_le, sizeof(v1_le));
  	memcpy(&v2_le, value2_le, sizeof(v2_le));
  	unpack_block_time(le64_to_cpu(v1_le), &b1, &t);
  	unpack_block_time(le64_to_cpu(v2_le), &b2, &t);
  
  	return b1 == b2;
  }
018cede93   Mike Snitzer   dm persistent dat...
347
  static void subtree_inc(void *context, const void *value)
991d9fa02   Joe Thornber   dm: add thin prov...
348
349
350
351
352
353
354
355
356
  {
  	struct dm_btree_info *info = context;
  	__le64 root_le;
  	uint64_t root;
  
  	memcpy(&root_le, value, sizeof(root_le));
  	root = le64_to_cpu(root_le);
  	dm_tm_inc(info->tm, root);
  }
018cede93   Mike Snitzer   dm persistent dat...
357
  static void subtree_dec(void *context, const void *value)
991d9fa02   Joe Thornber   dm: add thin prov...
358
359
360
361
362
363
364
365
  {
  	struct dm_btree_info *info = context;
  	__le64 root_le;
  	uint64_t root;
  
  	memcpy(&root_le, value, sizeof(root_le));
  	root = le64_to_cpu(root_le);
  	if (dm_btree_del(info, root))
29f929b52   Mike Snitzer   dm thin metadata:...
366
  		DMERR("btree delete failed");
991d9fa02   Joe Thornber   dm: add thin prov...
367
  }
018cede93   Mike Snitzer   dm persistent dat...
368
  static int subtree_equal(void *context, const void *value1_le, const void *value2_le)
991d9fa02   Joe Thornber   dm: add thin prov...
369
370
371
372
373
374
375
376
377
  {
  	__le64 v1_le, v2_le;
  	memcpy(&v1_le, value1_le, sizeof(v1_le));
  	memcpy(&v2_le, value2_le, sizeof(v2_le));
  
  	return v1_le == v2_le;
  }
  
  /*----------------------------------------------------------------*/
873f258be   Mike Snitzer   dm thin metadata:...
378
379
380
381
  /*
   * Variant that is used for in-core only changes or code that
   * shouldn't put the pool in service on its own (e.g. commit).
   */
1426201af   Mike Snitzer   dm thin metadata:...
382
  static inline void pmd_write_lock_in_core(struct dm_pool_metadata *pmd)
6a1b1ddc6   Mike Snitzer   dm thin metadata:...
383
384
385
386
  	__acquires(pmd->root_lock)
  {
  	down_write(&pmd->root_lock);
  }
6a1b1ddc6   Mike Snitzer   dm thin metadata:...
387
388
389
  
  static inline void pmd_write_lock(struct dm_pool_metadata *pmd)
  {
1426201af   Mike Snitzer   dm thin metadata:...
390
  	pmd_write_lock_in_core(pmd);
873f258be   Mike Snitzer   dm thin metadata:...
391
392
  	if (unlikely(!pmd->in_service))
  		pmd->in_service = true;
6a1b1ddc6   Mike Snitzer   dm thin metadata:...
393
394
395
396
397
398
399
400
401
  }
  
  static inline void pmd_write_unlock(struct dm_pool_metadata *pmd)
  	__releases(pmd->root_lock)
  {
  	up_write(&pmd->root_lock);
  }
  
  /*----------------------------------------------------------------*/
259711920   Joe Thornber   dm thin metadata:...
402
403
404
405
406
407
408
409
410
411
412
413
414
  static int superblock_lock_zero(struct dm_pool_metadata *pmd,
  				struct dm_block **sblock)
  {
  	return dm_bm_write_lock_zero(pmd->bm, THIN_SUPERBLOCK_LOCATION,
  				     &sb_validator, sblock);
  }
  
  static int superblock_lock(struct dm_pool_metadata *pmd,
  			   struct dm_block **sblock)
  {
  	return dm_bm_write_lock(pmd->bm, THIN_SUPERBLOCK_LOCATION,
  				&sb_validator, sblock);
  }
332627db0   Joe Thornber   dm thin metadata:...
415
  static int __superblock_all_zeroes(struct dm_block_manager *bm, int *result)
991d9fa02   Joe Thornber   dm: add thin prov...
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
  {
  	int r;
  	unsigned i;
  	struct dm_block *b;
  	__le64 *data_le, zero = cpu_to_le64(0);
  	unsigned block_size = dm_bm_block_size(bm) / sizeof(__le64);
  
  	/*
  	 * We can't use a validator here - it may be all zeroes.
  	 */
  	r = dm_bm_read_lock(bm, THIN_SUPERBLOCK_LOCATION, NULL, &b);
  	if (r)
  		return r;
  
  	data_le = dm_block_data(b);
  	*result = 1;
  	for (i = 0; i < block_size; i++) {
  		if (data_le[i] != zero) {
  			*result = 0;
  			break;
  		}
  	}
4c7da06f5   Mikulas Patocka   dm persistent dat...
438
439
440
  	dm_bm_unlock(b);
  
  	return 0;
991d9fa02   Joe Thornber   dm: add thin prov...
441
  }
41675aea3   Joe Thornber   dm thin metadata:...
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
  static void __setup_btree_details(struct dm_pool_metadata *pmd)
  {
  	pmd->info.tm = pmd->tm;
  	pmd->info.levels = 2;
  	pmd->info.value_type.context = pmd->data_sm;
  	pmd->info.value_type.size = sizeof(__le64);
  	pmd->info.value_type.inc = data_block_inc;
  	pmd->info.value_type.dec = data_block_dec;
  	pmd->info.value_type.equal = data_block_equal;
  
  	memcpy(&pmd->nb_info, &pmd->info, sizeof(pmd->nb_info));
  	pmd->nb_info.tm = pmd->nb_tm;
  
  	pmd->tl_info.tm = pmd->tm;
  	pmd->tl_info.levels = 1;
e3cbf9451   Joe Thornber   dm persistent dat...
457
  	pmd->tl_info.value_type.context = &pmd->bl_info;
41675aea3   Joe Thornber   dm thin metadata:...
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
  	pmd->tl_info.value_type.size = sizeof(__le64);
  	pmd->tl_info.value_type.inc = subtree_inc;
  	pmd->tl_info.value_type.dec = subtree_dec;
  	pmd->tl_info.value_type.equal = subtree_equal;
  
  	pmd->bl_info.tm = pmd->tm;
  	pmd->bl_info.levels = 1;
  	pmd->bl_info.value_type.context = pmd->data_sm;
  	pmd->bl_info.value_type.size = sizeof(__le64);
  	pmd->bl_info.value_type.inc = data_block_inc;
  	pmd->bl_info.value_type.dec = data_block_dec;
  	pmd->bl_info.value_type.equal = data_block_equal;
  
  	pmd->details_info.tm = pmd->tm;
  	pmd->details_info.levels = 1;
  	pmd->details_info.value_type.context = NULL;
  	pmd->details_info.value_type.size = sizeof(struct disk_device_details);
  	pmd->details_info.value_type.inc = NULL;
  	pmd->details_info.value_type.dec = NULL;
  	pmd->details_info.value_type.equal = NULL;
  }
5a32083d0   Joe Thornber   dm: take care to ...
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
  static int save_sm_roots(struct dm_pool_metadata *pmd)
  {
  	int r;
  	size_t len;
  
  	r = dm_sm_root_size(pmd->metadata_sm, &len);
  	if (r < 0)
  		return r;
  
  	r = dm_sm_copy_root(pmd->metadata_sm, &pmd->metadata_space_map_root, len);
  	if (r < 0)
  		return r;
  
  	r = dm_sm_root_size(pmd->data_sm, &len);
  	if (r < 0)
  		return r;
  
  	return dm_sm_copy_root(pmd->data_sm, &pmd->data_space_map_root, len);
  }
  
  static void copy_sm_roots(struct dm_pool_metadata *pmd,
  			  struct thin_disk_superblock *disk)
  {
  	memcpy(&disk->metadata_space_map_root,
  	       &pmd->metadata_space_map_root,
  	       sizeof(pmd->metadata_space_map_root));
  
  	memcpy(&disk->data_space_map_root,
  	       &pmd->data_space_map_root,
  	       sizeof(pmd->data_space_map_root));
  }
9cb6653f9   Joe Thornber   dm thin metadata:...
510
511
512
513
514
515
516
517
518
  static int __write_initial_superblock(struct dm_pool_metadata *pmd)
  {
  	int r;
  	struct dm_block *sblock;
  	struct thin_disk_superblock *disk_super;
  	sector_t bdev_size = i_size_read(pmd->bdev->bd_inode) >> SECTOR_SHIFT;
  
  	if (bdev_size > THIN_METADATA_MAX_SECTORS)
  		bdev_size = THIN_METADATA_MAX_SECTORS;
5a32083d0   Joe Thornber   dm: take care to ...
519
  	r = dm_sm_commit(pmd->data_sm);
10d2a9ff7   Joe Thornber   dm thin metadata:...
520
521
  	if (r < 0)
  		return r;
91bcdb92d   Joe Thornber   dm thin metadata:...
522
  	r = dm_tm_pre_commit(pmd->tm);
10d2a9ff7   Joe Thornber   dm thin metadata:...
523
524
  	if (r < 0)
  		return r;
91bcdb92d   Joe Thornber   dm thin metadata:...
525
  	r = save_sm_roots(pmd);
10d2a9ff7   Joe Thornber   dm thin metadata:...
526
527
  	if (r < 0)
  		return r;
9cb6653f9   Joe Thornber   dm thin metadata:...
528
529
530
531
532
  	r = superblock_lock_zero(pmd, &sblock);
  	if (r)
  		return r;
  
  	disk_super = dm_block_data(sblock);
10d2a9ff7   Joe Thornber   dm thin metadata:...
533
  	disk_super->flags = 0;
583ceee2e   Joe Thornber   dm thin metadata:...
534
  	memset(disk_super->uuid, 0, sizeof(disk_super->uuid));
9cb6653f9   Joe Thornber   dm thin metadata:...
535
536
537
  	disk_super->magic = cpu_to_le64(THIN_SUPERBLOCK_MAGIC);
  	disk_super->version = cpu_to_le32(THIN_VERSION);
  	disk_super->time = 0;
10d2a9ff7   Joe Thornber   dm thin metadata:...
538
539
  	disk_super->trans_id = 0;
  	disk_super->held_root = 0;
5a32083d0   Joe Thornber   dm: take care to ...
540
  	copy_sm_roots(pmd, disk_super);
10d2a9ff7   Joe Thornber   dm thin metadata:...
541
542
543
  
  	disk_super->data_mapping_root = cpu_to_le64(pmd->root);
  	disk_super->device_details_root = cpu_to_le64(pmd->details_root);
7d48935ef   Mike Snitzer   dm thin: allow me...
544
  	disk_super->metadata_block_size = cpu_to_le32(THIN_METADATA_BLOCK_SIZE);
9cb6653f9   Joe Thornber   dm thin metadata:...
545
546
  	disk_super->metadata_nr_blocks = cpu_to_le64(bdev_size >> SECTOR_TO_BLOCK_SHIFT);
  	disk_super->data_block_size = cpu_to_le32(pmd->data_block_size);
270938bac   Joe Thornber   dm thin metadata:...
547
  	return dm_tm_commit(pmd->tm, sblock);
9cb6653f9   Joe Thornber   dm thin metadata:...
548
  }
a97e5e6fd   Joe Thornber   dm thin metadata:...
549
  static int __format_metadata(struct dm_pool_metadata *pmd)
991d9fa02   Joe Thornber   dm: add thin prov...
550
551
  {
  	int r;
384ef0e62   Joe Thornber   dm persistent dat...
552

e4d2205cd   Joe Thornber   dm thin metadata:...
553
554
555
556
557
558
  	r = dm_tm_create_with_sm(pmd->bm, THIN_SUPERBLOCK_LOCATION,
  				 &pmd->tm, &pmd->metadata_sm);
  	if (r < 0) {
  		DMERR("tm_create_with_sm failed");
  		return r;
  	}
991d9fa02   Joe Thornber   dm: add thin prov...
559

a97e5e6fd   Joe Thornber   dm thin metadata:...
560
  	pmd->data_sm = dm_sm_disk_create(pmd->tm, 0);
e4d2205cd   Joe Thornber   dm thin metadata:...
561
562
563
  	if (IS_ERR(pmd->data_sm)) {
  		DMERR("sm_disk_create failed");
  		r = PTR_ERR(pmd->data_sm);
0fa5b17b0   Joe Thornber   dm thin metadata:...
564
  		goto bad_cleanup_tm;
991d9fa02   Joe Thornber   dm: add thin prov...
565
  	}
d6332814e   Joe Thornber   dm thin metadata:...
566
  	pmd->nb_tm = dm_tm_create_non_blocking_clone(pmd->tm);
991d9fa02   Joe Thornber   dm: add thin prov...
567
  	if (!pmd->nb_tm) {
0fa5b17b0   Joe Thornber   dm thin metadata:...
568
  		DMERR("could not create non-blocking clone tm");
991d9fa02   Joe Thornber   dm: add thin prov...
569
  		r = -ENOMEM;
0fa5b17b0   Joe Thornber   dm thin metadata:...
570
  		goto bad_cleanup_data_sm;
991d9fa02   Joe Thornber   dm: add thin prov...
571
  	}
41675aea3   Joe Thornber   dm thin metadata:...
572
  	__setup_btree_details(pmd);
991d9fa02   Joe Thornber   dm: add thin prov...
573

9cb6653f9   Joe Thornber   dm thin metadata:...
574
575
  	r = dm_btree_empty(&pmd->info, &pmd->root);
  	if (r < 0)
0fa5b17b0   Joe Thornber   dm thin metadata:...
576
  		goto bad_cleanup_nb_tm;
9cb6653f9   Joe Thornber   dm thin metadata:...
577
578
579
580
  
  	r = dm_btree_empty(&pmd->details_info, &pmd->details_root);
  	if (r < 0) {
  		DMERR("couldn't create devices root");
0fa5b17b0   Joe Thornber   dm thin metadata:...
581
  		goto bad_cleanup_nb_tm;
9cb6653f9   Joe Thornber   dm thin metadata:...
582
583
584
585
  	}
  
  	r = __write_initial_superblock(pmd);
  	if (r)
0fa5b17b0   Joe Thornber   dm thin metadata:...
586
  		goto bad_cleanup_nb_tm;
9cb6653f9   Joe Thornber   dm thin metadata:...
587

991d9fa02   Joe Thornber   dm: add thin prov...
588
  	return 0;
0fa5b17b0   Joe Thornber   dm thin metadata:...
589
590
591
  bad_cleanup_nb_tm:
  	dm_tm_destroy(pmd->nb_tm);
  bad_cleanup_data_sm:
d6332814e   Joe Thornber   dm thin metadata:...
592
  	dm_sm_destroy(pmd->data_sm);
0fa5b17b0   Joe Thornber   dm thin metadata:...
593
  bad_cleanup_tm:
d6332814e   Joe Thornber   dm thin metadata:...
594
595
  	dm_tm_destroy(pmd->tm);
  	dm_sm_destroy(pmd->metadata_sm);
991d9fa02   Joe Thornber   dm: add thin prov...
596
597
598
  
  	return r;
  }
d73ec5253   Mike Snitzer   dm thin metadata:...
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
  static int __check_incompat_features(struct thin_disk_superblock *disk_super,
  				     struct dm_pool_metadata *pmd)
  {
  	uint32_t features;
  
  	features = le32_to_cpu(disk_super->incompat_flags) & ~THIN_FEATURE_INCOMPAT_SUPP;
  	if (features) {
  		DMERR("could not access metadata due to unsupported optional features (%lx).",
  		      (unsigned long)features);
  		return -EINVAL;
  	}
  
  	/*
  	 * Check for read-only metadata to skip the following RDWR checks.
  	 */
  	if (get_disk_ro(pmd->bdev->bd_disk))
  		return 0;
  
  	features = le32_to_cpu(disk_super->compat_ro_flags) & ~THIN_FEATURE_COMPAT_RO_SUPP;
  	if (features) {
  		DMERR("could not access metadata RDWR due to unsupported optional features (%lx).",
  		      (unsigned long)features);
  		return -EINVAL;
  	}
  
  	return 0;
  }
e4d2205cd   Joe Thornber   dm thin metadata:...
626
627
628
629
630
631
632
633
634
635
636
637
638
639
  static int __open_metadata(struct dm_pool_metadata *pmd)
  {
  	int r;
  	struct dm_block *sblock;
  	struct thin_disk_superblock *disk_super;
  
  	r = dm_bm_read_lock(pmd->bm, THIN_SUPERBLOCK_LOCATION,
  			    &sb_validator, &sblock);
  	if (r < 0) {
  		DMERR("couldn't read superblock");
  		return r;
  	}
  
  	disk_super = dm_block_data(sblock);
d73ec5253   Mike Snitzer   dm thin metadata:...
640

9aec8629e   Mike Snitzer   dm thin metadata:...
641
642
643
644
645
646
647
648
  	/* Verify the data block size hasn't changed */
  	if (le32_to_cpu(disk_super->data_block_size) != pmd->data_block_size) {
  		DMERR("changing the data block size (from %u to %llu) is not supported",
  		      le32_to_cpu(disk_super->data_block_size),
  		      (unsigned long long)pmd->data_block_size);
  		r = -EINVAL;
  		goto bad_unlock_sblock;
  	}
d73ec5253   Mike Snitzer   dm thin metadata:...
649
  	r = __check_incompat_features(disk_super, pmd);
0fa5b17b0   Joe Thornber   dm thin metadata:...
650
651
  	if (r < 0)
  		goto bad_unlock_sblock;
d73ec5253   Mike Snitzer   dm thin metadata:...
652

e4d2205cd   Joe Thornber   dm thin metadata:...
653
654
655
656
657
658
  	r = dm_tm_open_with_sm(pmd->bm, THIN_SUPERBLOCK_LOCATION,
  			       disk_super->metadata_space_map_root,
  			       sizeof(disk_super->metadata_space_map_root),
  			       &pmd->tm, &pmd->metadata_sm);
  	if (r < 0) {
  		DMERR("tm_open_with_sm failed");
0fa5b17b0   Joe Thornber   dm thin metadata:...
659
  		goto bad_unlock_sblock;
e4d2205cd   Joe Thornber   dm thin metadata:...
660
661
662
663
664
665
  	}
  
  	pmd->data_sm = dm_sm_disk_open(pmd->tm, disk_super->data_space_map_root,
  				       sizeof(disk_super->data_space_map_root));
  	if (IS_ERR(pmd->data_sm)) {
  		DMERR("sm_disk_open failed");
e4d2205cd   Joe Thornber   dm thin metadata:...
666
  		r = PTR_ERR(pmd->data_sm);
0fa5b17b0   Joe Thornber   dm thin metadata:...
667
  		goto bad_cleanup_tm;
e4d2205cd   Joe Thornber   dm thin metadata:...
668
  	}
e4d2205cd   Joe Thornber   dm thin metadata:...
669
670
  	pmd->nb_tm = dm_tm_create_non_blocking_clone(pmd->tm);
  	if (!pmd->nb_tm) {
0fa5b17b0   Joe Thornber   dm thin metadata:...
671
  		DMERR("could not create non-blocking clone tm");
e4d2205cd   Joe Thornber   dm thin metadata:...
672
  		r = -ENOMEM;
0fa5b17b0   Joe Thornber   dm thin metadata:...
673
  		goto bad_cleanup_data_sm;
e4d2205cd   Joe Thornber   dm thin metadata:...
674
675
676
  	}
  
  	__setup_btree_details(pmd);
4c7da06f5   Mikulas Patocka   dm persistent dat...
677
678
679
  	dm_bm_unlock(sblock);
  
  	return 0;
e4d2205cd   Joe Thornber   dm thin metadata:...
680

0fa5b17b0   Joe Thornber   dm thin metadata:...
681
  bad_cleanup_data_sm:
e4d2205cd   Joe Thornber   dm thin metadata:...
682
  	dm_sm_destroy(pmd->data_sm);
0fa5b17b0   Joe Thornber   dm thin metadata:...
683
  bad_cleanup_tm:
e4d2205cd   Joe Thornber   dm thin metadata:...
684
685
  	dm_tm_destroy(pmd->tm);
  	dm_sm_destroy(pmd->metadata_sm);
0fa5b17b0   Joe Thornber   dm thin metadata:...
686
687
  bad_unlock_sblock:
  	dm_bm_unlock(sblock);
e4d2205cd   Joe Thornber   dm thin metadata:...
688
689
690
  
  	return r;
  }
66b1edc05   Joe Thornber   dm thin metadata:...
691
  static int __open_or_format_metadata(struct dm_pool_metadata *pmd, bool format_device)
e4d2205cd   Joe Thornber   dm thin metadata:...
692
  {
8801e0694   Joe Thornber   dm thin metadata:...
693
  	int r, unformatted;
237074c0a   Joe Thornber   dm thin metadata:...
694

8801e0694   Joe Thornber   dm thin metadata:...
695
  	r = __superblock_all_zeroes(pmd->bm, &unformatted);
237074c0a   Joe Thornber   dm thin metadata:...
696
697
  	if (r)
  		return r;
8801e0694   Joe Thornber   dm thin metadata:...
698
  	if (unformatted)
66b1edc05   Joe Thornber   dm thin metadata:...
699
700
701
  		return format_device ? __format_metadata(pmd) : -EPERM;
  
  	return __open_metadata(pmd);
e4d2205cd   Joe Thornber   dm thin metadata:...
702
  }
66b1edc05   Joe Thornber   dm thin metadata:...
703
  static int __create_persistent_data_objects(struct dm_pool_metadata *pmd, bool format_device)
332627db0   Joe Thornber   dm thin metadata:...
704
705
  {
  	int r;
7d48935ef   Mike Snitzer   dm thin: allow me...
706
  	pmd->bm = dm_block_manager_create(pmd->bdev, THIN_METADATA_BLOCK_SIZE << SECTOR_SHIFT,
332627db0   Joe Thornber   dm thin metadata:...
707
708
709
710
711
  					  THIN_MAX_CONCURRENT_LOCKS);
  	if (IS_ERR(pmd->bm)) {
  		DMERR("could not create block manager");
  		return PTR_ERR(pmd->bm);
  	}
66b1edc05   Joe Thornber   dm thin metadata:...
712
  	r = __open_or_format_metadata(pmd, format_device);
332627db0   Joe Thornber   dm thin metadata:...
713
714
715
716
717
  	if (r)
  		dm_block_manager_destroy(pmd->bm);
  
  	return r;
  }
f9dd9352b   Joe Thornber   dm thin metadata:...
718
719
720
721
722
723
724
725
  static void __destroy_persistent_data_objects(struct dm_pool_metadata *pmd)
  {
  	dm_sm_destroy(pmd->data_sm);
  	dm_sm_destroy(pmd->metadata_sm);
  	dm_tm_destroy(pmd->nb_tm);
  	dm_tm_destroy(pmd->tm);
  	dm_block_manager_destroy(pmd->bm);
  }
991d9fa02   Joe Thornber   dm: add thin prov...
726
727
728
  static int __begin_transaction(struct dm_pool_metadata *pmd)
  {
  	int r;
991d9fa02   Joe Thornber   dm: add thin prov...
729
730
731
732
  	struct thin_disk_superblock *disk_super;
  	struct dm_block *sblock;
  
  	/*
991d9fa02   Joe Thornber   dm: add thin prov...
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
  	 * We re-read the superblock every time.  Shouldn't need to do this
  	 * really.
  	 */
  	r = dm_bm_read_lock(pmd->bm, THIN_SUPERBLOCK_LOCATION,
  			    &sb_validator, &sblock);
  	if (r)
  		return r;
  
  	disk_super = dm_block_data(sblock);
  	pmd->time = le32_to_cpu(disk_super->time);
  	pmd->root = le64_to_cpu(disk_super->data_mapping_root);
  	pmd->details_root = le64_to_cpu(disk_super->device_details_root);
  	pmd->trans_id = le64_to_cpu(disk_super->trans_id);
  	pmd->flags = le32_to_cpu(disk_super->flags);
  	pmd->data_block_size = le32_to_cpu(disk_super->data_block_size);
991d9fa02   Joe Thornber   dm: add thin prov...
748
  	dm_bm_unlock(sblock);
d73ec5253   Mike Snitzer   dm thin metadata:...
749
  	return 0;
991d9fa02   Joe Thornber   dm: add thin prov...
750
751
752
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
  }
  
  static int __write_changed_details(struct dm_pool_metadata *pmd)
  {
  	int r;
  	struct dm_thin_device *td, *tmp;
  	struct disk_device_details details;
  	uint64_t key;
  
  	list_for_each_entry_safe(td, tmp, &pmd->thin_devices, list) {
  		if (!td->changed)
  			continue;
  
  		key = td->id;
  
  		details.mapped_blocks = cpu_to_le64(td->mapped_blocks);
  		details.transaction_id = cpu_to_le64(td->transaction_id);
  		details.creation_time = cpu_to_le32(td->creation_time);
  		details.snapshotted_time = cpu_to_le32(td->snapshotted_time);
  		__dm_bless_for_disk(&details);
  
  		r = dm_btree_insert(&pmd->details_info, pmd->details_root,
  				    &key, &details, &pmd->details_root);
  		if (r)
  			return r;
  
  		if (td->open_count)
  			td->changed = 0;
  		else {
  			list_del(&td->list);
  			kfree(td);
  		}
991d9fa02   Joe Thornber   dm: add thin prov...
782
783
784
785
786
787
788
  	}
  
  	return 0;
  }
  
  static int __commit_transaction(struct dm_pool_metadata *pmd)
  {
991d9fa02   Joe Thornber   dm: add thin prov...
789
  	int r;
991d9fa02   Joe Thornber   dm: add thin prov...
790
791
792
793
794
795
796
  	struct thin_disk_superblock *disk_super;
  	struct dm_block *sblock;
  
  	/*
  	 * We need to know if the thin_disk_superblock exceeds a 512-byte sector.
  	 */
  	BUILD_BUG_ON(sizeof(struct thin_disk_superblock) > 512);
1426201af   Mike Snitzer   dm thin metadata:...
797
  	BUG_ON(!rwsem_is_locked(&pmd->root_lock));
991d9fa02   Joe Thornber   dm: add thin prov...
798

873f258be   Mike Snitzer   dm thin metadata:...
799
800
  	if (unlikely(!pmd->in_service))
  		return 0;
d2688d36c   Nikos Tsironis   dm thin metadata:...
801
802
803
804
805
806
807
  	if (pmd->pre_commit_fn) {
  		r = pmd->pre_commit_fn(pmd->pre_commit_context);
  		if (r < 0) {
  			DMERR("pre-commit callback failed");
  			return r;
  		}
  	}
991d9fa02   Joe Thornber   dm: add thin prov...
808
809
  	r = __write_changed_details(pmd);
  	if (r < 0)
d973ac196   Joe Thornber   dm thin metadata:...
810
  		return r;
991d9fa02   Joe Thornber   dm: add thin prov...
811

991d9fa02   Joe Thornber   dm: add thin prov...
812
813
  	r = dm_sm_commit(pmd->data_sm);
  	if (r < 0)
d973ac196   Joe Thornber   dm thin metadata:...
814
  		return r;
991d9fa02   Joe Thornber   dm: add thin prov...
815
816
817
  
  	r = dm_tm_pre_commit(pmd->tm);
  	if (r < 0)
d973ac196   Joe Thornber   dm thin metadata:...
818
  		return r;
991d9fa02   Joe Thornber   dm: add thin prov...
819

5a32083d0   Joe Thornber   dm: take care to ...
820
821
822
  	r = save_sm_roots(pmd);
  	if (r < 0)
  		return r;
259711920   Joe Thornber   dm thin metadata:...
823
  	r = superblock_lock(pmd, &sblock);
991d9fa02   Joe Thornber   dm: add thin prov...
824
  	if (r)
d973ac196   Joe Thornber   dm thin metadata:...
825
  		return r;
991d9fa02   Joe Thornber   dm: add thin prov...
826
827
828
829
830
831
832
  
  	disk_super = dm_block_data(sblock);
  	disk_super->time = cpu_to_le32(pmd->time);
  	disk_super->data_mapping_root = cpu_to_le64(pmd->root);
  	disk_super->device_details_root = cpu_to_le64(pmd->details_root);
  	disk_super->trans_id = cpu_to_le64(pmd->trans_id);
  	disk_super->flags = cpu_to_le32(pmd->flags);
5a32083d0   Joe Thornber   dm: take care to ...
833
  	copy_sm_roots(pmd, disk_super);
991d9fa02   Joe Thornber   dm: add thin prov...
834

eb04cf634   Joe Thornber   dm thin metadata:...
835
  	return dm_tm_commit(pmd->tm, sblock);
991d9fa02   Joe Thornber   dm: add thin prov...
836
  }
3ab918281   Joe Thornber   dm thin metadata:...
837
838
839
840
841
842
843
844
845
846
  static void __set_metadata_reserve(struct dm_pool_metadata *pmd)
  {
  	int r;
  	dm_block_t total;
  	dm_block_t max_blocks = 4096; /* 16M */
  
  	r = dm_sm_get_nr_blocks(pmd->metadata_sm, &total);
  	if (r) {
  		DMERR("could not get size of metadata device");
  		pmd->metadata_reserve = max_blocks;
013ad0439   Mike Snitzer   dm thin metadata:...
847
848
  	} else
  		pmd->metadata_reserve = min(max_blocks, div_u64(total, 10));
3ab918281   Joe Thornber   dm thin metadata:...
849
  }
991d9fa02   Joe Thornber   dm: add thin prov...
850
  struct dm_pool_metadata *dm_pool_metadata_open(struct block_device *bdev,
66b1edc05   Joe Thornber   dm thin metadata:...
851
852
  					       sector_t data_block_size,
  					       bool format_device)
991d9fa02   Joe Thornber   dm: add thin prov...
853
854
  {
  	int r;
991d9fa02   Joe Thornber   dm: add thin prov...
855
  	struct dm_pool_metadata *pmd;
991d9fa02   Joe Thornber   dm: add thin prov...
856
857
858
859
860
861
  
  	pmd = kmalloc(sizeof(*pmd), GFP_KERNEL);
  	if (!pmd) {
  		DMERR("could not allocate metadata struct");
  		return ERR_PTR(-ENOMEM);
  	}
6a0ebd31b   Joe Thornber   dm thin metadata:...
862
863
864
  	init_rwsem(&pmd->root_lock);
  	pmd->time = 0;
  	INIT_LIST_HEAD(&pmd->thin_devices);
da105ed5f   Joe Thornber   dm thin metadata:...
865
  	pmd->fail_io = false;
873f258be   Mike Snitzer   dm thin metadata:...
866
  	pmd->in_service = false;
332627db0   Joe Thornber   dm thin metadata:...
867
  	pmd->bdev = bdev;
9cb6653f9   Joe Thornber   dm thin metadata:...
868
  	pmd->data_block_size = data_block_size;
d2688d36c   Nikos Tsironis   dm thin metadata:...
869
870
  	pmd->pre_commit_fn = NULL;
  	pmd->pre_commit_context = NULL;
991d9fa02   Joe Thornber   dm: add thin prov...
871

66b1edc05   Joe Thornber   dm thin metadata:...
872
  	r = __create_persistent_data_objects(pmd, format_device);
991d9fa02   Joe Thornber   dm: add thin prov...
873
  	if (r) {
991d9fa02   Joe Thornber   dm: add thin prov...
874
875
876
  		kfree(pmd);
  		return ERR_PTR(r);
  	}
991d9fa02   Joe Thornber   dm: add thin prov...
877

270938bac   Joe Thornber   dm thin metadata:...
878
879
880
881
882
  	r = __begin_transaction(pmd);
  	if (r < 0) {
  		if (dm_pool_metadata_close(pmd) < 0)
  			DMWARN("%s: dm_pool_metadata_close() failed.", __func__);
  		return ERR_PTR(r);
991d9fa02   Joe Thornber   dm: add thin prov...
883
  	}
3ab918281   Joe Thornber   dm thin metadata:...
884
  	__set_metadata_reserve(pmd);
991d9fa02   Joe Thornber   dm: add thin prov...
885
  	return pmd;
991d9fa02   Joe Thornber   dm: add thin prov...
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
  }
  
  int dm_pool_metadata_close(struct dm_pool_metadata *pmd)
  {
  	int r;
  	unsigned open_devices = 0;
  	struct dm_thin_device *td, *tmp;
  
  	down_read(&pmd->root_lock);
  	list_for_each_entry_safe(td, tmp, &pmd->thin_devices, list) {
  		if (td->open_count)
  			open_devices++;
  		else {
  			list_del(&td->list);
  			kfree(td);
  		}
  	}
  	up_read(&pmd->root_lock);
  
  	if (open_devices) {
  		DMERR("attempt to close pmd when %u device(s) are still open",
  		       open_devices);
  		return -EBUSY;
  	}
1426201af   Mike Snitzer   dm thin metadata:...
910
  	pmd_write_lock_in_core(pmd);
49f154c73   Mike Snitzer   dm thin metadata:...
911
  	if (!dm_bm_is_read_only(pmd->bm) && !pmd->fail_io) {
12ba58af4   Joe Thornber   dm thin metadata:...
912
913
914
915
916
  		r = __commit_transaction(pmd);
  		if (r < 0)
  			DMWARN("%s: __commit_transaction() failed, error = %d",
  			       __func__, r);
  	}
da105ed5f   Joe Thornber   dm thin metadata:...
917
918
  	if (!pmd->fail_io)
  		__destroy_persistent_data_objects(pmd);
1426201af   Mike Snitzer   dm thin metadata:...
919
  	pmd_write_unlock(pmd);
991d9fa02   Joe Thornber   dm: add thin prov...
920

da105ed5f   Joe Thornber   dm thin metadata:...
921
  	kfree(pmd);
991d9fa02   Joe Thornber   dm: add thin prov...
922
923
  	return 0;
  }
1f3db25d8   Mike Snitzer   dm thin metadata:...
924
925
926
927
928
  /*
   * __open_device: Returns @td corresponding to device with id @dev,
   * creating it if @create is set and incrementing @td->open_count.
   * On failure, @td is undefined.
   */
991d9fa02   Joe Thornber   dm: add thin prov...
929
930
931
932
933
934
935
936
937
938
  static int __open_device(struct dm_pool_metadata *pmd,
  			 dm_thin_id dev, int create,
  			 struct dm_thin_device **td)
  {
  	int r, changed = 0;
  	struct dm_thin_device *td2;
  	uint64_t key = dev;
  	struct disk_device_details details_le;
  
  	/*
1f3db25d8   Mike Snitzer   dm thin metadata:...
939
  	 * If the device is already open, return it.
991d9fa02   Joe Thornber   dm: add thin prov...
940
941
942
  	 */
  	list_for_each_entry(td2, &pmd->thin_devices, list)
  		if (td2->id == dev) {
1f3db25d8   Mike Snitzer   dm thin metadata:...
943
944
945
946
947
  			/*
  			 * May not create an already-open device.
  			 */
  			if (create)
  				return -EEXIST;
991d9fa02   Joe Thornber   dm: add thin prov...
948
949
950
951
952
953
954
955
956
957
958
959
960
  			td2->open_count++;
  			*td = td2;
  			return 0;
  		}
  
  	/*
  	 * Check the device exists.
  	 */
  	r = dm_btree_lookup(&pmd->details_info, pmd->details_root,
  			    &key, &details_le);
  	if (r) {
  		if (r != -ENODATA || !create)
  			return r;
1f3db25d8   Mike Snitzer   dm thin metadata:...
961
962
963
  		/*
  		 * Create new device.
  		 */
991d9fa02   Joe Thornber   dm: add thin prov...
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
  		changed = 1;
  		details_le.mapped_blocks = 0;
  		details_le.transaction_id = cpu_to_le64(pmd->trans_id);
  		details_le.creation_time = cpu_to_le32(pmd->time);
  		details_le.snapshotted_time = cpu_to_le32(pmd->time);
  	}
  
  	*td = kmalloc(sizeof(**td), GFP_NOIO);
  	if (!*td)
  		return -ENOMEM;
  
  	(*td)->pmd = pmd;
  	(*td)->id = dev;
  	(*td)->open_count = 1;
  	(*td)->changed = changed;
da105ed5f   Joe Thornber   dm thin metadata:...
979
  	(*td)->aborted_with_changes = false;
991d9fa02   Joe Thornber   dm: add thin prov...
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
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
  	(*td)->mapped_blocks = le64_to_cpu(details_le.mapped_blocks);
  	(*td)->transaction_id = le64_to_cpu(details_le.transaction_id);
  	(*td)->creation_time = le32_to_cpu(details_le.creation_time);
  	(*td)->snapshotted_time = le32_to_cpu(details_le.snapshotted_time);
  
  	list_add(&(*td)->list, &pmd->thin_devices);
  
  	return 0;
  }
  
  static void __close_device(struct dm_thin_device *td)
  {
  	--td->open_count;
  }
  
  static int __create_thin(struct dm_pool_metadata *pmd,
  			 dm_thin_id dev)
  {
  	int r;
  	dm_block_t dev_root;
  	uint64_t key = dev;
  	struct disk_device_details details_le;
  	struct dm_thin_device *td;
  	__le64 value;
  
  	r = dm_btree_lookup(&pmd->details_info, pmd->details_root,
  			    &key, &details_le);
  	if (!r)
  		return -EEXIST;
  
  	/*
  	 * Create an empty btree for the mappings.
  	 */
  	r = dm_btree_empty(&pmd->bl_info, &dev_root);
  	if (r)
  		return r;
  
  	/*
  	 * Insert it into the main mapping tree.
  	 */
  	value = cpu_to_le64(dev_root);
  	__dm_bless_for_disk(&value);
  	r = dm_btree_insert(&pmd->tl_info, pmd->root, &key, &value, &pmd->root);
  	if (r) {
  		dm_btree_del(&pmd->bl_info, dev_root);
  		return r;
  	}
  
  	r = __open_device(pmd, dev, 1, &td);
  	if (r) {
991d9fa02   Joe Thornber   dm: add thin prov...
1030
1031
1032
1033
  		dm_btree_remove(&pmd->tl_info, pmd->root, &key, &pmd->root);
  		dm_btree_del(&pmd->bl_info, dev_root);
  		return r;
  	}
991d9fa02   Joe Thornber   dm: add thin prov...
1034
1035
1036
1037
1038
1039
1040
  	__close_device(td);
  
  	return r;
  }
  
  int dm_pool_create_thin(struct dm_pool_metadata *pmd, dm_thin_id dev)
  {
da105ed5f   Joe Thornber   dm thin metadata:...
1041
  	int r = -EINVAL;
991d9fa02   Joe Thornber   dm: add thin prov...
1042

6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1043
  	pmd_write_lock(pmd);
da105ed5f   Joe Thornber   dm thin metadata:...
1044
1045
  	if (!pmd->fail_io)
  		r = __create_thin(pmd, dev);
6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1046
  	pmd_write_unlock(pmd);
991d9fa02   Joe Thornber   dm: add thin prov...
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
  
  	return r;
  }
  
  static int __set_snapshot_details(struct dm_pool_metadata *pmd,
  				  struct dm_thin_device *snap,
  				  dm_thin_id origin, uint32_t time)
  {
  	int r;
  	struct dm_thin_device *td;
  
  	r = __open_device(pmd, origin, 0, &td);
  	if (r)
  		return r;
  
  	td->changed = 1;
  	td->snapshotted_time = time;
  
  	snap->mapped_blocks = td->mapped_blocks;
  	snap->snapshotted_time = time;
  	__close_device(td);
  
  	return 0;
  }
  
  static int __create_snap(struct dm_pool_metadata *pmd,
  			 dm_thin_id dev, dm_thin_id origin)
  {
  	int r;
  	dm_block_t origin_root;
  	uint64_t key = origin, dev_key = dev;
  	struct dm_thin_device *td;
  	struct disk_device_details details_le;
  	__le64 value;
  
  	/* check this device is unused */
  	r = dm_btree_lookup(&pmd->details_info, pmd->details_root,
  			    &dev_key, &details_le);
  	if (!r)
  		return -EEXIST;
  
  	/* find the mapping tree for the origin */
  	r = dm_btree_lookup(&pmd->tl_info, pmd->root, &key, &value);
  	if (r)
  		return r;
  	origin_root = le64_to_cpu(value);
  
  	/* clone the origin, an inc will do */
  	dm_tm_inc(pmd->tm, origin_root);
  
  	/* insert into the main mapping tree */
  	value = cpu_to_le64(origin_root);
  	__dm_bless_for_disk(&value);
  	key = dev;
  	r = dm_btree_insert(&pmd->tl_info, pmd->root, &key, &value, &pmd->root);
  	if (r) {
  		dm_tm_dec(pmd->tm, origin_root);
  		return r;
  	}
  
  	pmd->time++;
  
  	r = __open_device(pmd, dev, 1, &td);
  	if (r)
  		goto bad;
  
  	r = __set_snapshot_details(pmd, td, origin, pmd->time);
1f3db25d8   Mike Snitzer   dm thin metadata:...
1114
  	__close_device(td);
991d9fa02   Joe Thornber   dm: add thin prov...
1115
1116
  	if (r)
  		goto bad;
991d9fa02   Joe Thornber   dm: add thin prov...
1117
1118
1119
  	return 0;
  
  bad:
991d9fa02   Joe Thornber   dm: add thin prov...
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
  	dm_btree_remove(&pmd->tl_info, pmd->root, &key, &pmd->root);
  	dm_btree_remove(&pmd->details_info, pmd->details_root,
  			&key, &pmd->details_root);
  	return r;
  }
  
  int dm_pool_create_snap(struct dm_pool_metadata *pmd,
  				 dm_thin_id dev,
  				 dm_thin_id origin)
  {
da105ed5f   Joe Thornber   dm thin metadata:...
1130
  	int r = -EINVAL;
991d9fa02   Joe Thornber   dm: add thin prov...
1131

6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1132
  	pmd_write_lock(pmd);
da105ed5f   Joe Thornber   dm thin metadata:...
1133
1134
  	if (!pmd->fail_io)
  		r = __create_snap(pmd, dev, origin);
6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1135
  	pmd_write_unlock(pmd);
991d9fa02   Joe Thornber   dm: add thin prov...
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
  
  	return r;
  }
  
  static int __delete_device(struct dm_pool_metadata *pmd, dm_thin_id dev)
  {
  	int r;
  	uint64_t key = dev;
  	struct dm_thin_device *td;
  
  	/* TODO: failure should mark the transaction invalid */
  	r = __open_device(pmd, dev, 0, &td);
  	if (r)
  		return r;
  
  	if (td->open_count > 1) {
  		__close_device(td);
  		return -EBUSY;
  	}
  
  	list_del(&td->list);
  	kfree(td);
  	r = dm_btree_remove(&pmd->details_info, pmd->details_root,
  			    &key, &pmd->details_root);
  	if (r)
  		return r;
  
  	r = dm_btree_remove(&pmd->tl_info, pmd->root, &key, &pmd->root);
  	if (r)
  		return r;
991d9fa02   Joe Thornber   dm: add thin prov...
1166
1167
1168
1169
1170
1171
  	return 0;
  }
  
  int dm_pool_delete_thin_device(struct dm_pool_metadata *pmd,
  			       dm_thin_id dev)
  {
da105ed5f   Joe Thornber   dm thin metadata:...
1172
  	int r = -EINVAL;
991d9fa02   Joe Thornber   dm: add thin prov...
1173

6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1174
  	pmd_write_lock(pmd);
da105ed5f   Joe Thornber   dm thin metadata:...
1175
1176
  	if (!pmd->fail_io)
  		r = __delete_device(pmd, dev);
6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1177
  	pmd_write_unlock(pmd);
991d9fa02   Joe Thornber   dm: add thin prov...
1178
1179
1180
1181
1182
1183
1184
1185
  
  	return r;
  }
  
  int dm_pool_set_metadata_transaction_id(struct dm_pool_metadata *pmd,
  					uint64_t current_id,
  					uint64_t new_id)
  {
da105ed5f   Joe Thornber   dm thin metadata:...
1186
  	int r = -EINVAL;
6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1187
  	pmd_write_lock(pmd);
da105ed5f   Joe Thornber   dm thin metadata:...
1188
1189
1190
  
  	if (pmd->fail_io)
  		goto out;
991d9fa02   Joe Thornber   dm: add thin prov...
1191
  	if (pmd->trans_id != current_id) {
991d9fa02   Joe Thornber   dm: add thin prov...
1192
  		DMERR("mismatched transaction id");
da105ed5f   Joe Thornber   dm thin metadata:...
1193
  		goto out;
991d9fa02   Joe Thornber   dm: add thin prov...
1194
1195
1196
  	}
  
  	pmd->trans_id = new_id;
da105ed5f   Joe Thornber   dm thin metadata:...
1197
1198
1199
  	r = 0;
  
  out:
6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1200
  	pmd_write_unlock(pmd);
991d9fa02   Joe Thornber   dm: add thin prov...
1201

da105ed5f   Joe Thornber   dm thin metadata:...
1202
  	return r;
991d9fa02   Joe Thornber   dm: add thin prov...
1203
1204
1205
1206
1207
  }
  
  int dm_pool_get_metadata_transaction_id(struct dm_pool_metadata *pmd,
  					uint64_t *result)
  {
da105ed5f   Joe Thornber   dm thin metadata:...
1208
  	int r = -EINVAL;
991d9fa02   Joe Thornber   dm: add thin prov...
1209
  	down_read(&pmd->root_lock);
da105ed5f   Joe Thornber   dm thin metadata:...
1210
1211
1212
1213
  	if (!pmd->fail_io) {
  		*result = pmd->trans_id;
  		r = 0;
  	}
991d9fa02   Joe Thornber   dm: add thin prov...
1214
  	up_read(&pmd->root_lock);
da105ed5f   Joe Thornber   dm thin metadata:...
1215
  	return r;
991d9fa02   Joe Thornber   dm: add thin prov...
1216
  }
cc8394d86   Joe Thornber   dm thin: provide ...
1217
1218
1219
1220
1221
1222
1223
1224
  static int __reserve_metadata_snap(struct dm_pool_metadata *pmd)
  {
  	int r, inc;
  	struct thin_disk_superblock *disk_super;
  	struct dm_block *copy, *sblock;
  	dm_block_t held_root;
  
  	/*
49e99fc71   Joe Thornber   dm thin metadata:...
1225
1226
1227
  	 * We commit to ensure the btree roots which we increment in a
  	 * moment are up to date.
  	 */
a1ed4d9e9   Mike Snitzer   dm thin metadata:...
1228
1229
1230
1231
1232
1233
  	r = __commit_transaction(pmd);
  	if (r < 0) {
  		DMWARN("%s: __commit_transaction() failed, error = %d",
  		       __func__, r);
  		return r;
  	}
49e99fc71   Joe Thornber   dm thin metadata:...
1234
1235
  
  	/*
cc8394d86   Joe Thornber   dm thin: provide ...
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
  	 * Copy the superblock.
  	 */
  	dm_sm_inc_block(pmd->metadata_sm, THIN_SUPERBLOCK_LOCATION);
  	r = dm_tm_shadow_block(pmd->tm, THIN_SUPERBLOCK_LOCATION,
  			       &sb_validator, &copy, &inc);
  	if (r)
  		return r;
  
  	BUG_ON(!inc);
  
  	held_root = dm_block_location(copy);
  	disk_super = dm_block_data(copy);
  
  	if (le64_to_cpu(disk_super->held_root)) {
  		DMWARN("Pool metadata snapshot already exists: release this before taking another.");
  
  		dm_tm_dec(pmd->tm, held_root);
  		dm_tm_unlock(pmd->tm, copy);
cc8394d86   Joe Thornber   dm thin: provide ...
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
  		return -EBUSY;
  	}
  
  	/*
  	 * Wipe the spacemap since we're not publishing this.
  	 */
  	memset(&disk_super->data_space_map_root, 0,
  	       sizeof(disk_super->data_space_map_root));
  	memset(&disk_super->metadata_space_map_root, 0,
  	       sizeof(disk_super->metadata_space_map_root));
  
  	/*
  	 * Increment the data structures that need to be preserved.
  	 */
  	dm_tm_inc(pmd->tm, le64_to_cpu(disk_super->data_mapping_root));
  	dm_tm_inc(pmd->tm, le64_to_cpu(disk_super->device_details_root));
  	dm_tm_unlock(pmd->tm, copy);
  
  	/*
  	 * Write the held root into the superblock.
  	 */
259711920   Joe Thornber   dm thin metadata:...
1275
  	r = superblock_lock(pmd, &sblock);
cc8394d86   Joe Thornber   dm thin: provide ...
1276
1277
  	if (r) {
  		dm_tm_dec(pmd->tm, held_root);
cc8394d86   Joe Thornber   dm thin: provide ...
1278
1279
1280
1281
1282
1283
  		return r;
  	}
  
  	disk_super = dm_block_data(sblock);
  	disk_super->held_root = cpu_to_le64(held_root);
  	dm_bm_unlock(sblock);
cc8394d86   Joe Thornber   dm thin: provide ...
1284
1285
1286
1287
1288
  	return 0;
  }
  
  int dm_pool_reserve_metadata_snap(struct dm_pool_metadata *pmd)
  {
da105ed5f   Joe Thornber   dm thin metadata:...
1289
  	int r = -EINVAL;
cc8394d86   Joe Thornber   dm thin: provide ...
1290

6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1291
  	pmd_write_lock(pmd);
da105ed5f   Joe Thornber   dm thin metadata:...
1292
1293
  	if (!pmd->fail_io)
  		r = __reserve_metadata_snap(pmd);
6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1294
  	pmd_write_unlock(pmd);
cc8394d86   Joe Thornber   dm thin: provide ...
1295
1296
1297
1298
1299
  
  	return r;
  }
  
  static int __release_metadata_snap(struct dm_pool_metadata *pmd)
991d9fa02   Joe Thornber   dm: add thin prov...
1300
1301
1302
  {
  	int r;
  	struct thin_disk_superblock *disk_super;
cc8394d86   Joe Thornber   dm thin: provide ...
1303
1304
  	struct dm_block *sblock, *copy;
  	dm_block_t held_root;
991d9fa02   Joe Thornber   dm: add thin prov...
1305

259711920   Joe Thornber   dm thin metadata:...
1306
  	r = superblock_lock(pmd, &sblock);
991d9fa02   Joe Thornber   dm: add thin prov...
1307
1308
1309
1310
  	if (r)
  		return r;
  
  	disk_super = dm_block_data(sblock);
cc8394d86   Joe Thornber   dm thin: provide ...
1311
1312
  	held_root = le64_to_cpu(disk_super->held_root);
  	disk_super->held_root = cpu_to_le64(0);
cc8394d86   Joe Thornber   dm thin: provide ...
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
  
  	dm_bm_unlock(sblock);
  
  	if (!held_root) {
  		DMWARN("No pool metadata snapshot found: nothing to release.");
  		return -EINVAL;
  	}
  
  	r = dm_tm_read_lock(pmd->tm, held_root, &sb_validator, &copy);
  	if (r)
  		return r;
  
  	disk_super = dm_block_data(copy);
7f518ad0a   Joe Thornber   dm thin metadata:...
1326
1327
  	dm_btree_del(&pmd->info, le64_to_cpu(disk_super->data_mapping_root));
  	dm_btree_del(&pmd->details_info, le64_to_cpu(disk_super->device_details_root));
cc8394d86   Joe Thornber   dm thin: provide ...
1328
  	dm_sm_dec_block(pmd->metadata_sm, held_root);
4c7da06f5   Mikulas Patocka   dm persistent dat...
1329
1330
1331
  	dm_tm_unlock(pmd->tm, copy);
  
  	return 0;
cc8394d86   Joe Thornber   dm thin: provide ...
1332
1333
1334
1335
  }
  
  int dm_pool_release_metadata_snap(struct dm_pool_metadata *pmd)
  {
da105ed5f   Joe Thornber   dm thin metadata:...
1336
  	int r = -EINVAL;
cc8394d86   Joe Thornber   dm thin: provide ...
1337

6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1338
  	pmd_write_lock(pmd);
da105ed5f   Joe Thornber   dm thin metadata:...
1339
1340
  	if (!pmd->fail_io)
  		r = __release_metadata_snap(pmd);
6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1341
  	pmd_write_unlock(pmd);
cc8394d86   Joe Thornber   dm thin: provide ...
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
  
  	return r;
  }
  
  static int __get_metadata_snap(struct dm_pool_metadata *pmd,
  			       dm_block_t *result)
  {
  	int r;
  	struct thin_disk_superblock *disk_super;
  	struct dm_block *sblock;
  
  	r = dm_bm_read_lock(pmd->bm, THIN_SUPERBLOCK_LOCATION,
  			    &sb_validator, &sblock);
  	if (r)
  		return r;
  
  	disk_super = dm_block_data(sblock);
991d9fa02   Joe Thornber   dm: add thin prov...
1359
  	*result = le64_to_cpu(disk_super->held_root);
4c7da06f5   Mikulas Patocka   dm persistent dat...
1360
1361
1362
  	dm_bm_unlock(sblock);
  
  	return 0;
991d9fa02   Joe Thornber   dm: add thin prov...
1363
  }
cc8394d86   Joe Thornber   dm thin: provide ...
1364
1365
  int dm_pool_get_metadata_snap(struct dm_pool_metadata *pmd,
  			      dm_block_t *result)
991d9fa02   Joe Thornber   dm: add thin prov...
1366
  {
da105ed5f   Joe Thornber   dm thin metadata:...
1367
  	int r = -EINVAL;
991d9fa02   Joe Thornber   dm: add thin prov...
1368
1369
  
  	down_read(&pmd->root_lock);
da105ed5f   Joe Thornber   dm thin metadata:...
1370
1371
  	if (!pmd->fail_io)
  		r = __get_metadata_snap(pmd, result);
991d9fa02   Joe Thornber   dm: add thin prov...
1372
1373
1374
1375
1376
1377
1378
1379
  	up_read(&pmd->root_lock);
  
  	return r;
  }
  
  int dm_pool_open_thin_device(struct dm_pool_metadata *pmd, dm_thin_id dev,
  			     struct dm_thin_device **td)
  {
da105ed5f   Joe Thornber   dm thin metadata:...
1380
  	int r = -EINVAL;
991d9fa02   Joe Thornber   dm: add thin prov...
1381

6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1382
  	pmd_write_lock_in_core(pmd);
da105ed5f   Joe Thornber   dm thin metadata:...
1383
1384
  	if (!pmd->fail_io)
  		r = __open_device(pmd, dev, 0, td);
6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1385
  	pmd_write_unlock(pmd);
991d9fa02   Joe Thornber   dm: add thin prov...
1386
1387
1388
1389
1390
1391
  
  	return r;
  }
  
  int dm_pool_close_thin_device(struct dm_thin_device *td)
  {
6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1392
  	pmd_write_lock_in_core(td->pmd);
991d9fa02   Joe Thornber   dm: add thin prov...
1393
  	__close_device(td);
6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1394
  	pmd_write_unlock(td->pmd);
991d9fa02   Joe Thornber   dm: add thin prov...
1395
1396
1397
1398
1399
1400
1401
1402
  
  	return 0;
  }
  
  dm_thin_id dm_thin_dev_id(struct dm_thin_device *td)
  {
  	return td->id;
  }
19fa1a675   Joe Thornber   dm thin: fix disc...
1403
1404
1405
1406
1407
1408
  /*
   * Check whether @time (of block creation) is older than @td's last snapshot.
   * If so then the associated block is shared with the last snapshot device.
   * Any block on a device created *after* the device last got snapshotted is
   * necessarily not shared.
   */
17b7d63f7   Mike Snitzer   dm thin: clean up...
1409
  static bool __snapshotted_since(struct dm_thin_device *td, uint32_t time)
991d9fa02   Joe Thornber   dm: add thin prov...
1410
1411
1412
  {
  	return td->snapshotted_time > time;
  }
3d5f67332   Joe Thornber   dm thin metadata:...
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
  static void unpack_lookup_result(struct dm_thin_device *td, __le64 value,
  				 struct dm_thin_lookup_result *result)
  {
  	uint64_t block_time = 0;
  	dm_block_t exception_block;
  	uint32_t exception_time;
  
  	block_time = le64_to_cpu(value);
  	unpack_block_time(block_time, &exception_block, &exception_time);
  	result->block = exception_block;
  	result->shared = __snapshotted_since(td, exception_time);
  }
086fbbbda   Joe Thornber   dm thin metadata:...
1425
1426
  static int __find_block(struct dm_thin_device *td, dm_block_t block,
  			int can_issue_io, struct dm_thin_lookup_result *result)
991d9fa02   Joe Thornber   dm: add thin prov...
1427
  {
e5cfc69a5   Joe Thornber   dm thin metadata:...
1428
  	int r;
991d9fa02   Joe Thornber   dm: add thin prov...
1429
1430
1431
  	__le64 value;
  	struct dm_pool_metadata *pmd = td->pmd;
  	dm_block_t keys[2] = { td->id, block };
da105ed5f   Joe Thornber   dm thin metadata:...
1432
  	struct dm_btree_info *info;
991d9fa02   Joe Thornber   dm: add thin prov...
1433

e5cfc69a5   Joe Thornber   dm thin metadata:...
1434
1435
1436
1437
  	if (can_issue_io) {
  		info = &pmd->info;
  	} else
  		info = &pmd->nb_info;
da105ed5f   Joe Thornber   dm thin metadata:...
1438

e5cfc69a5   Joe Thornber   dm thin metadata:...
1439
  	r = dm_btree_lookup(info, pmd->root, keys, &value);
3d5f67332   Joe Thornber   dm thin metadata:...
1440
1441
  	if (!r)
  		unpack_lookup_result(td, value, result);
3d5f67332   Joe Thornber   dm thin metadata:...
1442
1443
  	return r;
  }
086fbbbda   Joe Thornber   dm thin metadata:...
1444
1445
  int dm_thin_find_block(struct dm_thin_device *td, dm_block_t block,
  		       int can_issue_io, struct dm_thin_lookup_result *result)
3d5f67332   Joe Thornber   dm thin metadata:...
1446
1447
  {
  	int r;
3d5f67332   Joe Thornber   dm thin metadata:...
1448
  	struct dm_pool_metadata *pmd = td->pmd;
3d5f67332   Joe Thornber   dm thin metadata:...
1449
1450
1451
1452
1453
  
  	down_read(&pmd->root_lock);
  	if (pmd->fail_io) {
  		up_read(&pmd->root_lock);
  		return -EINVAL;
991d9fa02   Joe Thornber   dm: add thin prov...
1454
  	}
086fbbbda   Joe Thornber   dm thin metadata:...
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
  	r = __find_block(td, block, can_issue_io, result);
  
  	up_read(&pmd->root_lock);
  	return r;
  }
  
  static int __find_next_mapped_block(struct dm_thin_device *td, dm_block_t block,
  					  dm_block_t *vblock,
  					  struct dm_thin_lookup_result *result)
  {
  	int r;
  	__le64 value;
  	struct dm_pool_metadata *pmd = td->pmd;
  	dm_block_t keys[2] = { td->id, block };
3d5f67332   Joe Thornber   dm thin metadata:...
1469
1470
1471
  	r = dm_btree_lookup_next(&pmd->info, pmd->root, keys, vblock, &value);
  	if (!r)
  		unpack_lookup_result(td, value, result);
991d9fa02   Joe Thornber   dm: add thin prov...
1472
1473
  	return r;
  }
086fbbbda   Joe Thornber   dm thin metadata:...
1474
1475
1476
1477
  static int __find_mapped_range(struct dm_thin_device *td,
  			       dm_block_t begin, dm_block_t end,
  			       dm_block_t *thin_begin, dm_block_t *thin_end,
  			       dm_block_t *pool_begin, bool *maybe_shared)
a5d895a90   Joe Thornber   dm thin metadata:...
1478
1479
1480
1481
1482
1483
1484
  {
  	int r;
  	dm_block_t pool_end;
  	struct dm_thin_lookup_result lookup;
  
  	if (end < begin)
  		return -ENODATA;
086fbbbda   Joe Thornber   dm thin metadata:...
1485
  	r = __find_next_mapped_block(td, begin, &begin, &lookup);
3d5f67332   Joe Thornber   dm thin metadata:...
1486
1487
  	if (r)
  		return r;
a5d895a90   Joe Thornber   dm thin metadata:...
1488

3d5f67332   Joe Thornber   dm thin metadata:...
1489
  	if (begin >= end)
a5d895a90   Joe Thornber   dm thin metadata:...
1490
1491
1492
1493
1494
1495
1496
1497
1498
  		return -ENODATA;
  
  	*thin_begin = begin;
  	*pool_begin = lookup.block;
  	*maybe_shared = lookup.shared;
  
  	begin++;
  	pool_end = *pool_begin + 1;
  	while (begin != end) {
086fbbbda   Joe Thornber   dm thin metadata:...
1499
  		r = __find_block(td, begin, true, &lookup);
a5d895a90   Joe Thornber   dm thin metadata:...
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
  		if (r) {
  			if (r == -ENODATA)
  				break;
  			else
  				return r;
  		}
  
  		if ((lookup.block != pool_end) ||
  		    (lookup.shared != *maybe_shared))
  			break;
  
  		pool_end++;
  		begin++;
  	}
  
  	*thin_end = begin;
  	return 0;
  }
086fbbbda   Joe Thornber   dm thin metadata:...
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
  int dm_thin_find_mapped_range(struct dm_thin_device *td,
  			      dm_block_t begin, dm_block_t end,
  			      dm_block_t *thin_begin, dm_block_t *thin_end,
  			      dm_block_t *pool_begin, bool *maybe_shared)
  {
  	int r = -EINVAL;
  	struct dm_pool_metadata *pmd = td->pmd;
  
  	down_read(&pmd->root_lock);
  	if (!pmd->fail_io) {
  		r = __find_mapped_range(td, begin, end, thin_begin, thin_end,
  					pool_begin, maybe_shared);
  	}
  	up_read(&pmd->root_lock);
  
  	return r;
  }
991d9fa02   Joe Thornber   dm: add thin prov...
1535
1536
1537
1538
1539
1540
1541
  static int __insert(struct dm_thin_device *td, dm_block_t block,
  		    dm_block_t data_block)
  {
  	int r, inserted;
  	__le64 value;
  	struct dm_pool_metadata *pmd = td->pmd;
  	dm_block_t keys[2] = { td->id, block };
991d9fa02   Joe Thornber   dm: add thin prov...
1542
1543
1544
1545
1546
1547
1548
  	value = cpu_to_le64(pack_block_time(data_block, pmd->time));
  	__dm_bless_for_disk(&value);
  
  	r = dm_btree_insert_notify(&pmd->info, pmd->root, keys, &value,
  				   &pmd->root, &inserted);
  	if (r)
  		return r;
40db5a537   Joe Thornber   dm thin metadata:...
1549
1550
  	td->changed = 1;
  	if (inserted)
991d9fa02   Joe Thornber   dm: add thin prov...
1551
  		td->mapped_blocks++;
991d9fa02   Joe Thornber   dm: add thin prov...
1552
1553
1554
1555
1556
1557
1558
  
  	return 0;
  }
  
  int dm_thin_insert_block(struct dm_thin_device *td, dm_block_t block,
  			 dm_block_t data_block)
  {
da105ed5f   Joe Thornber   dm thin metadata:...
1559
  	int r = -EINVAL;
991d9fa02   Joe Thornber   dm: add thin prov...
1560

6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1561
  	pmd_write_lock(td->pmd);
da105ed5f   Joe Thornber   dm thin metadata:...
1562
1563
  	if (!td->pmd->fail_io)
  		r = __insert(td, block, data_block);
6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1564
  	pmd_write_unlock(td->pmd);
991d9fa02   Joe Thornber   dm: add thin prov...
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
  
  	return r;
  }
  
  static int __remove(struct dm_thin_device *td, dm_block_t block)
  {
  	int r;
  	struct dm_pool_metadata *pmd = td->pmd;
  	dm_block_t keys[2] = { td->id, block };
  
  	r = dm_btree_remove(&pmd->info, pmd->root, keys, &pmd->root);
  	if (r)
  		return r;
af63bcb81   Joe Thornber   dm thin metadata:...
1578
1579
  	td->mapped_blocks--;
  	td->changed = 1;
991d9fa02   Joe Thornber   dm: add thin prov...
1580
1581
1582
  
  	return 0;
  }
6550f075f   Joe Thornber   dm thin metadata:...
1583
1584
1585
  static int __remove_range(struct dm_thin_device *td, dm_block_t begin, dm_block_t end)
  {
  	int r;
993ceab91   Joe Thornber   dm thin metadata:...
1586
  	unsigned count, total_count = 0;
6550f075f   Joe Thornber   dm thin metadata:...
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
  	struct dm_pool_metadata *pmd = td->pmd;
  	dm_block_t keys[1] = { td->id };
  	__le64 value;
  	dm_block_t mapping_root;
  
  	/*
  	 * Find the mapping tree
  	 */
  	r = dm_btree_lookup(&pmd->tl_info, pmd->root, keys, &value);
  	if (r)
  		return r;
  
  	/*
  	 * Remove from the mapping tree, taking care to inc the
  	 * ref count so it doesn't get deleted.
  	 */
  	mapping_root = le64_to_cpu(value);
  	dm_tm_inc(pmd->tm, mapping_root);
  	r = dm_btree_remove(&pmd->tl_info, pmd->root, keys, &pmd->root);
  	if (r)
  		return r;
993ceab91   Joe Thornber   dm thin metadata:...
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
  	/*
  	 * Remove leaves stops at the first unmapped entry, so we have to
  	 * loop round finding mapped ranges.
  	 */
  	while (begin < end) {
  		r = dm_btree_lookup_next(&pmd->bl_info, mapping_root, &begin, &begin, &value);
  		if (r == -ENODATA)
  			break;
  
  		if (r)
  			return r;
  
  		if (begin >= end)
  			break;
  
  		r = dm_btree_remove_leaves(&pmd->bl_info, mapping_root, &begin, end, &mapping_root, &count);
  		if (r)
  			return r;
  
  		total_count += count;
  	}
6550f075f   Joe Thornber   dm thin metadata:...
1629

993ceab91   Joe Thornber   dm thin metadata:...
1630
  	td->mapped_blocks -= total_count;
6550f075f   Joe Thornber   dm thin metadata:...
1631
1632
1633
1634
1635
1636
1637
1638
1639
  	td->changed = 1;
  
  	/*
  	 * Reinsert the mapping tree.
  	 */
  	value = cpu_to_le64(mapping_root);
  	__dm_bless_for_disk(&value);
  	return dm_btree_insert(&pmd->tl_info, pmd->root, keys, &value, &pmd->root);
  }
991d9fa02   Joe Thornber   dm: add thin prov...
1640
1641
  int dm_thin_remove_block(struct dm_thin_device *td, dm_block_t block)
  {
da105ed5f   Joe Thornber   dm thin metadata:...
1642
  	int r = -EINVAL;
991d9fa02   Joe Thornber   dm: add thin prov...
1643

6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1644
  	pmd_write_lock(td->pmd);
da105ed5f   Joe Thornber   dm thin metadata:...
1645
1646
  	if (!td->pmd->fail_io)
  		r = __remove(td, block);
6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1647
  	pmd_write_unlock(td->pmd);
19fa1a675   Joe Thornber   dm thin: fix disc...
1648
1649
1650
  
  	return r;
  }
6550f075f   Joe Thornber   dm thin metadata:...
1651
1652
1653
1654
  int dm_thin_remove_range(struct dm_thin_device *td,
  			 dm_block_t begin, dm_block_t end)
  {
  	int r = -EINVAL;
6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1655
  	pmd_write_lock(td->pmd);
6550f075f   Joe Thornber   dm thin metadata:...
1656
1657
  	if (!td->pmd->fail_io)
  		r = __remove_range(td, begin, end);
6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1658
  	pmd_write_unlock(td->pmd);
6550f075f   Joe Thornber   dm thin metadata:...
1659
1660
1661
  
  	return r;
  }
d445bd9ce   Joe Thornber   dm thin: fix pass...
1662
  int dm_pool_block_is_shared(struct dm_pool_metadata *pmd, dm_block_t b, bool *result)
19fa1a675   Joe Thornber   dm thin: fix disc...
1663
1664
1665
1666
1667
1668
1669
  {
  	int r;
  	uint32_t ref_count;
  
  	down_read(&pmd->root_lock);
  	r = dm_sm_get_count(pmd->data_sm, b, &ref_count);
  	if (!r)
d445bd9ce   Joe Thornber   dm thin: fix pass...
1670
  		*result = (ref_count > 1);
19fa1a675   Joe Thornber   dm thin: fix disc...
1671
  	up_read(&pmd->root_lock);
991d9fa02   Joe Thornber   dm: add thin prov...
1672
1673
1674
  
  	return r;
  }
2a0fbffb1   Joe Thornber   dm thin: fix a ra...
1675
1676
1677
  int dm_pool_inc_data_range(struct dm_pool_metadata *pmd, dm_block_t b, dm_block_t e)
  {
  	int r = 0;
6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1678
  	pmd_write_lock(pmd);
2a0fbffb1   Joe Thornber   dm thin: fix a ra...
1679
1680
1681
1682
1683
  	for (; b != e; b++) {
  		r = dm_sm_inc_block(pmd->data_sm, b);
  		if (r)
  			break;
  	}
6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1684
  	pmd_write_unlock(pmd);
2a0fbffb1   Joe Thornber   dm thin: fix a ra...
1685
1686
1687
1688
1689
1690
1691
  
  	return r;
  }
  
  int dm_pool_dec_data_range(struct dm_pool_metadata *pmd, dm_block_t b, dm_block_t e)
  {
  	int r = 0;
6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1692
  	pmd_write_lock(pmd);
2a0fbffb1   Joe Thornber   dm thin: fix a ra...
1693
1694
1695
1696
1697
  	for (; b != e; b++) {
  		r = dm_sm_dec_block(pmd->data_sm, b);
  		if (r)
  			break;
  	}
6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1698
  	pmd_write_unlock(pmd);
2a0fbffb1   Joe Thornber   dm thin: fix a ra...
1699
1700
1701
  
  	return r;
  }
40db5a537   Joe Thornber   dm thin metadata:...
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
  bool dm_thin_changed_this_transaction(struct dm_thin_device *td)
  {
  	int r;
  
  	down_read(&td->pmd->root_lock);
  	r = td->changed;
  	up_read(&td->pmd->root_lock);
  
  	return r;
  }
4d1662a30   Mike Snitzer   dm thin: avoid me...
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
  bool dm_pool_changed_this_transaction(struct dm_pool_metadata *pmd)
  {
  	bool r = false;
  	struct dm_thin_device *td, *tmp;
  
  	down_read(&pmd->root_lock);
  	list_for_each_entry_safe(td, tmp, &pmd->thin_devices, list) {
  		if (td->changed) {
  			r = td->changed;
  			break;
  		}
  	}
  	up_read(&pmd->root_lock);
  
  	return r;
  }
da105ed5f   Joe Thornber   dm thin metadata:...
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
  bool dm_thin_aborted_changes(struct dm_thin_device *td)
  {
  	bool r;
  
  	down_read(&td->pmd->root_lock);
  	r = td->aborted_with_changes;
  	up_read(&td->pmd->root_lock);
  
  	return r;
  }
991d9fa02   Joe Thornber   dm: add thin prov...
1738
1739
  int dm_pool_alloc_data_block(struct dm_pool_metadata *pmd, dm_block_t *result)
  {
da105ed5f   Joe Thornber   dm thin metadata:...
1740
  	int r = -EINVAL;
991d9fa02   Joe Thornber   dm: add thin prov...
1741

6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1742
  	pmd_write_lock(pmd);
da105ed5f   Joe Thornber   dm thin metadata:...
1743
1744
  	if (!pmd->fail_io)
  		r = dm_sm_new_block(pmd->data_sm, result);
6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1745
  	pmd_write_unlock(pmd);
991d9fa02   Joe Thornber   dm: add thin prov...
1746
1747
1748
1749
1750
1751
  
  	return r;
  }
  
  int dm_pool_commit_metadata(struct dm_pool_metadata *pmd)
  {
da105ed5f   Joe Thornber   dm thin metadata:...
1752
  	int r = -EINVAL;
991d9fa02   Joe Thornber   dm: add thin prov...
1753

873f258be   Mike Snitzer   dm thin metadata:...
1754
1755
1756
1757
  	/*
  	 * Care is taken to not have commit be what
  	 * triggers putting the thin-pool in-service.
  	 */
1426201af   Mike Snitzer   dm thin metadata:...
1758
  	pmd_write_lock_in_core(pmd);
da105ed5f   Joe Thornber   dm thin metadata:...
1759
1760
  	if (pmd->fail_io)
  		goto out;
991d9fa02   Joe Thornber   dm: add thin prov...
1761
1762
  
  	r = __commit_transaction(pmd);
873f258be   Mike Snitzer   dm thin metadata:...
1763
  	if (r < 0)
991d9fa02   Joe Thornber   dm: add thin prov...
1764
1765
1766
1767
1768
1769
1770
  		goto out;
  
  	/*
  	 * Open the next transaction.
  	 */
  	r = __begin_transaction(pmd);
  out:
6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1771
  	pmd_write_unlock(pmd);
991d9fa02   Joe Thornber   dm: add thin prov...
1772
1773
  	return r;
  }
da105ed5f   Joe Thornber   dm thin metadata:...
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
  static void __set_abort_with_changes_flags(struct dm_pool_metadata *pmd)
  {
  	struct dm_thin_device *td;
  
  	list_for_each_entry(td, &pmd->thin_devices, list)
  		td->aborted_with_changes = td->changed;
  }
  
  int dm_pool_abort_metadata(struct dm_pool_metadata *pmd)
  {
  	int r = -EINVAL;
6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1785
  	pmd_write_lock(pmd);
da105ed5f   Joe Thornber   dm thin metadata:...
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
  	if (pmd->fail_io)
  		goto out;
  
  	__set_abort_with_changes_flags(pmd);
  	__destroy_persistent_data_objects(pmd);
  	r = __create_persistent_data_objects(pmd, false);
  	if (r)
  		pmd->fail_io = true;
  
  out:
6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1796
  	pmd_write_unlock(pmd);
da105ed5f   Joe Thornber   dm thin metadata:...
1797
1798
1799
  
  	return r;
  }
991d9fa02   Joe Thornber   dm: add thin prov...
1800
1801
  int dm_pool_get_free_block_count(struct dm_pool_metadata *pmd, dm_block_t *result)
  {
da105ed5f   Joe Thornber   dm thin metadata:...
1802
  	int r = -EINVAL;
991d9fa02   Joe Thornber   dm: add thin prov...
1803
1804
  
  	down_read(&pmd->root_lock);
da105ed5f   Joe Thornber   dm thin metadata:...
1805
1806
  	if (!pmd->fail_io)
  		r = dm_sm_get_nr_free(pmd->data_sm, result);
991d9fa02   Joe Thornber   dm: add thin prov...
1807
1808
1809
1810
1811
1812
1813
1814
  	up_read(&pmd->root_lock);
  
  	return r;
  }
  
  int dm_pool_get_free_metadata_block_count(struct dm_pool_metadata *pmd,
  					  dm_block_t *result)
  {
da105ed5f   Joe Thornber   dm thin metadata:...
1815
  	int r = -EINVAL;
991d9fa02   Joe Thornber   dm: add thin prov...
1816
1817
  
  	down_read(&pmd->root_lock);
da105ed5f   Joe Thornber   dm thin metadata:...
1818
1819
  	if (!pmd->fail_io)
  		r = dm_sm_get_nr_free(pmd->metadata_sm, result);
3ab918281   Joe Thornber   dm thin metadata:...
1820
1821
1822
1823
1824
1825
1826
  
  	if (!r) {
  		if (*result < pmd->metadata_reserve)
  			*result = 0;
  		else
  			*result -= pmd->metadata_reserve;
  	}
991d9fa02   Joe Thornber   dm: add thin prov...
1827
1828
1829
1830
1831
1832
1833
1834
  	up_read(&pmd->root_lock);
  
  	return r;
  }
  
  int dm_pool_get_metadata_dev_size(struct dm_pool_metadata *pmd,
  				  dm_block_t *result)
  {
da105ed5f   Joe Thornber   dm thin metadata:...
1835
  	int r = -EINVAL;
991d9fa02   Joe Thornber   dm: add thin prov...
1836
1837
  
  	down_read(&pmd->root_lock);
da105ed5f   Joe Thornber   dm thin metadata:...
1838
1839
  	if (!pmd->fail_io)
  		r = dm_sm_get_nr_blocks(pmd->metadata_sm, result);
991d9fa02   Joe Thornber   dm: add thin prov...
1840
1841
1842
1843
  	up_read(&pmd->root_lock);
  
  	return r;
  }
991d9fa02   Joe Thornber   dm: add thin prov...
1844
1845
  int dm_pool_get_data_dev_size(struct dm_pool_metadata *pmd, dm_block_t *result)
  {
da105ed5f   Joe Thornber   dm thin metadata:...
1846
  	int r = -EINVAL;
991d9fa02   Joe Thornber   dm: add thin prov...
1847
1848
  
  	down_read(&pmd->root_lock);
da105ed5f   Joe Thornber   dm thin metadata:...
1849
1850
  	if (!pmd->fail_io)
  		r = dm_sm_get_nr_blocks(pmd->data_sm, result);
991d9fa02   Joe Thornber   dm: add thin prov...
1851
1852
1853
1854
1855
1856
1857
  	up_read(&pmd->root_lock);
  
  	return r;
  }
  
  int dm_thin_get_mapped_count(struct dm_thin_device *td, dm_block_t *result)
  {
da105ed5f   Joe Thornber   dm thin metadata:...
1858
  	int r = -EINVAL;
991d9fa02   Joe Thornber   dm: add thin prov...
1859
1860
1861
  	struct dm_pool_metadata *pmd = td->pmd;
  
  	down_read(&pmd->root_lock);
da105ed5f   Joe Thornber   dm thin metadata:...
1862
1863
1864
1865
  	if (!pmd->fail_io) {
  		*result = td->mapped_blocks;
  		r = 0;
  	}
991d9fa02   Joe Thornber   dm: add thin prov...
1866
  	up_read(&pmd->root_lock);
da105ed5f   Joe Thornber   dm thin metadata:...
1867
  	return r;
991d9fa02   Joe Thornber   dm: add thin prov...
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
  }
  
  static int __highest_block(struct dm_thin_device *td, dm_block_t *result)
  {
  	int r;
  	__le64 value_le;
  	dm_block_t thin_root;
  	struct dm_pool_metadata *pmd = td->pmd;
  
  	r = dm_btree_lookup(&pmd->tl_info, pmd->root, &td->id, &value_le);
  	if (r)
  		return r;
  
  	thin_root = le64_to_cpu(value_le);
  
  	return dm_btree_find_highest_key(&pmd->bl_info, thin_root, result);
  }
  
  int dm_thin_get_highest_mapped_block(struct dm_thin_device *td,
  				     dm_block_t *result)
  {
da105ed5f   Joe Thornber   dm thin metadata:...
1889
  	int r = -EINVAL;
991d9fa02   Joe Thornber   dm: add thin prov...
1890
1891
1892
  	struct dm_pool_metadata *pmd = td->pmd;
  
  	down_read(&pmd->root_lock);
da105ed5f   Joe Thornber   dm thin metadata:...
1893
1894
  	if (!pmd->fail_io)
  		r = __highest_block(td, result);
991d9fa02   Joe Thornber   dm: add thin prov...
1895
1896
1897
1898
  	up_read(&pmd->root_lock);
  
  	return r;
  }
b17446df2   Joe Thornber   dm thin: refactor...
1899
  static int __resize_space_map(struct dm_space_map *sm, dm_block_t new_count)
991d9fa02   Joe Thornber   dm: add thin prov...
1900
1901
1902
  {
  	int r;
  	dm_block_t old_count;
b17446df2   Joe Thornber   dm thin: refactor...
1903
  	r = dm_sm_get_nr_blocks(sm, &old_count);
991d9fa02   Joe Thornber   dm: add thin prov...
1904
1905
1906
1907
1908
1909
1910
  	if (r)
  		return r;
  
  	if (new_count == old_count)
  		return 0;
  
  	if (new_count < old_count) {
b17446df2   Joe Thornber   dm thin: refactor...
1911
  		DMERR("cannot reduce size of space map");
991d9fa02   Joe Thornber   dm: add thin prov...
1912
1913
  		return -EINVAL;
  	}
b17446df2   Joe Thornber   dm thin: refactor...
1914
  	return dm_sm_extend(sm, new_count - old_count);
991d9fa02   Joe Thornber   dm: add thin prov...
1915
1916
1917
1918
  }
  
  int dm_pool_resize_data_dev(struct dm_pool_metadata *pmd, dm_block_t new_count)
  {
da105ed5f   Joe Thornber   dm thin metadata:...
1919
  	int r = -EINVAL;
991d9fa02   Joe Thornber   dm: add thin prov...
1920

6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1921
  	pmd_write_lock(pmd);
da105ed5f   Joe Thornber   dm thin metadata:...
1922
  	if (!pmd->fail_io)
b17446df2   Joe Thornber   dm thin: refactor...
1923
  		r = __resize_space_map(pmd->data_sm, new_count);
6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1924
  	pmd_write_unlock(pmd);
991d9fa02   Joe Thornber   dm: add thin prov...
1925
1926
1927
  
  	return r;
  }
12ba58af4   Joe Thornber   dm thin metadata:...
1928

24347e959   Joe Thornber   dm thin: detect m...
1929
1930
1931
  int dm_pool_resize_metadata_dev(struct dm_pool_metadata *pmd, dm_block_t new_count)
  {
  	int r = -EINVAL;
6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1932
  	pmd_write_lock(pmd);
3ab918281   Joe Thornber   dm thin metadata:...
1933
  	if (!pmd->fail_io) {
24347e959   Joe Thornber   dm thin: detect m...
1934
  		r = __resize_space_map(pmd->metadata_sm, new_count);
3ab918281   Joe Thornber   dm thin metadata:...
1935
1936
1937
  		if (!r)
  			__set_metadata_reserve(pmd);
  	}
6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1938
  	pmd_write_unlock(pmd);
24347e959   Joe Thornber   dm thin: detect m...
1939
1940
1941
  
  	return r;
  }
12ba58af4   Joe Thornber   dm thin metadata:...
1942
1943
  void dm_pool_metadata_read_only(struct dm_pool_metadata *pmd)
  {
6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1944
  	pmd_write_lock_in_core(pmd);
12ba58af4   Joe Thornber   dm thin metadata:...
1945
  	dm_bm_set_read_only(pmd->bm);
6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1946
  	pmd_write_unlock(pmd);
12ba58af4   Joe Thornber   dm thin metadata:...
1947
  }
ac8c3f3df   Joe Thornber   dm thin: generate...
1948

9b7aaa64f   Joe Thornber   dm thin: allow po...
1949
1950
  void dm_pool_metadata_read_write(struct dm_pool_metadata *pmd)
  {
6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1951
  	pmd_write_lock_in_core(pmd);
9b7aaa64f   Joe Thornber   dm thin: allow po...
1952
  	dm_bm_set_read_write(pmd->bm);
6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1953
  	pmd_write_unlock(pmd);
9b7aaa64f   Joe Thornber   dm thin: allow po...
1954
  }
ac8c3f3df   Joe Thornber   dm thin: generate...
1955
1956
1957
1958
1959
1960
  int dm_pool_register_metadata_threshold(struct dm_pool_metadata *pmd,
  					dm_block_t threshold,
  					dm_sm_threshold_fn fn,
  					void *context)
  {
  	int r;
6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1961
  	pmd_write_lock_in_core(pmd);
ac8c3f3df   Joe Thornber   dm thin: generate...
1962
  	r = dm_sm_register_threshold_callback(pmd->metadata_sm, threshold, fn, context);
6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1963
  	pmd_write_unlock(pmd);
ac8c3f3df   Joe Thornber   dm thin: generate...
1964
1965
1966
  
  	return r;
  }
07f2b6e03   Mike Snitzer   dm thin: ensure u...
1967

d2688d36c   Nikos Tsironis   dm thin metadata:...
1968
1969
1970
1971
1972
1973
1974
1975
1976
  void dm_pool_register_pre_commit_callback(struct dm_pool_metadata *pmd,
  					  dm_pool_pre_commit_fn fn,
  					  void *context)
  {
  	pmd_write_lock_in_core(pmd);
  	pmd->pre_commit_fn = fn;
  	pmd->pre_commit_context = context;
  	pmd_write_unlock(pmd);
  }
07f2b6e03   Mike Snitzer   dm thin: ensure u...
1977
1978
  int dm_pool_metadata_set_needs_check(struct dm_pool_metadata *pmd)
  {
54fa16ee5   Mike Snitzer   dm thin metadata:...
1979
  	int r = -EINVAL;
07f2b6e03   Mike Snitzer   dm thin: ensure u...
1980
1981
  	struct dm_block *sblock;
  	struct thin_disk_superblock *disk_super;
6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1982
  	pmd_write_lock(pmd);
54fa16ee5   Mike Snitzer   dm thin metadata:...
1983
1984
  	if (pmd->fail_io)
  		goto out;
07f2b6e03   Mike Snitzer   dm thin: ensure u...
1985
1986
1987
1988
  	pmd->flags |= THIN_METADATA_NEEDS_CHECK_FLAG;
  
  	r = superblock_lock(pmd, &sblock);
  	if (r) {
54fa16ee5   Mike Snitzer   dm thin metadata:...
1989
  		DMERR("couldn't lock superblock");
07f2b6e03   Mike Snitzer   dm thin: ensure u...
1990
1991
1992
1993
1994
1995
1996
1997
  		goto out;
  	}
  
  	disk_super = dm_block_data(sblock);
  	disk_super->flags = cpu_to_le32(pmd->flags);
  
  	dm_bm_unlock(sblock);
  out:
6a1b1ddc6   Mike Snitzer   dm thin metadata:...
1998
  	pmd_write_unlock(pmd);
07f2b6e03   Mike Snitzer   dm thin: ensure u...
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
  	return r;
  }
  
  bool dm_pool_metadata_needs_check(struct dm_pool_metadata *pmd)
  {
  	bool needs_check;
  
  	down_read(&pmd->root_lock);
  	needs_check = pmd->flags & THIN_METADATA_NEEDS_CHECK_FLAG;
  	up_read(&pmd->root_lock);
  
  	return needs_check;
  }
8a01a6af7   Joe Thornber   dm thin: prefetch...
2012
2013
2014
  
  void dm_pool_issue_prefetches(struct dm_pool_metadata *pmd)
  {
2eae9e448   Joe Thornber   dm thin metadata:...
2015
2016
2017
2018
  	down_read(&pmd->root_lock);
  	if (!pmd->fail_io)
  		dm_tm_issue_prefetches(pmd->tm);
  	up_read(&pmd->root_lock);
8a01a6af7   Joe Thornber   dm thin: prefetch...
2019
  }