Skip to content

Commit 2dd9c25

Browse files
jthornberkergon
authored andcommitted
dm thin: support read only external snapshot origins
Support the use of an external _read only_ device as an origin for a thin device. Any read to an unprovisioned area of the thin device will be passed through to the origin. Writes trigger allocation of new blocks as usual. One possible use case for this would be VM hosts that want to run guests on thinly-provisioned volumes but have the base image on another device (possibly shared between many VMs). Signed-off-by: Joe Thornber <[email protected]> Signed-off-by: Mike Snitzer <[email protected]> Signed-off-by: Alasdair G Kergon <[email protected]>
1 parent c4a69ec commit 2dd9c25

File tree

2 files changed

+109
-15
lines changed

2 files changed

+109
-15
lines changed

Documentation/device-mapper/thin-provisioning.txt

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,38 @@ ii) Using an internal snapshot.
169169

170170
dmsetup create snap --table "0 2097152 thin /dev/mapper/pool 1"
171171

172+
External snapshots
173+
------------------
174+
175+
You can use an external _read only_ device as an origin for a
176+
thinly-provisioned volume. Any read to an unprovisioned area of the
177+
thin device will be passed through to the origin. Writes trigger
178+
the allocation of new blocks as usual.
179+
180+
One use case for this is VM hosts that want to run guests on
181+
thinly-provisioned volumes but have the base image on another device
182+
(possibly shared between many VMs).
183+
184+
You must not write to the origin device if you use this technique!
185+
Of course, you may write to the thin device and take internal snapshots
186+
of the thin volume.
187+
188+
i) Creating a snapshot of an external device
189+
190+
This is the same as creating a thin device.
191+
You don't mention the origin at this stage.
192+
193+
dmsetup message /dev/mapper/pool 0 "create_thin 0"
194+
195+
ii) Using a snapshot of an external device.
196+
197+
Append an extra parameter to the thin target specifying the origin:
198+
199+
dmsetup create snap --table "0 2097152 thin /dev/mapper/pool 0 /dev/image"
200+
201+
N.B. All descendants (internal snapshots) of this snapshot require the
202+
same extra origin parameter.
203+
172204
Deactivation
173205
------------
174206

@@ -254,7 +286,7 @@ iii) Messages
254286

255287
i) Constructor
256288

257-
thin <pool dev> <dev id>
289+
thin <pool dev> <dev id> [<external origin dev>]
258290

259291
pool dev:
260292
the thin-pool device, e.g. /dev/mapper/my_pool or 253:0
@@ -263,6 +295,11 @@ i) Constructor
263295
the internal device identifier of the device to be
264296
activated.
265297

298+
external origin dev:
299+
an optional block device outside the pool to be treated as a
300+
read-only snapshot origin: reads to unprovisioned areas of the
301+
thin target will be mapped to this device.
302+
266303
The pool doesn't store any size against the thin devices. If you
267304
load a thin target that is smaller than you've been using previously,
268305
then you'll have no access to blocks mapped beyond the end. If you

drivers/md/dm-thin.c

Lines changed: 71 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,7 @@ struct pool_c {
549549
*/
550550
struct thin_c {
551551
struct dm_dev *pool_dev;
552+
struct dm_dev *origin_dev;
552553
dm_thin_id dev_id;
553554

554555
struct pool *pool;
@@ -666,14 +667,16 @@ static void remap(struct thin_c *tc, struct bio *bio, dm_block_t block)
666667
(bio->bi_sector & pool->offset_mask);
667668
}
668669

669-
static void remap_and_issue(struct thin_c *tc, struct bio *bio,
670-
dm_block_t block)
670+
static void remap_to_origin(struct thin_c *tc, struct bio *bio)
671+
{
672+
bio->bi_bdev = tc->origin_dev->bdev;
673+
}
674+
675+
static void issue(struct thin_c *tc, struct bio *bio)
671676
{
672677
struct pool *pool = tc->pool;
673678
unsigned long flags;
674679

675-
remap(tc, bio, block);
676-
677680
/*
678681
* Batch together any FUA/FLUSH bios we find and then issue
679682
* a single commit for them in process_deferred_bios().
@@ -686,6 +689,19 @@ static void remap_and_issue(struct thin_c *tc, struct bio *bio,
686689
generic_make_request(bio);
687690
}
688691

692+
static void remap_to_origin_and_issue(struct thin_c *tc, struct bio *bio)
693+
{
694+
remap_to_origin(tc, bio);
695+
issue(tc, bio);
696+
}
697+
698+
static void remap_and_issue(struct thin_c *tc, struct bio *bio,
699+
dm_block_t block)
700+
{
701+
remap(tc, bio, block);
702+
issue(tc, bio);
703+
}
704+
689705
/*
690706
* wake_worker() is used when new work is queued and when pool_resume is
691707
* ready to continue deferred IO processing.
@@ -932,7 +948,8 @@ static struct new_mapping *get_next_mapping(struct pool *pool)
932948
}
933949

934950
static void schedule_copy(struct thin_c *tc, dm_block_t virt_block,
935-
dm_block_t data_origin, dm_block_t data_dest,
951+
struct dm_dev *origin, dm_block_t data_origin,
952+
dm_block_t data_dest,
936953
struct cell *cell, struct bio *bio)
937954
{
938955
int r;
@@ -964,7 +981,7 @@ static void schedule_copy(struct thin_c *tc, dm_block_t virt_block,
964981
} else {
965982
struct dm_io_region from, to;
966983

967-
from.bdev = tc->pool_dev->bdev;
984+
from.bdev = origin->bdev;
968985
from.sector = data_origin * pool->sectors_per_block;
969986
from.count = pool->sectors_per_block;
970987

@@ -982,6 +999,22 @@ static void schedule_copy(struct thin_c *tc, dm_block_t virt_block,
982999
}
9831000
}
9841001

1002+
static void schedule_internal_copy(struct thin_c *tc, dm_block_t virt_block,
1003+
dm_block_t data_origin, dm_block_t data_dest,
1004+
struct cell *cell, struct bio *bio)
1005+
{
1006+
schedule_copy(tc, virt_block, tc->pool_dev,
1007+
data_origin, data_dest, cell, bio);
1008+
}
1009+
1010+
static void schedule_external_copy(struct thin_c *tc, dm_block_t virt_block,
1011+
dm_block_t data_dest,
1012+
struct cell *cell, struct bio *bio)
1013+
{
1014+
schedule_copy(tc, virt_block, tc->origin_dev,
1015+
virt_block, data_dest, cell, bio);
1016+
}
1017+
9851018
static void schedule_zero(struct thin_c *tc, dm_block_t virt_block,
9861019
dm_block_t data_block, struct cell *cell,
9871020
struct bio *bio)
@@ -1128,8 +1161,8 @@ static void break_sharing(struct thin_c *tc, struct bio *bio, dm_block_t block,
11281161
r = alloc_data_block(tc, &data_block);
11291162
switch (r) {
11301163
case 0:
1131-
schedule_copy(tc, block, lookup_result->block,
1132-
data_block, cell, bio);
1164+
schedule_internal_copy(tc, block, lookup_result->block,
1165+
data_block, cell, bio);
11331166
break;
11341167

11351168
case -ENOSPC:
@@ -1203,7 +1236,10 @@ static void provision_block(struct thin_c *tc, struct bio *bio, dm_block_t block
12031236
r = alloc_data_block(tc, &data_block);
12041237
switch (r) {
12051238
case 0:
1206-
schedule_zero(tc, block, data_block, cell, bio);
1239+
if (tc->origin_dev)
1240+
schedule_external_copy(tc, block, data_block, cell, bio);
1241+
else
1242+
schedule_zero(tc, block, data_block, cell, bio);
12071243
break;
12081244

12091245
case -ENOSPC:
@@ -1254,7 +1290,11 @@ static void process_bio(struct thin_c *tc, struct bio *bio)
12541290
break;
12551291

12561292
case -ENODATA:
1257-
provision_block(tc, bio, block, cell);
1293+
if (bio_data_dir(bio) == READ && tc->origin_dev) {
1294+
cell_release_singleton(cell, bio);
1295+
remap_to_origin_and_issue(tc, bio);
1296+
} else
1297+
provision_block(tc, bio, block, cell);
12581298
break;
12591299

12601300
default:
@@ -2237,6 +2277,8 @@ static void thin_dtr(struct dm_target *ti)
22372277
__pool_dec(tc->pool);
22382278
dm_pool_close_thin_device(tc->td);
22392279
dm_put_device(ti, tc->pool_dev);
2280+
if (tc->origin_dev)
2281+
dm_put_device(ti, tc->origin_dev);
22402282
kfree(tc);
22412283

22422284
mutex_unlock(&dm_thin_pool_table.mutex);
@@ -2245,21 +2287,22 @@ static void thin_dtr(struct dm_target *ti)
22452287
/*
22462288
* Thin target parameters:
22472289
*
2248-
* <pool_dev> <dev_id>
2290+
* <pool_dev> <dev_id> [origin_dev]
22492291
*
22502292
* pool_dev: the path to the pool (eg, /dev/mapper/my_pool)
22512293
* dev_id: the internal device identifier
2294+
* origin_dev: a device external to the pool that should act as the origin
22522295
*/
22532296
static int thin_ctr(struct dm_target *ti, unsigned argc, char **argv)
22542297
{
22552298
int r;
22562299
struct thin_c *tc;
2257-
struct dm_dev *pool_dev;
2300+
struct dm_dev *pool_dev, *origin_dev;
22582301
struct mapped_device *pool_md;
22592302

22602303
mutex_lock(&dm_thin_pool_table.mutex);
22612304

2262-
if (argc != 2) {
2305+
if (argc != 2 && argc != 3) {
22632306
ti->error = "Invalid argument count";
22642307
r = -EINVAL;
22652308
goto out_unlock;
@@ -2272,6 +2315,15 @@ static int thin_ctr(struct dm_target *ti, unsigned argc, char **argv)
22722315
goto out_unlock;
22732316
}
22742317

2318+
if (argc == 3) {
2319+
r = dm_get_device(ti, argv[2], FMODE_READ, &origin_dev);
2320+
if (r) {
2321+
ti->error = "Error opening origin device";
2322+
goto bad_origin_dev;
2323+
}
2324+
tc->origin_dev = origin_dev;
2325+
}
2326+
22752327
r = dm_get_device(ti, argv[0], dm_table_get_mode(ti->table), &pool_dev);
22762328
if (r) {
22772329
ti->error = "Error opening pool device";
@@ -2324,6 +2376,9 @@ static int thin_ctr(struct dm_target *ti, unsigned argc, char **argv)
23242376
bad_common:
23252377
dm_put_device(ti, tc->pool_dev);
23262378
bad_pool_dev:
2379+
if (tc->origin_dev)
2380+
dm_put_device(ti, tc->origin_dev);
2381+
bad_origin_dev:
23272382
kfree(tc);
23282383
out_unlock:
23292384
mutex_unlock(&dm_thin_pool_table.mutex);
@@ -2382,6 +2437,8 @@ static int thin_status(struct dm_target *ti, status_type_t type,
23822437
DMEMIT("%s %lu",
23832438
format_dev_t(buf, tc->pool_dev->bdev->bd_dev),
23842439
(unsigned long) tc->dev_id);
2440+
if (tc->origin_dev)
2441+
DMEMIT(" %s", format_dev_t(buf, tc->origin_dev->bdev->bd_dev));
23852442
break;
23862443
}
23872444
}
@@ -2419,7 +2476,7 @@ static void thin_io_hints(struct dm_target *ti, struct queue_limits *limits)
24192476

24202477
static struct target_type thin_target = {
24212478
.name = "thin",
2422-
.version = {1, 0, 0},
2479+
.version = {1, 1, 0},
24232480
.module = THIS_MODULE,
24242481
.ctr = thin_ctr,
24252482
.dtr = thin_dtr,

0 commit comments

Comments
 (0)