diff options
Diffstat (limited to 'drivers/md')
-rw-r--r-- | drivers/md/dm-snap.c | 33 |
1 files changed, 27 insertions, 6 deletions
diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c index 23e3396e43b9..7ddee7c0c518 100644 --- a/drivers/md/dm-snap.c +++ b/drivers/md/dm-snap.c | |||
@@ -300,8 +300,10 @@ static void __insert_origin(struct origin *o) | |||
300 | * Returns number of snapshots registered using the supplied cow device, plus: | 300 | * Returns number of snapshots registered using the supplied cow device, plus: |
301 | * snap_src - a snapshot suitable for use as a source of exception handover | 301 | * snap_src - a snapshot suitable for use as a source of exception handover |
302 | * snap_dest - a snapshot capable of receiving exception handover. | 302 | * snap_dest - a snapshot capable of receiving exception handover. |
303 | * snap_merge - an existing snapshot-merge target linked to the same origin. | ||
304 | * There can be at most one snapshot-merge target. The parameter is optional. | ||
303 | * | 305 | * |
304 | * Possible return values and states: | 306 | * Possible return values and states of snap_src and snap_dest. |
305 | * 0: NULL, NULL - first new snapshot | 307 | * 0: NULL, NULL - first new snapshot |
306 | * 1: snap_src, NULL - normal snapshot | 308 | * 1: snap_src, NULL - normal snapshot |
307 | * 2: snap_src, snap_dest - waiting for handover | 309 | * 2: snap_src, snap_dest - waiting for handover |
@@ -310,7 +312,8 @@ static void __insert_origin(struct origin *o) | |||
310 | */ | 312 | */ |
311 | static int __find_snapshots_sharing_cow(struct dm_snapshot *snap, | 313 | static int __find_snapshots_sharing_cow(struct dm_snapshot *snap, |
312 | struct dm_snapshot **snap_src, | 314 | struct dm_snapshot **snap_src, |
313 | struct dm_snapshot **snap_dest) | 315 | struct dm_snapshot **snap_dest, |
316 | struct dm_snapshot **snap_merge) | ||
314 | { | 317 | { |
315 | struct dm_snapshot *s; | 318 | struct dm_snapshot *s; |
316 | struct origin *o; | 319 | struct origin *o; |
@@ -322,6 +325,8 @@ static int __find_snapshots_sharing_cow(struct dm_snapshot *snap, | |||
322 | goto out; | 325 | goto out; |
323 | 326 | ||
324 | list_for_each_entry(s, &o->snapshots, list) { | 327 | list_for_each_entry(s, &o->snapshots, list) { |
328 | if (dm_target_is_snapshot_merge(s->ti) && snap_merge) | ||
329 | *snap_merge = s; | ||
325 | if (!bdev_equal(s->cow->bdev, snap->cow->bdev)) | 330 | if (!bdev_equal(s->cow->bdev, snap->cow->bdev)) |
326 | continue; | 331 | continue; |
327 | 332 | ||
@@ -349,9 +354,11 @@ out: | |||
349 | static int __validate_exception_handover(struct dm_snapshot *snap) | 354 | static int __validate_exception_handover(struct dm_snapshot *snap) |
350 | { | 355 | { |
351 | struct dm_snapshot *snap_src = NULL, *snap_dest = NULL; | 356 | struct dm_snapshot *snap_src = NULL, *snap_dest = NULL; |
357 | struct dm_snapshot *snap_merge = NULL; | ||
352 | 358 | ||
353 | /* Does snapshot need exceptions handed over to it? */ | 359 | /* Does snapshot need exceptions handed over to it? */ |
354 | if ((__find_snapshots_sharing_cow(snap, &snap_src, &snap_dest) == 2) || | 360 | if ((__find_snapshots_sharing_cow(snap, &snap_src, &snap_dest, |
361 | &snap_merge) == 2) || | ||
355 | snap_dest) { | 362 | snap_dest) { |
356 | snap->ti->error = "Snapshot cow pairing for exception " | 363 | snap->ti->error = "Snapshot cow pairing for exception " |
357 | "table handover failed"; | 364 | "table handover failed"; |
@@ -365,6 +372,20 @@ static int __validate_exception_handover(struct dm_snapshot *snap) | |||
365 | if (!snap_src) | 372 | if (!snap_src) |
366 | return 0; | 373 | return 0; |
367 | 374 | ||
375 | /* | ||
376 | * Non-snapshot-merge handover? | ||
377 | */ | ||
378 | if (!dm_target_is_snapshot_merge(snap->ti)) | ||
379 | return 1; | ||
380 | |||
381 | /* | ||
382 | * Do not allow more than one merging snapshot. | ||
383 | */ | ||
384 | if (snap_merge) { | ||
385 | snap->ti->error = "A snapshot is already merging."; | ||
386 | return -EINVAL; | ||
387 | } | ||
388 | |||
368 | return 1; | 389 | return 1; |
369 | } | 390 | } |
370 | 391 | ||
@@ -933,7 +954,7 @@ static void snapshot_dtr(struct dm_target *ti) | |||
933 | 954 | ||
934 | down_read(&_origins_lock); | 955 | down_read(&_origins_lock); |
935 | /* Check whether exception handover must be cancelled */ | 956 | /* Check whether exception handover must be cancelled */ |
936 | (void) __find_snapshots_sharing_cow(s, &snap_src, &snap_dest); | 957 | (void) __find_snapshots_sharing_cow(s, &snap_src, &snap_dest, NULL); |
937 | if (snap_src && snap_dest && (s == snap_src)) { | 958 | if (snap_src && snap_dest && (s == snap_src)) { |
938 | down_write(&snap_dest->lock); | 959 | down_write(&snap_dest->lock); |
939 | snap_dest->valid = 0; | 960 | snap_dest->valid = 0; |
@@ -1399,7 +1420,7 @@ static int snapshot_preresume(struct dm_target *ti) | |||
1399 | struct dm_snapshot *snap_src = NULL, *snap_dest = NULL; | 1420 | struct dm_snapshot *snap_src = NULL, *snap_dest = NULL; |
1400 | 1421 | ||
1401 | down_read(&_origins_lock); | 1422 | down_read(&_origins_lock); |
1402 | (void) __find_snapshots_sharing_cow(s, &snap_src, &snap_dest); | 1423 | (void) __find_snapshots_sharing_cow(s, &snap_src, &snap_dest, NULL); |
1403 | if (snap_src && snap_dest) { | 1424 | if (snap_src && snap_dest) { |
1404 | down_read(&snap_src->lock); | 1425 | down_read(&snap_src->lock); |
1405 | if (s == snap_src) { | 1426 | if (s == snap_src) { |
@@ -1424,7 +1445,7 @@ static void snapshot_resume(struct dm_target *ti) | |||
1424 | struct dm_snapshot *snap_src = NULL, *snap_dest = NULL; | 1445 | struct dm_snapshot *snap_src = NULL, *snap_dest = NULL; |
1425 | 1446 | ||
1426 | down_read(&_origins_lock); | 1447 | down_read(&_origins_lock); |
1427 | (void) __find_snapshots_sharing_cow(s, &snap_src, &snap_dest); | 1448 | (void) __find_snapshots_sharing_cow(s, &snap_src, &snap_dest, NULL); |
1428 | if (snap_src && snap_dest) { | 1449 | if (snap_src && snap_dest) { |
1429 | down_write(&snap_src->lock); | 1450 | down_write(&snap_src->lock); |
1430 | down_write_nested(&snap_dest->lock, SINGLE_DEPTH_NESTING); | 1451 | down_write_nested(&snap_dest->lock, SINGLE_DEPTH_NESTING); |