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