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