1 /* $NetBSD: device-mapper.c,v 1.14 2010/01/03 22:55:25 haad Exp $ */
4 * Copyright (c) 2008 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
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.
33 * I want to say thank you to all people who helped me with this project.
36 #include <sys/types.h>
37 #include <sys/param.h>
41 #include <sys/device.h>
44 #include <sys/disklabel.h>
45 #include <sys/ioctl.h>
46 #include <sys/ioccom.h>
48 #include <sys/module.h>
50 #include "netbsd-dm.h"
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_size(dmsize
);
61 /* attach and detach routines */
65 static int dm_cmd_to_fun(prop_dictionary_t
);
66 static int disk_ioctl_switch(dev_t
, u_long
, void *);
67 static int dm_ioctl_switch(u_long
);
68 static void dmminphys(struct buf
*);
70 /* CF attach/detach functions used for power management */
71 static int dm_detach(device_t
, int);
72 static void dm_attach(device_t
, device_t
, void *);
73 static int dm_match(device_t
, cfdata_t
, void *);
75 /* ***Variable-definitions*** */
76 const struct bdevsw dm_bdevsw
= {
79 .d_strategy
= dmstrategy
,
83 .d_flag
= D_DISK
| D_MPSAFE
86 const struct cdevsw dm_cdevsw
= {
96 .d_kqfilter
= nokqfilter
,
97 .d_flag
= D_DISK
| D_MPSAFE
100 const struct dkdriver dmdkdriver
= {
101 .d_strategy
= dmstrategy
105 /* Autoconf defines */
106 CFDRIVER_DECL(dm
, DV_DISK
, NULL
);
109 CFATTACH_DECL3_NEW(dm
, 0,
110 dm_match
, dm_attach
, dm_detach
, NULL
, NULL
, NULL
,
111 DVF_DETACH_SHUTDOWN
);
113 extern struct cfdriver dm_cd
;
115 extern uint64_t dev_counter
;
118 * This array is used to translate cmd to function pointer.
120 * Interface between libdevmapper and lvm2tools uses different
121 * names for one IOCTL call because libdevmapper do another thing
122 * then. When I run "info" or "mknodes" libdevmapper will send same
123 * ioctl to kernel but will do another things in userspace.
126 struct cmd_function cmd_fn
[] = {
127 { .cmd
= "version", .fn
= dm_get_version_ioctl
},
128 { .cmd
= "targets", .fn
= dm_list_versions_ioctl
},
129 { .cmd
= "create", .fn
= dm_dev_create_ioctl
},
130 { .cmd
= "info", .fn
= dm_dev_status_ioctl
},
131 { .cmd
= "mknodes", .fn
= dm_dev_status_ioctl
},
132 { .cmd
= "names", .fn
= dm_dev_list_ioctl
},
133 { .cmd
= "suspend", .fn
= dm_dev_suspend_ioctl
},
134 { .cmd
= "remove", .fn
= dm_dev_remove_ioctl
},
135 { .cmd
= "rename", .fn
= dm_dev_rename_ioctl
},
136 { .cmd
= "resume", .fn
= dm_dev_resume_ioctl
},
137 { .cmd
= "clear", .fn
= dm_table_clear_ioctl
},
138 { .cmd
= "deps", .fn
= dm_table_deps_ioctl
},
139 { .cmd
= "reload", .fn
= dm_table_load_ioctl
},
140 { .cmd
= "status", .fn
= dm_table_status_ioctl
},
141 { .cmd
= "table", .fn
= dm_table_status_ioctl
},
145 MODULE(MODULE_CLASS_DRIVER
, dm
, NULL
);
147 /* New module handle routine */
149 dm_modcmd(modcmd_t cmd
, void *arg
)
152 int bmajor
= -1, cmajor
= -1;
158 case MODULE_CMD_INIT
:
161 error
= config_cfdriver_attach(&dm_cd
);
165 error
= config_cfattach_attach(dm_cd
.cd_name
, &dm_ca
);
167 config_cfdriver_detach(&dm_cd
);
168 aprint_error("Unable to register cfattach for dm driver\n");
173 error
= devsw_attach("dm", &dm_bdevsw
, &bmajor
,
174 &dm_cdevsw
, &cmajor
);
177 case MODULE_CMD_FINI
:
179 * Disable unloading of dm module if there are any devices
180 * defined in driver. This is probably too strong we need
181 * to disable auto-unload only if there is mounted dm device
188 error
= config_cfattach_detach(dm_cd
.cd_name
, &dm_ca
);
192 config_cfdriver_detach(&dm_cd
);
194 devsw_detach(&dm_bdevsw
, &dm_cdevsw
);
196 case MODULE_CMD_STAT
:
206 if (cmd
== MODULE_CMD_INIT
)
217 * Autoconfiguration match function for pseudo-device glue.
220 dm_match(device_t parent
, cfdata_t match
,
224 /* Pseudo-device; always present. */
231 * Autoconfiguration attach function for pseudo-device glue.
234 dm_attach(device_t parent
, device_t self
,
244 * Autoconfiguration detach function for pseudo-device glue.
245 * This routine is called by dm_ioctl::dm_dev_remove_ioctl and by autoconf to
246 * remove devices created in device-mapper.
249 dm_detach(device_t self
, int flags
)
253 /* Detach device from global device list */
254 if ((dmv
= dm_dev_detach(self
)) == NULL
)
257 /* Destroy active table first. */
258 dm_table_destroy(&dmv
->table_head
, DM_TABLE_ACTIVE
);
260 /* Destroy inactive table if exits, too. */
261 dm_table_destroy(&dmv
->table_head
, DM_TABLE_INACTIVE
);
263 dm_table_head_destroy(&dmv
->table_head
);
265 /* Destroy disk device structure */
266 disk_detach(dmv
->diskp
);
267 disk_destroy(dmv
->diskp
);
270 (void)dm_dev_free(dmv
);
272 /* Decrement device counter After removing device */
273 atomic_dec_64(&dev_counter
);
290 /* Destroy routine */
303 dmopen(dev_t dev
, int flags
, int mode
, struct lwp
*l
)
306 aprint_debug("dm open routine called %" PRIu32
"\n", minor(dev
));
311 dmclose(dev_t dev
, int flags
, int mode
, struct lwp
*l
)
314 aprint_debug("dm close routine called %" PRIu32
"\n", minor(dev
));
320 dmioctl(dev_t dev
, const u_long cmd
, void *data
, int flag
, struct lwp
*l
)
323 prop_dictionary_t dm_dict_in
;
327 aprint_debug("dmioctl called\n");
329 KASSERT(data
!= NULL
);
331 if (( r
= disk_ioctl_switch(dev
, cmd
, data
)) == ENOTTY
) {
332 struct plistref
*pref
= (struct plistref
*) data
;
334 /* Check if we were called with NETBSD_DM_IOCTL ioctl
336 if ((r
= dm_ioctl_switch(cmd
)) != 0)
339 if((r
= prop_dictionary_copyin_ioctl(pref
, cmd
, &dm_dict_in
)) != 0)
342 if ((r
= dm_check_version(dm_dict_in
)) != 0)
345 /* run ioctl routine */
346 if ((r
= dm_cmd_to_fun(dm_dict_in
)) != 0)
350 r
= prop_dictionary_copyout_ioctl(pref
, cmd
, dm_dict_in
);
351 prop_object_release(dm_dict_in
);
358 * Translate command sent from libdevmapper to func.
361 dm_cmd_to_fun(prop_dictionary_t dm_dict
){
363 prop_string_t command
;
367 if ((command
= prop_dictionary_get(dm_dict
, DM_IOCTL_COMMAND
)) == NULL
)
370 for(i
= 0; cmd_fn
[i
].cmd
!= NULL
; i
++)
371 if (prop_string_equals_cstring(command
, cmd_fn
[i
].cmd
))
374 if (cmd_fn
[i
].cmd
== NULL
)
377 aprint_debug("ioctl %s called\n", cmd_fn
[i
].cmd
);
378 r
= cmd_fn
[i
].fn(dm_dict
);
383 /* Call apropriate ioctl handler function. */
385 dm_ioctl_switch(u_long cmd
)
390 case NETBSD_DM_IOCTL
:
391 aprint_debug("dm NetBSD_DM_IOCTL called\n");
394 aprint_debug("dm unknown ioctl called\n");
396 break; /* NOT REACHED */
403 * Check for disk specific ioctls.
407 disk_ioctl_switch(dev_t dev
, u_long cmd
, void *data
)
414 struct dkwedge_info
*dkw
= (void *) data
;
416 if ((dmv
= dm_dev_lookup(NULL
, NULL
, minor(dev
))) == NULL
)
419 aprint_debug("DIOCGWEDGEINFO ioctl called\n");
421 strlcpy(dkw
->dkw_devname
, dmv
->name
, 16);
422 strlcpy(dkw
->dkw_wname
, dmv
->name
, DM_NAME_LEN
);
423 strlcpy(dkw
->dkw_parent
, dmv
->name
, 16);
426 dkw
->dkw_size
= dm_table_size(&dmv
->table_head
);
427 strcpy(dkw
->dkw_ptype
, DKW_PTYPE_FFS
);
435 struct plistref
*pref
= (struct plistref
*) data
;
437 if ((dmv
= dm_dev_lookup(NULL
, NULL
, minor(dev
))) == NULL
)
440 if (dmv
->diskp
->dk_info
== NULL
) {
444 prop_dictionary_copyout_ioctl(pref
, cmd
,
445 dmv
->diskp
->dk_info
);
452 aprint_debug("unknown disk_ioctl called\n");
454 break; /* NOT REACHED */
461 * Do all IO operations on dm logical devices.
464 dmstrategy(struct buf
*bp
)
468 dm_table_entry_t
*table_en
;
473 uint64_t buf_start
, buf_len
, issued_len
;
474 uint64_t table_start
, table_end
;
477 buf_start
= bp
->b_blkno
* DEV_BSIZE
;
478 buf_len
= bp
->b_bcount
;
486 if ((dmv
= dm_dev_lookup(NULL
, NULL
, minor(bp
->b_dev
))) == NULL
) {
488 bp
->b_resid
= bp
->b_bcount
;
493 if (bounds_check_with_mediasize(bp
, DEV_BSIZE
,
494 dm_table_size(&dmv
->table_head
)) <= 0) {
496 bp
->b_resid
= bp
->b_bcount
;
502 * disk(9) is part of device structure and it can't be used without
503 * mutual exclusion, use diskp_mtx until it will be fixed.
505 mutex_enter(&dmv
->diskp_mtx
);
506 disk_busy(dmv
->diskp
);
507 mutex_exit(&dmv
->diskp_mtx
);
509 /* Select active table */
510 tbl
= dm_table_get_entry(&dmv
->table_head
, DM_TABLE_ACTIVE
);
512 /* Nested buffers count down to zero therefore I have
513 to set bp->b_resid to maximal value. */
514 bp
->b_resid
= bp
->b_bcount
;
517 * Find out what tables I want to select.
519 SLIST_FOREACH(table_en
, tbl
, next
)
521 /* I need need number of bytes not blocks. */
522 table_start
= table_en
->start
* DEV_BSIZE
;
524 * I have to sub 1 from table_en->length to prevent
527 table_end
= table_start
+ (table_en
->length
)* DEV_BSIZE
;
529 start
= MAX(table_start
, buf_start
);
531 end
= MIN(table_end
, buf_start
+ buf_len
);
533 aprint_debug("----------------------------------------\n");
534 aprint_debug("table_start %010" PRIu64
", table_end %010"
535 PRIu64
"\n", table_start
, table_end
);
536 aprint_debug("buf_start %010" PRIu64
", buf_len %010"
537 PRIu64
"\n", buf_start
, buf_len
);
538 aprint_debug("start-buf_start %010"PRIu64
", end %010"
539 PRIu64
"\n", start
- buf_start
, end
);
540 aprint_debug("start %010" PRIu64
" , end %010"
541 PRIu64
"\n", start
, end
);
542 aprint_debug("\n----------------------------------------\n");
545 /* create nested buffer */
546 nestbuf
= getiobuf(NULL
, true);
548 nestiobuf_setup(bp
, nestbuf
, start
- buf_start
,
551 issued_len
+= end
- start
;
553 /* I need number of blocks. */
554 nestbuf
->b_blkno
= (start
- table_start
) / DEV_BSIZE
;
556 table_en
->target
->strategy(table_en
, nestbuf
);
560 if (issued_len
< buf_len
)
561 nestiobuf_done(bp
, buf_len
- issued_len
, EINVAL
);
563 mutex_enter(&dmv
->diskp_mtx
);
564 disk_unbusy(dmv
->diskp
, buf_len
, bp
!= NULL
? bp
->b_flags
& B_READ
: 0);
565 mutex_exit(&dmv
->diskp_mtx
);
567 dm_table_release(&dmv
->table_head
, DM_TABLE_ACTIVE
);
575 dmread(dev_t dev
, struct uio
*uio
, int flag
)
578 return (physio(dmstrategy
, NULL
, dev
, B_READ
, dmminphys
, uio
));
582 dmwrite(dev_t dev
, struct uio
*uio
, int flag
)
585 return (physio(dmstrategy
, NULL
, dev
, B_WRITE
, dmminphys
, uio
));
596 if ((dmv
= dm_dev_lookup(NULL
, NULL
, minor(dev
))) == NULL
)
599 size
= dm_table_size(&dmv
->table_head
);
606 dmminphys(struct buf
*bp
)
609 bp
->b_bcount
= MIN(bp
->b_bcount
, MAXPHYS
);
613 dmgetproperties(struct disk
*disk
, dm_table_head_t
*head
)
615 prop_dictionary_t disk_info
, odisk_info
, geom
;
618 dmp_size
= dm_table_size(head
);
619 disk_info
= prop_dictionary_create();
620 geom
= prop_dictionary_create();
622 prop_dictionary_set_cstring_nocopy(disk_info
, "type", "ESDI");
623 prop_dictionary_set_uint64(geom
, "sectors-per-unit", dmp_size
);
624 prop_dictionary_set_uint32(geom
, "sector-size",
625 DEV_BSIZE
/* XXX 512? */);
626 prop_dictionary_set_uint32(geom
, "sectors-per-track", 32);
627 prop_dictionary_set_uint32(geom
, "tracks-per-cylinder", 64);
628 prop_dictionary_set_uint32(geom
, "cylinders-per-unit", dmp_size
/ 2048);
629 prop_dictionary_set(disk_info
, "geometry", geom
);
630 prop_object_release(geom
);
632 odisk_info
= disk
->dk_info
;
633 disk
->dk_info
= disk_info
;
635 if (odisk_info
!= NULL
)
636 prop_object_release(odisk_info
);