device-mapper.c revision 1.1.2.3 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 /* Called to initialize disklabel values for readdisklabel. */
72 static void dmgetdefaultdisklabel(struct dm_dev *, dev_t);
73
74 /* ***Variable-definitions*** */
75 const struct bdevsw dm_bdevsw = {
76 dmopen, dmclose, dmstrategy, dmioctl, dmdump, dmsize,
77 D_DISK
78 };
79
80 const struct cdevsw dm_cdevsw = {
81 dmopen, dmclose, dmread, dmwrite, dmioctl,
82 nostop, notty, nopoll, nommap, nokqfilter, D_DISK
83 };
84
85 #ifdef __LKM__
86
87 /* lkm module init routine */
88 int dm_lkmentry(struct lkm_table *, int, int);
89
90 /* lkm module handle routine */
91 static int dm_handle(struct lkm_table *, int);
92
93 MOD_DEV("dm", "dm", &dm_bdevsw, -1, &dm_cdevsw, -1);
94
95 #endif
96
97 /* Info about all devices */
98 struct dm_softc *dm_sc;
99
100 /*
101 * This array is used to translate cmd to function pointer.
102 *
103 * Interface between libdevmapper and lvm2tools uses different
104 * names for one IOCTL call because libdevmapper do another thing
105 * then. When I run "info" or "mknodes" libdevmapper will send same
106 * ioctl to kernel but will do another things in userspace.
107 *
108 */
109 struct cmd_function cmd_fn[] = {
110 {"version", dm_get_version_ioctl},
111 {"targets", dm_list_versions_ioctl},
112 {"create", dm_dev_create_ioctl},
113 {"info", dm_dev_status_ioctl},
114 {"mknodes", dm_dev_status_ioctl},
115 {"names", dm_dev_list_ioctl},
116 {"suspend", dm_dev_suspend_ioctl},
117 {"remove", dm_dev_remove_ioctl},
118 {"rename", dm_dev_rename_ioctl},
119 {"resume", dm_dev_resume_ioctl},
120 {"clear", dm_table_clear_ioctl},
121 {"deps", dm_table_deps_ioctl},
122 {"reload", dm_table_load_ioctl},
123 {"status", dm_table_status_ioctl},
124 {"table", dm_table_status_ioctl},
125 {NULL, NULL}
126 };
127
128 /* attach routine */
129 int
130 dmattach(void)
131 {
132
133 dm_sc = (struct dm_softc *)kmem_alloc(sizeof(struct dm_softc), KM_NOSLEEP);
134
135 if (dm_sc == NULL){
136 aprint_error("Not enough memory for dm device.\n");
137 return(ENOMEM);
138 }
139
140 dm_sc->sc_minor_num = 0;
141 dm_sc->sc_ref_count = 0;
142
143 dm_target_init();
144
145 dm_dev_init();
146
147 dm_pdev_init();
148
149 return 0;
150 }
151
152 /* Destroy routine */
153 int
154 dmdestroy(void)
155 {
156
157 (void)kmem_free(dm_sc, sizeof(struct dm_softc));
158
159 return 0;
160 }
161
162 #ifdef __LKM__
163
164 /* lkm entry for ld */
165 int
166 dm_lkmentry(struct lkm_table *lkmtp, int cmd, int ver)
167 {
168 DISPATCH(lkmtp, cmd, ver, dm_handle, dm_handle, dm_handle);
169 }
170
171 /* handle function for all load/stat/unload actions */
172 static int
173 dm_handle(struct lkm_table *lkmtp, int cmd)
174 {
175
176 int r;
177
178 r=0;
179
180 switch(cmd) {
181
182 case LKM_E_LOAD:
183 aprint_error("Loading dm driver.\n");
184 dmattach();
185 break;
186
187 case LKM_E_UNLOAD:
188 aprint_error("Unloading dm driver.\n");
189 dmdestroy();
190 break;
191
192 case LKM_E_STAT:
193 aprint_error("Opened dm devices: \n");
194 break;
195 }
196
197 return r;
198 }
199
200 #endif
201
202
203 static int
204 dmopen(dev_t dev, int flags, int mode, struct lwp *l)
205 {
206
207 struct dm_dev *dmv;
208
209 aprint_verbose("open routine called %d\n",minor(dev));
210
211 if ((dmv = dm_dev_lookup_minor(minor(dev))) != NULL) {
212 if (dmv->dm_dklabel == NULL)
213 dmgetdisklabel(dmv, dev);
214
215 dmv->ref_cnt++;
216 }
217
218 return 0;
219 }
220
221
222 static int
223 dmclose(dev_t dev, int flags, int mode, struct lwp *l)
224 {
225 struct dm_dev *dmv;
226
227 if ((dmv = dm_dev_lookup_minor(minor(dev))) != NULL)
228 dmv->ref_cnt--;
229
230 aprint_verbose("CLOSE routine called\n");
231
232 return 0;
233 }
234
235 /*
236 * Called after ioctl call on mapper/control or dm device.
237 */
238 static int
239 dmioctl(dev_t dev, const u_long cmd, void *data, int flag, struct lwp *l)
240 {
241 int r;
242 prop_dictionary_t dm_dict_in;
243
244 r = 0;
245
246 if (data == NULL)
247 return(EINVAL);
248
249 if (disk_ioctl_switch(dev, cmd, data) != 0) {
250 struct plistref *pref = (struct plistref *) data;
251
252 r = prop_dictionary_copyin_ioctl(pref, cmd, &dm_dict_in);
253 if (r)
254 return r;
255
256 dm_check_version(dm_dict_in);
257
258 /* call cmd selected function */
259 r = dm_ioctl_switch(cmd);
260 if (r < 0)
261 goto out;
262
263 char *xml;
264 xml = prop_dictionary_externalize(dm_dict_in);
265 aprint_verbose("%s\n",xml);
266
267 r = dm_cmd_to_fun(dm_dict_in);
268 if (r != 0)
269 goto out;
270
271 r = prop_dictionary_copyout_ioctl(pref, cmd, dm_dict_in);
272 }
273
274 out:
275 return r;
276 }
277 /*
278 * Translate command sent from libdevmapper to func.
279 */
280 static int
281 dm_cmd_to_fun(prop_dictionary_t dm_dict){
282 int i,len,slen;
283 int r;
284 const char *command;
285
286 r = 0;
287
288 (void)prop_dictionary_get_cstring_nocopy(dm_dict, DM_IOCTL_COMMAND,
289 &command);
290
291 len = strlen(command);
292
293 for(i=0;cmd_fn[i].cmd != NULL;i++){
294 slen = strlen(cmd_fn[i].cmd);
295
296 if (len != slen)
297 continue;
298
299 if ((strncmp(command, cmd_fn[i].cmd, slen)) == 0) {
300 aprint_verbose("ioctl command: %s\n", command);
301 r = cmd_fn[i].fn(dm_dict);
302 break;
303 }
304 }
305
306 return r;
307 }
308
309 /* Call apropriate ioctl handler function. */
310 static int
311 dm_ioctl_switch(u_long cmd)
312 {
313 int r;
314
315 r = 0;
316
317 switch(cmd) {
318
319 case NETBSD_DM_IOCTL:
320 aprint_verbose("NetBSD_DM_IOCTL called\n");
321 break;
322
323 default:
324 aprint_verbose("unknown ioctl called\n");
325 return EPASSTHROUGH;
326 break; /* NOT REACHED */
327 }
328
329 return r;
330 }
331
332 /*
333 * Check for disk specific ioctls.
334 */
335
336 static int
337 disk_ioctl_switch(dev_t dev, u_long cmd, void *data)
338 {
339 struct dm_dev *dmv;
340
341 if ((dmv = dm_dev_lookup_minor(minor(dev))) == NULL)
342 return 1;
343
344 switch(cmd) {
345
346 case DIOCGWEDGEINFO:
347 {
348 struct dkwedge_info *dkw = (void *) data;
349
350 aprint_verbose("DIOCGWEDGEINFO ioctl called\n");
351
352 strlcpy(dkw->dkw_devname, dmv->name, 16);
353 strlcpy(dkw->dkw_wname, dmv->name, DM_NAME_LEN);
354 strlcpy(dkw->dkw_parent, dmv->name, 16);
355
356 dkw->dkw_offset = 0;
357 dkw->dkw_size = dmsize(dev);
358 strcpy(dkw->dkw_ptype, DKW_PTYPE_FFS);
359
360 break;
361 }
362
363 case DIOCGDINFO:
364 *(struct disklabel *)data = *(dmv->dm_dklabel);
365 break;
366
367 case DIOCGPART:
368 case DIOCWDINFO:
369 case DIOCSDINFO:
370 case DIOCKLABEL:
371 case DIOCWLABEL:
372 case DIOCGDEFLABEL:
373
374 default:
375 aprint_verbose("unknown disk_ioctl called\n");
376 return 1;
377 break; /* NOT REACHED */
378 }
379
380 return 0;
381 }
382
383 /*
384 * Do all IO operations on dm logical devices.
385 */
386 static void
387 dmstrategy(struct buf *bp)
388 {
389
390 struct dm_dev *dmv;
391 struct dm_table *tbl;
392
393 struct dm_table_entry *table_en;
394
395 struct buf *nestbuf;
396
397 uint64_t table_start;
398 uint64_t table_end;
399
400 uint64_t buf_start;
401 uint64_t buf_len;
402
403 uint64_t start;
404 uint64_t end;
405
406 uint64_t issued_len;
407
408 buf_start = bp->b_blkno * DEV_BSIZE;
409 buf_len = bp->b_bcount;
410
411 tbl = NULL; /* XXX gcc uninitialized */
412
413 table_end = 0;
414
415 issued_len = 0;
416
417 /*aprint_verbose("dmstrategy routine called %d--%d\n",
418 minor(bp->b_dev),bp->b_bcount);*/
419
420 if ( (dmv = dm_dev_lookup_minor(minor(bp->b_dev))) == NULL) {
421 bp->b_error = EIO;
422 bp->b_resid = bp->b_bcount;
423 biodone(bp);
424 return;
425 }
426
427 /* Select active table */
428 tbl = &dmv->tables[dmv->cur_active_table];
429
430 /* Nested buffers count down to zero therefore I have
431 to set bp->b_resid to maximal value. */
432 bp->b_resid = bp->b_bcount;
433
434 /*
435 * Find out what tables I want to select.
436 */
437 SLIST_FOREACH(table_en, tbl, next)
438 {
439
440 /* I need need number of bytes not blocks. */
441 table_start = table_en->start * DEV_BSIZE;
442 /*
443 * I have to sub 1 from table_en->length to prevent
444 * off by one error
445 */
446 table_end = table_start + (table_en->length)* DEV_BSIZE;
447
448 start = MAX(table_start, buf_start);
449
450 end = MIN(table_end, buf_start + buf_len);
451
452 aprint_debug("----------------------------------------\n");
453 aprint_debug("table_start %010" PRIu64", table_end %010"
454 PRIu64 "\n", table_start, table_end);
455 aprint_debug("buf_start %010" PRIu64", buf_len %010"
456 PRIu64"\n", buf_start, buf_len);
457 aprint_debug("start-buf_start %010"PRIu64", end %010"
458 PRIu64"\n", start - buf_start, end);
459 aprint_debug("end-start %010" PRIu64 "\n", end - start);
460 aprint_debug("\n----------------------------------------\n");
461
462 if (start < end) {
463 /* create nested buffer */
464 nestbuf = getiobuf(NULL, true);
465
466 nestiobuf_setup(bp, nestbuf, start - buf_start,
467 (end-start));
468
469 issued_len += end-start;
470
471 /* I need number of blocks. */
472 nestbuf->b_blkno = (start - table_start) / DEV_BSIZE;
473
474 table_en->target->strategy(table_en, nestbuf);
475 }
476 }
477
478 if (issued_len < buf_len)
479 nestiobuf_done(bp, buf_len - issued_len, EINVAL);
480
481 return;
482 }
483
484
485 static int
486 dmread(dev_t dev, struct uio *uio, int flag)
487 {
488 return (physio(dmstrategy, NULL, dev, B_READ, dmminphys, uio));
489 }
490
491 static int
492 dmwrite(dev_t dev, struct uio *uio, int flag)
493 {
494 return (physio(dmstrategy, NULL, dev, B_WRITE, dmminphys, uio));
495 }
496
497 static int
498 dmdump(dev_t dev, daddr_t blkno, void *va, size_t size)
499 {
500 return ENODEV;
501 }
502
503 static int
504 dmsize(dev_t dev)
505 {
506 struct dm_dev *dmv;
507 struct dm_table *tbl;
508 struct dm_table_entry *table_en;
509
510 uint64_t length;
511
512 length = 0;
513
514 aprint_debug("dmsize routine called %d\n", minor(dev));
515
516 if ( (dmv = dm_dev_lookup_minor(minor(dev))) == NULL)
517 return ENODEV;
518
519 /* Select active table */
520 tbl = &dmv->tables[dmv->cur_active_table];
521
522 /*
523 * Find out what tables I want to select.
524 * if length => rawblkno then we should used that table.
525 */
526 SLIST_FOREACH(table_en, tbl, next)
527 length += table_en->length;
528
529 return length;
530 }
531
532 static void
533 dmminphys(struct buf *bp)
534 {
535 bp->b_bcount = MIN(bp->b_bcount, MAXPHYS);
536 }
537 /*
538 * Load the label information on the named device
539 * Actually fabricate a disklabel
540 *
541 * EVENTUALLY take information about different
542 * data tracks from the TOC and put it in the disklabel
543 */
544
545
546 static void
547 dmgetdisklabel(struct dm_dev *dmv, dev_t dev)
548 {
549 struct cpu_disklabel cpulp;
550 struct dm_pdev *dmp;
551
552 if ((dmv->dm_dklabel = kmem_zalloc(sizeof(struct disklabel), KM_NOSLEEP))
553 == NULL)
554 return;
555
556 memset(&cpulp, 0, sizeof(cpulp));
557
558 dmp = SLIST_FIRST(&dmv->pdevs);
559
560 dmgetdefaultdisklabel(dmv, dev);
561
562 return;
563 }
564
565 /*
566 * Initialize disklabel values, so we can use it for readdisklabel.
567 */
568 static void
569 dmgetdefaultdisklabel(struct dm_dev *dmv, dev_t dev)
570 {
571 struct disklabel *lp = dmv->dm_dklabel;
572 struct partition *pp;
573 int dmp_size;
574
575 dmp_size = dmsize(dev);
576
577 /*
578 * Size must be at least 2048 DEV_BSIZE blocks
579 * (1M) in order to use this geometry.
580 */
581
582 lp->d_secperunit = dmp_size;
583 lp->d_secsize = DEV_BSIZE;
584 lp->d_nsectors = 32;
585 lp->d_ntracks = 64;
586 lp->d_ncylinders = dmp_size / (lp->d_nsectors * lp->d_ntracks);
587 lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
588
589 strncpy(lp->d_typename, "lvm", sizeof(lp->d_typename));
590 lp->d_type = DTYPE_DM;
591 strncpy(lp->d_packname, "fictitious", sizeof(lp->d_packname));
592 lp->d_rpm = 3600;
593 lp->d_interleave = 1;
594 lp->d_flags = 0;
595
596 pp = &lp->d_partitions[RAW_PART];
597 /*
598 * This is logical offset and therefore it can be 0
599 * I will consider table offsets later in dmstrategy.
600 */
601 pp->p_offset = 0;
602 pp->p_size = lp->d_secperunit;
603 pp->p_fstype = FS_BSDFFS; /* default value */
604 lp->d_npartitions = RAW_PART + 1;
605
606 lp->d_magic = DISKMAGIC;
607 lp->d_magic2 = DISKMAGIC;
608 lp->d_checksum = dkcksum(lp);
609 }
610