1 /* $NetBSD: dk.c,v 1.51 2009/09/08 21:14:33 pooka Exp $ */
4 * Copyright (c) 2004, 2005, 2006, 2007 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.
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: dk.c,v 1.51 2009/09/08 21:14:33 pooka Exp $");
36 #include "opt_dkwedge.h"
39 #include <sys/param.h>
40 #include <sys/systm.h>
42 #include <sys/errno.h>
44 #include <sys/ioctl.h>
45 #include <sys/disklabel.h>
47 #include <sys/fcntl.h>
50 #include <sys/vnode.h>
53 #include <sys/callout.h>
54 #include <sys/kernel.h>
55 #include <sys/malloc.h>
56 #include <sys/device.h>
57 #include <sys/kauth.h>
59 #include <miscfs/specfs/specdev.h>
61 MALLOC_DEFINE(M_DKWEDGE
, "dkwedge", "Disk wedge structures");
65 DKW_STATE_RUNNING
= 1,
70 struct dkwedge_softc
{
71 struct device
*sc_dev
; /* pointer to our pseudo-device */
72 struct cfdata sc_cfdata
; /* our cfdata structure */
73 uint8_t sc_wname
[128]; /* wedge name (Unicode, UTF-8) */
75 dkwedge_state_t sc_state
; /* state this wedge is in */
77 struct disk
*sc_parent
; /* parent disk */
78 daddr_t sc_offset
; /* LBA offset of wedge in parent */
79 uint64_t sc_size
; /* size of wedge in blocks */
80 char sc_ptype
[32]; /* partition type */
81 dev_t sc_pdev
; /* cached parent's dev_t */
82 /* link on parent's wedge list */
83 LIST_ENTRY(dkwedge_softc
) sc_plink
;
85 struct disk sc_dk
; /* our own disk structure */
86 struct bufq_state
*sc_bufq
; /* buffer queue */
87 struct callout sc_restart_ch
; /* callout to restart I/O */
89 u_int sc_iopend
; /* I/Os pending */
90 int sc_flags
; /* flags (splbio) */
93 #define DK_F_WAIT_DRAIN 0x0001 /* waiting for I/O to drain */
95 static void dkstart(struct dkwedge_softc
*);
96 static void dkiodone(struct buf
*);
97 static void dkrestart(void *);
98 static void dkminphys(struct buf
*);
100 static int dklastclose(struct dkwedge_softc
*);
101 static int dkwedge_detach(device_t
, int);
103 static dev_type_open(dkopen
);
104 static dev_type_close(dkclose
);
105 static dev_type_read(dkread
);
106 static dev_type_write(dkwrite
);
107 static dev_type_ioctl(dkioctl
);
108 static dev_type_strategy(dkstrategy
);
109 static dev_type_dump(dkdump
);
110 static dev_type_size(dksize
);
112 const struct bdevsw dk_bdevsw
= {
113 dkopen
, dkclose
, dkstrategy
, dkioctl
, dkdump
, dksize
, D_DISK
116 const struct cdevsw dk_cdevsw
= {
117 dkopen
, dkclose
, dkread
, dkwrite
, dkioctl
,
118 nostop
, notty
, nopoll
, nommap
, nokqfilter
, D_DISK
121 static struct dkwedge_softc
**dkwedges
;
122 static u_int ndkwedges
;
123 static krwlock_t dkwedges_lock
;
125 static LIST_HEAD(, dkwedge_discovery_method
) dkwedge_discovery_methods
;
126 static krwlock_t dkwedge_discovery_methods_lock
;
131 * Autoconfiguration match function for pseudo-device glue.
134 dkwedge_match(device_t parent
, cfdata_t match
,
138 /* Pseudo-device; always present. */
145 * Autoconfiguration attach function for pseudo-device glue.
148 dkwedge_attach(device_t parent
, device_t self
,
152 if (!pmf_device_register(self
, NULL
, NULL
))
153 aprint_error_dev(self
, "couldn't establish power handler\n");
156 CFDRIVER_DECL(dk
, DV_DISK
, NULL
);
157 CFATTACH_DECL3_NEW(dk
, 0,
158 dkwedge_match
, dkwedge_attach
, dkwedge_detach
, NULL
, NULL
, NULL
,
159 DVF_DETACH_SHUTDOWN
);
162 * dkwedge_wait_drain:
164 * Wait for I/O on the wedge to drain.
165 * NOTE: Must be called at splbio()!
168 dkwedge_wait_drain(struct dkwedge_softc
*sc
)
171 while (sc
->sc_iopend
!= 0) {
172 sc
->sc_flags
|= DK_F_WAIT_DRAIN
;
173 (void) tsleep(&sc
->sc_iopend
, PRIBIO
, "dkdrn", 0);
178 * dkwedge_compute_pdev:
180 * Compute the parent disk's dev_t.
183 dkwedge_compute_pdev(const char *pname
, dev_t
*pdevp
)
185 const char *name
, *cp
;
190 if ((pmaj
= devsw_name2blk(name
, devname
, sizeof(devname
))) == -1)
193 name
+= strlen(devname
);
194 for (cp
= name
, punit
= 0; *cp
>= '0' && *cp
<= '9'; cp
++)
195 punit
= (punit
* 10) + (*cp
- '0');
197 /* Invalid parent disk name. */
201 *pdevp
= MAKEDISKDEV(pmaj
, punit
, RAW_PART
);
207 * dkwedge_array_expand:
209 * Expand the dkwedges array.
212 dkwedge_array_expand(void)
214 int newcnt
= ndkwedges
+ 16;
215 struct dkwedge_softc
**newarray
, **oldarray
;
217 newarray
= malloc(newcnt
* sizeof(*newarray
), M_DKWEDGE
,
219 if ((oldarray
= dkwedges
) != NULL
)
220 memcpy(newarray
, dkwedges
, ndkwedges
* sizeof(*newarray
));
223 if (oldarray
!= NULL
)
224 free(oldarray
, M_DKWEDGE
);
228 dkgetproperties(struct disk
*disk
, struct dkwedge_info
*dkw
)
230 prop_dictionary_t disk_info
, odisk_info
, geom
;
232 disk_info
= prop_dictionary_create();
234 prop_dictionary_set_cstring_nocopy(disk_info
, "type", "ESDI");
236 geom
= prop_dictionary_create();
238 prop_dictionary_set_uint64(geom
, "sectors-per-unit", dkw
->dkw_size
);
240 prop_dictionary_set_uint32(geom
, "sector-size",
241 DEV_BSIZE
/* XXX 512? */);
243 prop_dictionary_set_uint32(geom
, "sectors-per-track", 32);
245 prop_dictionary_set_uint32(geom
, "tracks-per-cylinder", 64);
247 prop_dictionary_set_uint32(geom
, "cylinders-per-unit", dkw
->dkw_size
/ 2048);
249 prop_dictionary_set(disk_info
, "geometry", geom
);
250 prop_object_release(geom
);
252 odisk_info
= disk
->dk_info
;
254 disk
->dk_info
= disk_info
;
256 if (odisk_info
!= NULL
)
257 prop_object_release(odisk_info
);
261 * dkwedge_add: [exported function]
263 * Add a disk wedge based on the provided information.
265 * The incoming dkw_devname[] is ignored, instead being
266 * filled in and returned to the caller.
269 dkwedge_add(struct dkwedge_info
*dkw
)
271 struct dkwedge_softc
*sc
, *lsc
;
277 dkw
->dkw_parent
[sizeof(dkw
->dkw_parent
) - 1] = '\0';
278 pdk
= disk_find(dkw
->dkw_parent
);
282 error
= dkwedge_compute_pdev(pdk
->dk_name
, &pdev
);
286 if (dkw
->dkw_offset
< 0)
289 sc
= malloc(sizeof(*sc
), M_DKWEDGE
, M_WAITOK
|M_ZERO
);
290 sc
->sc_state
= DKW_STATE_LARVAL
;
293 sc
->sc_offset
= dkw
->dkw_offset
;
294 sc
->sc_size
= dkw
->dkw_size
;
296 memcpy(sc
->sc_wname
, dkw
->dkw_wname
, sizeof(sc
->sc_wname
));
297 sc
->sc_wname
[sizeof(sc
->sc_wname
) - 1] = '\0';
299 memcpy(sc
->sc_ptype
, dkw
->dkw_ptype
, sizeof(sc
->sc_ptype
));
300 sc
->sc_ptype
[sizeof(sc
->sc_ptype
) - 1] = '\0';
302 bufq_alloc(&sc
->sc_bufq
, "fcfs", 0);
304 callout_init(&sc
->sc_restart_ch
, 0);
305 callout_setfunc(&sc
->sc_restart_ch
, dkrestart
, sc
);
308 * Wedge will be added; increment the wedge count for the parent.
309 * Only allow this to happend if RAW_PART is the only thing open.
311 mutex_enter(&pdk
->dk_openlock
);
312 if (pdk
->dk_openmask
& ~(1 << RAW_PART
))
315 /* Check for wedge overlap. */
316 LIST_FOREACH(lsc
, &pdk
->dk_wedges
, sc_plink
) {
317 daddr_t lastblk
= sc
->sc_offset
+ sc
->sc_size
- 1;
318 daddr_t llastblk
= lsc
->sc_offset
+ lsc
->sc_size
- 1;
320 if (sc
->sc_offset
>= lsc
->sc_offset
&&
321 sc
->sc_offset
<= llastblk
) {
322 /* Overlaps the tail of the exsiting wedge. */
325 if (lastblk
>= lsc
->sc_offset
&&
326 lastblk
<= llastblk
) {
327 /* Overlaps the head of the existing wedge. */
335 LIST_INSERT_HEAD(&pdk
->dk_wedges
, sc
, sc_plink
);
338 mutex_exit(&pdk
->dk_openlock
);
340 bufq_free(sc
->sc_bufq
);
345 /* Fill in our cfdata for the pseudo-device glue. */
346 sc
->sc_cfdata
.cf_name
= dk_cd
.cd_name
;
347 sc
->sc_cfdata
.cf_atname
= dk_ca
.ca_name
;
348 /* sc->sc_cfdata.cf_unit set below */
349 sc
->sc_cfdata
.cf_fstate
= FSTATE_STAR
;
351 /* Insert the larval wedge into the array. */
352 rw_enter(&dkwedges_lock
, RW_WRITER
);
354 struct dkwedge_softc
**scpp
;
357 * Check for a duplicate wname while searching for
360 for (scpp
= NULL
, unit
= 0; unit
< ndkwedges
; unit
++) {
361 if (dkwedges
[unit
] == NULL
) {
363 scpp
= &dkwedges
[unit
];
364 sc
->sc_cfdata
.cf_unit
= unit
;
368 if (strcmp(dkwedges
[unit
]->sc_wname
,
369 sc
->sc_wname
) == 0) {
377 KASSERT(unit
== ndkwedges
);
379 dkwedge_array_expand();
381 KASSERT(scpp
== &dkwedges
[sc
->sc_cfdata
.cf_unit
]);
386 rw_exit(&dkwedges_lock
);
388 mutex_enter(&pdk
->dk_openlock
);
390 LIST_REMOVE(sc
, sc_plink
);
391 mutex_exit(&pdk
->dk_openlock
);
393 bufq_free(sc
->sc_bufq
);
399 * Now that we know the unit #, attach a pseudo-device for
400 * this wedge instance. This will provide us with the
401 * "struct device" necessary for glue to other parts of the
404 * This should never fail, unless we're almost totally out of
407 if ((sc
->sc_dev
= config_attach_pseudo(&sc
->sc_cfdata
)) == NULL
) {
408 aprint_error("%s%u: unable to attach pseudo-device\n",
409 sc
->sc_cfdata
.cf_name
, sc
->sc_cfdata
.cf_unit
);
411 rw_enter(&dkwedges_lock
, RW_WRITER
);
412 dkwedges
[sc
->sc_cfdata
.cf_unit
] = NULL
;
413 rw_exit(&dkwedges_lock
);
415 mutex_enter(&pdk
->dk_openlock
);
417 LIST_REMOVE(sc
, sc_plink
);
418 mutex_exit(&pdk
->dk_openlock
);
420 bufq_free(sc
->sc_bufq
);
425 /* Return the devname to the caller. */
426 strlcpy(dkw
->dkw_devname
, device_xname(sc
->sc_dev
),
427 sizeof(dkw
->dkw_devname
));
430 * XXX Really ought to make the disk_attach() and the changing
431 * of state to RUNNING atomic.
434 disk_init(&sc
->sc_dk
, device_xname(sc
->sc_dev
), NULL
);
435 dkgetproperties(&sc
->sc_dk
, dkw
);
436 disk_attach(&sc
->sc_dk
);
438 /* Disk wedge is ready for use! */
439 sc
->sc_state
= DKW_STATE_RUNNING
;
441 /* Announce our arrival. */
442 aprint_normal("%s at %s: %s\n", device_xname(sc
->sc_dev
), pdk
->dk_name
,
443 sc
->sc_wname
); /* XXX Unicode */
444 aprint_normal("%s: %"PRIu64
" blocks at %"PRId64
", type: %s\n",
445 device_xname(sc
->sc_dev
), sc
->sc_size
, sc
->sc_offset
, sc
->sc_ptype
);
453 * Lookup a disk wedge based on the provided information.
454 * NOTE: We look up the wedge based on the wedge devname,
457 * Return NULL if the wedge is not found, otherwise return
458 * the wedge's softc. Assign the wedge's unit number to unitp
459 * if unitp is not NULL.
461 static struct dkwedge_softc
*
462 dkwedge_find(struct dkwedge_info
*dkw
, u_int
*unitp
)
464 struct dkwedge_softc
*sc
= NULL
;
467 /* Find our softc. */
468 dkw
->dkw_devname
[sizeof(dkw
->dkw_devname
) - 1] = '\0';
469 rw_enter(&dkwedges_lock
, RW_READER
);
470 for (unit
= 0; unit
< ndkwedges
; unit
++) {
471 if ((sc
= dkwedges
[unit
]) != NULL
&&
472 strcmp(device_xname(sc
->sc_dev
), dkw
->dkw_devname
) == 0 &&
473 strcmp(sc
->sc_parent
->dk_name
, dkw
->dkw_parent
) == 0) {
477 rw_exit(&dkwedges_lock
);
478 if (unit
== ndkwedges
)
488 * dkwedge_del: [exported function]
490 * Delete a disk wedge based on the provided information.
491 * NOTE: We look up the wedge based on the wedge devname,
495 dkwedge_del(struct dkwedge_info
*dkw
)
497 struct dkwedge_softc
*sc
= NULL
;
499 /* Find our softc. */
500 if ((sc
= dkwedge_find(dkw
, NULL
)) == NULL
)
503 return config_detach(sc
->sc_dev
, DETACH_FORCE
| DETACH_QUIET
);
507 dkwedge_begindetach(struct dkwedge_softc
*sc
, int flags
)
509 struct disk
*dk
= &sc
->sc_dk
;
513 mutex_enter(&dk
->dk_openlock
);
514 mutex_enter(&sc
->sc_parent
->dk_rawlock
);
515 if (dk
->dk_openmask
== 0)
516 ; /* nothing to do */
517 else if ((flags
& DETACH_FORCE
) == 0)
520 rc
= dklastclose(sc
);
521 mutex_exit(&sc
->sc_parent
->dk_rawlock
);
522 mutex_exit(&dk
->dk_openlock
);
530 * Autoconfiguration detach function for pseudo-device glue.
533 dkwedge_detach(device_t self
, int flags
)
535 struct dkwedge_softc
*sc
= NULL
;
537 int bmaj
, cmaj
, rc
, s
;
539 rw_enter(&dkwedges_lock
, RW_WRITER
);
540 for (unit
= 0; unit
< ndkwedges
; unit
++) {
541 if ((sc
= dkwedges
[unit
]) != NULL
&& sc
->sc_dev
== self
)
544 if (unit
== ndkwedges
)
546 else if ((rc
= dkwedge_begindetach(sc
, flags
)) == 0) {
547 /* Mark the wedge as dying. */
548 sc
->sc_state
= DKW_STATE_DYING
;
550 rw_exit(&dkwedges_lock
);
555 pmf_device_deregister(self
);
557 /* Locate the wedge major numbers. */
558 bmaj
= bdevsw_lookup_major(&dk_bdevsw
);
559 cmaj
= cdevsw_lookup_major(&dk_cdevsw
);
561 /* Kill any pending restart. */
562 callout_stop(&sc
->sc_restart_ch
);
565 * dkstart() will kill any queued buffers now that the
566 * state of the wedge is not RUNNING. Once we've done
567 * that, wait for any other pending I/O to complete.
571 dkwedge_wait_drain(sc
);
574 /* Nuke the vnodes for any open instances. */
575 vdevgone(bmaj
, unit
, unit
, VBLK
);
576 vdevgone(cmaj
, unit
, unit
, VCHR
);
578 /* Clean up the parent. */
579 mutex_enter(&sc
->sc_dk
.dk_openlock
);
580 mutex_enter(&sc
->sc_parent
->dk_rawlock
);
581 if (sc
->sc_dk
.dk_openmask
) {
582 if (sc
->sc_parent
->dk_rawopens
-- == 1) {
583 KASSERT(sc
->sc_parent
->dk_rawvp
!= NULL
);
584 (void) vn_close(sc
->sc_parent
->dk_rawvp
, FREAD
| FWRITE
,
586 sc
->sc_parent
->dk_rawvp
= NULL
;
588 sc
->sc_dk
.dk_openmask
= 0;
590 mutex_exit(&sc
->sc_parent
->dk_rawlock
);
591 mutex_exit(&sc
->sc_dk
.dk_openlock
);
593 /* Announce our departure. */
594 aprint_normal("%s at %s (%s) deleted\n", device_xname(sc
->sc_dev
),
595 sc
->sc_parent
->dk_name
,
596 sc
->sc_wname
); /* XXX Unicode */
598 mutex_enter(&sc
->sc_parent
->dk_openlock
);
599 sc
->sc_parent
->dk_nwedges
--;
600 LIST_REMOVE(sc
, sc_plink
);
601 mutex_exit(&sc
->sc_parent
->dk_openlock
);
603 /* Delete our buffer queue. */
604 bufq_free(sc
->sc_bufq
);
606 /* Detach from the disk list. */
607 disk_detach(&sc
->sc_dk
);
608 disk_destroy(&sc
->sc_dk
);
611 rw_enter(&dkwedges_lock
, RW_WRITER
);
612 dkwedges
[unit
] = NULL
;
613 sc
->sc_state
= DKW_STATE_DEAD
;
614 rw_exit(&dkwedges_lock
);
622 * dkwedge_delall: [exported function]
624 * Delete all of the wedges on the specified disk. Used when
625 * a disk is being detached.
628 dkwedge_delall(struct disk
*pdk
)
630 struct dkwedge_info dkw
;
631 struct dkwedge_softc
*sc
;
634 mutex_enter(&pdk
->dk_openlock
);
635 if ((sc
= LIST_FIRST(&pdk
->dk_wedges
)) == NULL
) {
636 KASSERT(pdk
->dk_nwedges
== 0);
637 mutex_exit(&pdk
->dk_openlock
);
640 strcpy(dkw
.dkw_parent
, pdk
->dk_name
);
641 strlcpy(dkw
.dkw_devname
, device_xname(sc
->sc_dev
),
642 sizeof(dkw
.dkw_devname
));
643 mutex_exit(&pdk
->dk_openlock
);
644 (void) dkwedge_del(&dkw
);
649 * dkwedge_list: [exported function]
651 * List all of the wedges on a particular disk.
652 * If p == NULL, the buffer is in kernel space. Otherwise, it is
653 * in user space of the specified process.
656 dkwedge_list(struct disk
*pdk
, struct dkwedge_list
*dkwl
, struct lwp
*l
)
660 struct dkwedge_softc
*sc
;
661 struct dkwedge_info dkw
;
664 iov
.iov_base
= dkwl
->dkwl_buf
;
665 iov
.iov_len
= dkwl
->dkwl_bufsize
;
670 uio
.uio_resid
= dkwl
->dkwl_bufsize
;
671 uio
.uio_rw
= UIO_READ
;
672 KASSERT(l
== curlwp
);
673 uio
.uio_vmspace
= l
->l_proc
->p_vmspace
;
675 dkwl
->dkwl_ncopied
= 0;
677 mutex_enter(&pdk
->dk_openlock
);
678 LIST_FOREACH(sc
, &pdk
->dk_wedges
, sc_plink
) {
679 if (uio
.uio_resid
< sizeof(dkw
))
682 if (sc
->sc_state
!= DKW_STATE_RUNNING
)
685 strlcpy(dkw
.dkw_devname
, device_xname(sc
->sc_dev
),
686 sizeof(dkw
.dkw_devname
));
687 memcpy(dkw
.dkw_wname
, sc
->sc_wname
, sizeof(dkw
.dkw_wname
));
688 dkw
.dkw_wname
[sizeof(dkw
.dkw_wname
) - 1] = '\0';
689 strcpy(dkw
.dkw_parent
, sc
->sc_parent
->dk_name
);
690 dkw
.dkw_offset
= sc
->sc_offset
;
691 dkw
.dkw_size
= sc
->sc_size
;
692 strcpy(dkw
.dkw_ptype
, sc
->sc_ptype
);
694 error
= uiomove(&dkw
, sizeof(dkw
), &uio
);
697 dkwl
->dkwl_ncopied
++;
699 dkwl
->dkwl_nwedges
= pdk
->dk_nwedges
;
700 mutex_exit(&pdk
->dk_openlock
);
706 dkwedge_find_by_wname(const char *wname
)
709 struct dkwedge_softc
*sc
;
712 rw_enter(&dkwedges_lock
, RW_WRITER
);
713 for (i
= 0; i
< ndkwedges
; i
++) {
714 if ((sc
= dkwedges
[i
]) == NULL
)
716 if (strcmp(sc
->sc_wname
, wname
) == 0) {
719 "WARNING: double match for wedge name %s "
720 "(%s, %s)\n", wname
, device_xname(dv
),
721 device_xname(sc
->sc_dev
));
727 rw_exit(&dkwedges_lock
);
732 dkwedge_print_wnames(void)
734 struct dkwedge_softc
*sc
;
737 rw_enter(&dkwedges_lock
, RW_WRITER
);
738 for (i
= 0; i
< ndkwedges
; i
++) {
739 if ((sc
= dkwedges
[i
]) == NULL
)
741 printf(" wedge:%s", sc
->sc_wname
);
743 rw_exit(&dkwedges_lock
);
747 * dkwedge_set_bootwedge
749 * Set the booted_wedge global based on the specified parent name
753 dkwedge_set_bootwedge(device_t parent
, daddr_t startblk
, uint64_t nblks
)
755 struct dkwedge_softc
*sc
;
758 rw_enter(&dkwedges_lock
, RW_WRITER
);
759 for (i
= 0; i
< ndkwedges
; i
++) {
760 if ((sc
= dkwedges
[i
]) == NULL
)
762 if (strcmp(sc
->sc_parent
->dk_name
, device_xname(parent
)) == 0 &&
763 sc
->sc_offset
== startblk
&&
764 sc
->sc_size
== nblks
) {
766 printf("WARNING: double match for boot wedge "
768 device_xname(booted_wedge
),
769 device_xname(sc
->sc_dev
));
772 booted_device
= parent
;
773 booted_wedge
= sc
->sc_dev
;
774 booted_partition
= 0;
778 * XXX What if we don't find one? Should we create a special
781 rw_exit(&dkwedges_lock
);
785 * We need a dummy object to stuff into the dkwedge discovery method link
786 * set to ensure that there is always at least one object in the set.
788 static struct dkwedge_discovery_method dummy_discovery_method
;
789 __link_set_add_bss(dkwedge_methods
, dummy_discovery_method
);
794 * Initialize the disk wedge subsystem.
799 __link_set_decl(dkwedge_methods
, struct dkwedge_discovery_method
);
800 struct dkwedge_discovery_method
* const *ddmp
;
801 struct dkwedge_discovery_method
*lddm
, *ddm
;
803 rw_init(&dkwedges_lock
);
804 rw_init(&dkwedge_discovery_methods_lock
);
806 if (config_cfdriver_attach(&dk_cd
) != 0)
807 panic("dkwedge: unable to attach cfdriver");
808 if (config_cfattach_attach(dk_cd
.cd_name
, &dk_ca
) != 0)
809 panic("dkwedge: unable to attach cfattach");
811 rw_enter(&dkwedge_discovery_methods_lock
, RW_WRITER
);
813 LIST_INIT(&dkwedge_discovery_methods
);
815 __link_set_foreach(ddmp
, dkwedge_methods
) {
817 if (ddm
== &dummy_discovery_method
)
819 if (LIST_EMPTY(&dkwedge_discovery_methods
)) {
820 LIST_INSERT_HEAD(&dkwedge_discovery_methods
,
824 LIST_FOREACH(lddm
, &dkwedge_discovery_methods
, ddm_list
) {
825 if (ddm
->ddm_priority
== lddm
->ddm_priority
) {
826 aprint_error("dk-method-%s: method \"%s\" "
827 "already exists at priority %d\n",
828 ddm
->ddm_name
, lddm
->ddm_name
,
833 if (ddm
->ddm_priority
< lddm
->ddm_priority
) {
834 /* Higher priority; insert before. */
835 LIST_INSERT_BEFORE(lddm
, ddm
, ddm_list
);
838 if (LIST_NEXT(lddm
, ddm_list
) == NULL
) {
839 /* Last one; insert after. */
840 KASSERT(lddm
->ddm_priority
< ddm
->ddm_priority
);
841 LIST_INSERT_AFTER(lddm
, ddm
, ddm_list
);
847 rw_exit(&dkwedge_discovery_methods_lock
);
850 #ifdef DKWEDGE_AUTODISCOVER
851 int dkwedge_autodiscover
= 1;
853 int dkwedge_autodiscover
= 0;
857 * dkwedge_discover: [exported function]
859 * Discover the wedges on a newly attached disk.
862 dkwedge_discover(struct disk
*pdk
)
864 struct dkwedge_discovery_method
*ddm
;
870 * Require people playing with wedges to enable this explicitly.
872 if (dkwedge_autodiscover
== 0)
875 rw_enter(&dkwedge_discovery_methods_lock
, RW_READER
);
877 error
= dkwedge_compute_pdev(pdk
->dk_name
, &pdev
);
879 aprint_error("%s: unable to compute pdev, error = %d\n",
880 pdk
->dk_name
, error
);
884 error
= bdevvp(pdev
, &vp
);
886 aprint_error("%s: unable to find vnode for pdev, error = %d\n",
887 pdk
->dk_name
, error
);
891 error
= vn_lock(vp
, LK_EXCLUSIVE
| LK_RETRY
);
893 aprint_error("%s: unable to lock vnode for pdev, error = %d\n",
894 pdk
->dk_name
, error
);
899 error
= VOP_OPEN(vp
, FREAD
, NOCRED
);
901 aprint_error("%s: unable to open device, error = %d\n",
902 pdk
->dk_name
, error
);
909 * For each supported partition map type, look to see if
910 * this map type exists. If so, parse it and add the
911 * corresponding wedges.
913 LIST_FOREACH(ddm
, &dkwedge_discovery_methods
, ddm_list
) {
914 error
= (*ddm
->ddm_discover
)(pdk
, vp
);
916 /* Successfully created wedges; we're done. */
921 error
= vn_close(vp
, FREAD
, NOCRED
);
923 aprint_error("%s: unable to close device, error = %d\n",
924 pdk
->dk_name
, error
);
925 /* We'll just assume the vnode has been cleaned up. */
928 rw_exit(&dkwedge_discovery_methods_lock
);
934 * Read some data from the specified disk, used for
935 * partition discovery.
938 dkwedge_read(struct disk
*pdk
, struct vnode
*vp
, daddr_t blkno
,
939 void *tbuf
, size_t len
)
944 bp
= getiobuf(vp
, true);
946 bp
->b_dev
= vp
->v_rdev
;
950 bp
->b_flags
= B_READ
;
952 SET(bp
->b_cflags
, BC_BUSY
); /* mark buffer busy */
954 VOP_STRATEGY(vp
, bp
);
955 result
= biowait(bp
);
964 * Look up a dkwedge_softc based on the provided dev_t.
966 static struct dkwedge_softc
*
967 dkwedge_lookup(dev_t dev
)
969 int unit
= minor(dev
);
971 if (unit
>= ndkwedges
)
974 KASSERT(dkwedges
!= NULL
);
976 return (dkwedges
[unit
]);
980 * dkopen: [devsw entry point]
985 dkopen(dev_t dev
, int flags
, int fmt
, struct lwp
*l
)
987 struct dkwedge_softc
*sc
= dkwedge_lookup(dev
);
994 if (sc
->sc_state
!= DKW_STATE_RUNNING
)
998 * We go through a complicated little dance to only open the parent
999 * vnode once per wedge, no matter how many times the wedge is
1000 * opened. The reason? We see one dkopen() per open call, but
1001 * only dkclose() on the last close.
1003 mutex_enter(&sc
->sc_dk
.dk_openlock
);
1004 mutex_enter(&sc
->sc_parent
->dk_rawlock
);
1005 if (sc
->sc_dk
.dk_openmask
== 0) {
1006 if (sc
->sc_parent
->dk_rawopens
== 0) {
1007 KASSERT(sc
->sc_parent
->dk_rawvp
== NULL
);
1008 error
= bdevvp(sc
->sc_pdev
, &vp
);
1011 error
= vn_lock(vp
, LK_EXCLUSIVE
| LK_RETRY
);
1016 error
= VOP_OPEN(vp
, FREAD
| FWRITE
, NOCRED
);
1021 /* VOP_OPEN() doesn't do this for us. */
1022 mutex_enter(&vp
->v_interlock
);
1024 mutex_exit(&vp
->v_interlock
);
1026 sc
->sc_parent
->dk_rawvp
= vp
;
1028 sc
->sc_parent
->dk_rawopens
++;
1031 sc
->sc_dk
.dk_copenmask
|= 1;
1033 sc
->sc_dk
.dk_bopenmask
|= 1;
1034 sc
->sc_dk
.dk_openmask
=
1035 sc
->sc_dk
.dk_copenmask
| sc
->sc_dk
.dk_bopenmask
;
1038 mutex_exit(&sc
->sc_parent
->dk_rawlock
);
1039 mutex_exit(&sc
->sc_dk
.dk_openlock
);
1044 * Caller must hold sc->sc_dk.dk_openlock and sc->sc_parent->dk_rawlock.
1047 dklastclose(struct dkwedge_softc
*sc
)
1051 if (sc
->sc_parent
->dk_rawopens
-- == 1) {
1052 KASSERT(sc
->sc_parent
->dk_rawvp
!= NULL
);
1053 error
= vn_close(sc
->sc_parent
->dk_rawvp
,
1054 FREAD
| FWRITE
, NOCRED
);
1055 sc
->sc_parent
->dk_rawvp
= NULL
;
1061 * dkclose: [devsw entry point]
1066 dkclose(dev_t dev
, int flags
, int fmt
, struct lwp
*l
)
1068 struct dkwedge_softc
*sc
= dkwedge_lookup(dev
);
1071 KASSERT(sc
->sc_dk
.dk_openmask
!= 0);
1073 mutex_enter(&sc
->sc_dk
.dk_openlock
);
1074 mutex_enter(&sc
->sc_parent
->dk_rawlock
);
1077 sc
->sc_dk
.dk_copenmask
&= ~1;
1079 sc
->sc_dk
.dk_bopenmask
&= ~1;
1080 sc
->sc_dk
.dk_openmask
=
1081 sc
->sc_dk
.dk_copenmask
| sc
->sc_dk
.dk_bopenmask
;
1083 if (sc
->sc_dk
.dk_openmask
== 0)
1084 error
= dklastclose(sc
);
1086 mutex_exit(&sc
->sc_parent
->dk_rawlock
);
1087 mutex_exit(&sc
->sc_dk
.dk_openlock
);
1093 * dkstragegy: [devsw entry point]
1095 * Perform I/O based on the wedge I/O strategy.
1098 dkstrategy(struct buf
*bp
)
1100 struct dkwedge_softc
*sc
= dkwedge_lookup(bp
->b_dev
);
1103 if (sc
->sc_state
!= DKW_STATE_RUNNING
) {
1104 bp
->b_error
= ENXIO
;
1108 /* If it's an empty transfer, wake up the top half now. */
1109 if (bp
->b_bcount
== 0)
1112 /* Make sure it's in-range. */
1113 if (bounds_check_with_mediasize(bp
, DEV_BSIZE
, sc
->sc_size
) <= 0)
1116 /* Translate it to the parent's raw LBA. */
1117 bp
->b_rawblkno
= bp
->b_blkno
+ sc
->sc_offset
;
1119 /* Place it in the queue and start I/O on the unit. */
1122 bufq_put(sc
->sc_bufq
, bp
);
1128 bp
->b_resid
= bp
->b_bcount
;
1135 * Start I/O that has been enqueued on the wedge.
1136 * NOTE: Must be called at splbio()!
1139 dkstart(struct dkwedge_softc
*sc
)
1142 struct buf
*bp
, *nbp
;
1144 /* Do as much work as has been enqueued. */
1145 while ((bp
= bufq_peek(sc
->sc_bufq
)) != NULL
) {
1146 if (sc
->sc_state
!= DKW_STATE_RUNNING
) {
1147 (void) bufq_get(sc
->sc_bufq
);
1148 if (sc
->sc_iopend
-- == 1 &&
1149 (sc
->sc_flags
& DK_F_WAIT_DRAIN
) != 0) {
1150 sc
->sc_flags
&= ~DK_F_WAIT_DRAIN
;
1151 wakeup(&sc
->sc_iopend
);
1153 bp
->b_error
= ENXIO
;
1154 bp
->b_resid
= bp
->b_bcount
;
1158 /* Instrumentation. */
1159 disk_busy(&sc
->sc_dk
);
1161 nbp
= getiobuf(sc
->sc_parent
->dk_rawvp
, false);
1164 * No resources to run this request; leave the
1165 * buffer queued up, and schedule a timer to
1166 * restart the queue in 1/2 a second.
1168 disk_unbusy(&sc
->sc_dk
, 0, bp
->b_flags
& B_READ
);
1169 callout_schedule(&sc
->sc_restart_ch
, hz
/ 2);
1173 (void) bufq_get(sc
->sc_bufq
);
1175 nbp
->b_data
= bp
->b_data
;
1176 nbp
->b_flags
= bp
->b_flags
;
1177 nbp
->b_oflags
= bp
->b_oflags
;
1178 nbp
->b_cflags
= bp
->b_cflags
;
1179 nbp
->b_iodone
= dkiodone
;
1180 nbp
->b_proc
= bp
->b_proc
;
1181 nbp
->b_blkno
= bp
->b_rawblkno
;
1182 nbp
->b_dev
= sc
->sc_parent
->dk_rawvp
->v_rdev
;
1183 nbp
->b_bcount
= bp
->b_bcount
;
1184 nbp
->b_private
= bp
;
1185 BIO_COPYPRIO(nbp
, bp
);
1188 if ((nbp
->b_flags
& B_READ
) == 0) {
1189 mutex_enter(&vp
->v_interlock
);
1191 mutex_exit(&vp
->v_interlock
);
1193 VOP_STRATEGY(vp
, nbp
);
1200 * I/O to a wedge has completed; alert the top half.
1201 * NOTE: Must be called at splbio()!
1204 dkiodone(struct buf
*bp
)
1206 struct buf
*obp
= bp
->b_private
;
1207 struct dkwedge_softc
*sc
= dkwedge_lookup(obp
->b_dev
);
1209 if (bp
->b_error
!= 0)
1210 obp
->b_error
= bp
->b_error
;
1211 obp
->b_resid
= bp
->b_resid
;
1214 if (sc
->sc_iopend
-- == 1 && (sc
->sc_flags
& DK_F_WAIT_DRAIN
) != 0) {
1215 sc
->sc_flags
&= ~DK_F_WAIT_DRAIN
;
1216 wakeup(&sc
->sc_iopend
);
1219 disk_unbusy(&sc
->sc_dk
, obp
->b_bcount
- obp
->b_resid
,
1220 obp
->b_flags
& B_READ
);
1224 /* Kick the queue in case there is more work we can do. */
1231 * Restart the work queue after it was stalled due to
1232 * a resource shortage. Invoked via a callout.
1237 struct dkwedge_softc
*sc
= v
;
1248 * Call parent's minphys function.
1251 dkminphys(struct buf
*bp
)
1253 struct dkwedge_softc
*sc
= dkwedge_lookup(bp
->b_dev
);
1257 bp
->b_dev
= sc
->sc_pdev
;
1258 (*sc
->sc_parent
->dk_driver
->d_minphys
)(bp
);
1263 * dkread: [devsw entry point]
1265 * Read from a wedge.
1268 dkread(dev_t dev
, struct uio
*uio
, int flags
)
1270 struct dkwedge_softc
*sc
= dkwedge_lookup(dev
);
1272 if (sc
->sc_state
!= DKW_STATE_RUNNING
)
1275 return (physio(dkstrategy
, NULL
, dev
, B_READ
, dkminphys
, uio
));
1279 * dkwrite: [devsw entry point]
1284 dkwrite(dev_t dev
, struct uio
*uio
, int flags
)
1286 struct dkwedge_softc
*sc
= dkwedge_lookup(dev
);
1288 if (sc
->sc_state
!= DKW_STATE_RUNNING
)
1291 return (physio(dkstrategy
, NULL
, dev
, B_WRITE
, dkminphys
, uio
));
1295 * dkioctl: [devsw entry point]
1297 * Perform an ioctl request on a wedge.
1300 dkioctl(dev_t dev
, u_long cmd
, void *data
, int flag
, struct lwp
*l
)
1302 struct dkwedge_softc
*sc
= dkwedge_lookup(dev
);
1305 if (sc
->sc_state
!= DKW_STATE_RUNNING
)
1308 error
= disk_ioctl(&sc
->sc_dk
, cmd
, data
, flag
, l
);
1309 if (error
!= EPASSTHROUGH
)
1317 * XXX Do we really need to care about having a writable
1318 * file descriptor here?
1320 if ((flag
& FWRITE
) == 0)
1323 error
= VOP_IOCTL(sc
->sc_parent
->dk_rawvp
,
1325 l
!= NULL
? l
->l_cred
: NOCRED
);
1327 case DIOCGWEDGEINFO
:
1329 struct dkwedge_info
*dkw
= (void *) data
;
1331 strlcpy(dkw
->dkw_devname
, device_xname(sc
->sc_dev
),
1332 sizeof(dkw
->dkw_devname
));
1333 memcpy(dkw
->dkw_wname
, sc
->sc_wname
, sizeof(dkw
->dkw_wname
));
1334 dkw
->dkw_wname
[sizeof(dkw
->dkw_wname
) - 1] = '\0';
1335 strcpy(dkw
->dkw_parent
, sc
->sc_parent
->dk_name
);
1336 dkw
->dkw_offset
= sc
->sc_offset
;
1337 dkw
->dkw_size
= sc
->sc_size
;
1338 strcpy(dkw
->dkw_ptype
, sc
->sc_ptype
);
1351 * dksize: [devsw entry point]
1353 * Query the size of a wedge for the purpose of performing a dump
1354 * or for swapping to.
1359 struct dkwedge_softc
*sc
= dkwedge_lookup(dev
);
1365 if (sc
->sc_state
!= DKW_STATE_RUNNING
)
1368 mutex_enter(&sc
->sc_dk
.dk_openlock
);
1369 mutex_enter(&sc
->sc_parent
->dk_rawlock
);
1371 /* Our content type is static, no need to open the device. */
1373 if (strcmp(sc
->sc_ptype
, DKW_PTYPE_SWAP
) == 0) {
1374 /* Saturate if we are larger than INT_MAX. */
1375 if (sc
->sc_size
> INT_MAX
)
1378 rv
= (int) sc
->sc_size
;
1381 mutex_exit(&sc
->sc_parent
->dk_rawlock
);
1382 mutex_exit(&sc
->sc_dk
.dk_openlock
);
1388 * dkdump: [devsw entry point]
1390 * Perform a crash dump to a wedge.
1393 dkdump(dev_t dev
, daddr_t blkno
, void *va
, size_t size
)
1395 struct dkwedge_softc
*sc
= dkwedge_lookup(dev
);
1396 const struct bdevsw
*bdev
;
1402 if (sc
->sc_state
!= DKW_STATE_RUNNING
)
1405 mutex_enter(&sc
->sc_dk
.dk_openlock
);
1406 mutex_enter(&sc
->sc_parent
->dk_rawlock
);
1408 /* Our content type is static, no need to open the device. */
1410 if (strcmp(sc
->sc_ptype
, DKW_PTYPE_SWAP
) != 0) {
1414 if (size
% DEV_BSIZE
!= 0) {
1418 if (blkno
+ size
/ DEV_BSIZE
> sc
->sc_size
) {
1419 printf("%s: blkno (%" PRIu64
") + size / DEV_BSIZE (%zu) > "
1420 "sc->sc_size (%" PRIu64
")\n", __func__
, blkno
,
1421 size
/ DEV_BSIZE
, sc
->sc_size
);
1426 bdev
= bdevsw_lookup(sc
->sc_pdev
);
1427 rv
= (*bdev
->d_dump
)(sc
->sc_pdev
, blkno
+ sc
->sc_offset
, va
, size
);
1430 mutex_exit(&sc
->sc_parent
->dk_rawlock
);
1431 mutex_exit(&sc
->sc_dk
.dk_openlock
);
1441 config_handle_wedges(struct device
*dv
, int par
)
1443 struct dkwedge_list wl
;
1444 struct dkwedge_info
*wi
;
1449 if ((vn
= opendisk(dv
)) == NULL
)
1452 wl
.dkwl_bufsize
= sizeof(*wi
) * 16;
1453 wl
.dkwl_buf
= wi
= malloc(wl
.dkwl_bufsize
, M_TEMP
, M_WAITOK
);
1455 error
= VOP_IOCTL(vn
, DIOCLWEDGES
, &wl
, FREAD
, NOCRED
);
1456 VOP_CLOSE(vn
, FREAD
, NOCRED
);
1460 printf("%s: List wedges returned %d\n",
1461 device_xname(dv
), error
);
1468 printf("%s: Returned %u(%u) wedges\n", device_xname(dv
),
1469 wl
.dkwl_nwedges
, wl
.dkwl_ncopied
);
1471 snprintf(diskname
, sizeof(diskname
), "%s%c", device_xname(dv
),
1474 for (i
= 0; i
< wl
.dkwl_ncopied
; i
++) {
1476 printf("%s: Looking for %s in %s\n",
1477 device_xname(dv
), diskname
, wi
[i
].dkw_wname
);
1479 if (strcmp(wi
[i
].dkw_wname
, diskname
) == 0)
1483 if (i
== wl
.dkwl_ncopied
) {
1485 printf("%s: Cannot find wedge with parent %s\n",
1486 device_xname(dv
), diskname
);
1493 printf("%s: Setting boot wedge %s (%s) at %llu %llu\n",
1494 device_xname(dv
), wi
[i
].dkw_devname
, wi
[i
].dkw_wname
,
1495 (unsigned long long)wi
[i
].dkw_offset
,
1496 (unsigned long long)wi
[i
].dkw_size
);
1498 dkwedge_set_bootwedge(dv
, wi
[i
].dkw_offset
, wi
[i
].dkw_size
);