Blame view

drivers/md/dm-snap-persistent.c 20.5 KB
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
1
2
3
4
5
6
7
8
  /*
   * Copyright (C) 2001-2002 Sistina Software (UK) Limited.
   * Copyright (C) 2006-2008 Red Hat GmbH
   *
   * This file is released under the GPL.
   */
  
  #include "dm-exception-store.h"
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
9
10
11
12
  
  #include <linux/mm.h>
  #include <linux/pagemap.h>
  #include <linux/vmalloc.h>
daaa5f7cb   Paul Gortmaker   md: Add in export...
13
  #include <linux/export.h>
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
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
  #include <linux/slab.h>
  #include <linux/dm-io.h>
  
  #define DM_MSG_PREFIX "persistent snapshot"
  #define DM_CHUNK_SIZE_DEFAULT_SECTORS 32	/* 16KB */
  
  /*-----------------------------------------------------------------
   * Persistent snapshots, by persistent we mean that the snapshot
   * will survive a reboot.
   *---------------------------------------------------------------*/
  
  /*
   * We need to store a record of which parts of the origin have
   * been copied to the snapshot device.  The snapshot code
   * requires that we copy exception chunks to chunk aligned areas
   * of the COW store.  It makes sense therefore, to store the
   * metadata in chunk size blocks.
   *
   * There is no backward or forward compatibility implemented,
   * snapshots with different disk versions than the kernel will
   * not be usable.  It is expected that "lvcreate" will blank out
   * the start of a fresh COW device before calling the snapshot
   * constructor.
   *
   * The first chunk of the COW device just contains the header.
   * After this there is a chunk filled with exception metadata,
   * followed by as many exception chunks as can fit in the
   * metadata areas.
   *
   * All on disk structures are in little-endian format.  The end
   * of the exceptions info is indicated by an exception with a
   * new_chunk of 0, which is invalid since it would point to the
   * header chunk.
   */
  
  /*
   * Magic for persistent snapshots: "SnAp" - Feeble isn't it.
   */
  #define SNAP_MAGIC 0x70416e53
  
  /*
   * The on-disk version of the metadata.
   */
  #define SNAPSHOT_DISK_VERSION 1
4454a6216   Mikulas Patocka   dm exception stor...
58
  #define NUM_SNAPSHOT_HDR_CHUNKS 1
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
59
  struct disk_header {
283a8328c   Alasdair G Kergon   dm: suppress endi...
60
  	__le32 magic;
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
61
62
63
64
65
  
  	/*
  	 * Is this snapshot valid.  There is no way of recovering
  	 * an invalid snapshot.
  	 */
283a8328c   Alasdair G Kergon   dm: suppress endi...
66
  	__le32 valid;
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
67
68
69
70
71
  
  	/*
  	 * Simple, incrementing version. no backward
  	 * compatibility.
  	 */
283a8328c   Alasdair G Kergon   dm: suppress endi...
72
  	__le32 version;
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
73
74
  
  	/* In sectors */
283a8328c   Alasdair G Kergon   dm: suppress endi...
75
76
  	__le32 chunk_size;
  } __packed;
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
77
78
  
  struct disk_exception {
283a8328c   Alasdair G Kergon   dm: suppress endi...
79
80
81
82
83
  	__le64 old_chunk;
  	__le64 new_chunk;
  } __packed;
  
  struct core_exception {
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
84
85
86
87
88
89
90
91
92
93
94
95
96
  	uint64_t old_chunk;
  	uint64_t new_chunk;
  };
  
  struct commit_callback {
  	void (*callback)(void *, int success);
  	void *context;
  };
  
  /*
   * The top level structure for a persistent exception store.
   */
  struct pstore {
71fab00a6   Jonathan Brassow   dm snapshot: remo...
97
  	struct dm_exception_store *store;
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
  	int version;
  	int valid;
  	uint32_t exceptions_per_area;
  
  	/*
  	 * Now that we have an asynchronous kcopyd there is no
  	 * need for large chunk sizes, so it wont hurt to have a
  	 * whole chunks worth of metadata in memory at once.
  	 */
  	void *area;
  
  	/*
  	 * An area of zeros used to clear the next area.
  	 */
  	void *zero_area;
  
  	/*
61578dcd3   Mikulas Patocka   dm snapshot: fix ...
115
116
117
118
119
120
121
  	 * An area used for header. The header can be written
  	 * concurrently with metadata (when invalidating the snapshot),
  	 * so it needs a separate buffer.
  	 */
  	void *header_area;
  
  	/*
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
122
123
124
125
126
127
128
  	 * Used to keep track of which metadata area the data in
  	 * 'chunk' refers to.
  	 */
  	chunk_t current_area;
  
  	/*
  	 * The next free chunk for an exception.
4454a6216   Mikulas Patocka   dm exception stor...
129
130
131
132
133
134
135
136
137
138
139
140
141
142
  	 *
  	 * When creating exceptions, all the chunks here and above are
  	 * free.  It holds the next chunk to be allocated.  On rare
  	 * occasions (e.g. after a system crash) holes can be left in
  	 * the exception store because chunks can be committed out of
  	 * order.
  	 *
  	 * When merging exceptions, it does not necessarily mean all the
  	 * chunks here and above are free.  It holds the value it would
  	 * have held if all chunks had been committed in order of
  	 * allocation.  Consequently the value may occasionally be
  	 * slightly too low, but since it's only used for 'status' and
  	 * it can never reach its minimum value too early this doesn't
  	 * matter.
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
143
  	 */
4454a6216   Mikulas Patocka   dm exception stor...
144

4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
  	chunk_t next_free;
  
  	/*
  	 * The index of next free exception in the current
  	 * metadata area.
  	 */
  	uint32_t current_committed;
  
  	atomic_t pending_count;
  	uint32_t callback_count;
  	struct commit_callback *callbacks;
  	struct dm_io_client *io_client;
  
  	struct workqueue_struct *metadata_wq;
  };
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
160
161
162
163
  static int alloc_area(struct pstore *ps)
  {
  	int r = -ENOMEM;
  	size_t len;
71fab00a6   Jonathan Brassow   dm snapshot: remo...
164
  	len = ps->store->chunk_size << SECTOR_SHIFT;
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
165
166
167
168
169
170
171
  
  	/*
  	 * Allocate the chunk_size block of memory that will hold
  	 * a single metadata area.
  	 */
  	ps->area = vmalloc(len);
  	if (!ps->area)
61578dcd3   Mikulas Patocka   dm snapshot: fix ...
172
  		goto err_area;
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
173

e29e65aac   Joe Perches   dm: use vzalloc
174
  	ps->zero_area = vzalloc(len);
61578dcd3   Mikulas Patocka   dm snapshot: fix ...
175
176
  	if (!ps->zero_area)
  		goto err_zero_area;
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
177

61578dcd3   Mikulas Patocka   dm snapshot: fix ...
178
179
180
  	ps->header_area = vmalloc(len);
  	if (!ps->header_area)
  		goto err_header_area;
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
181
  	return 0;
61578dcd3   Mikulas Patocka   dm snapshot: fix ...
182
183
184
185
186
187
188
189
190
  
  err_header_area:
  	vfree(ps->zero_area);
  
  err_zero_area:
  	vfree(ps->area);
  
  err_area:
  	return r;
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
191
192
193
194
  }
  
  static void free_area(struct pstore *ps)
  {
a32079ce1   Jonathan Brassow   dm snapshot: pers...
195
196
  	if (ps->area)
  		vfree(ps->area);
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
197
  	ps->area = NULL;
a32079ce1   Jonathan Brassow   dm snapshot: pers...
198
199
200
  
  	if (ps->zero_area)
  		vfree(ps->zero_area);
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
201
  	ps->zero_area = NULL;
61578dcd3   Mikulas Patocka   dm snapshot: fix ...
202
203
204
205
  
  	if (ps->header_area)
  		vfree(ps->header_area);
  	ps->header_area = NULL;
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
  }
  
  struct mdata_req {
  	struct dm_io_region *where;
  	struct dm_io_request *io_req;
  	struct work_struct work;
  	int result;
  };
  
  static void do_metadata(struct work_struct *work)
  {
  	struct mdata_req *req = container_of(work, struct mdata_req, work);
  
  	req->result = dm_io(req->io_req, 1, req->where, NULL);
  }
  
  /*
   * Read or write a chunk aligned and sized block of data from a device.
   */
02d2fd31d   Mikulas Patocka   dm snapshot: refa...
225
226
  static int chunk_io(struct pstore *ps, void *area, chunk_t chunk, int rw,
  		    int metadata)
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
227
228
  {
  	struct dm_io_region where = {
fc56f6fbc   Mike Snitzer   dm snapshot: move...
229
  		.bdev = dm_snap_cow(ps->store->snap)->bdev,
71fab00a6   Jonathan Brassow   dm snapshot: remo...
230
231
  		.sector = ps->store->chunk_size * chunk,
  		.count = ps->store->chunk_size,
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
232
233
234
235
  	};
  	struct dm_io_request io_req = {
  		.bi_rw = rw,
  		.mem.type = DM_IO_VMA,
02d2fd31d   Mikulas Patocka   dm snapshot: refa...
236
  		.mem.ptr.vma = area,
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
  		.client = ps->io_client,
  		.notify.fn = NULL,
  	};
  	struct mdata_req req;
  
  	if (!metadata)
  		return dm_io(&io_req, 1, &where, NULL);
  
  	req.where = &where;
  	req.io_req = &io_req;
  
  	/*
  	 * Issue the synchronous I/O from a different thread
  	 * to avoid generic_make_request recursion.
  	 */
ca1cab37d   Andrew Morton   workqueues: s/ON_...
252
  	INIT_WORK_ONSTACK(&req.work, do_metadata);
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
253
  	queue_work(ps->metadata_wq, &req.work);
239c8dd53   Tejun Heo   dm snapshot: pers...
254
  	flush_work(&req.work);
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
255
256
257
258
259
260
261
262
263
  
  	return req.result;
  }
  
  /*
   * Convert a metadata area index to a chunk index.
   */
  static chunk_t area_location(struct pstore *ps, chunk_t area)
  {
87c961cb7   Tomohiro Kusumi   dm snapshot: pers...
264
  	return NUM_SNAPSHOT_HDR_CHUNKS + ((ps->exceptions_per_area + 1) * area);
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
265
266
267
268
269
270
271
272
273
274
275
276
  }
  
  /*
   * Read or write a metadata area.  Remembering to skip the first
   * chunk which holds the header.
   */
  static int area_io(struct pstore *ps, int rw)
  {
  	int r;
  	chunk_t chunk;
  
  	chunk = area_location(ps, ps->current_area);
02d2fd31d   Mikulas Patocka   dm snapshot: refa...
277
  	r = chunk_io(ps, ps->area, chunk, rw, 0);
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
278
279
280
281
282
283
284
285
  	if (r)
  		return r;
  
  	return 0;
  }
  
  static void zero_memory_area(struct pstore *ps)
  {
71fab00a6   Jonathan Brassow   dm snapshot: remo...
286
  	memset(ps->area, 0, ps->store->chunk_size << SECTOR_SHIFT);
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
287
288
289
290
  }
  
  static int zero_disk_area(struct pstore *ps, chunk_t area)
  {
02d2fd31d   Mikulas Patocka   dm snapshot: refa...
291
  	return chunk_io(ps, ps->zero_area, area_location(ps, area), WRITE, 0);
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
292
293
294
295
296
297
  }
  
  static int read_header(struct pstore *ps, int *new_snapshot)
  {
  	int r;
  	struct disk_header *dh;
df96eee67   Mikulas Patocka   dm snapshot: use ...
298
  	unsigned chunk_size;
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
299
  	int chunk_size_supplied = 1;
ae0b7448e   Mikulas Patocka   dm snapshot: fix ...
300
  	char *chunk_err;
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
301
302
  
  	/*
df96eee67   Mikulas Patocka   dm snapshot: use ...
303
304
  	 * Use default chunk size (or logical_block_size, if larger)
  	 * if none supplied
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
305
  	 */
71fab00a6   Jonathan Brassow   dm snapshot: remo...
306
307
  	if (!ps->store->chunk_size) {
  		ps->store->chunk_size = max(DM_CHUNK_SIZE_DEFAULT_SECTORS,
fc56f6fbc   Mike Snitzer   dm snapshot: move...
308
309
  		    bdev_logical_block_size(dm_snap_cow(ps->store->snap)->
  					    bdev) >> 9);
71fab00a6   Jonathan Brassow   dm snapshot: remo...
310
311
  		ps->store->chunk_mask = ps->store->chunk_size - 1;
  		ps->store->chunk_shift = ffs(ps->store->chunk_size) - 1;
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
312
313
  		chunk_size_supplied = 0;
  	}
bda8efec5   Mikulas Patocka   dm io: use fixed ...
314
  	ps->io_client = dm_io_client_create();
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
315
316
317
318
319
320
  	if (IS_ERR(ps->io_client))
  		return PTR_ERR(ps->io_client);
  
  	r = alloc_area(ps);
  	if (r)
  		return r;
61578dcd3   Mikulas Patocka   dm snapshot: fix ...
321
  	r = chunk_io(ps, ps->header_area, 0, READ, 1);
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
322
323
  	if (r)
  		goto bad;
61578dcd3   Mikulas Patocka   dm snapshot: fix ...
324
  	dh = ps->header_area;
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
  
  	if (le32_to_cpu(dh->magic) == 0) {
  		*new_snapshot = 1;
  		return 0;
  	}
  
  	if (le32_to_cpu(dh->magic) != SNAP_MAGIC) {
  		DMWARN("Invalid or corrupt snapshot");
  		r = -ENXIO;
  		goto bad;
  	}
  
  	*new_snapshot = 0;
  	ps->valid = le32_to_cpu(dh->valid);
  	ps->version = le32_to_cpu(dh->version);
  	chunk_size = le32_to_cpu(dh->chunk_size);
ae0b7448e   Mikulas Patocka   dm snapshot: fix ...
341
  	if (ps->store->chunk_size == chunk_size)
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
342
  		return 0;
ae0b7448e   Mikulas Patocka   dm snapshot: fix ...
343
  	if (chunk_size_supplied)
df96eee67   Mikulas Patocka   dm snapshot: use ...
344
345
346
  		DMWARN("chunk size %u in device metadata overrides "
  		       "table chunk size of %u.",
  		       chunk_size, ps->store->chunk_size);
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
347
348
349
  
  	/* We had a bogus chunk_size. Fix stuff up. */
  	free_area(ps);
ae0b7448e   Mikulas Patocka   dm snapshot: fix ...
350
351
352
  	r = dm_exception_store_set_chunk_size(ps->store, chunk_size,
  					      &chunk_err);
  	if (r) {
df96eee67   Mikulas Patocka   dm snapshot: use ...
353
354
  		DMERR("invalid on-disk chunk size %u: %s.",
  		      chunk_size, chunk_err);
ae0b7448e   Mikulas Patocka   dm snapshot: fix ...
355
356
  		return r;
  	}
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
357

4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
358
359
360
361
362
363
364
365
366
367
368
  	r = alloc_area(ps);
  	return r;
  
  bad:
  	free_area(ps);
  	return r;
  }
  
  static int write_header(struct pstore *ps)
  {
  	struct disk_header *dh;
61578dcd3   Mikulas Patocka   dm snapshot: fix ...
369
  	memset(ps->header_area, 0, ps->store->chunk_size << SECTOR_SHIFT);
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
370

61578dcd3   Mikulas Patocka   dm snapshot: fix ...
371
  	dh = ps->header_area;
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
372
373
374
  	dh->magic = cpu_to_le32(SNAP_MAGIC);
  	dh->valid = cpu_to_le32(ps->valid);
  	dh->version = cpu_to_le32(ps->version);
71fab00a6   Jonathan Brassow   dm snapshot: remo...
375
  	dh->chunk_size = cpu_to_le32(ps->store->chunk_size);
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
376

61578dcd3   Mikulas Patocka   dm snapshot: fix ...
377
  	return chunk_io(ps, ps->header_area, 0, WRITE, 1);
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
378
379
380
381
382
383
384
385
386
387
388
389
390
  }
  
  /*
   * Access functions for the disk exceptions, these do the endian conversions.
   */
  static struct disk_exception *get_exception(struct pstore *ps, uint32_t index)
  {
  	BUG_ON(index >= ps->exceptions_per_area);
  
  	return ((struct disk_exception *) ps->area) + index;
  }
  
  static void read_exception(struct pstore *ps,
283a8328c   Alasdair G Kergon   dm: suppress endi...
391
  			   uint32_t index, struct core_exception *result)
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
392
  {
283a8328c   Alasdair G Kergon   dm: suppress endi...
393
  	struct disk_exception *de = get_exception(ps, index);
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
394
395
  
  	/* copy it */
283a8328c   Alasdair G Kergon   dm: suppress endi...
396
397
  	result->old_chunk = le64_to_cpu(de->old_chunk);
  	result->new_chunk = le64_to_cpu(de->new_chunk);
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
398
399
400
  }
  
  static void write_exception(struct pstore *ps,
283a8328c   Alasdair G Kergon   dm: suppress endi...
401
  			    uint32_t index, struct core_exception *e)
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
402
  {
283a8328c   Alasdair G Kergon   dm: suppress endi...
403
  	struct disk_exception *de = get_exception(ps, index);
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
404
405
  
  	/* copy it */
283a8328c   Alasdair G Kergon   dm: suppress endi...
406
407
  	de->old_chunk = cpu_to_le64(e->old_chunk);
  	de->new_chunk = cpu_to_le64(e->new_chunk);
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
408
  }
4454a6216   Mikulas Patocka   dm exception stor...
409
410
  static void clear_exception(struct pstore *ps, uint32_t index)
  {
283a8328c   Alasdair G Kergon   dm: suppress endi...
411
  	struct disk_exception *de = get_exception(ps, index);
4454a6216   Mikulas Patocka   dm exception stor...
412
413
  
  	/* clear it */
283a8328c   Alasdair G Kergon   dm: suppress endi...
414
415
  	de->old_chunk = 0;
  	de->new_chunk = 0;
4454a6216   Mikulas Patocka   dm exception stor...
416
  }
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
417
418
419
420
421
  /*
   * Registers the exceptions that are present in the current area.
   * 'full' is filled in to indicate if the area has been
   * filled.
   */
a159c1ac5   Jonathan Brassow   dm snapshot: exte...
422
423
424
425
426
  static int insert_exceptions(struct pstore *ps,
  			     int (*callback)(void *callback_context,
  					     chunk_t old, chunk_t new),
  			     void *callback_context,
  			     int *full)
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
427
428
429
  {
  	int r;
  	unsigned int i;
283a8328c   Alasdair G Kergon   dm: suppress endi...
430
  	struct core_exception e;
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
431
432
433
434
435
  
  	/* presume the area is full */
  	*full = 1;
  
  	for (i = 0; i < ps->exceptions_per_area; i++) {
283a8328c   Alasdair G Kergon   dm: suppress endi...
436
  		read_exception(ps, i, &e);
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
437
438
439
440
441
442
443
  
  		/*
  		 * If the new_chunk is pointing at the start of
  		 * the COW device, where the first metadata area
  		 * is we know that we've hit the end of the
  		 * exceptions.  Therefore the area is not full.
  		 */
283a8328c   Alasdair G Kergon   dm: suppress endi...
444
  		if (e.new_chunk == 0LL) {
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
445
446
447
448
449
450
451
452
  			ps->current_committed = i;
  			*full = 0;
  			break;
  		}
  
  		/*
  		 * Keep track of the start of the free chunks.
  		 */
283a8328c   Alasdair G Kergon   dm: suppress endi...
453
454
  		if (ps->next_free <= e.new_chunk)
  			ps->next_free = e.new_chunk + 1;
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
455
456
457
458
  
  		/*
  		 * Otherwise we add the exception to the snapshot.
  		 */
283a8328c   Alasdair G Kergon   dm: suppress endi...
459
  		r = callback(callback_context, e.old_chunk, e.new_chunk);
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
460
461
462
463
464
465
  		if (r)
  			return r;
  	}
  
  	return 0;
  }
a159c1ac5   Jonathan Brassow   dm snapshot: exte...
466
467
468
469
  static int read_exceptions(struct pstore *ps,
  			   int (*callback)(void *callback_context, chunk_t old,
  					   chunk_t new),
  			   void *callback_context)
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
470
471
472
473
474
475
476
477
478
479
480
  {
  	int r, full = 1;
  
  	/*
  	 * Keeping reading chunks and inserting exceptions until
  	 * we find a partially full area.
  	 */
  	for (ps->current_area = 0; full; ps->current_area++) {
  		r = area_io(ps, READ);
  		if (r)
  			return r;
a159c1ac5   Jonathan Brassow   dm snapshot: exte...
481
  		r = insert_exceptions(ps, callback, callback_context, &full);
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
482
483
484
485
486
487
488
489
490
491
492
493
494
  		if (r)
  			return r;
  	}
  
  	ps->current_area--;
  
  	return 0;
  }
  
  static struct pstore *get_info(struct dm_exception_store *store)
  {
  	return (struct pstore *) store->context;
  }
985903bb3   Mike Snitzer   dm snapshot: add ...
495
496
497
498
  static void persistent_usage(struct dm_exception_store *store,
  			     sector_t *total_sectors,
  			     sector_t *sectors_allocated,
  			     sector_t *metadata_sectors)
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
499
  {
985903bb3   Mike Snitzer   dm snapshot: add ...
500
501
502
  	struct pstore *ps = get_info(store);
  
  	*sectors_allocated = ps->next_free * store->chunk_size;
fc56f6fbc   Mike Snitzer   dm snapshot: move...
503
  	*total_sectors = get_dev_size(dm_snap_cow(store->snap)->bdev);
985903bb3   Mike Snitzer   dm snapshot: add ...
504
505
506
507
508
509
  
  	/*
  	 * First chunk is the fixed header.
  	 * Then there are (ps->current_area + 1) metadata chunks, each one
  	 * separated from the next by ps->exceptions_per_area data chunks.
  	 */
4454a6216   Mikulas Patocka   dm exception stor...
510
511
  	*metadata_sectors = (ps->current_area + 1 + NUM_SNAPSHOT_HDR_CHUNKS) *
  			    store->chunk_size;
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
512
  }
493df71c6   Jonathan Brassow   dm exception stor...
513
  static void persistent_dtr(struct dm_exception_store *store)
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
514
515
516
517
  {
  	struct pstore *ps = get_info(store);
  
  	destroy_workqueue(ps->metadata_wq);
a32079ce1   Jonathan Brassow   dm snapshot: pers...
518
519
520
521
  
  	/* Created in read_header */
  	if (ps->io_client)
  		dm_io_client_destroy(ps->io_client);
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
522
  	free_area(ps);
a32079ce1   Jonathan Brassow   dm snapshot: pers...
523
524
525
526
  
  	/* Allocated in persistent_read_metadata */
  	if (ps->callbacks)
  		vfree(ps->callbacks);
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
527
528
  	kfree(ps);
  }
a159c1ac5   Jonathan Brassow   dm snapshot: exte...
529
530
531
532
  static int persistent_read_metadata(struct dm_exception_store *store,
  				    int (*callback)(void *callback_context,
  						    chunk_t old, chunk_t new),
  				    void *callback_context)
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
533
534
535
536
537
538
539
540
541
542
543
544
545
546
  {
  	int r, uninitialized_var(new_snapshot);
  	struct pstore *ps = get_info(store);
  
  	/*
  	 * Read the snapshot header.
  	 */
  	r = read_header(ps, &new_snapshot);
  	if (r)
  		return r;
  
  	/*
  	 * Now we know correct chunk_size, complete the initialisation.
  	 */
71fab00a6   Jonathan Brassow   dm snapshot: remo...
547
548
  	ps->exceptions_per_area = (ps->store->chunk_size << SECTOR_SHIFT) /
  				  sizeof(struct disk_exception);
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
549
  	ps->callbacks = dm_vcalloc(ps->exceptions_per_area,
a2d2b0345   Jonathan Brassow   dm snapshot: styl...
550
  				   sizeof(*ps->callbacks));
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
  	if (!ps->callbacks)
  		return -ENOMEM;
  
  	/*
  	 * Do we need to setup a new snapshot ?
  	 */
  	if (new_snapshot) {
  		r = write_header(ps);
  		if (r) {
  			DMWARN("write_header failed");
  			return r;
  		}
  
  		ps->current_area = 0;
  		zero_memory_area(ps);
  		r = zero_disk_area(ps, 0);
f5acc8342   Jon Brassow   dm snapshot: avoi...
567
  		if (r)
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
568
  			DMWARN("zero_disk_area(0) failed");
f5acc8342   Jon Brassow   dm snapshot: avoi...
569
570
571
572
573
574
575
576
577
578
  		return r;
  	}
  	/*
  	 * Sanity checks.
  	 */
  	if (ps->version != SNAPSHOT_DISK_VERSION) {
  		DMWARN("unable to handle snapshot disk version %d",
  		       ps->version);
  		return -EINVAL;
  	}
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
579

f5acc8342   Jon Brassow   dm snapshot: avoi...
580
581
582
583
584
  	/*
  	 * Metadata are valid, but snapshot is invalidated
  	 */
  	if (!ps->valid)
  		return 1;
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
585

f5acc8342   Jon Brassow   dm snapshot: avoi...
586
587
588
589
  	/*
  	 * Read the metadata.
  	 */
  	r = read_exceptions(ps, callback, callback_context);
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
590

f5acc8342   Jon Brassow   dm snapshot: avoi...
591
  	return r;
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
592
  }
a159c1ac5   Jonathan Brassow   dm snapshot: exte...
593
  static int persistent_prepare_exception(struct dm_exception_store *store,
1d4989c85   Jon Brassow   dm snapshot: rena...
594
  					struct dm_exception *e)
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
595
596
597
598
  {
  	struct pstore *ps = get_info(store);
  	uint32_t stride;
  	chunk_t next_free;
fc56f6fbc   Mike Snitzer   dm snapshot: move...
599
  	sector_t size = get_dev_size(dm_snap_cow(store->snap)->bdev);
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
600
601
  
  	/* Is there enough room ? */
d02168495   Jonathan Brassow   dm exception stor...
602
  	if (size < ((ps->next_free + 1) * store->chunk_size))
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
  		return -ENOSPC;
  
  	e->new_chunk = ps->next_free;
  
  	/*
  	 * Move onto the next free pending, making sure to take
  	 * into account the location of the metadata chunks.
  	 */
  	stride = (ps->exceptions_per_area + 1);
  	next_free = ++ps->next_free;
  	if (sector_div(next_free, stride) == 1)
  		ps->next_free++;
  
  	atomic_inc(&ps->pending_count);
  	return 0;
  }
a159c1ac5   Jonathan Brassow   dm snapshot: exte...
619
  static void persistent_commit_exception(struct dm_exception_store *store,
1d4989c85   Jon Brassow   dm snapshot: rena...
620
  					struct dm_exception *e,
a159c1ac5   Jonathan Brassow   dm snapshot: exte...
621
622
  					void (*callback) (void *, int success),
  					void *callback_context)
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
623
624
625
  {
  	unsigned int i;
  	struct pstore *ps = get_info(store);
283a8328c   Alasdair G Kergon   dm: suppress endi...
626
  	struct core_exception ce;
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
627
  	struct commit_callback *cb;
283a8328c   Alasdair G Kergon   dm: suppress endi...
628
629
630
  	ce.old_chunk = e->old_chunk;
  	ce.new_chunk = e->new_chunk;
  	write_exception(ps, ps->current_committed++, &ce);
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
  
  	/*
  	 * Add the callback to the back of the array.  This code
  	 * is the only place where the callback array is
  	 * manipulated, and we know that it will never be called
  	 * multiple times concurrently.
  	 */
  	cb = ps->callbacks + ps->callback_count++;
  	cb->callback = callback;
  	cb->context = callback_context;
  
  	/*
  	 * If there are exceptions in flight and we have not yet
  	 * filled this metadata area there's nothing more to do.
  	 */
  	if (!atomic_dec_and_test(&ps->pending_count) &&
  	    (ps->current_committed != ps->exceptions_per_area))
  		return;
  
  	/*
  	 * If we completely filled the current area, then wipe the next one.
  	 */
  	if ((ps->current_committed == ps->exceptions_per_area) &&
a2d2b0345   Jonathan Brassow   dm snapshot: styl...
654
  	    zero_disk_area(ps, ps->current_area + 1))
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
655
656
657
658
659
  		ps->valid = 0;
  
  	/*
  	 * Commit exceptions to disk.
  	 */
d87f4c14f   Tejun Heo   dm: implement REQ...
660
  	if (ps->valid && area_io(ps, WRITE_FLUSH_FUA))
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
  		ps->valid = 0;
  
  	/*
  	 * Advance to the next area if this one is full.
  	 */
  	if (ps->current_committed == ps->exceptions_per_area) {
  		ps->current_committed = 0;
  		ps->current_area++;
  		zero_memory_area(ps);
  	}
  
  	for (i = 0; i < ps->callback_count; i++) {
  		cb = ps->callbacks + i;
  		cb->callback(cb->context, ps->valid);
  	}
  
  	ps->callback_count = 0;
  }
4454a6216   Mikulas Patocka   dm exception stor...
679
680
681
682
683
  static int persistent_prepare_merge(struct dm_exception_store *store,
  				    chunk_t *last_old_chunk,
  				    chunk_t *last_new_chunk)
  {
  	struct pstore *ps = get_info(store);
283a8328c   Alasdair G Kergon   dm: suppress endi...
684
  	struct core_exception ce;
4454a6216   Mikulas Patocka   dm exception stor...
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
  	int nr_consecutive;
  	int r;
  
  	/*
  	 * When current area is empty, move back to preceding area.
  	 */
  	if (!ps->current_committed) {
  		/*
  		 * Have we finished?
  		 */
  		if (!ps->current_area)
  			return 0;
  
  		ps->current_area--;
  		r = area_io(ps, READ);
  		if (r < 0)
  			return r;
  		ps->current_committed = ps->exceptions_per_area;
  	}
283a8328c   Alasdair G Kergon   dm: suppress endi...
704
705
706
  	read_exception(ps, ps->current_committed - 1, &ce);
  	*last_old_chunk = ce.old_chunk;
  	*last_new_chunk = ce.new_chunk;
4454a6216   Mikulas Patocka   dm exception stor...
707
708
709
710
711
712
713
714
  
  	/*
  	 * Find number of consecutive chunks within the current area,
  	 * working backwards.
  	 */
  	for (nr_consecutive = 1; nr_consecutive < ps->current_committed;
  	     nr_consecutive++) {
  		read_exception(ps, ps->current_committed - 1 - nr_consecutive,
283a8328c   Alasdair G Kergon   dm: suppress endi...
715
716
717
  			       &ce);
  		if (ce.old_chunk != *last_old_chunk - nr_consecutive ||
  		    ce.new_chunk != *last_new_chunk - nr_consecutive)
4454a6216   Mikulas Patocka   dm exception stor...
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
  			break;
  	}
  
  	return nr_consecutive;
  }
  
  static int persistent_commit_merge(struct dm_exception_store *store,
  				   int nr_merged)
  {
  	int r, i;
  	struct pstore *ps = get_info(store);
  
  	BUG_ON(nr_merged > ps->current_committed);
  
  	for (i = 0; i < nr_merged; i++)
  		clear_exception(ps, ps->current_committed - 1 - i);
762a80d9f   Mikulas Patocka   dm snapshot: flus...
734
  	r = area_io(ps, WRITE_FLUSH_FUA);
4454a6216   Mikulas Patocka   dm exception stor...
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
  	if (r < 0)
  		return r;
  
  	ps->current_committed -= nr_merged;
  
  	/*
  	 * At this stage, only persistent_usage() uses ps->next_free, so
  	 * we make no attempt to keep ps->next_free strictly accurate
  	 * as exceptions may have been committed out-of-order originally.
  	 * Once a snapshot has become merging, we set it to the value it
  	 * would have held had all the exceptions been committed in order.
  	 *
  	 * ps->current_area does not get reduced by prepare_merge() until
  	 * after commit_merge() has removed the nr_merged previous exceptions.
  	 */
87c961cb7   Tomohiro Kusumi   dm snapshot: pers...
750
751
  	ps->next_free = area_location(ps, ps->current_area) +
  			ps->current_committed + 1;
4454a6216   Mikulas Patocka   dm exception stor...
752
753
754
  
  	return 0;
  }
a159c1ac5   Jonathan Brassow   dm snapshot: exte...
755
  static void persistent_drop_snapshot(struct dm_exception_store *store)
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
756
757
758
759
760
761
762
  {
  	struct pstore *ps = get_info(store);
  
  	ps->valid = 0;
  	if (write_header(ps))
  		DMWARN("write header failed");
  }
493df71c6   Jonathan Brassow   dm exception stor...
763
764
  static int persistent_ctr(struct dm_exception_store *store,
  			  unsigned argc, char **argv)
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
765
766
767
768
  {
  	struct pstore *ps;
  
  	/* allocate the pstore */
a32079ce1   Jonathan Brassow   dm snapshot: pers...
769
  	ps = kzalloc(sizeof(*ps), GFP_KERNEL);
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
770
771
  	if (!ps)
  		return -ENOMEM;
71fab00a6   Jonathan Brassow   dm snapshot: remo...
772
  	ps->store = store;
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
773
774
775
  	ps->valid = 1;
  	ps->version = SNAPSHOT_DISK_VERSION;
  	ps->area = NULL;
61578dcd3   Mikulas Patocka   dm snapshot: fix ...
776
777
  	ps->zero_area = NULL;
  	ps->header_area = NULL;
4454a6216   Mikulas Patocka   dm exception stor...
778
  	ps->next_free = NUM_SNAPSHOT_HDR_CHUNKS + 1; /* header and 1st area */
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
779
780
781
782
783
  	ps->current_committed = 0;
  
  	ps->callback_count = 0;
  	atomic_set(&ps->pending_count, 0);
  	ps->callbacks = NULL;
239c8dd53   Tejun Heo   dm snapshot: pers...
784
  	ps->metadata_wq = alloc_workqueue("ksnaphd", WQ_MEM_RECLAIM, 0);
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
785
786
787
788
789
  	if (!ps->metadata_wq) {
  		kfree(ps);
  		DMERR("couldn't start header metadata update thread");
  		return -ENOMEM;
  	}
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
790
791
792
793
  	store->context = ps;
  
  	return 0;
  }
1e302a929   Jonathan Brassow   dm snapshot: move...
794
795
796
  static unsigned persistent_status(struct dm_exception_store *store,
  				  status_type_t status, char *result,
  				  unsigned maxlen)
493df71c6   Jonathan Brassow   dm exception stor...
797
  {
1e302a929   Jonathan Brassow   dm snapshot: move...
798
799
800
801
802
803
  	unsigned sz = 0;
  
  	switch (status) {
  	case STATUSTYPE_INFO:
  		break;
  	case STATUSTYPE_TABLE:
fc56f6fbc   Mike Snitzer   dm snapshot: move...
804
  		DMEMIT(" P %llu", (unsigned long long)store->chunk_size);
1e302a929   Jonathan Brassow   dm snapshot: move...
805
  	}
493df71c6   Jonathan Brassow   dm exception stor...
806
807
808
809
810
811
812
813
814
815
816
817
  
  	return sz;
  }
  
  static struct dm_exception_store_type _persistent_type = {
  	.name = "persistent",
  	.module = THIS_MODULE,
  	.ctr = persistent_ctr,
  	.dtr = persistent_dtr,
  	.read_metadata = persistent_read_metadata,
  	.prepare_exception = persistent_prepare_exception,
  	.commit_exception = persistent_commit_exception,
4454a6216   Mikulas Patocka   dm exception stor...
818
819
  	.prepare_merge = persistent_prepare_merge,
  	.commit_merge = persistent_commit_merge,
493df71c6   Jonathan Brassow   dm exception stor...
820
  	.drop_snapshot = persistent_drop_snapshot,
985903bb3   Mike Snitzer   dm snapshot: add ...
821
  	.usage = persistent_usage,
493df71c6   Jonathan Brassow   dm exception stor...
822
823
824
825
826
827
828
829
830
831
832
  	.status = persistent_status,
  };
  
  static struct dm_exception_store_type _persistent_compat_type = {
  	.name = "P",
  	.module = THIS_MODULE,
  	.ctr = persistent_ctr,
  	.dtr = persistent_dtr,
  	.read_metadata = persistent_read_metadata,
  	.prepare_exception = persistent_prepare_exception,
  	.commit_exception = persistent_commit_exception,
4454a6216   Mikulas Patocka   dm exception stor...
833
834
  	.prepare_merge = persistent_prepare_merge,
  	.commit_merge = persistent_commit_merge,
493df71c6   Jonathan Brassow   dm exception stor...
835
  	.drop_snapshot = persistent_drop_snapshot,
985903bb3   Mike Snitzer   dm snapshot: add ...
836
  	.usage = persistent_usage,
493df71c6   Jonathan Brassow   dm exception stor...
837
838
  	.status = persistent_status,
  };
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
839
840
  int dm_persistent_snapshot_init(void)
  {
493df71c6   Jonathan Brassow   dm exception stor...
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
  	int r;
  
  	r = dm_exception_store_type_register(&_persistent_type);
  	if (r) {
  		DMERR("Unable to register persistent exception store type");
  		return r;
  	}
  
  	r = dm_exception_store_type_register(&_persistent_compat_type);
  	if (r) {
  		DMERR("Unable to register old-style persistent exception "
  		      "store type");
  		dm_exception_store_type_unregister(&_persistent_type);
  		return r;
  	}
  
  	return r;
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
858
859
860
861
  }
  
  void dm_persistent_snapshot_exit(void)
  {
493df71c6   Jonathan Brassow   dm exception stor...
862
863
  	dm_exception_store_type_unregister(&_persistent_type);
  	dm_exception_store_type_unregister(&_persistent_compat_type);
4db6bfe02   Alasdair G Kergon   dm snapshot: spli...
864
  }