1 /* $NetBSD: dksubr.c,v 1.39 2009/01/11 09:54:41 cegger Exp $ */
4 * Copyright (c) 1996, 1997, 1998, 1999, 2002, 2008 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jason R. Thorpe and Roland C. Dowdeswell.
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.
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: dksubr.c,v 1.39 2009/01/11 09:54:41 cegger Exp $");
35 #include <sys/param.h>
36 #include <sys/systm.h>
39 #include <sys/ioctl.h>
40 #include <sys/device.h>
42 #include <sys/disklabel.h>
45 #include <sys/vnode.h>
46 #include <sys/fcntl.h>
47 #include <sys/namei.h>
49 #include <dev/dkvar.h>
54 #define DKDB_FOLLOW 0x1
56 #define DKDB_VNODE 0x4
58 #define IFDEBUG(x,y) if (dkdebug & (x)) y
59 #define DPRINTF(x,y) IFDEBUG(x, printf y)
60 #define DPRINTF_FOLLOW(y) DPRINTF(DKDB_FOLLOW, y)
64 #define DPRINTF_FOLLOW(y)
67 #define DKLABELDEV(dev) \
68 (MAKEDISKDEV(major((dev)), DISKUNIT((dev)), RAW_PART))
70 static void dk_makedisklabel(struct dk_intf
*, struct dk_softc
*);
73 dk_sc_init(struct dk_softc
*dksc
, void *osc
, const char *xname
)
76 memset(dksc
, 0x0, sizeof(*dksc
));
78 strncpy(dksc
->sc_xname
, xname
, DK_XNAME_SIZE
);
79 dksc
->sc_dkdev
.dk_name
= dksc
->sc_xname
;
84 dk_open(struct dk_intf
*di
, struct dk_softc
*dksc
, dev_t dev
,
85 int flags
, int fmt
, struct lwp
*l
)
87 struct disklabel
*lp
= dksc
->sc_dkdev
.dk_label
;
88 int part
= DISKPART(dev
);
89 int pmask
= 1 << part
;
91 struct disk
*dk
= &dksc
->sc_dkdev
;
93 DPRINTF_FOLLOW(("dk_open(%s, %p, 0x%"PRIx64
", 0x%x)\n",
94 di
->di_dkname
, dksc
, dev
, flags
));
96 mutex_enter(&dk
->dk_openlock
);
100 * If there are wedges, and this is not RAW_PART, then we
103 if (dk
->dk_nwedges
!= 0 && part
!= RAW_PART
) {
111 * If we're init'ed and there are no other open partitions then
112 * update the in-core disklabel.
114 if ((dksc
->sc_flags
& DKF_INITED
)) {
115 if (dk
->dk_openmask
== 0) {
116 dk_getdisklabel(di
, dksc
, dev
);
118 /* XXX re-discover wedges? */
121 /* Fail if we can't find the partition. */
122 if ((part
!= RAW_PART
) &&
123 (((dksc
->sc_flags
& DKF_INITED
) == 0) ||
124 ((part
>= lp
->d_npartitions
) ||
125 (lp
->d_partitions
[part
].p_fstype
== FS_UNUSED
)))) {
130 /* Mark our unit as open. */
133 dk
->dk_copenmask
|= pmask
;
136 dk
->dk_bopenmask
|= pmask
;
140 dk
->dk_openmask
= dk
->dk_copenmask
| dk
->dk_bopenmask
;
143 mutex_exit(&dk
->dk_openlock
);
149 dk_close(struct dk_intf
*di
, struct dk_softc
*dksc
, dev_t dev
,
150 int flags
, int fmt
, struct lwp
*l
)
152 int part
= DISKPART(dev
);
153 int pmask
= 1 << part
;
154 struct disk
*dk
= &dksc
->sc_dkdev
;
156 DPRINTF_FOLLOW(("dk_close(%s, %p, 0x%"PRIx64
", 0x%x)\n",
157 di
->di_dkname
, dksc
, dev
, flags
));
159 mutex_enter(&dk
->dk_openlock
);
163 dk
->dk_copenmask
&= ~pmask
;
166 dk
->dk_bopenmask
&= ~pmask
;
169 dk
->dk_openmask
= dk
->dk_copenmask
| dk
->dk_bopenmask
;
171 mutex_exit(&dk
->dk_openlock
);
176 dk_strategy(struct dk_intf
*di
, struct dk_softc
*dksc
, struct buf
*bp
)
182 DPRINTF_FOLLOW(("dk_strategy(%s, %p, %p)\n",
183 di
->di_dkname
, dksc
, bp
));
185 if (!(dksc
->sc_flags
& DKF_INITED
)) {
186 DPRINTF_FOLLOW(("dk_strategy: not inited\n"));
192 /* XXX look for some more errors, c.f. ld.c */
194 bp
->b_resid
= bp
->b_bcount
;
196 /* If there is nothing to do, then we are done */
197 if (bp
->b_bcount
== 0) {
202 wlabel
= dksc
->sc_flags
& (DKF_WLABEL
|DKF_LABELLING
);
203 if (DISKPART(bp
->b_dev
) != RAW_PART
&&
204 bounds_check_with_label(&dksc
->sc_dkdev
, bp
, wlabel
) <= 0) {
210 if (DISKPART(bp
->b_dev
) != RAW_PART
) {
211 struct partition
*pp
;
214 &dksc
->sc_dkdev
.dk_label
->d_partitions
[DISKPART(bp
->b_dev
)];
215 blkno
+= pp
->p_offset
;
217 bp
->b_rawblkno
= blkno
;
220 * Start the unit by calling the start routine
221 * provided by the individual driver.
224 bufq_put(dksc
->sc_bufq
, bp
);
231 dk_start(struct dk_intf
*di
, struct dk_softc
*dksc
)
235 DPRINTF_FOLLOW(("dk_start(%s, %p)\n", di
->di_dkname
, dksc
));
237 /* Process the work queue */
238 while ((bp
= bufq_get(dksc
->sc_bufq
)) != NULL
) {
239 if (di
->di_diskstart(dksc
, bp
) != 0) {
240 bufq_put(dksc
->sc_bufq
, bp
);
247 dk_iodone(struct dk_intf
*di
, struct dk_softc
*dksc
)
250 DPRINTF_FOLLOW(("dk_iodone(%s, %p)\n", di
->di_dkname
, dksc
));
252 /* We kick the queue in case we are able to get more work done */
257 dk_size(struct dk_intf
*di
, struct dk_softc
*dksc
, dev_t dev
)
259 struct disklabel
*lp
;
264 if ((dksc
->sc_flags
& DKF_INITED
) == 0)
267 part
= DISKPART(dev
);
268 is_open
= dksc
->sc_dkdev
.dk_openmask
& (1 << part
);
270 if (!is_open
&& di
->di_open(dev
, 0, S_IFBLK
, curlwp
))
273 lp
= dksc
->sc_dkdev
.dk_label
;
274 if (lp
->d_partitions
[part
].p_fstype
!= FS_SWAP
)
277 size
= lp
->d_partitions
[part
].p_size
*
278 (lp
->d_secsize
/ DEV_BSIZE
);
280 if (!is_open
&& di
->di_close(dev
, 0, S_IFBLK
, curlwp
))
287 dk_ioctl(struct dk_intf
*di
, struct dk_softc
*dksc
, dev_t dev
,
288 u_long cmd
, void *data
, int flag
, struct lwp
*l
)
290 struct disklabel
*lp
;
292 #ifdef __HAVE_OLD_DISKLABEL
293 struct disklabel newlabel
;
297 DPRINTF_FOLLOW(("dk_ioctl(%s, %p, 0x%"PRIx64
", 0x%lx)\n",
298 di
->di_dkname
, dksc
, dev
, cmd
));
300 /* ensure that the pseudo disk is open for writes for these commands */
304 #ifdef __HAVE_OLD_DISKLABEL
309 if ((flag
& FWRITE
) == 0)
313 /* ensure that the pseudo-disk is initialized for these */
321 #ifdef __HAVE_OLD_DISKLABEL
327 if ((dksc
->sc_flags
& DKF_INITED
) == 0)
333 *(struct disklabel
*)data
= *(dksc
->sc_dkdev
.dk_label
);
336 #ifdef __HAVE_OLD_DISKLABEL
338 newlabel
= *(dksc
->sc_dkdev
.dk_label
);
339 if (newlabel
.d_npartitions
> OLDMAXPARTITIONS
)
341 memcpy(data
, &newlabel
, sizeof (struct olddisklabel
));
346 ((struct partinfo
*)data
)->disklab
= dksc
->sc_dkdev
.dk_label
;
347 ((struct partinfo
*)data
)->part
=
348 &dksc
->sc_dkdev
.dk_label
->d_partitions
[DISKPART(dev
)];
353 #ifdef __HAVE_OLD_DISKLABEL
357 #ifdef __HAVE_OLD_DISKLABEL
358 if (cmd
== ODIOCSDINFO
|| cmd
== ODIOCWDINFO
) {
359 memset(&newlabel
, 0, sizeof newlabel
);
360 memcpy(&newlabel
, data
, sizeof (struct olddisklabel
));
364 lp
= (struct disklabel
*)data
;
366 dk
= &dksc
->sc_dkdev
;
367 mutex_enter(&dk
->dk_openlock
);
368 dksc
->sc_flags
|= DKF_LABELLING
;
370 error
= setdisklabel(dksc
->sc_dkdev
.dk_label
,
371 lp
, 0, dksc
->sc_dkdev
.dk_cpulabel
);
373 if (cmd
== DIOCWDINFO
374 #ifdef __HAVE_OLD_DISKLABEL
375 || cmd
== ODIOCWDINFO
378 error
= writedisklabel(DKLABELDEV(dev
),
379 di
->di_strategy
, dksc
->sc_dkdev
.dk_label
,
380 dksc
->sc_dkdev
.dk_cpulabel
);
383 dksc
->sc_flags
&= ~DKF_LABELLING
;
384 mutex_exit(&dk
->dk_openlock
);
388 if (*(int *)data
!= 0)
389 dksc
->sc_flags
|= DKF_WLABEL
;
391 dksc
->sc_flags
&= ~DKF_WLABEL
;
395 dk_getdefaultlabel(di
, dksc
, (struct disklabel
*)data
);
398 #ifdef __HAVE_OLD_DISKLABEL
400 dk_getdefaultlabel(di
, dksc
, &newlabel
);
401 if (newlabel
.d_npartitions
> OLDMAXPARTITIONS
)
403 memcpy(data
, &newlabel
, sizeof (struct olddisklabel
));
409 struct dkwedge_info
*dkw
= (void *)data
;
411 if ((flag
& FWRITE
) == 0)
414 /* If the ioctl happens here, the parent is us. */
415 strcpy(dkw
->dkw_parent
, dksc
->sc_dkdev
.dk_name
);
416 return (dkwedge_add(dkw
));
421 struct dkwedge_info
*dkw
= (void *)data
;
423 if ((flag
& FWRITE
) == 0)
426 /* If the ioctl happens here, the parent is us. */
427 strcpy(dkw
->dkw_parent
, dksc
->sc_dkdev
.dk_name
);
428 return (dkwedge_del(dkw
));
433 struct dkwedge_list
*dkwl
= (void *)data
;
435 return (dkwedge_list(&dksc
->sc_dkdev
, dkwl
, l
));
440 struct disk_strategy
*dks
= (void *)data
;
444 strlcpy(dks
->dks_name
, bufq_getstrategyname(dksc
->sc_bufq
),
445 sizeof(dks
->dks_name
));
447 dks
->dks_paramlen
= 0;
454 struct disk_strategy
*dks
= (void *)data
;
455 struct bufq_state
*new;
456 struct bufq_state
*old
;
459 if ((flag
& FWRITE
) == 0) {
462 if (dks
->dks_param
!= NULL
) {
465 dks
->dks_name
[sizeof(dks
->dks_name
) - 1] = 0; /* ensure term */
466 error
= bufq_alloc(&new, dks
->dks_name
,
467 BUFQ_EXACT
|BUFQ_SORT_RAWBLOCK
);
489 * dk_dump dumps all of physical memory into the partition specified.
490 * This requires substantially more framework than {s,w}ddump, and hence
491 * is probably much more fragile.
493 * XXX: we currently do not implement this.
496 #define DKF_READYFORDUMP (DKF_INITED|DKF_TAKEDUMP)
497 #define DKFF_READYFORDUMP(x) (((x) & DKF_READYFORDUMP) == DKF_READYFORDUMP)
498 static volatile int dk_dumping
= 0;
502 dk_dump(struct dk_intf
*di
, struct dk_softc
*dksc
, dev_t dev
,
503 daddr_t blkno
, void *va
, size_t size
)
507 * ensure that we consider this device to be safe for dumping,
508 * and that the device is configured.
510 if (!DKFF_READYFORDUMP(dksc
->sc_flags
))
513 /* ensure that we are not already dumping */
518 /* XXX: unimplemented */
522 /* XXX: actually for now, we are going to leave this alone */
528 dk_getdefaultlabel(struct dk_intf
*di
, struct dk_softc
*dksc
,
529 struct disklabel
*lp
)
531 struct dk_geom
*pdg
= &dksc
->sc_geom
;
533 memset(lp
, 0, sizeof(*lp
));
535 lp
->d_secperunit
= dksc
->sc_size
;
536 lp
->d_secsize
= pdg
->pdg_secsize
;
537 lp
->d_nsectors
= pdg
->pdg_nsectors
;
538 lp
->d_ntracks
= pdg
->pdg_ntracks
;
539 lp
->d_ncylinders
= pdg
->pdg_ncylinders
;
540 lp
->d_secpercyl
= lp
->d_ntracks
* lp
->d_nsectors
;
542 strncpy(lp
->d_typename
, di
->di_dkname
, sizeof(lp
->d_typename
));
543 lp
->d_type
= di
->di_dtype
;
544 strncpy(lp
->d_packname
, "fictitious", sizeof(lp
->d_packname
));
546 lp
->d_interleave
= 1;
549 lp
->d_partitions
[RAW_PART
].p_offset
= 0;
550 lp
->d_partitions
[RAW_PART
].p_size
= dksc
->sc_size
;
551 lp
->d_partitions
[RAW_PART
].p_fstype
= FS_UNUSED
;
552 lp
->d_npartitions
= RAW_PART
+ 1;
554 lp
->d_magic
= DISKMAGIC
;
555 lp
->d_magic2
= DISKMAGIC
;
556 lp
->d_checksum
= dkcksum(dksc
->sc_dkdev
.dk_label
);
561 dk_getdisklabel(struct dk_intf
*di
, struct dk_softc
*dksc
, dev_t dev
)
563 struct disklabel
*lp
= dksc
->sc_dkdev
.dk_label
;
564 struct cpu_disklabel
*clp
= dksc
->sc_dkdev
.dk_cpulabel
;
565 struct partition
*pp
;
567 const char *errstring
;
569 memset(clp
, 0x0, sizeof(*clp
));
570 dk_getdefaultlabel(di
, dksc
, lp
);
571 errstring
= readdisklabel(DKLABELDEV(dev
), di
->di_strategy
,
572 dksc
->sc_dkdev
.dk_label
, dksc
->sc_dkdev
.dk_cpulabel
);
574 dk_makedisklabel(di
, dksc
);
575 if (dksc
->sc_flags
& DKF_WARNLABEL
)
576 printf("%s: %s\n", dksc
->sc_xname
, errstring
);
580 if ((dksc
->sc_flags
& DKF_LABELSANITY
) == 0)
584 if (lp
->d_secperunit
!= dksc
->sc_size
)
585 printf("WARNING: %s: total sector size in disklabel (%d) "
586 "!= the size of %s (%lu)\n", dksc
->sc_xname
,
587 lp
->d_secperunit
, di
->di_dkname
, (u_long
)dksc
->sc_size
);
589 for (i
=0; i
< lp
->d_npartitions
; i
++) {
590 pp
= &lp
->d_partitions
[i
];
591 if (pp
->p_offset
+ pp
->p_size
> dksc
->sc_size
)
592 printf("WARNING: %s: end of partition `%c' exceeds "
593 "the size of %s (%lu)\n", dksc
->sc_xname
,
594 'a' + i
, di
->di_dkname
, (u_long
)dksc
->sc_size
);
600 dk_makedisklabel(struct dk_intf
*di
, struct dk_softc
*dksc
)
602 struct disklabel
*lp
= dksc
->sc_dkdev
.dk_label
;
604 lp
->d_partitions
[RAW_PART
].p_fstype
= FS_BSDFFS
;
605 strncpy(lp
->d_packname
, "default label", sizeof(lp
->d_packname
));
606 lp
->d_checksum
= dkcksum(lp
);
609 /* This function is taken from ccd.c:1.76 --rcd */
612 * XXX this function looks too generic for dksubr.c, shouldn't we
613 * put it somewhere better?
617 * Lookup the provided name in the filesystem. If the file exists,
618 * is a valid block device, and isn't being used by anyone else,
619 * set *vpp to the file's vnode.
622 dk_lookup(const char *path
, struct lwp
*l
, struct vnode
**vpp
,
631 return ESRCH
; /* Is ESRCH the best choice? */
633 NDINIT(&nd
, LOOKUP
, FOLLOW
, segflg
, path
);
634 if ((error
= vn_open(&nd
, FREAD
| FWRITE
, 0)) != 0) {
635 DPRINTF((DKDB_FOLLOW
|DKDB_INIT
),
636 ("dk_lookup: vn_open error = %d\n", error
));
641 if ((error
= VOP_GETATTR(vp
, &va
, l
->l_cred
)) != 0) {
642 DPRINTF((DKDB_FOLLOW
|DKDB_INIT
),
643 ("dk_lookup: getattr error = %d\n", error
));
647 /* XXX: eventually we should handle VREG, too. */
648 if (va
.va_type
!= VBLK
) {
653 IFDEBUG(DKDB_VNODE
, vprint("dk_lookup: vnode info", vp
));
660 (void) vn_close(vp
, FREAD
| FWRITE
, l
->l_cred
);