1 /* $NetBSD: dm_target_snapshot.c,v 1.11 2009/12/01 23:12:10 haad Exp $ */
4 * Copyright (c) 2008 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
33 * 1. Suspend my_data to temporarily stop any I/O while the snapshot is being
35 * dmsetup suspend my_data
37 * 2. Create the snapshot-origin device with no table.
38 * dmsetup create my_data_org
40 * 3. Read the table from my_data and load it into my_data_org.
41 * dmsetup table my_data | dmsetup load my_data_org
43 * 4. Resume this new table.
44 * dmsetup resume my_data_org
46 * 5. Create the snapshot device with no table.
47 * dmsetup create my_data_snap
49 * 6. Load the table into my_data_snap. This uses /dev/hdd1 as the COW device and
50 * uses a 32kB chunk-size.
51 * echo "0 `blockdev --getsize /dev/mapper/my_data` snapshot \
52 * /dev/mapper/my_data_org /dev/hdd1 p 64" | dmsetup load my_data_snap
54 * 7. Reload my_data as a snapshot-origin device that points to my_data_org.
55 * echo "0 `blockdev --getsize /dev/mapper/my_data` snapshot-origin \
56 * /dev/mapper/my_data_org" | dmsetup load my_data
58 * 8. Resume the snapshot and origin devices.
59 * dmsetup resume my_data_snap
60 * dmsetup resume my_data
62 * Before snapshot creation
64 * | my_data; 0 1024 linear /dev/sd1a 384|
66 * After snapshot creation
67 * |my_data_org;0 1024 linear /dev/sd1a 384|
69 * |my_data; 0 1024 snapshot-origin /dev/vg00/my_data_org|
71 * |my_data_snap; 0 1024 snapshot /dev/vg00/my_data /dev/mapper/my_data_cow P 8
73 * |my_data_cow; 0 256 linear /dev/sd1a 1408|
77 * This file implements initial version of device-mapper snapshot target.
79 #include <sys/types.h>
80 #include <sys/param.h>
84 #include <sys/vnode.h>
88 #ifdef DM_TARGET_MODULE
90 * Every target can be compiled directly to dm driver or as a
91 * separate module this part of target is used for loading targets
93 * Target can be unloaded from kernel only if there are no users of
94 * it e.g. there are no devices which uses that target.
96 #include <sys/kernel.h>
97 #include <sys/module.h>
99 MODULE(MODULE_CLASS_MISC
, dm_target_snapshot
, "dm");
102 dm_target_snapshot_modcmd(modcmd_t cmd
, void *arg
)
104 dm_target_t
*dmt
, *dmt1
;
111 case MODULE_CMD_INIT
:
112 if (((dmt
= dm_target_lookup("snapshot")) != NULL
)) {
113 dm_target_unbusy(dmt
);
116 if (((dmt
= dm_target_lookup("snapshot-origin")) != NULL
)) {
117 dm_target_unbusy(dmt
);
120 dmt
= dm_target_alloc("snapshot");
121 dmt1
= dm_target_alloc("snapshot-origin");
126 strlcpy(dmt
->name
, "snapshot", DM_MAX_TYPE_NAME
);
127 dmt
->init
= &dm_target_snapshot_init
;
128 dmt
->status
= &dm_target_snapshot_status
;
129 dmt
->strategy
= &dm_target_snapshot_strategy
;
130 dmt
->deps
= &dm_target_snapshot_deps
;
131 dmt
->destroy
= &dm_target_snapshot_destroy
;
132 dmt
->upcall
= &dm_target_snapshot_upcall
;
134 r
= dm_target_insert(dmt
);
136 dmt1
->version
[0] = 1;
137 dmt1
->version
[1] = 0;
138 dmt1
->version
[2] = 5;
139 strlcpy(dmt1
->name
, "snapshot-origin", DM_MAX_TYPE_NAME
);
140 dmt1
->init
= &dm_target_snapshot_orig_init
;
141 dmt1
->status
= &dm_target_snapshot_orig_status
;
142 dmt1
->strategy
= &dm_target_snapshot_orig_strategy
;
143 dmt1
->deps
= &dm_target_snapshot_orig_deps
;
144 dmt1
->destroy
= &dm_target_snapshot_orig_destroy
;
145 dmt1
->upcall
= &dm_target_snapshot_orig_upcall
;
147 r
= dm_target_insert(dmt1
);
150 case MODULE_CMD_FINI
:
152 * Try to remove snapshot target if it works remove snap-origin
153 * it is not possible to remove snapshot and do not remove
154 * snap-origin because they are used together.
156 if ((r
= dm_target_rem("snapshot")) == 0)
157 r
= dm_target_rem("snapshot-origin");
161 case MODULE_CMD_STAT
:
173 * Init function called from dm_table_load_ioctl.
174 * argv: /dev/mapper/my_data_org /dev/mapper/tsc_cow_dev p 64
175 * snapshot_origin device, cow device, persistent flag, chunk size
178 dm_target_snapshot_init(dm_dev_t
* dmv
, void **target_config
, char *params
)
180 dm_target_snapshot_config_t
*tsc
;
181 dm_pdev_t
*dmp_snap
, *dmp_cow
;
189 * Parse a string, containing tokens delimited by white space,
190 * into an argument vector
192 for (ap
= argv
; ap
< &argv
[4] &&
193 (*ap
= strsep(¶ms
, " \t")) != NULL
;) {
198 printf("Snapshot target init function called!!\n");
199 printf("Snapshotted device: %s, cow device %s,\n\t persistent flag: %s, "
200 "chunk size: %s\n", argv
[0], argv
[1], argv
[2], argv
[3]);
202 /* Insert snap device to global pdev list */
203 if ((dmp_snap
= dm_pdev_insert(argv
[0])) == NULL
)
206 if ((tsc
= kmem_alloc(sizeof(dm_target_snapshot_config_t
), KM_NOSLEEP
))
210 tsc
->tsc_persistent_dev
= 0;
212 /* There is now cow device for nonpersistent snapshot devices */
213 if (strcmp(argv
[2], "p") == 0) {
214 tsc
->tsc_persistent_dev
= 1;
216 /* Insert cow device to global pdev list */
217 if ((dmp_cow
= dm_pdev_insert(argv
[1])) == NULL
)
220 tsc
->tsc_chunk_size
= atoi(argv
[3]);
222 tsc
->tsc_snap_dev
= dmp_snap
;
223 tsc
->tsc_cow_dev
= dmp_cow
;
225 *target_config
= tsc
;
227 dmv
->dev_type
= DM_SNAPSHOT_DEV
;
232 * Status routine is called to get params string, which is target
233 * specific. When dm_table_status_ioctl is called with flag
234 * DM_STATUS_TABLE_FLAG I have to sent params string back.
237 dm_target_snapshot_status(void *target_config
)
239 dm_target_snapshot_config_t
*tsc
;
243 size_t prm_len
, cow_len
;
244 char *params
, *cow_name
;
253 printf("Snapshot target status function called\n");
255 /* count number of chars in offset */
256 for (i
= tsc
->tsc_chunk_size
; i
!= 0; i
/= 10)
259 if (tsc
->tsc_persistent_dev
)
260 cow_len
= strlen(tsc
->tsc_cow_dev
->name
);
262 /* length of names + count of chars + spaces and null char */
263 prm_len
= strlen(tsc
->tsc_snap_dev
->name
) + cow_len
+ count
+ 5;
265 if ((params
= kmem_alloc(prm_len
, KM_NOSLEEP
)) == NULL
)
268 printf("%s %s %s %" PRIu64
"\n", tsc
->tsc_snap_dev
->name
,
269 tsc
->tsc_cow_dev
->name
, tsc
->tsc_persistent_dev
? "p" : "n",
270 tsc
->tsc_chunk_size
);
272 snprintf(params
, prm_len
, "%s %s %s %" PRIu64
, tsc
->tsc_snap_dev
->name
,
273 tsc
->tsc_persistent_dev
? tsc
->tsc_cow_dev
->name
: "",
274 tsc
->tsc_persistent_dev
? "p" : "n",
275 tsc
->tsc_chunk_size
);
279 /* Strategy routine called from dm_strategy. */
281 dm_target_snapshot_strategy(dm_table_entry_t
* table_en
, struct buf
* bp
)
284 printf("Snapshot target read function called!!\n");
293 /* Doesn't do anything here. */
295 dm_target_snapshot_destroy(dm_table_entry_t
* table_en
)
297 dm_target_snapshot_config_t
*tsc
;
300 * Destroy function is called for every target even if it
301 * doesn't have target_config.
304 if (table_en
->target_config
== NULL
)
307 printf("Snapshot target destroy function called\n");
309 tsc
= table_en
->target_config
;
311 /* Decrement pdev ref counter if 0 remove it */
312 dm_pdev_decr(tsc
->tsc_snap_dev
);
314 if (tsc
->tsc_persistent_dev
)
315 dm_pdev_decr(tsc
->tsc_cow_dev
);
317 /* Unbusy target so we can unload it */
318 dm_target_unbusy(table_en
->target
);
320 kmem_free(table_en
->target_config
, sizeof(dm_target_snapshot_config_t
));
322 table_en
->target_config
= NULL
;
326 /* Add this target dependiences to prop_array_t */
328 dm_target_snapshot_deps(dm_table_entry_t
* table_en
,
329 prop_array_t prop_array
)
331 dm_target_snapshot_config_t
*tsc
;
336 if (table_en
->target_config
== NULL
)
339 tsc
= table_en
->target_config
;
341 if ((error
= VOP_GETATTR(tsc
->tsc_snap_dev
->pdev_vnode
, &va
, curlwp
->l_cred
)) != 0)
344 prop_array_add_uint64(prop_array
, (uint64_t) va
.va_rdev
);
346 if (tsc
->tsc_persistent_dev
) {
348 if ((error
= VOP_GETATTR(tsc
->tsc_cow_dev
->pdev_vnode
, &va
,
349 curlwp
->l_cred
)) != 0)
352 prop_array_add_uint64(prop_array
, (uint64_t) va
.va_rdev
);
357 /* Upcall is used to inform other depended devices about IO. */
359 dm_target_snapshot_upcall(dm_table_entry_t
* table_en
, struct buf
* bp
)
361 printf("dm_target_snapshot_upcall called\n");
363 printf("upcall buf flags %s %s\n",
364 (bp
->b_flags
& B_WRITE
) ? "B_WRITE" : "",
365 (bp
->b_flags
& B_READ
) ? "B_READ" : "");
370 * dm target snapshot origin routines.
372 * Keep for compatibility with linux lvm2tools. They use two targets
373 * to implement snapshots. Snapshot target will implement exception
374 * store and snapshot origin will implement device which calls every
375 * snapshot device when write is done on master device.
379 * Init function called from dm_table_load_ioctl.
381 * argv: /dev/mapper/my_data_real
384 dm_target_snapshot_orig_init(dm_dev_t
* dmv
, void **target_config
,
385 prop_dictionary_t dict
)
387 dm_target_snapshot_origin_config_t
*tsoc
;
393 printf("Snapshot origin target init function called!!\n");
394 printf("Parent device: %s\n", params
);
396 /* Insert snap device to global pdev list */
397 if ((dmp_real
= dm_pdev_insert(params
)) == NULL
)
400 if ((tsoc
= kmem_alloc(sizeof(dm_target_snapshot_origin_config_t
), KM_NOSLEEP
))
404 tsoc
->tsoc_real_dev
= dmp_real
;
406 dmv
->dev_type
= DM_SNAPSHOT_ORIG_DEV
;
408 *target_config
= tsoc
;
413 * Status routine is called to get params string, which is target
414 * specific. When dm_table_status_ioctl is called with flag
415 * DM_STATUS_TABLE_FLAG I have to sent params string back.
418 dm_target_snapshot_orig_status(void *target_config
)
420 dm_target_snapshot_origin_config_t
*tsoc
;
425 tsoc
= target_config
;
429 printf("Snapshot origin target status function called\n");
431 /* length of names + count of chars + spaces and null char */
432 prm_len
= strlen(tsoc
->tsoc_real_dev
->name
) + 1;
434 printf("real_dev name %s\n", tsoc
->tsoc_real_dev
->name
);
436 if ((params
= kmem_alloc(prm_len
, KM_NOSLEEP
)) == NULL
)
439 printf("%s\n", tsoc
->tsoc_real_dev
->name
);
441 snprintf(params
, prm_len
, "%s", tsoc
->tsoc_real_dev
->name
);
445 /* Strategy routine called from dm_strategy. */
447 dm_target_snapshot_orig_strategy(dm_table_entry_t
* table_en
, struct buf
* bp
)
450 printf("Snapshot_Orig target read function called!!\n");
459 /* Decrement pdev and free allocated space. */
461 dm_target_snapshot_orig_destroy(dm_table_entry_t
* table_en
)
463 dm_target_snapshot_origin_config_t
*tsoc
;
466 * Destroy function is called for every target even if it
467 * doesn't have target_config.
470 if (table_en
->target_config
== NULL
)
473 tsoc
= table_en
->target_config
;
475 /* Decrement pdev ref counter if 0 remove it */
476 dm_pdev_decr(tsoc
->tsoc_real_dev
);
478 /* Unbusy target so we can unload it */
479 dm_target_unbusy(table_en
->target
);
481 kmem_free(table_en
->target_config
, sizeof(dm_target_snapshot_origin_config_t
));
483 table_en
->target_config
= NULL
;
488 * Get target deps and add them to prop_array_t.
491 dm_target_snapshot_orig_deps(dm_table_entry_t
* table_en
,
492 prop_array_t prop_array
)
494 dm_target_snapshot_origin_config_t
*tsoc
;
499 if (table_en
->target_config
== NULL
)
502 tsoc
= table_en
->target_config
;
504 if ((error
= VOP_GETATTR(tsoc
->tsoc_real_dev
->pdev_vnode
, &va
,
505 curlwp
->l_cred
)) != 0)
508 prop_array_add_uint64(prop_array
, (uint64_t) va
.va_rdev
);
512 /* Unsupported for this target. */
514 dm_target_snapshot_orig_upcall(dm_table_entry_t
* table_en
, struct buf
* bp
)