1 /* $NetBSD: dm_target_snapshot.c,v 1.38 2020/01/21 16:27:53 tkusumi Exp $ */ 2 3 /* 4 * Copyright (c) 2008 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Adam Hamsik. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 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. 18 * 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. 30 */ 31 #include <sys/cdefs.h> 32 __KERNEL_RCSID(0, "$NetBSD: dm_target_snapshot.c,v 1.38 2020/01/21 16:27:53 tkusumi Exp $"); 33 34 /* 35 * 1. Suspend my_data to temporarily stop any I/O while the snapshot is being 36 * activated. 37 * dmsetup suspend my_data 38 * 39 * 2. Create the snapshot-origin device with no table. 40 * dmsetup create my_data_org 41 * 42 * 3. Read the table from my_data and load it into my_data_org. 43 * dmsetup table my_data | dmsetup load my_data_org 44 * 45 * 4. Resume this new table. 46 * dmsetup resume my_data_org 47 * 48 * 5. Create the snapshot device with no table. 49 * dmsetup create my_data_snap 50 * 51 * 6. Load the table into my_data_snap. This uses /dev/hdd1 as the COW device and 52 * uses a 32kB chunk-size. 53 * echo "0 `blockdev --getsize /dev/mapper/my_data` snapshot \ 54 * /dev/mapper/my_data_org /dev/hdd1 p 64" | dmsetup load my_data_snap 55 * 56 * 7. Reload my_data as a snapshot-origin device that points to my_data_org. 57 * echo "0 `blockdev --getsize /dev/mapper/my_data` snapshot-origin \ 58 * /dev/mapper/my_data_org" | dmsetup load my_data 59 * 60 * 8. Resume the snapshot and origin devices. 61 * dmsetup resume my_data_snap 62 * dmsetup resume my_data 63 * 64 * Before snapshot creation 65 * dev_name; dev table 66 * | my_data; 0 1024 linear /dev/sd1a 384| 67 * 68 * After snapshot creation 69 * |my_data_org;0 1024 linear /dev/sd1a 384| 70 * / 71 * |my_data; 0 1024 snapshot-origin /dev/vg00/my_data_org| 72 * / 73 * |my_data_snap; 0 1024 snapshot /dev/vg00/my_data /dev/mapper/my_data_cow P 8 74 * \ 75 * |my_data_cow; 0 256 linear /dev/sd1a 1408| 76 */ 77 78 /* 79 * This file implements initial version of device-mapper snapshot target. 80 */ 81 #include <sys/types.h> 82 #include <sys/param.h> 83 #include <sys/buf.h> 84 #include <sys/kmem.h> 85 86 #include "dm.h" 87 88 /* dm_target_snapshot.c */ 89 int dm_target_snapshot_init(dm_table_entry_t *, int, char **); 90 char *dm_target_snapshot_table(void *); 91 int dm_target_snapshot_strategy(dm_table_entry_t *, struct buf *); 92 int dm_target_snapshot_sync(dm_table_entry_t *); 93 int dm_target_snapshot_destroy(dm_table_entry_t *); 94 //int dm_target_snapshot_upcall(dm_table_entry_t *, struct buf *); 95 96 /* dm snapshot origin driver */ 97 int dm_target_snapshot_orig_init(dm_table_entry_t *, int, char **); 98 char *dm_target_snapshot_orig_table(void *); 99 int dm_target_snapshot_orig_strategy(dm_table_entry_t *, struct buf *); 100 int dm_target_snapshot_orig_sync(dm_table_entry_t *); 101 int dm_target_snapshot_orig_destroy(dm_table_entry_t *); 102 //int dm_target_snapshot_orig_upcall(dm_table_entry_t *, struct buf *); 103 104 typedef struct target_snapshot_config { 105 dm_pdev_t *tsc_snap_dev; 106 /* cow dev is set only for persistent snapshot devices */ 107 dm_pdev_t *tsc_cow_dev; 108 109 uint64_t tsc_chunk_size; 110 uint32_t tsc_persistent_dev; 111 } dm_target_snapshot_config_t; 112 113 typedef struct target_snapshot_origin_config { 114 dm_pdev_t *tsoc_real_dev; 115 /* list of snapshots ? */ 116 } dm_target_snapshot_origin_config_t; 117 118 #ifdef DM_TARGET_MODULE 119 /* 120 * Every target can be compiled directly to dm driver or as a 121 * separate module this part of target is used for loading targets 122 * to dm driver. 123 * Target can be unloaded from kernel only if there are no users of 124 * it e.g. there are no devices which uses that target. 125 */ 126 #include <sys/kernel.h> 127 #include <sys/module.h> 128 129 MODULE(MODULE_CLASS_MISC, dm_target_snapshot, "dm"); 130 131 static int 132 dm_target_snapshot_modcmd(modcmd_t cmd, void *arg) 133 { 134 dm_target_t *dmt, *dmt1; 135 int r; 136 137 switch (cmd) { 138 case MODULE_CMD_INIT: 139 if (((dmt = dm_target_lookup("snapshot")) != NULL)) { 140 dm_target_unbusy(dmt); 141 return EEXIST; 142 } 143 if (((dmt = dm_target_lookup("snapshot-origin")) != NULL)) { 144 dm_target_unbusy(dmt); 145 return EEXIST; 146 } 147 dmt = dm_target_alloc("snapshot"); 148 dmt1 = dm_target_alloc("snapshot-origin"); 149 150 dmt->version[0] = 1; 151 dmt->version[1] = 0; 152 dmt->version[2] = 5; 153 dmt->init = &dm_target_snapshot_init; 154 dmt->table = &dm_target_snapshot_table; 155 dmt->strategy = &dm_target_snapshot_strategy; 156 dmt->sync = &dm_target_snapshot_sync; 157 dmt->destroy = &dm_target_snapshot_destroy; 158 //dmt->upcall = &dm_target_snapshot_upcall; 159 160 r = dm_target_insert(dmt); 161 162 dmt1->version[0] = 1; 163 dmt1->version[1] = 0; 164 dmt1->version[2] = 5; 165 dmt1->init = &dm_target_snapshot_orig_init; 166 dmt1->table = &dm_target_snapshot_orig_table; 167 dmt1->strategy = &dm_target_snapshot_orig_strategy; 168 dmt1->sync = &dm_target_snapshot_orig_sync; 169 dmt1->destroy = &dm_target_snapshot_orig_destroy; 170 //dmt1->upcall = &dm_target_snapshot_orig_upcall; 171 172 r = dm_target_insert(dmt1); 173 break; 174 175 case MODULE_CMD_FINI: 176 /* 177 * Try to remove snapshot target if it works remove snap-origin 178 * it is not possible to remove snapshot and do not remove 179 * snap-origin because they are used together. 180 */ 181 if ((r = dm_target_rem("snapshot")) == 0) 182 r = dm_target_rem("snapshot-origin"); 183 184 break; 185 186 case MODULE_CMD_STAT: 187 return ENOTTY; 188 189 default: 190 return ENOTTY; 191 } 192 193 return r; 194 } 195 #endif 196 197 /* 198 * Init function called from dm_table_load_ioctl. 199 * argv: /dev/mapper/my_data_org /dev/mapper/tsc_cow_dev p 64 200 * snapshot_origin device, cow device, persistent flag, chunk size 201 */ 202 int 203 dm_target_snapshot_init(dm_table_entry_t *table_en, int argc, char **argv) 204 { 205 dm_target_snapshot_config_t *tsc; 206 dm_pdev_t *dmp_snap, *dmp_cow; 207 208 dmp_cow = NULL; 209 210 printf("Snapshot target init function called!!\n"); 211 printf("Snapshotted device: %s, cow device %s,\n\t persistent flag: %s, " 212 "chunk size: %s\n", argv[0], argv[1], argv[2], argv[3]); 213 214 /* Insert snap device to global pdev list */ 215 if ((dmp_snap = dm_pdev_insert(argv[0])) == NULL) 216 return ENOENT; 217 218 tsc = kmem_alloc(sizeof(*tsc), KM_SLEEP); 219 tsc->tsc_persistent_dev = 0; 220 221 /* There is now cow device for nonpersistent snapshot devices */ 222 if (strcmp(argv[2], "p") == 0) { 223 tsc->tsc_persistent_dev = 1; 224 225 /* Insert cow device to global pdev list */ 226 if ((dmp_cow = dm_pdev_insert(argv[1])) == NULL) { 227 kmem_free(tsc, sizeof(*tsc)); 228 return ENOENT; 229 } 230 } 231 tsc->tsc_chunk_size = atoi64(argv[3]); 232 233 tsc->tsc_snap_dev = dmp_snap; 234 tsc->tsc_cow_dev = dmp_cow; 235 236 dm_table_add_deps(table_en, dmp_snap); 237 dm_table_add_deps(table_en, dmp_cow); 238 table_en->target_config = tsc; 239 240 return 0; 241 } 242 243 /* 244 * Table routine is called to get params string, which is target 245 * specific. When dm_table_status_ioctl is called with flag 246 * DM_STATUS_TABLE_FLAG I have to sent params string back. 247 */ 248 char * 249 dm_target_snapshot_table(void *target_config) 250 { 251 dm_target_snapshot_config_t *tsc; 252 253 uint32_t i; 254 uint32_t count; 255 size_t prm_len, cow_len; 256 char *params; 257 258 tsc = target_config; 259 260 prm_len = 0; 261 cow_len = 0; 262 count = 0; 263 264 printf("Snapshot target table function called\n"); 265 266 /* count number of chars in offset */ 267 for (i = tsc->tsc_chunk_size; i != 0; i /= 10) 268 count++; 269 270 if (tsc->tsc_persistent_dev) 271 cow_len = strlen(tsc->tsc_cow_dev->name); 272 273 /* length of names + count of chars + spaces and null char */ 274 prm_len = strlen(tsc->tsc_snap_dev->name) + cow_len + count + 5; 275 276 params = kmem_alloc(prm_len, KM_SLEEP); 277 278 printf("%s %s %s %" PRIu64 "\n", tsc->tsc_snap_dev->name, 279 tsc->tsc_cow_dev->name, tsc->tsc_persistent_dev ? "p" : "n", 280 tsc->tsc_chunk_size); 281 282 snprintf(params, prm_len, "%s %s %s %" PRIu64, tsc->tsc_snap_dev->name, 283 tsc->tsc_persistent_dev ? tsc->tsc_cow_dev->name : "", 284 tsc->tsc_persistent_dev ? "p" : "n", 285 tsc->tsc_chunk_size); 286 287 return params; 288 } 289 290 /* Strategy routine called from dm_strategy. */ 291 int 292 dm_target_snapshot_strategy(dm_table_entry_t *table_en, struct buf *bp) 293 { 294 295 bp->b_error = EIO; 296 bp->b_resid = 0; 297 298 biodone(bp); 299 300 return 0; 301 } 302 303 int 304 dm_target_snapshot_sync(dm_table_entry_t *table_en) 305 { 306 307 return 0; 308 } 309 310 /* Doesn't do anything here. */ 311 int 312 dm_target_snapshot_destroy(dm_table_entry_t *table_en) 313 { 314 315 /* 316 * Destroy function is called for every target even if it 317 * doesn't have target_config. 318 */ 319 if (table_en->target_config == NULL) 320 goto out; 321 322 printf("Snapshot target destroy function called\n"); 323 324 dm_target_snapshot_config_t *tsc = table_en->target_config; 325 326 /* Decrement pdev ref counter if 0 remove it */ 327 dm_pdev_decr(tsc->tsc_snap_dev); 328 if (tsc->tsc_persistent_dev) 329 dm_pdev_decr(tsc->tsc_cow_dev); 330 331 kmem_free(tsc, sizeof(*tsc)); 332 out: 333 /* Unbusy target so we can unload it */ 334 dm_target_unbusy(table_en->target); 335 336 return 0; 337 } 338 339 #if 0 340 /* Upcall is used to inform other depended devices about IO. */ 341 int 342 dm_target_snapshot_upcall(dm_table_entry_t *table_en, struct buf *bp) 343 { 344 345 printf("dm_target_snapshot_upcall called\n"); 346 347 printf("upcall buf flags %s %s\n", 348 (bp->b_flags & B_WRITE) ? "B_WRITE" : "", 349 (bp->b_flags & B_READ) ? "B_READ" : ""); 350 351 return 0; 352 } 353 #endif 354 355 /* 356 * dm target snapshot origin routines. 357 * 358 * Keep for compatibility with linux lvm2tools. They use two targets 359 * to implement snapshots. Snapshot target will implement exception 360 * store and snapshot origin will implement device which calls every 361 * snapshot device when write is done on master device. 362 */ 363 364 /* 365 * Init function called from dm_table_load_ioctl. 366 * 367 * argv: /dev/mapper/my_data_real 368 */ 369 int 370 dm_target_snapshot_orig_init(dm_table_entry_t *table_en, int argc, char **argv) 371 { 372 dm_target_snapshot_origin_config_t *tsoc; 373 dm_pdev_t *dmp_real; 374 375 printf("Snapshot origin target init function called!!\n"); 376 printf("Parent device: %s\n", argv[0]); 377 378 /* Insert snap device to global pdev list */ 379 if ((dmp_real = dm_pdev_insert(argv[0])) == NULL) 380 return ENOENT; 381 382 tsoc = kmem_alloc(sizeof(dm_target_snapshot_origin_config_t), KM_SLEEP); 383 tsoc->tsoc_real_dev = dmp_real; 384 385 dm_table_add_deps(table_en, dmp_real); 386 table_en->target_config = tsoc; 387 388 return 0; 389 } 390 391 /* 392 * Table routine is called to get params string, which is target 393 * specific. When dm_table_status_ioctl is called with flag 394 * DM_STATUS_TABLE_FLAG I have to sent params string back. 395 */ 396 char * 397 dm_target_snapshot_orig_table(void *target_config) 398 { 399 dm_target_snapshot_origin_config_t *tsoc; 400 401 size_t prm_len; 402 char *params; 403 404 tsoc = target_config; 405 406 prm_len = 0; 407 408 printf("Snapshot origin target table function called\n"); 409 410 /* length of names + count of chars + spaces and null char */ 411 prm_len = strlen(tsoc->tsoc_real_dev->name) + 1; 412 413 printf("real_dev name %s\n", tsoc->tsoc_real_dev->name); 414 415 params = kmem_alloc(prm_len, KM_SLEEP); 416 417 printf("%s\n", tsoc->tsoc_real_dev->name); 418 419 snprintf(params, prm_len, "%s", tsoc->tsoc_real_dev->name); 420 421 return params; 422 } 423 424 /* Strategy routine called from dm_strategy. */ 425 int 426 dm_target_snapshot_orig_strategy(dm_table_entry_t *table_en, struct buf *bp) 427 { 428 429 bp->b_error = EIO; 430 bp->b_resid = 0; 431 432 biodone(bp); 433 434 return 0; 435 } 436 437 /* 438 * Sync underlying disk caches. 439 */ 440 int 441 dm_target_snapshot_orig_sync(dm_table_entry_t *table_en) 442 { 443 int cmd; 444 dm_target_snapshot_origin_config_t *tsoc; 445 446 tsoc = table_en->target_config; 447 448 cmd = 1; 449 450 return VOP_IOCTL(tsoc->tsoc_real_dev->pdev_vnode, DIOCCACHESYNC, 451 &cmd, FREAD|FWRITE, kauth_cred_get()); 452 } 453 454 /* Decrement pdev and free allocated space. */ 455 int 456 dm_target_snapshot_orig_destroy(dm_table_entry_t *table_en) 457 { 458 459 /* 460 * Destroy function is called for every target even if it 461 * doesn't have target_config. 462 */ 463 464 if (table_en->target_config == NULL) 465 goto out; 466 467 dm_target_snapshot_origin_config_t *tsoc = table_en->target_config; 468 469 /* Decrement pdev ref counter if 0 remove it */ 470 dm_pdev_decr(tsoc->tsoc_real_dev); 471 472 kmem_free(tsoc, sizeof(*tsoc)); 473 out: 474 /* Unbusy target so we can unload it */ 475 dm_target_unbusy(table_en->target); 476 477 return 0; 478 } 479 480 #if 0 481 /* Unsupported for this target. */ 482 int 483 dm_target_snapshot_orig_upcall(dm_table_entry_t *table_en, struct buf *bp) 484 { 485 486 return 0; 487 } 488 #endif 489