dm_target_snapshot.c revision 1.2 1 /* $NetBSD: dm_target_snapshot.c,v 1.2 2008/12/19 15:24:03 haad 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
32 /*
33 * 1. Suspend my_data to temporarily stop any I/O while the snapshot is being
34 * activated.
35 * dmsetup suspend my_data
36 *
37 * 2. Create the snapshot-origin device with no table.
38 * dmsetup create my_data_org
39 *
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
42 *
43 * 4. Resume this new table.
44 * dmsetup resume my_data_org
45 *
46 * 5. Create the snapshot device with no table.
47 * dmsetup create my_data_snap
48 *
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
53 *
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
57 *
58 * 8. Resume the snapshot and origin devices.
59 * dmsetup resume my_data_snap
60 * dmsetup resume my_data
61 */
62
63 /*
64 * This file implements initial version of device-mapper snapshot target.
65 */
66 #include <sys/types.h>
67 #include <sys/param.h>
68
69 #include <sys/buf.h>
70 #include <sys/kmem.h>
71 #include <sys/vnode.h>
72
73 #include "dm.h"
74
75 /*
76 * Init function called from dm_table_load_ioctl.
77 * argv: /dev/mapper/my_data_org /dev/mapper/tsc_cow_dev p 64
78 * snapshot_origin device, cow device, persistent flag, chunk size
79 */
80 int
81 dm_target_snapshot_init(dm_dev_t *dmv, void **target_config, char *params)
82 {
83 dm_target_snapshot_config_t *tsc;
84 dm_pdev_t *dmp_snap, *dmp_cow;
85 char **ap, *argv[5];
86
87 dmp_cow = NULL;
88
89 if (params == NULL)
90 return EINVAL;
91 /*
92 * Parse a string, containing tokens delimited by white space,
93 * into an argument vector
94 */
95 for (ap = argv; ap < &argv[4] &&
96 (*ap = strsep(¶ms, " \t")) != NULL;) {
97 if (**ap != '\0')
98 ap++;
99 }
100
101 printf("Snapshot target init function called!!\n");
102 printf("Snapshotted device: %s, cow device %s,\n\t persistent flag: %s, "
103 "chunk size: %s\n", argv[0], argv[1], argv[2], argv[3]);
104
105 /* Insert snap device to global pdev list */
106 if ((dmp_snap = dm_pdev_insert(argv[0])) == NULL)
107 return ENOENT;
108
109 if ((tsc = kmem_alloc(sizeof(dm_target_snapshot_config_t), KM_NOSLEEP))
110 == NULL)
111 return 1;
112
113 tsc->tsc_persistent_dev = 0;
114
115 /* There is now cow device for nonpersistent snapshot devices */
116 if (strcmp(argv[2], "p") == 0) {
117 tsc->tsc_persistent_dev = 1;
118
119 /* Insert cow device to global pdev list */
120 if ((dmp_cow = dm_pdev_insert(argv[1])) == NULL)
121 return ENOENT;
122 }
123
124 tsc->tsc_chunk_size = atoi(argv[3]);
125
126 tsc->tsc_snap_dev = dmp_snap;
127 tsc->tsc_cow_dev = dmp_cow;
128
129 *target_config = tsc;
130
131 dmv->dev_type = DM_SNAPSHOT_DEV;
132
133 return 0;
134 }
135
136 /*
137 * Status routine is called to get params string, which is target
138 * specific. When dm_table_status_ioctl is called with flag
139 * DM_STATUS_TABLE_FLAG I have to sent params string back.
140 */
141 char *
142 dm_target_snapshot_status(void *target_config)
143 {
144 dm_target_snapshot_config_t *tsc;
145
146 uint32_t i;
147 uint32_t count;
148 size_t prm_len, cow_len;
149 char *params, *cow_name;
150
151 tsc = target_config;
152
153 prm_len = 0;
154 cow_len = 0;
155 count = 0;
156 cow_name = NULL;
157
158 printf("Snapshot target status function called\n");
159
160 /* count number of chars in offset */
161 for(i = tsc->tsc_chunk_size; i != 0; i /= 10)
162 count++;
163
164 if(tsc->tsc_persistent_dev)
165 cow_len = strlen(tsc->tsc_cow_dev->name);
166
167 /* length of names + count of chars + spaces and null char */
168 prm_len = strlen(tsc->tsc_snap_dev->name) + cow_len + count + 5;
169
170 if ((params = kmem_alloc(prm_len, KM_NOSLEEP)) == NULL)
171 return NULL;
172
173 printf("%s %s %s %"PRIu64"\n", tsc->tsc_snap_dev->name,
174 tsc->tsc_cow_dev->name, tsc->tsc_persistent_dev ? "p":"n",
175 tsc->tsc_chunk_size);
176
177 snprintf(params, prm_len, "%s %s %s %"PRIu64, tsc->tsc_snap_dev->name,
178 tsc->tsc_persistent_dev ? tsc->tsc_cow_dev->name : "",
179 tsc->tsc_persistent_dev ? "p":"n",
180 tsc->tsc_chunk_size);
181
182 return params;
183 }
184
185 /* Strategy routine called from dm_strategy. */
186 int
187 dm_target_snapshot_strategy(dm_table_entry_t *table_en, struct buf *bp)
188 {
189
190 printf("Snapshot target read function called!!\n");
191
192 bp->b_error = EIO;
193 bp->b_resid = 0;
194
195 biodone(bp);
196
197 return 0;
198 }
199
200 /* Doesn't do anything here. */
201 int
202 dm_target_snapshot_destroy(dm_table_entry_t *table_en)
203 {
204 dm_target_snapshot_config_t *tsc;
205
206 /*
207 * Destroy function is called for every target even if it
208 * doesn't have target_config.
209 */
210
211 if (table_en->target_config == NULL)
212 return 0;
213
214 printf("Snapshot target destroy function called\n");
215
216 tsc = table_en->target_config;
217
218 /* Decrement pdev ref counter if 0 remove it */
219 dm_pdev_decr(tsc->tsc_snap_dev);
220
221 if (tsc->tsc_persistent_dev)
222 dm_pdev_decr(tsc->tsc_cow_dev);
223
224 kmem_free(table_en->target_config, sizeof(dm_target_snapshot_config_t));
225
226 table_en->target_config = NULL;
227
228 return 0;
229 }
230
231 /* Add this target dependiences to prop_array_t */
232 int
233 dm_target_snapshot_deps(dm_table_entry_t *table_en,
234 prop_array_t prop_array)
235 {
236 dm_target_snapshot_config_t *tsc;
237 struct vattr va;
238
239 int error;
240
241 if (table_en->target_config == NULL)
242 return 0;
243
244 tsc = table_en->target_config;
245
246 if ((error = VOP_GETATTR(tsc->tsc_snap_dev->pdev_vnode, &va, curlwp->l_cred)) != 0)
247 return error;
248
249 prop_array_add_uint64(prop_array, (uint64_t)va.va_rdev);
250
251 if (tsc->tsc_persistent_dev) {
252
253 if ((error = VOP_GETATTR(tsc->tsc_cow_dev->pdev_vnode, &va,
254 curlwp->l_cred)) != 0)
255 return error;
256
257 prop_array_add_uint64(prop_array, (uint64_t)va.va_rdev);
258
259 }
260
261 return 0;
262 }
263
264 /* Upcall is used to inform other depended devices about IO. */
265 int
266 dm_target_snapshot_upcall(dm_table_entry_t *table_en, struct buf *bp)
267 {
268 printf("dm_target_snapshot_upcall called\n");
269
270 printf("upcall buf flags %s %s\n",
271 (bp->b_flags & B_WRITE) ? "B_WRITE":"",
272 (bp->b_flags & B_READ) ? "B_READ":"");
273
274 return 0;
275 }
276
277 /*
278 * dm target snapshot origin routines.
279 *
280 * Keep for compatibility with linux lvm2tools. They use two targets
281 * to implement snapshots. Snapshot target will implement exception
282 * store and snapshot origin will implement device which calls every
283 * snapshot device when write is done on master device.
284 */
285
286 /*
287 * Init function called from dm_table_load_ioctl.
288 *
289 * argv: /dev/mapper/my_data_real
290 */
291 int
292 dm_target_snapshot_orig_init(dm_dev_t *dmv, void **target_config,
293 char *params)
294 {
295 dm_target_snapshot_origin_config_t *tsoc;
296 dm_pdev_t *dmp_real;
297
298 if (params == NULL)
299 return EINVAL;
300
301 printf("Snapshot origin target init function called!!\n");
302 printf("Parent device: %s\n", params);
303
304 /* Insert snap device to global pdev list */
305 if ((dmp_real = dm_pdev_insert(params)) == NULL)
306 return ENOENT;
307
308 if ((tsoc = kmem_alloc(sizeof(dm_target_snapshot_origin_config_t), KM_NOSLEEP))
309 == NULL)
310 return 1;
311
312 tsoc->tsoc_real_dev = dmp_real;
313
314 dmv->dev_type = DM_SNAPSHOT_ORIG_DEV;
315
316 *target_config = tsoc;
317
318 return 0;
319 }
320
321 /*
322 * Status routine is called to get params string, which is target
323 * specific. When dm_table_status_ioctl is called with flag
324 * DM_STATUS_TABLE_FLAG I have to sent params string back.
325 */
326 char *
327 dm_target_snapshot_orig_status(void *target_config)
328 {
329 dm_target_snapshot_origin_config_t *tsoc;
330
331 size_t prm_len;
332 char *params;
333
334 tsoc = target_config;
335
336 prm_len = 0;
337
338 printf("Snapshot origin target status function called\n");
339
340 /* length of names + count of chars + spaces and null char */
341 prm_len = strlen(tsoc->tsoc_real_dev->name) + 1;
342
343 printf("real_dev name %s\n",tsoc->tsoc_real_dev->name);
344
345 if ((params = kmem_alloc(prm_len, KM_NOSLEEP)) == NULL)
346 return NULL;
347
348 printf("%s\n", tsoc->tsoc_real_dev->name);
349
350 snprintf(params, prm_len, "%s", tsoc->tsoc_real_dev->name);
351
352 return params;
353 }
354
355 /* Strategy routine called from dm_strategy. */
356 int
357 dm_target_snapshot_orig_strategy(dm_table_entry_t *table_en, struct buf *bp)
358 {
359
360 printf("Snapshot_Orig target read function called!!\n");
361
362 bp->b_error = EIO;
363 bp->b_resid = 0;
364
365 biodone(bp);
366
367 return 0;
368 }
369
370 /* Decrement pdev and free allocated space. */
371 int
372 dm_target_snapshot_orig_destroy(dm_table_entry_t *table_en)
373 {
374 dm_target_snapshot_origin_config_t *tsoc;
375
376 /*
377 * Destroy function is called for every target even if it
378 * doesn't have target_config.
379 */
380
381 if (table_en->target_config == NULL)
382 return 0;
383
384 tsoc = table_en->target_config;
385
386 /* Decrement pdev ref counter if 0 remove it */
387 dm_pdev_decr(tsoc->tsoc_real_dev);
388
389 kmem_free(table_en->target_config, sizeof(dm_target_snapshot_origin_config_t));
390
391 table_en->target_config = NULL;
392
393 return 0;
394 }
395
396 /*
397 * Get target deps and add them to prop_array_t.
398 */
399 int
400 dm_target_snapshot_orig_deps(dm_table_entry_t *table_en,
401 prop_array_t prop_array)
402 {
403 dm_target_snapshot_origin_config_t *tsoc;
404 struct vattr va;
405
406 int error;
407
408 if (table_en->target_config == NULL)
409 return 0;
410
411 tsoc = table_en->target_config;
412
413 if ((error = VOP_GETATTR(tsoc->tsoc_real_dev->pdev_vnode, &va,
414 curlwp->l_cred)) != 0)
415 return error;
416
417 prop_array_add_uint64(prop_array, (uint64_t)va.va_rdev);
418
419 return 0;
420 }
421
422 /* Unsupported for this target. */
423 int
424 dm_target_snapshot_orig_upcall(dm_table_entry_t *table_en, struct buf *bp)
425 {
426 return 0;
427 }
428