dm_target_snapshot.c revision 1.1.2.8 1 /* $NetBSD: dm_target_snapshot.c,v 1.1.2.8 2008/09/08 11:34:01 haad Exp $ */
2
3 /*
4 * Copyright (c) 1996, 1997, 1998, 1999, 2002 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(struct dm_dev *dmv, void **target_config, char *params)
82 {
83 struct target_snapshot_config *tsc;
84 struct dm_pdev *dmp_snap, *dmp_cow;
85 char **ap, *argv[5];
86
87 dmp_cow = NULL;
88
89 /*
90 * Parse a string, containing tokens delimited by white space,
91 * into an argument vector
92 */
93 for (ap = argv; ap < &argv[4] &&
94 (*ap = strsep(¶ms, " \t")) != NULL;) {
95 if (**ap != '\0')
96 ap++;
97 }
98
99 printf("Snapshot target init function called!!\n");
100 printf("Snapshotted device: %s, cow device %s,\n\t persistent flag: %s, "
101 "chunk size: %s\n", argv[0], argv[1], argv[2], argv[3]);
102
103 /* Insert snap device to global pdev list */
104 if ((dmp_snap = dm_pdev_insert(argv[0])) == NULL)
105 return ENOENT;
106
107 /* Lookup for snapshot pdev entry in device list and insert */
108 if ((dm_pdev_lookup_name_list(dmp_snap->name, &dmv->pdevs)) == NULL)
109 SLIST_INSERT_HEAD(&dmv->pdevs, dmp_snap, next_dev_pdev);
110
111 if ((tsc = kmem_alloc(sizeof(struct target_snapshot_config), KM_NOSLEEP))
112 == NULL)
113 return 1;
114
115 tsc->tsc_persistent_dev = 0;
116
117 /* There is now cow device for nonpersistent snapshot devices */
118 if (strcmp(argv[2], "p") == 0) {
119 tsc->tsc_persistent_dev = 1;
120
121 /* Insert cow device to global pdev list */
122 if ((dmp_cow = dm_pdev_insert(argv[1])) == NULL)
123 return ENOENT;
124
125 /* Lookup for cow pdev entry in device list and insert */
126 if ((dm_pdev_lookup_name_list(dmp_cow->name, &dmv->pdevs)) == NULL)
127 SLIST_INSERT_HEAD(&dmv->pdevs, dmp_cow, next_dev_pdev);
128 }
129
130 tsc->tsc_chunk_size = atoi(argv[3]);
131
132 tsc->tsc_snap_dev = dmp_snap;
133 tsc->tsc_cow_dev = dmp_cow;
134
135 *target_config = tsc;
136
137 dmv->dev_type = DM_SNAPSHOT_DEV;
138
139 return 0;
140 }
141
142 /*
143 * Status routine is called to get params string, which is target
144 * specific. When dm_table_status_ioctl is called with flag
145 * DM_STATUS_TABLE_FLAG I have to sent params string back.
146 */
147 char *
148 dm_target_snapshot_status(void *target_config)
149 {
150 struct target_snapshot_config *tsc;
151
152 uint32_t i;
153 uint32_t count;
154 size_t prm_len, cow_len;
155 char *params, *cow_name;
156
157 tsc = target_config;
158
159 prm_len = 0;
160 cow_len = 0;
161 count = 0;
162
163 cow_name = NULL;
164
165 printf("Snapshot target status function called\n");
166
167 /* count number of chars in offset */
168 for(i = tsc->tsc_chunk_size; i != 0; i /= 10)
169 count++;
170
171 if(tsc->tsc_persistent_dev)
172 cow_len = strlen(tsc->tsc_cow_dev->name);
173
174 /* length of names + count of chars + spaces and null char */
175 prm_len = strlen(tsc->tsc_snap_dev->name) + cow_len + count + 5;
176
177 if ((params = kmem_alloc(prm_len, KM_NOSLEEP)) == NULL)
178 return NULL;
179
180 printf("%s %s %s %"PRIu64"\n", tsc->tsc_snap_dev->name,
181 tsc->tsc_cow_dev->name, tsc->tsc_persistent_dev ? "p":"n",
182 tsc->tsc_chunk_size);
183
184 snprintf(params, prm_len, "%s %s %s %"PRIu64, tsc->tsc_snap_dev->name,
185 tsc->tsc_persistent_dev ? tsc->tsc_cow_dev->name : "",
186 tsc->tsc_persistent_dev ? "p":"n",
187 tsc->tsc_chunk_size);
188
189 return params;
190 }
191
192 /* Strategy routine called from dm_strategy. */
193 int
194 dm_target_snapshot_strategy(struct dm_table_entry *table_en, struct buf *bp)
195 {
196
197 printf("Snapshot target read function called!!\n");
198
199 bp->b_error = EIO;
200 bp->b_resid = 0;
201
202 biodone(bp);
203
204 return 0;
205 }
206
207 /* Doesn't do anything here. */
208 int
209 dm_target_snapshot_destroy(struct dm_table_entry *table_en)
210 {
211 struct target_snapshot_config *tsc;
212
213 /*
214 * Destroy function is called for every target even if it
215 * doesn't have target_config.
216 */
217
218 if (table_en->target_config == NULL)
219 return 0;
220
221 printf("Snapshot target destroy function called\n");
222
223 tsc = table_en->target_config;
224
225 /* Decrement device list reference counter */
226 tsc->tsc_snap_dev->list_ref_cnt--;
227
228 /* If there is no other table which reference this pdev remove it. */
229 if (tsc->tsc_snap_dev->list_ref_cnt == 0)
230 SLIST_REMOVE(&table_en->dm_dev->pdevs, tsc->tsc_snap_dev, dm_pdev, next_dev_pdev);
231
232 /* Decrement pdev ref counter if 0 remove it */
233 dm_pdev_decr(tsc->tsc_snap_dev);
234
235 if (tsc->tsc_persistent_dev) {
236 tsc->tsc_cow_dev->list_ref_cnt--;
237
238 if (tsc->tsc_cow_dev->list_ref_cnt == 0)
239 SLIST_REMOVE(&table_en->dm_dev->pdevs, tsc->tsc_cow_dev, dm_pdev, next_dev_pdev);
240
241 dm_pdev_decr(tsc->tsc_cow_dev);
242 }
243
244 kmem_free(table_en->target_config, sizeof(struct target_snapshot_config));
245
246 table_en->target_config = NULL;
247
248 return 0;
249 }
250
251 /* Unsupported for this target. */
252 int
253 dm_target_snapshot_upcall(struct dm_table_entry *table_en, struct buf *bp)
254 {
255 printf("dm_target_snapshot_upcall called\n");
256
257 printf("upcall buf flags %s %s\n",
258 (bp->b_flags & B_WRITE) ? "B_WRITE":"",
259 (bp->b_flags & B_READ) ? "B_READ":"");
260
261 return 0;
262 }
263
264 /*
265 * dm target snapshot origin routines.
266 *
267 * Keep for compatibility with linux lvm2tools. They use two targets
268 * to implement snapshots. Snapshot target will implement exception
269 * store and snapshot origin will implement device which calls every
270 * snapshot device when write is done on master device.
271 */
272
273 /*
274 * Init function called from dm_table_load_ioctl.
275 *
276 * argv: /dev/mapper/my_data_real
277 */
278 int
279 dm_target_snapshot_orig_init(struct dm_dev *dmv, void **target_config,
280 char *params)
281 {
282 struct target_snapshot_origin_config *tsoc;
283 struct dm_pdev *dmp_real;
284
285 printf("Snapshot origin target init function called!!\n");
286 printf("Parent device: %s\n", params);
287
288 /* Insert snap device to global pdev list */
289 if ((dmp_real = dm_pdev_insert(params)) == NULL)
290 return ENOENT;
291
292 /* Lookup for snapshot pdev entry in device list and insert */
293 if ((dm_pdev_lookup_name_list(dmp_real->name, &dmv->pdevs)) == NULL)
294 SLIST_INSERT_HEAD(&dmv->pdevs, dmp_real, next_dev_pdev);
295
296 if ((tsoc = kmem_alloc(sizeof(struct target_snapshot_origin_config), KM_NOSLEEP))
297 == NULL)
298 return 1;
299
300 tsoc->tsoc_real_dev = dmp_real;
301
302 dmv->dev_type = DM_SNAPSHOT_ORIG_DEV;
303
304 *target_config = tsoc;
305
306 return 0;
307 }
308
309 /*
310 * Status routine is called to get params string, which is target
311 * specific. When dm_table_status_ioctl is called with flag
312 * DM_STATUS_TABLE_FLAG I have to sent params string back.
313 */
314 char *
315 dm_target_snapshot_orig_status(void *target_config)
316 {
317 struct target_snapshot_origin_config *tsoc;
318
319 size_t prm_len;
320 char *params;
321
322 tsoc = target_config;
323
324 prm_len = 0;
325
326 printf("Snapshot origin target status function called\n");
327
328 /* length of names + count of chars + spaces and null char */
329 prm_len = strlen(tsoc->tsoc_real_dev->name) + 1;
330
331 printf("real_dev name %s\n",tsoc->tsoc_real_dev->name);
332
333 if ((params = kmem_alloc(prm_len, KM_NOSLEEP)) == NULL)
334 return NULL;
335
336 printf("%s\n", tsoc->tsoc_real_dev->name);
337
338 snprintf(params, prm_len, "%s", tsoc->tsoc_real_dev->name);
339
340 return params;
341 }
342
343 /* Strategy routine called from dm_strategy. */
344 int
345 dm_target_snapshot_orig_strategy(struct dm_table_entry *table_en, struct buf *bp)
346 {
347
348 printf("Snapshot_Orig target read function called!!\n");
349
350 bp->b_error = EIO;
351 bp->b_resid = 0;
352
353 biodone(bp);
354
355 return 0;
356 }
357
358 /* Doesn't do anything here. */
359 int
360 dm_target_snapshot_orig_destroy(struct dm_table_entry *table_en)
361 {
362 table_en->target_config = NULL;
363
364 return 0;
365 }
366
367 /* Unsupported for this target. */
368 int
369 dm_target_snapshot_orig_upcall(struct dm_table_entry *table_en, struct buf *bp)
370 {
371 return 0;
372 }
373