device-mapper.c revision 1.1.2.2 1 /*
2 * Copyright (c) 1996, 1997, 1998, 1999, 2002 The NetBSD Foundation, Inc.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to The NetBSD Foundation
6 * by Adam Hamsik.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 /*
31 * I want to say thank you to all people who helped me with this project.
32 */
33
34 #include <sys/types.h>
35 #include <sys/param.h>
36
37 #include <sys/buf.h>
38 #include <sys/conf.h>
39 #include <sys/dkio.h>
40 #include <sys/disk.h>
41 #include <sys/disklabel.h>
42 #include <sys/errno.h>
43 #include <sys/ioctl.h>
44 #include <sys/ioccom.h>
45 #include <sys/kmem.h>
46 #ifdef __LKM__
47 #include <sys/lkm.h>
48 #endif
49
50 #include "netbsd-dm.h"
51 #include "dm.h"
52
53 static dev_type_open(dmopen);
54 static dev_type_close(dmclose);
55 static dev_type_read(dmread);
56 static dev_type_write(dmwrite);
57 static dev_type_ioctl(dmioctl);
58 static dev_type_strategy(dmstrategy);
59 static dev_type_dump(dmdump);
60 static dev_type_size(dmsize);
61
62 /* attach and detach routines */
63 int dmattach(void);
64 int dmdestroy(void);
65
66 static int dm_cmd_to_fun(prop_dictionary_t);
67 static int disk_ioctl_switch(dev_t, u_long, void *);
68 static int dm_ioctl_switch(u_long);
69 static void dmminphys(struct buf*);
70 static void dmgetdisklabel(struct dm_dev*, dev_t);
71 /* ***Variable-definitions*** */
72 const struct bdevsw dm_bdevsw = {
73 dmopen, dmclose, dmstrategy, dmioctl, dmdump, dmsize,
74 D_DISK
75 };
76
77 const struct cdevsw dm_cdevsw = {
78 dmopen, dmclose, dmread, dmwrite, dmioctl,
79 nostop, notty, nopoll, nommap, nokqfilter, D_DISK
80 };
81
82 #ifdef __LKM__
83
84 /* lkm module init routine */
85 int dm_lkmentry(struct lkm_table *, int, int);
86
87 /* lkm module handle routine */
88 static int dm_handle(struct lkm_table *, int);
89
90 MOD_DEV("dm", "dm", &dm_bdevsw, -1, &dm_cdevsw, -1);
91
92 #endif
93
94 /* Info about all devices */
95 struct dm_softc *dm_sc;
96
97 /*
98 * This array is used to translate cmd to function pointer.
99 *
100 * Interface between libdevmapper and lvm2tools uses different
101 * names for one IOCTL call because libdevmapper do another thing
102 * then. When I run "info" or "mknodes" libdevmapper will send same
103 * ioctl to kernel but will do another things in userspace.
104 *
105 */
106 struct cmd_function cmd_fn[] = {
107 {"version", dm_get_version_ioctl},
108 {"targets", dm_list_versions_ioctl},
109 {"create", dm_dev_create_ioctl},
110 {"info", dm_dev_status_ioctl},
111 {"mknodes", dm_dev_status_ioctl},
112 {"names", dm_dev_list_ioctl},
113 {"suspend", dm_dev_suspend_ioctl},
114 {"remove", dm_dev_remove_ioctl},
115 {"rename", dm_dev_rename_ioctl},
116 {"resume", dm_dev_resume_ioctl},
117 {"clear", dm_table_clear_ioctl},
118 {"deps", dm_table_deps_ioctl},
119 {"reload", dm_table_load_ioctl},
120 {"status", dm_table_status_ioctl},
121 {"table", dm_table_status_ioctl},
122 {NULL, NULL}
123 };
124
125 /* attach routine */
126 int
127 dmattach(void)
128 {
129
130 dm_sc = (struct dm_softc *)kmem_alloc(sizeof(struct dm_softc), KM_NOSLEEP);
131
132 if (dm_sc == NULL){
133 aprint_error("Not enough memory for dm device.\n");
134 return(ENOMEM);
135 }
136
137 dm_sc->sc_minor_num = 0;
138 dm_sc->sc_ref_count = 0;
139
140 dm_target_init();
141
142 dm_dev_init();
143
144 dm_pdev_init();
145
146 return 0;
147 }
148
149 /* Destroy routine */
150 int
151 dmdestroy(void)
152 {
153
154 (void)kmem_free(dm_sc, sizeof(struct dm_softc));
155
156 return 0;
157 }
158
159 #ifdef __LKM__
160
161 /* lkm entry for ld */
162 int
163 dm_lkmentry(struct lkm_table *lkmtp, int cmd, int ver)
164 {
165 DISPATCH(lkmtp, cmd, ver, dm_handle, dm_handle, dm_handle);
166 }
167
168 /* handle function for all load/stat/unload actions */
169 static int
170 dm_handle(struct lkm_table *lkmtp, int cmd)
171 {
172
173 int r;
174
175 r=0;
176
177 switch(cmd) {
178
179 case LKM_E_LOAD:
180 aprint_error("Loading dm driver.\n");
181 dmattach();
182 break;
183
184 case LKM_E_UNLOAD:
185 aprint_error("Unloading dm driver.\n");
186 dmdestroy();
187 break;
188
189 case LKM_E_STAT:
190 aprint_error("Opened dm devices: \n");
191 break;
192 }
193
194 return r;
195 }
196
197 #endif
198
199
200 static int
201 dmopen(dev_t dev, int flags, int mode, struct lwp *l)
202 {
203
204 struct dm_dev *dmv;
205
206 if ((dmv = dm_dev_lookup_minor(minor(dev))) != NULL) {
207 if (dmv->dm_dk == NULL)
208 dmgetdisklabel(dmv, dev);
209
210 dmv->ref_cnt++;
211 }
212
213 aprint_verbose("open routine called %d\n", minor(dev));
214
215 return 0;
216 }
217
218
219 static int
220 dmclose(dev_t dev, int flags, int mode, struct lwp *l)
221 {
222 struct dm_dev *dmv;
223
224 if ((dmv = dm_dev_lookup_minor(minor(dev))) != NULL)
225 dmv->ref_cnt--;
226
227 aprint_verbose("CLOSE routine called\n");
228
229 return 0;
230 }
231
232 /*
233 * Called after ioctl call on mapper/control or dm device.
234 */
235 static int
236 dmioctl(dev_t dev, const u_long cmd, void *data, int flag, struct lwp *l)
237 {
238 int r;
239 prop_dictionary_t dm_dict_in;
240
241 r = 0;
242
243 if (data == NULL)
244 return(EINVAL);
245
246 if (disk_ioctl_switch(dev, cmd, data) != 0) {
247 struct plistref *pref = (struct plistref *) data;
248
249 r = prop_dictionary_copyin_ioctl(pref, cmd, &dm_dict_in);
250 if (r)
251 return r;
252
253 dm_check_version(dm_dict_in);
254
255 /* call cmd selected function */
256 r = dm_ioctl_switch(cmd);
257 if (r < 0)
258 goto out;
259
260 char *xml;
261 xml = prop_dictionary_externalize(dm_dict_in);
262 aprint_verbose("%s\n",xml);
263
264 r = dm_cmd_to_fun(dm_dict_in);
265 if (r != 0)
266 goto out;
267
268 r = prop_dictionary_copyout_ioctl(pref, cmd, dm_dict_in);
269 }
270
271 out:
272 return r;
273 }
274 /*
275 * Translate command sent from libdevmapper to func.
276 */
277 static int
278 dm_cmd_to_fun(prop_dictionary_t dm_dict){
279 int i,len,slen;
280 int r;
281 const char *command;
282
283 r = 0;
284
285 (void)prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_COMMAND,
286 &command);
287
288 len = strlen(command);
289
290 for(i=0;cmd_fn[i].cmd != NULL;i++){
291 slen = strlen(cmd_fn[i].cmd);
292
293 if (len != slen)
294 continue;
295
296 if ((strncmp(command, cmd_fn[i].cmd, slen)) == 0) {
297 aprint_verbose("ioctl command: %s\n", command);
298 r = cmd_fn[i].fn(dm_dict);
299 break;
300 }
301 }
302
303 return r;
304 }
305
306 /* Call apropriate ioctl handler function. */
307 static int
308 dm_ioctl_switch(u_long cmd)
309 {
310 int r;
311
312 r = 0;
313
314 switch(cmd) {
315
316 case NETBSD_DM_IOCTL:
317 aprint_verbose("NetBSD_DM_IOCTL called\n");
318 break;
319
320 default:
321 aprint_verbose("unknown ioctl called\n");
322 return EPASSTHROUGH;
323 break; /* NOT REACHED */
324 }
325
326 return r;
327 }
328
329 /*
330 * Check for disk specific ioctls.
331 */
332
333 static int
334 disk_ioctl_switch(dev_t dev, u_long cmd, void *data)
335 {
336 struct dm_dev *dmv;
337
338 if ((dmv = dm_dev_lookup_minor(minor(dev))) == NULL)
339 return 1;
340
341 switch(cmd) {
342
343 case DIOCGWEDGEINFO:
344 {
345 struct dkwedge_info *dkw = (void *) data;
346
347 aprint_verbose("DIOCGWEDGEINFO ioctl called\n");
348
349 strlcpy(dkw->dkw_devname, dmv->name, 16);
350 strlcpy(dkw->dkw_wname, dmv->name, DM_NAME_LEN);
351 strlcpy(dkw->dkw_parent, dmv->name, 16);
352
353 dkw->dkw_offset = 0;
354 dkw->dkw_size = dmsize(dev);
355 strcpy(dkw->dkw_ptype, DKW_PTYPE_FFS);
356
357 break;
358 }
359
360 case DIOCGDINFO:
361 *(struct disklabel *)data = *(dmv->dm_dk->dk_label);
362 break;
363
364 case DIOCGPART:
365 case DIOCWDINFO:
366 case DIOCSDINFO:
367 case DIOCKLABEL:
368 case DIOCWLABEL:
369 case DIOCGDEFLABEL:
370
371 default:
372 aprint_verbose("unknown disk_ioctl called\n");
373 return 1;
374 break; /* NOT REACHED */
375 }
376
377 return 0;
378 }
379
380 /*
381 * Do all IO operations on dm logical devices.
382 */
383 static void
384 dmstrategy(struct buf *bp)
385 {
386
387 struct dm_dev *dmv;
388 struct dm_table *tbl;
389
390 struct dm_table_entry *table_en;
391
392 struct buf *nestbuf;
393
394 uint64_t table_start;
395 uint64_t table_end;
396
397 uint64_t buf_start;
398 uint64_t buf_len;
399
400 uint64_t start;
401 uint64_t end;
402
403 uint64_t issued_len;
404
405 buf_start = bp->b_blkno * DEV_BSIZE;
406 buf_len = bp->b_bcount;
407
408 tbl = NULL; /* XXX gcc uninitialized */
409
410 table_end = 0;
411
412 issued_len = 0;
413
414 /*aprint_verbose("dmstrategy routine called %d--%d\n",
415 minor(bp->b_dev),bp->b_bcount);*/
416
417 if ( (dmv = dm_dev_lookup_minor(minor(bp->b_dev))) == NULL) {
418 bp->b_error = EIO;
419 bp->b_resid = bp->b_bcount;
420 biodone(bp);
421 return;
422 }
423
424 /* Select active table */
425 tbl = &dmv->tables[dmv->cur_active_table];
426
427 /* Nested buffers count down to zero therefore I have
428 to set bp->b_resid to maximal value. */
429 bp->b_resid = bp->b_bcount;
430
431 /*
432 * Find out what tables I want to select.
433 */
434 SLIST_FOREACH(table_en, tbl, next)
435 {
436
437 /* I need need number of bytes not blocks. */
438 table_start = table_en->start * DEV_BSIZE;
439 /*
440 * I have to sub 1 from table_en->length to prevent
441 * off by one error
442 */
443 table_end = table_start + (table_en->length)* DEV_BSIZE;
444
445 start = MAX(table_start, buf_start);
446
447 end = MIN(table_end, buf_start + buf_len);
448
449 aprint_debug("----------------------------------------\n");
450 aprint_debug("table_start %010" PRIu64", table_end %010"
451 PRIu64 "\n", table_start, table_end);
452 aprint_debug("buf_start %010" PRIu64", buf_len %010"
453 PRIu64"\n", buf_start, buf_len);
454 aprint_debug("start-buf_start %010"PRIu64", end %010"
455 PRIu64"\n", start - buf_start, end);
456 aprint_debug("end-start %010" PRIu64 "\n", end - start);
457 aprint_debug("\n----------------------------------------\n");
458
459 if (start < end) {
460 /* create nested buffer */
461 nestbuf = getiobuf(NULL, true);
462
463 nestiobuf_setup(bp, nestbuf, start - buf_start,
464 (end-start));
465
466 issued_len += end-start;
467
468 /* I need number of blocks. */
469 nestbuf->b_blkno = (start - table_start) / DEV_BSIZE;
470
471 table_en->target->strategy(table_en, nestbuf);
472 }
473 }
474
475 if (issued_len < buf_len)
476 nestiobuf_done(bp, buf_len - issued_len, EINVAL);
477
478 return;
479 }
480
481
482 static int
483 dmread(dev_t dev, struct uio *uio, int flag)
484 {
485 return (physio(dmstrategy, NULL, dev, B_READ, dmminphys, uio));
486 }
487
488 static int
489 dmwrite(dev_t dev, struct uio *uio, int flag)
490 {
491 return (physio(dmstrategy, NULL, dev, B_WRITE, dmminphys, uio));
492 }
493
494 static int
495 dmdump(dev_t dev, daddr_t blkno, void *va, size_t size)
496 {
497 return ENODEV;
498 }
499
500 static int
501 dmsize(dev_t dev)
502 {
503 struct dm_dev *dmv;
504 struct dm_table *tbl;
505 struct dm_table_entry *table_en;
506
507 uint64_t length;
508
509 length = 0;
510
511 aprint_debug("dmsize routine called %d\n", minor(dev));
512
513 if ( (dmv = dm_dev_lookup_minor(minor(dev))) == NULL)
514 return ENODEV;
515
516 /* Select active table */
517 tbl = &dmv->tables[dmv->cur_active_table];
518
519 /*
520 * Find out what tables I want to select.
521 * if length => rawblkno then we should used that table.
522 */
523 SLIST_FOREACH(table_en, tbl, next)
524 length += table_en->length;
525
526 return length;
527 }
528
529 static void
530 dmminphys(struct buf *bp)
531 {
532 bp->b_bcount = MIN(bp->b_bcount, MAXPHYS);
533 }
534 /*
535 * Load the label information on the named device
536 * Actually fabricate a disklabel
537 *
538 * EVENTUALLY take information about different
539 * data tracks from the TOC and put it in the disklabel
540 */
541
542
543 static void
544 dmgetdisklabel(struct dm_dev *dmv, dev_t dev)
545 {
546 struct disklabel *lp = dmv->dm_dk->dk_label;
547 const char *errstring;
548
549 memset(dmv->dm_dk->dk_cpulabel, 0, sizeof(struct cpu_disklabel));
550
551 /*
552 * Call the generic disklabel extraction routine
553 */
554 errstring = readdisklabel(dev, dmstrategy, lp,
555 dmv->dm_dk->dk_cpulabel);
556
557 /* if all went OK, we are passed a NULL error string */
558 if (errstring == NULL)
559 return;
560
561 aprint_debug("\t%s\t %u sectors \n", dmv->name,
562 dmv->dm_dk->dk_label->d_secsize);
563 }
564
565