Subject: Re: port bio(4) and bioctl(8) from openbsd ?
To: None <tech-kern@NetBSD.org>
From: Manuel Bouyer <bouyer@antioche.eu.org>
List: tech-kern
Date: 04/21/2007 15:48:08
--mP3DRpeJDSE+ciuQ
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

Here's my latest patch about this.
I've an issue with lock usage in bio(4):
there is a bio_lock, which is a kmutex_t to protect the internal structures
of bio(4). A disk driver (for example mfi(4)) will also have its own
lock to protect its structure; and will probably call bio_register()
and bio_unregister() with this lock held.
The callback from bio(4) to mfi(4) shall be called with bio_lock held, to
avoid having the entry removed from under its feets (it's not the
case right now, but as nothing calls bio_unregister() for now, it's
safe).
Now we could have this deadlock (theorically; because mfi doesn't have
a mfi_detach):
mfi_detach() takes the mfi mutex, and calls bio_unregister() which takes
  bio_lock.
bioioctl() takes bio_lock, and call backs in mfi(4) which will want to
  take the mfi mutex.

I'm not a multithread expert programmer. How is such case handled usually ?
I think mfi_detach() should take bio_lock itself, so it can take locks
in an order which can avoid the deadlock. Or the callback in mfi could
release bio_lock itself before trying to take the mfi mutex, but I can't
see how to avoid the race in which the mfi device could dissapear between
releasing bio_lock and taking the mfi mutex at this point. Any idea ?

-- 
Manuel Bouyer <bouyer@antioche.eu.org>
     NetBSD: 26 ans d'experience feront toujours la difference
--

--mP3DRpeJDSE+ciuQ
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename=diff

Index: distrib/sets/lists/base/mi
===================================================================
RCS file: /cvsroot/src/distrib/sets/lists/base/mi,v
retrieving revision 1.700
diff -u -r1.700 mi
--- distrib/sets/lists/base/mi	8 Apr 2007 09:35:24 -0000	1.700
+++ distrib/sets/lists/base/mi	21 Apr 2007 13:26:12 -0000
@@ -123,6 +123,7 @@
 ./sbin/amrctl					base-sysutil-root
 ./sbin/apmlabel					base-sysutil-root
 ./sbin/atactl					base-sysutil-root
+./sbin/bioctl					base-sysutil-root
 ./sbin/badsect					base-sysutil-root
 ./sbin/brconfig					base-netutil-root
 ./sbin/ccdconfig				base-sysutil-root
Index: distrib/sets/lists/comp/mi
===================================================================
RCS file: /cvsroot/src/distrib/sets/lists/comp/mi,v
retrieving revision 1.1019
diff -u -r1.1019 mi
--- distrib/sets/lists/comp/mi	1 Apr 2007 18:52:28 -0000	1.1019
+++ distrib/sets/lists/comp/mi	21 Apr 2007 13:26:12 -0000
@@ -155,6 +155,7 @@
 ./usr/include/dev/auconv.h			comp-obsolete		obsolete
 ./usr/include/dev/audio_if.h			comp-obsolete		obsolete
 ./usr/include/dev/audiovar.h			comp-obsolete		obsolete
+./usr/include/dev/biovar.h			comp-c-include
 ./usr/include/dev/bluetooth			comp-c-include
 ./usr/include/dev/bluetooth/btdev.h		comp-c-include
 ./usr/include/dev/bluetooth/bthidev.h		comp-c-include
Index: distrib/sets/lists/man/mi
===================================================================
RCS file: /cvsroot/src/distrib/sets/lists/man/mi,v
retrieving revision 1.988
diff -u -r1.988 mi
--- distrib/sets/lists/man/mi	8 Apr 2007 09:35:25 -0000	1.988
+++ distrib/sets/lists/man/mi	21 Apr 2007 13:26:13 -0000
@@ -720,6 +720,7 @@
 ./usr/share/man/cat4/bge.0			man-sys-catman		.cat
 ./usr/share/man/cat4/bha.0			man-sys-catman		.cat
 ./usr/share/man/cat4/bicc.0			man-sys-catman		.cat
+./usr/share/man/cat4/bio.0			man-sys-catman		.cat
 ./usr/share/man/cat4/bktr.0			man-sys-catman		.cat
 ./usr/share/man/cat4/bluetooth.0		man-sys-catman		.cat
 ./usr/share/man/cat4/bmtphy.0			man-sys-catman		.cat
@@ -1802,6 +1803,7 @@
 ./usr/share/man/cat8/badsect.0			man-sysutil-catman	.cat
 ./usr/share/man/cat8/bebox/MAKEDEV.0		man-obsolete		obsolete
 ./usr/share/man/cat8/bebox/makedev.0		man-obsolete		obsolete
+./usr/share/man/cat8/bioctl.0			man-sysutil-catman	.cat
 ./usr/share/man/cat8/boot.0			man-sysutil-catman	.cat
 ./usr/share/man/cat8/bootparamd.0		man-bootserver-catman	.cat
 ./usr/share/man/cat8/bootpd.0			man-bootserver-catman	.cat
@@ -3154,6 +3156,7 @@
 ./usr/share/man/man4/bge.4			man-sys-man		.man
 ./usr/share/man/man4/bha.4			man-sys-man		.man
 ./usr/share/man/man4/bicc.4			man-sys-man		.man
+./usr/share/man/man4/bio.4			man-sys-man		.man
 ./usr/share/man/man4/bktr.4			man-sys-man		.man
 ./usr/share/man/man4/bluetooth.4		man-sys-man		.man
 ./usr/share/man/man4/bmtphy.4			man-sys-man		.man
@@ -4235,6 +4238,7 @@
 ./usr/share/man/man8/badsect.8			man-sysutil-man		.man
 ./usr/share/man/man8/bebox/MAKEDEV.8		man-obsolete		obsolete
 ./usr/share/man/man8/bebox/makedev.8		man-obsolete		obsolete
+./usr/share/man/man8/bioctl.8			man-sysutil-man		.man
 ./usr/share/man/man8/boot.8			man-sysutil-man		.man
 ./usr/share/man/man8/bootparamd.8		man-bootserver-man	.man
 ./usr/share/man/man8/bootpd.8			man-bootserver-man	.man
Index: sbin/Makefile
===================================================================
RCS file: /cvsroot/src/sbin/Makefile,v
retrieving revision 1.104
diff -u -r1.104 Makefile
--- sbin/Makefile	6 Mar 2007 11:28:45 -0000	1.104
+++ sbin/Makefile	21 Apr 2007 13:26:51 -0000
@@ -6,8 +6,8 @@
 
 .include <bsd.own.mk>
 
-SUBDIR=	amrctl apmlabel atactl badsect bim brconfig ccdconfig disklabel \
-	dkctl dkscan_bsdlabel dmesg \
+SUBDIR=	amrctl apmlabel atactl badsect bim bioctl brconfig ccdconfig \
+	disklabel dkctl dkscan_bsdlabel dmesg \
 	drvctl edlabel fastboot fdisk fsck fsirand gpt ifconfig init ldconfig \
 	mbrlabel mknod modload modunload mount newbtconf nologin \
 	pdisk ping pppoectl raidctl reboot rcorder rndctl route routed \
Index: share/man/man4/Makefile
===================================================================
RCS file: /cvsroot/src/share/man/man4/Makefile,v
retrieving revision 1.423
diff -u -r1.423 Makefile
--- share/man/man4/Makefile	25 Mar 2007 09:11:56 -0000	1.423
+++ share/man/man4/Makefile	21 Apr 2007 13:26:52 -0000
@@ -8,7 +8,7 @@
 	ath.4 atppc.4 attimer.4 atw.4 \
 	auacer.4 audio.4 audiocs.4 auich.4 \
 	auixp.4 autri.4 auvia.4 awi.4 azalia.4 \
-	bba.4 bce.4 be.4 bge.4 bnx.4 bha.4 bktr.4 bluetooth.4 bmtphy.4 \
+	bba.4 bce.4 be.4 bge.4 bnx.4 bha.4 bio.4 bktr.4 bluetooth.4 bmtphy.4 \
 	bpf.4 brgphy.4 bridge.4 bthidev.4 bthub.4 btkbd.4 btms.4 btsco.4 \
 	btuart.4 cac.4 cardbus.4 carp.4 ccd.4 cd.4 \
 	cec.4 cgd.4 cfb.4 ch.4 ciphy.4 ciss.4 clcs.4 clct.4 clnp.4 \
Index: share/man/man4/bio.4
===================================================================
RCS file: share/man/man4/bio.4
diff -N share/man/man4/bio.4
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ share/man/man4/bio.4	21 Apr 2007 13:26:52 -0000
@@ -0,0 +1,124 @@
+.\"	$NetBSD: $
+.\"	$OpenBSD: bio.4,v 1.19 2006/09/20 22:22:37 jmc Exp $
+.\"
+.\" Copyright (c) 2002 Niklas Hallqvist
+.\" Copyright (c) 2006 Marco Peereboom
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\"    derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+.\" DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+.\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+.\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd April 8, 2007
+.Dt BIO 4
+.Os
+.Sh NAME
+.Nm bio
+.Nd Block IO ioctl tunnel pseudo-device
+.Sh SYNOPSIS
+.Cd "pseudo-device bio"
+.Sh DESCRIPTION
+The
+.Nm
+driver provides userland applications
+.Xr ioctl 2
+access to devices otherwise not found as
+.Pa /dev
+nodes.
+The
+.Pa /dev/bio
+device node operates by delegating ioctl
+calls to a requested device driver.
+Only drivers which have registered with the
+.Nm
+device can be accessed via this interface.
+.Pp
+The following device drivers register with
+.Nm
+for volume management:
+.Pp
+.Bl -tag -width ciss(4)XX -offset indent -compact
+.\" .It Xr ami 4
+.\" American Megatrends Inc. MegaRAID PATA/SATA/SCSI RAID controller
+.\" .It Xr arc 4
+.\" Areca Technology Corporation SATA RAID controller
+.\" .It Xr ciss 4
+.\" Compaq Smart ARRAY 5/6 SAS/SATA/SCSI RAID controller
+.It Xr mfi 4
+LSI Logic & Dell MegaRAID SAS RAID controller
+.El
+.Pp
+The following ioctl calls apply to the
+.Nm bio
+device:
+.Bl -tag -width BIOCCAPABILITIES
+.It Dv BIOCLOCATE
+Locate a named device and give back a cookie to the application
+for subsequent ioctl calls.
+The cookie is used to tunnel further ioctls to the right device.
+.It Dv BIOCINQ
+Retrieve number of volumes and physical disks for a specific device.
+.It Dv BIOCDISK
+Retrieve detailed information for the specified physical disk.
+Information returned can include status, size, channel, target, lun,
+vendor name, serial number and processor device (ses or safte).
+.It Dv BIOCVOL
+Retrieve detailed information for the specified volume.
+Information returned can include status, size, RAID level, number of disks,
+device name association (sd?) and vendor name.
+.It Dv BIOCALARM
+Control the alarm beeper on the device.
+Supported states are: disable alarm, enable alarm, silence alarm, status and
+test alarm.
+.Pp
+Note:  These options might not be supported on all hardware.
+.It Dv BIOCBLINK
+Blink an LED of the specified physical disk.
+Supported blink states are: blink LED, unblink LED and blink alarm LED.
+.Pp
+Note:  This option is only supported if the disk is governed by ses(4) or
+safte(4) and the hardware supports hardware blinking.
+.It Dv BIOCSETSTATE
+Alter the state of specified physical disk.
+Supported states are: create hot-spare, online disk and offline disk.
+.El
+.Sh FILES
+.Bl -tag -width /dev/bio -compact
+.It Pa /dev/bio
+ioctl tunnel device
+.El
+.Sh SEE ALSO
+.Xr ioctl 2 ,
+.Xr bioctl 8
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Ox 3.2 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Niklas Hallqvist Aq niklas@openbsd.org .
+The API was written by
+.An Marco Peereboom Aq marco@openbsd.org .
Index: sys/arch/amd64/conf/GENERIC
===================================================================
RCS file: /cvsroot/src/sys/arch/amd64/conf/GENERIC,v
retrieving revision 1.137
diff -u -r1.137 GENERIC
--- sys/arch/amd64/conf/GENERIC	20 Mar 2007 21:29:28 -0000	1.137
+++ sys/arch/amd64/conf/GENERIC	21 Apr 2007 13:26:54 -0000
@@ -881,6 +881,7 @@
 pseudo-device	swcrypto		# software crypto implementation
 
 # disk/mass storage pseudo-devices
+pseudo-device	bio			# RAID control device driver
 pseudo-device	ccd		4	# concatenated/striped disk devices
 #pseudo-device	cgd		4	# cryptographic disk devices
 pseudo-device	raid		8	# RAIDframe disk driver
Index: sys/conf/files
===================================================================
RCS file: /cvsroot/src/sys/conf/files,v
retrieving revision 1.837
diff -u -r1.837 files
--- sys/conf/files	14 Apr 2007 19:33:29 -0000	1.837
+++ sys/conf/files	21 Apr 2007 13:27:03 -0000
@@ -1144,6 +1144,7 @@
 defpseudo ksyms
 defpseudo nullcons
 defpseudo lockstat
+defpseudo bio:		sysmon_envsys
 
 defpseudo loop:		ifnet
 defpseudo sl:		ifnet
@@ -1242,6 +1243,7 @@
 file	dev/audio.c			audio | midi | midibus	needs-flag
 file	dev/audiobell.c			audiobell
 file	dev/aurateconv.c		aurateconv		needs-flag
+file	dev/bio.c			bio			needs-flag
 file	dev/ccd.c			ccd			needs-flag
 file	dev/cgd.c			cgd			needs-flag
 file	dev/cgd_crypto.c		cgd
Index: sys/conf/majors
===================================================================
RCS file: /cvsroot/src/sys/conf/majors,v
retrieving revision 1.33
diff -u -r1.33 majors
--- sys/conf/majors	20 Mar 2007 16:37:12 -0000	1.33
+++ sys/conf/majors	21 Apr 2007 13:27:03 -0000
@@ -30,4 +30,5 @@
 device-major	puffs		char 178		puffs
 device-major	srt		char 179		srt
 device-major	drm		char 180		drmbase
+device-major	bio		char 181		bio
 device-major	twa		char 187		twa
Index: sys/dev/Makefile
===================================================================
RCS file: /cvsroot/src/sys/dev/Makefile,v
retrieving revision 1.24
diff -u -r1.24 Makefile
--- sys/dev/Makefile	7 Sep 2006 00:20:28 -0000	1.24
+++ sys/dev/Makefile	21 Apr 2007 13:27:03 -0000
@@ -6,6 +6,6 @@
 INCSDIR= /usr/include/dev
 
 # Only install includes which are used by userland
-INCS=	ccdvar.h cgdvar.h fssvar.h kttcpio.h lockstat.h md.h vndvar.h
+INCS=	biovar.h ccdvar.h cgdvar.h fssvar.h kttcpio.h lockstat.h md.h vndvar.h
 
 .include <bsd.kinc.mk>
Index: sys/dev/bio.c
===================================================================
RCS file: sys/dev/bio.c
diff -N sys/dev/bio.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/dev/bio.c	21 Apr 2007 13:27:03 -0000
@@ -0,0 +1,187 @@
+/*	$NetBSD: $ */
+/*	$OpenBSD: bio.c,v 1.9 2007/03/20 02:35:55 marco Exp $	*/
+
+/*
+ * Copyright (c) 2002 Niklas Hallqvist.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* A device controller ioctl tunnelling device.  */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: $");
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/device.h>
+#include <sys/event.h>
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+#include <sys/systm.h>
+#include <sys/mutex.h>
+
+#include <dev/biovar.h>
+
+struct bio_mapping {
+	LIST_ENTRY(bio_mapping) bm_link;
+	struct device *bm_dev;
+	int (*bm_ioctl)(struct device *, u_long, void *);
+};
+
+LIST_HEAD(, bio_mapping) bios = LIST_HEAD_INITIALIZER(bios);
+static kmutex_t bio_lock;
+
+void	bioattach(int);
+int	bioclose(dev_t, int, int, struct lwp *);
+int	bioioctl(dev_t, u_long, void *, int, struct lwp *);
+int	bioopen(dev_t, int, int, struct lwp *);
+
+int	bio_delegate_ioctl(struct bio_mapping *, u_long, void *);
+struct	bio_mapping *bio_lookup(char *);
+int	bio_validate(void *);
+
+const struct cdevsw bio_cdevsw = {
+        bioopen, bioclose, noread, nowrite, bioioctl,
+        nostop, notty, nopoll, nommap, nokqfilter, 0
+};
+
+
+void
+bioattach(int nunits)
+{
+	mutex_init(&bio_lock, MUTEX_DRIVER, IPL_BIO);
+}
+
+int
+bioopen(dev_t dev, int flags, int mode, struct lwp *l)
+{
+	return (0);
+}
+
+int
+bioclose(dev_t dev, int flags, int mode, struct lwp *l)
+{
+	return (0);
+}
+
+int
+bioioctl(dev_t dev, u_long cmd, void *addr, int flag, struct  lwp *l)
+{
+	struct bio_locate *locate;
+	struct bio_common *common;
+	char name[16];
+	int error;
+
+	switch (cmd) {
+	case BIOCLOCATE:
+		locate = (struct bio_locate *)addr;
+		error = copyinstr(locate->bl_name, name, 16, NULL);
+		if (error != 0)
+			return (error);
+		locate->bl_cookie = bio_lookup(name);
+		if (locate->bl_cookie == NULL)
+			return (ENOENT);
+		break;
+
+	default:
+		common = (struct bio_common *)addr;
+		if (!bio_validate(common->bc_cookie)) {
+			return (ENOENT);
+		}
+		error =  bio_delegate_ioctl(
+		    (struct bio_mapping *)common->bc_cookie, cmd, addr);
+		return (error);
+	}
+	return (0);
+}
+
+int
+bio_register(struct device *dev, int (*ioctl)(struct device *, u_long, void *))
+{
+	struct bio_mapping *bm;
+
+	MALLOC(bm, struct bio_mapping *, sizeof *bm, M_DEVBUF, M_NOWAIT);
+	if (bm == NULL)
+		return (ENOMEM);
+	bm->bm_dev = dev;
+	bm->bm_ioctl = ioctl;
+	mutex_enter(&bio_lock);
+	LIST_INSERT_HEAD(&bios, bm, bm_link);
+	mutex_exit(&bio_lock);
+	return (0);
+}
+
+void
+bio_unregister(struct device *dev)
+{
+	struct bio_mapping *bm, *next;
+
+	mutex_enter(&bio_lock);
+	for (bm = LIST_FIRST(&bios); bm != NULL; bm = next) {
+		next = LIST_NEXT(bm, bm_link);
+
+		if (dev == bm->bm_dev) {
+			LIST_REMOVE(bm, bm_link);
+			free(bm, M_DEVBUF);
+		}
+	}
+	mutex_exit(&bio_lock);
+}
+
+struct bio_mapping *
+bio_lookup(char *name)
+{
+	struct bio_mapping *bm;
+
+	mutex_enter(&bio_lock);
+	LIST_FOREACH(bm, &bios, bm_link) {
+		if (strcmp(name, bm->bm_dev->dv_xname) == 0) {
+			mutex_exit(&bio_lock);
+			return (bm);
+		}
+	}
+	mutex_exit(&bio_lock);
+	return (NULL);
+}
+
+int
+bio_validate(void *cookie)
+{
+	struct bio_mapping *bm;
+
+	mutex_enter(&bio_lock);
+	LIST_FOREACH(bm, &bios, bm_link) {
+		if (bm == cookie) {
+			mutex_exit(&bio_lock);
+			return (1);
+		}
+	}
+	mutex_exit(&bio_lock);
+	return (0);
+}
+
+int
+bio_delegate_ioctl(struct bio_mapping *bm, u_long cmd, void *addr)
+{
+	return (bm->bm_ioctl(bm->bm_dev, cmd, addr));
+}
Index: sys/dev/biovar.h
===================================================================
RCS file: sys/dev/biovar.h
diff -N sys/dev/biovar.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/dev/biovar.h	21 Apr 2007 13:27:03 -0000
@@ -0,0 +1,191 @@
+/*	$NetBSD: $ */
+/*	$OpenBSD: biovar.h,v 1.26 2007/03/19 03:02:08 marco Exp $	*/
+
+/*
+ * Copyright (c) 2002 Niklas Hallqvist.  All rights reserved.
+ * Copyright (c) 2005 Marco Peereboom.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Devices getting ioctls through this interface should use ioctl class 'B'
+ * and command numbers starting from 32, lower ones are reserved for generic
+ * ioctls.  All ioctl data must be structures which start with a void *
+ * cookie.
+ */
+
+#include <sys/types.h>
+
+struct bio_common {
+	void		*bc_cookie;
+};
+
+/* convert name to a cookie */
+#define BIOCLOCATE _IOWR('B', 0, struct bio_locate)
+struct bio_locate {
+	void		*bl_cookie;
+	char		*bl_name;
+};
+
+#ifdef _KERNEL
+int	bio_register(struct device *, int (*)(struct device *, u_long,
+    void *));
+void	bio_unregister(struct device *);
+#endif
+
+#define BIOCINQ _IOWR('B', 32, struct bioc_inq)
+struct bioc_inq {
+	void		*bi_cookie;
+
+	char		bi_dev[16];	/* controller device */
+	int		bi_novol;	/* nr of volumes */
+	int		bi_nodisk;	/* nr of total disks */
+};
+
+#define BIOCDISK _IOWR('B', 33, struct bioc_disk)
+/* structure that represents a disk in a RAID volume */
+struct bioc_disk {
+	void		*bd_cookie;
+
+	u_int16_t	bd_channel;
+	u_int16_t	bd_target;
+	u_int16_t	bd_lun;
+	u_int16_t	bd_other_id;	/* unused for now  */
+
+	int		bd_volid;	/* associate with volume */
+	int		bd_diskid;	/* virtual disk */
+	int		bd_status;	/* current status */
+#define BIOC_SDONLINE		0x00
+#define BIOC_SDONLINE_S		"Online"
+#define BIOC_SDOFFLINE		0x01
+#define BIOC_SDOFFLINE_S	"Offline"
+#define BIOC_SDFAILED		0x02
+#define BIOC_SDFAILED_S 	"Failed"
+#define BIOC_SDREBUILD		0x03
+#define BIOC_SDREBUILD_S	"Rebuild"
+#define BIOC_SDHOTSPARE		0x04
+#define BIOC_SDHOTSPARE_S	"Hot spare"
+#define BIOC_SDUNUSED		0x05
+#define BIOC_SDUNUSED_S		"Unused"
+#define BIOC_SDSCRUB		0x06
+#define BIOC_SDSCRUB_S		"Scrubbing"
+#define BIOC_SDINVALID		0xff
+#define BIOC_SDINVALID_S	"Invalid"
+	size_t		bd_size;	/* size of the disk */
+
+	char		bd_vendor[32];	/* scsi string */
+	char		bd_serial[32];	/* serial number */
+	char		bd_procdev[16];	/* processor device */
+};
+
+#define BIOCVOL _IOWR('B', 34, struct bioc_vol)
+/* structure that represents a RAID volume */
+struct bioc_vol {
+	void		*bv_cookie;
+	int		bv_volid;	/* volume id */
+
+	int16_t		bv_percent;	/* percent done operation */
+	u_int16_t	bv_seconds;	/* seconds of progress so far */
+
+	int		bv_status;	/* current status */
+#define BIOC_SVONLINE		0x00
+#define BIOC_SVONLINE_S		"Online"
+#define BIOC_SVOFFLINE		0x01
+#define BIOC_SVOFFLINE_S	"Offline"
+#define BIOC_SVDEGRADED		0x02
+#define BIOC_SVDEGRADED_S	"Degraded"
+#define BIOC_SVBUILDING		0x03
+#define BIOC_SVBUILDING_S	"Building"
+#define BIOC_SVSCRUB		0x04
+#define BIOC_SVSCRUB_S		"Scrubbing"
+#define BIOC_SVREBUILD		0x05
+#define BIOC_SVREBUILD_S	"Rebuild"
+#define BIOC_SVINVALID		0xff
+#define BIOC_SVINVALID_S	"Invalid"
+	size_t		bv_size;	/* size of the disk */
+	int		bv_level;	/* raid level */
+	int		bv_nodisk;	/* nr of drives */
+
+	char		bv_dev[16];	/* device */
+	char		bv_vendor[32];	/* scsi string */
+};
+
+#define BIOCALARM _IOWR('B', 35, struct bioc_alarm)
+struct bioc_alarm {
+	void		*ba_cookie;
+	int		ba_opcode;
+
+	int		ba_status;	/* only used with get state */
+#define BIOC_SADISABLE		0x00	/* disable alarm */
+#define BIOC_SAENABLE		0x01	/* enable alarm */
+#define BIOC_SASILENCE		0x02	/* silence alarm */
+#define BIOC_GASTATUS		0x03	/* get status */
+#define BIOC_SATEST		0x04	/* test alarm */
+};
+
+#define BIOCBLINK _IOWR('B', 36, struct bioc_blink)
+struct bioc_blink {
+	void		*bb_cookie;
+	u_int16_t	bb_channel;
+	u_int16_t	bb_target;
+
+	int		bb_status;	/* current status */
+#define BIOC_SBUNBLINK		0x00	/* disable blinking */
+#define BIOC_SBBLINK		0x01	/* enable blink */
+#define BIOC_SBALARM		0x02	/* enable alarm blink */
+};
+
+#define BIOCSETSTATE _IOWR('B', 37, struct bioc_setstate)
+struct bioc_setstate {
+	void		*bs_cookie;
+	u_int16_t	bs_channel;
+	u_int16_t	bs_target;
+	u_int16_t	bs_lun;
+	u_int16_t	bs_other_id;	/* unused for now  */
+
+	int		bs_status;	/* change to this status */
+#define BIOC_SSONLINE		0x00	/* online disk */
+#define BIOC_SSOFFLINE		0x01	/* offline disk */
+#define BIOC_SSHOTSPARE		0x02	/* mark as hotspare */
+#define BIOC_SSREBUILD		0x03	/* rebuild on this disk */
+	int		bs_volid;	/* volume id for rebuild */
+};
+
+#define BIOCCREATERAID _IOWR('B', 38, struct bioc_createraid)
+struct bioc_createraid {
+	void		*bc_cookie;
+	char		*bc_dev_list;
+	u_int16_t	bc_dev_list_len;
+	u_int16_t	bc_level;
+};
+
+/* kernel and userspace defines */
+#define BIOC_INQ		0x0001
+#define BIOC_DISK		0x0002
+#define BIOC_VOL		0x0004
+#define BIOC_ALARM		0x0008
+#define BIOC_BLINK		0x0010
+#define BIOC_SETSTATE		0x0020
+#define BIOC_CREATERAID		0x0040
+
+/* user space defines */
+#define BIOC_DEVLIST		0x10000
Index: sys/dev/ic/mfi.c
===================================================================
RCS file: /cvsroot/src/sys/dev/ic/mfi.c,v
retrieving revision 1.3
diff -u -r1.3 mfi.c
--- sys/dev/ic/mfi.c	4 Mar 2007 06:01:58 -0000	1.3
+++ sys/dev/ic/mfi.c	21 Apr 2007 13:27:03 -0000
@@ -19,7 +19,7 @@
 #include <sys/cdefs.h>
 __KERNEL_RCSID(0, "$NetBSD: mfi.c,v 1.3 2007/03/04 06:01:58 christos Exp $");
 
-/* #include "bio.h" XXX */
+#include "bio.h"
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -46,7 +46,6 @@
 
 #if NBIO > 0
 #include <dev/biovar.h>
-#include <sys/sensors.h>
 #endif /* NBIO > 0 */
 
 #ifdef MFI_DEBUG
@@ -103,7 +102,10 @@
 int		mfi_ioctl_setstate(struct mfi_softc *, struct bioc_setstate *);
 int		mfi_bio_hs(struct mfi_softc *, int, int, void *);
 int		mfi_create_sensors(struct mfi_softc *);
-void		mfi_refresh_sensors(void *);
+int		mfi_sensor_gtredata(struct sysmon_envsys *,
+		    struct envsys_tre_data *);
+int		mfi_sensor_streinfo(struct sysmon_envsys *,
+		    struct envsys_basic_info *);
 #endif /* NBIO > 0 */
 
 struct mfi_ccb *
@@ -586,8 +588,6 @@
 
 	TAILQ_INIT(&sc->sc_ccb_freeq);
 
-	/* rw_init(&sc->sc_lock, "mfi_lock"); XXX */
-
 	status = mfi_read(sc, MFI_OMSG0);
 	sc->sc_max_cmds = status & MFI_STATE_MAXCMD_MASK;
 	sc->sc_max_sgl = (status & MFI_STATE_MAXSGL_MASK) >> 16;
@@ -693,7 +693,6 @@
 		panic("%s: controller registration failed", DEVNAME(sc));
 	else
 		sc->sc_ioctl = mfi_ioctl;
-
 	if (mfi_create_sensors(sc) != 0)
 		aprint_error("%s: unable to create sensors\n", DEVNAME(sc));
 #endif /* NBIO > 0 */
@@ -1300,10 +1299,9 @@
 	struct mfi_softc	*sc = (struct mfi_softc *)dev;
 	int error = 0;
 
+	int s = splbio();
 	DNPRINTF(MFI_D_IOCTL, "%s: mfi_ioctl ", DEVNAME(sc));
 
-	rw_enter_write(&sc->sc_lock);
-
 	switch (cmd) {
 	case BIOCINQ:
 		DNPRINTF(MFI_D_IOCTL, "inq\n");
@@ -1339,8 +1337,7 @@
 		DNPRINTF(MFI_D_IOCTL, " invalid ioctl\n");
 		error = EINVAL;
 	}
-
-	rw_exit_write(&sc->sc_lock);
+	splx(s);
 
 	return (error);
 }
@@ -1468,7 +1465,7 @@
 	struct mfi_array	*ar;
 	struct mfi_ld_cfg	*ld;
 	struct mfi_pd_details	*pd;
-	struct scsi_inquiry_data *inqbuf;
+	struct scsipi_inquiry_data *inqbuf;
 	char			vend[8+16+4+1];
 	int			i, rv = EINVAL;
 	int			arr, vol, disk;
@@ -1478,7 +1475,7 @@
 	DNPRINTF(MFI_D_IOCTL, "%s: mfi_ioctl_disk %#x\n",
 	    DEVNAME(sc), bd->bd_diskid);
 
-	pd = malloc(sizeof *pd, M_DEVBUF, M_WAITOK);
+	pd = malloc(sizeof *pd, M_DEVBUF, M_WAITOK | M_ZERO);
 
 	/* send single element command to retrieve size for full structure */
 	cfg = malloc(sizeof *cfg, M_DEVBUF, M_WAITOK);
@@ -1554,6 +1551,7 @@
 
 	/* get the remaining fields */
 	*((uint16_t *)&mbox) = ar[arr].pd[disk].mar_pd.mfp_id;
+	memset(pd, 0, sizeof(*pd));
 	if (mfi_mgmt(sc, MR_DCMD_PD_GET_INFO, MFI_DATA_IN,
 	    sizeof *pd, pd, mbox))
 		goto freeme;
@@ -1563,7 +1561,7 @@
 	/* if pd->mpd_enc_idx is 0 then it is not in an enclosure */
 	bd->bd_channel = pd->mpd_enc_idx;
 
-	inqbuf = (struct scsi_inquiry_data *)&pd->mpd_inq_data;
+	inqbuf = (struct scsipi_inquiry_data *)&pd->mpd_inq_data;
 	memcpy(vend, inqbuf->vendor, sizeof vend - 1);
 	vend[sizeof vend - 1] = '\0';
 	strlcpy(bd->bd_vendor, vend, sizeof(bd->bd_vendor));
@@ -1761,7 +1759,7 @@
 	struct mfi_pd_details	*pd;
 	struct bioc_disk	*sdhs;
 	struct bioc_vol		*vdhs;
-	struct scsi_inquiry_data *inqbuf;
+	struct scsipi_inquiry_data *inqbuf;
 	char			vend[8+16+4+1];
 	int			i, rv = EINVAL;
 	uint32_t		size;
@@ -1772,7 +1770,7 @@
 	if (!bio_hs)
 		return (EINVAL);
 
-	pd = malloc(sizeof *pd, M_DEVBUF, M_WAITOK);
+	pd = malloc(sizeof *pd, M_DEVBUF, M_WAITOK | M_ZERO);
 
 	/* send single element command to retrieve size for full structure */
 	cfg = malloc(sizeof *cfg, M_DEVBUF, M_WAITOK);
@@ -1832,8 +1830,8 @@
 		sdhs->bd_size = pd->mpd_size / 2; /* XXX why? / 2 */
 		sdhs->bd_channel = pd->mpd_enc_idx;
 		sdhs->bd_target = pd->mpd_enc_slot;
-		inqbuf = (struct scsi_inquiry_data *)&pd->mpd_inq_data;
-		memcpy(vend, inqbuf->vendor, sizeof vend - 1);
+		inqbuf = (struct scsipi_inquiry_data *)&pd->mpd_inq_data;
+		memcpy(vend, inqbuf->vendor, sizeof(vend) - 1);
 		vend[sizeof vend - 1] = '\0';
 		strlcpy(sdhs->bd_vendor, vend, sizeof(sdhs->bd_vendor));
 		break;
@@ -1854,97 +1852,110 @@
 int
 mfi_create_sensors(struct mfi_softc *sc)
 {
-	struct device		*dev;
-	struct scsibus_softc	*ssc;
 	int			i;
+	struct envsys_range env_ranges[2];
+	int nsensors = sc->sc_ld_cnt;
 
-	TAILQ_FOREACH(dev, &alldevs, dv_list) {
-		if (dev->dv_parent != &sc->sc_dev)
-			continue;
-
-		/* check if this is the scsibus for the logical disks */
-		ssc = (struct scsibus_softc *)dev;
-		if (ssc->adapter_link == &sc->sc_link)
-			break;
+	env_ranges[0].low = 0;
+	env_ranges[0].high = nsensors;
+	env_ranges[0].units = ENVSYS_DRIVE;
+	env_ranges[1].low = 1;
+	env_ranges[1].high = 0;
+	env_ranges[1].units = 0;
+
+	sc->sc_sensor_data =
+	    malloc(sizeof(struct envsys_tre_data) * nsensors,
+		M_DEVBUF, M_NOWAIT | M_ZERO);
+	if (sc->sc_sensor_data == NULL) {
+		aprint_error("%s: can't allocate envsys_tre_data\n",
+		    DEVNAME(sc));
+		return(ENOMEM);
 	}
-
-	if (ssc == NULL)
-		return (1);
-
-	sc->sc_sensors = malloc(sizeof(struct sensor) * sc->sc_ld_cnt,
-	    M_DEVBUF, M_WAITOK);
-	if (sc->sc_sensors == NULL)
-		return (1);
-	bzero(sc->sc_sensors, sizeof(struct sensor) * sc->sc_ld_cnt);	
-
-	for (i = 0; i < sc->sc_ld_cnt; i++) {
-		if (ssc->sc_link[i][0] == NULL)
-			goto bad;
-
-		dev = ssc->sc_link[i][0]->device_softc;
-
-		sc->sc_sensors[i].type = SENSOR_DRIVE;
-		sc->sc_sensors[i].status = SENSOR_S_UNKNOWN;
-
-		strlcpy(sc->sc_sensors[i].device, DEVNAME(sc),
-		    sizeof(sc->sc_sensors[i].device));
-		strlcpy(sc->sc_sensors[i].desc, dev->dv_xname,
-		    sizeof(sc->sc_sensors[i].desc));
-
-		sensor_add(&sc->sc_sensors[i]);
+	sc->sc_sensor_info =
+	    malloc(sizeof(struct envsys_basic_info) * nsensors,
+		M_DEVBUF, M_NOWAIT | M_ZERO);
+	if (sc->sc_sensor_info == NULL) {
+		aprint_error("%s: can't allocate envsys_basic_info\n",
+		    DEVNAME(sc));
+		return(ENOMEM);
+	}
+	for (i = 0; i < nsensors; i++) {
+		sc->sc_sensor_data[i].sensor = i;
+		sc->sc_sensor_data[i].units = ENVSYS_DRIVE;
+		sc->sc_sensor_data[i].validflags = ENVSYS_FVALID;
+		sc->sc_sensor_data[i].warnflags = ENVSYS_WARN_OK;
+		sc->sc_sensor_info[i].sensor = i;
+		sc->sc_sensor_info[i].units = ENVSYS_DRIVE;
+		sc->sc_sensor_info[i].validflags = ENVSYS_FVALID;
+		/* logical drives */
+		snprintf(sc->sc_sensor_info[i].desc,
+		    sizeof(sc->sc_sensor_info[i].desc), "%s:%d",
+		    DEVNAME(sc), i);
+	}
+	sc->sc_ranges = env_ranges;
+	sc->sc_envsys.sme_cookie = sc;
+	sc->sc_envsys.sme_gtredata = mfi_sensor_gtredata;
+	sc->sc_envsys.sme_streinfo = mfi_sensor_streinfo;
+	sc->sc_envsys.sme_nsensors = sc->sc_ld_cnt;
+	sc->sc_envsys.sme_envsys_version = 1000;
+	if (sysmon_envsys_register(&sc->sc_envsys)) {
+		printf("%s: unable to register with sysmon\n", DEVNAME(sc));
+		return(1);
 	}
-
-	if (sensor_task_register(sc, mfi_refresh_sensors, 10) != 0)
-		goto bad;
-
 	return (0);
-
-bad:
-	while (--i >= 0)
-		sensor_del(&sc->sc_sensors[i]);
-	free(sc->sc_sensors, M_DEVBUF);
-
-	return (1);
 }
 
-void
-mfi_refresh_sensors(void *arg)
+int
+mfi_sensor_gtredata(struct sysmon_envsys *sme, struct envsys_tre_data *tred)
 {
-	struct mfi_softc	*sc = arg;
-	int			i;
+	struct mfi_softc	*sc = sme->sme_cookie;
 	struct bioc_vol		bv;
+	int s;
 
+	if (tred->sensor >= sc->sc_ld_cnt || tred->sensor < 0)
+		return EINVAL;
 
-	for (i = 0; i < sc->sc_ld_cnt; i++) {
-		bzero(&bv, sizeof(bv));
-		bv.bv_volid = i;
-		if (mfi_ioctl_vol(sc, &bv))
-			return;
-
-		switch(bv.bv_status) {
-		case BIOC_SVOFFLINE:
-			sc->sc_sensors[i].value = SENSOR_DRIVE_FAIL;
-			sc->sc_sensors[i].status = SENSOR_S_CRIT;
-			break;
+	bzero(&bv, sizeof(bv));
+	bv.bv_volid = tred->sensor;
+	s = splbio();
+	if (mfi_ioctl_vol(sc, &bv)) {
+		splx(s);
+		return EIO;
+	}
+	splx(s);
 
-		case BIOC_SVDEGRADED:
-			sc->sc_sensors[i].value = SENSOR_DRIVE_PFAIL;
-			sc->sc_sensors[i].status = SENSOR_S_WARN;
-			break;
+	switch(bv.bv_status) {
+	case BIOC_SVOFFLINE:
+		tred->cur.data_us = ENVSYS_DRIVE_FAIL;
+		tred->warnflags = ENVSYS_WARN_CRITOVER;
+		break;
 
-		case BIOC_SVSCRUB:
-		case BIOC_SVONLINE:
-			sc->sc_sensors[i].value = SENSOR_DRIVE_ONLINE;
-			sc->sc_sensors[i].status = SENSOR_S_OK;
-			break;
+	case BIOC_SVDEGRADED:
+		tred->cur.data_us = ENVSYS_DRIVE_PFAIL;
+		tred->warnflags = ENVSYS_WARN_OVER;
+		break;
 
-		case BIOC_SVINVALID:
-			/* FALLTRHOUGH */
-		default:
-			sc->sc_sensors[i].value = 0; /* unknown */
-			sc->sc_sensors[i].status = SENSOR_S_UNKNOWN;
-		}
+	case BIOC_SVSCRUB:
+	case BIOC_SVONLINE:
+		tred->cur.data_us = ENVSYS_DRIVE_ONLINE;
+		tred->warnflags = ENVSYS_WARN_OK;
+		break;
 
+	case BIOC_SVINVALID:
+		/* FALLTRHOUGH */
+	default:
+		tred->cur.data_us = 0; /* unknown */
+		tred->warnflags = ENVSYS_WARN_CRITOVER;
 	}
+	tred->validflags = ENVSYS_FVALID | ENVSYS_FCURVALID;
+	tred->units = ENVSYS_DRIVE;
+	return 0;
+}
+
+int
+mfi_sensor_streinfo(struct sysmon_envsys *sme, struct envsys_basic_info *binfo)
+{
+	binfo->validflags = 0;
+	return 0;
 }
 #endif /* NBIO > 0 */
Index: sys/dev/ic/mfivar.h
===================================================================
RCS file: /cvsroot/src/sys/dev/ic/mfivar.h,v
retrieving revision 1.3
diff -u -r1.3 mfivar.h
--- sys/dev/ic/mfivar.h	4 Mar 2007 06:01:58 -0000	1.3
+++ sys/dev/ic/mfivar.h	21 Apr 2007 13:27:03 -0000
@@ -16,6 +16,9 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include <dev/sysmon/sysmonvar.h>
+#include <sys/envsys.h>
+
 #define DEVNAME(_s)     ((_s)->sc_dev.dv_xname)
 
 /* #define MFI_DEBUG */
@@ -142,7 +145,11 @@
 
 	struct mfi_ccb_list	sc_ccb_freeq;
 
-	struct sensor		*sc_sensors;
+	struct sysmon_envsys    sc_envsys;
+#define sc_ranges       sc_envsys.sme_ranges
+#define sc_sensor_info  sc_envsys.sme_sensor_info
+#define sc_sensor_data  sc_envsys.sme_sensor_data
+
 };
 
 int	mfi_attach(struct mfi_softc *sc);
Index: sys/sys/envsys.h
===================================================================
RCS file: /cvsroot/src/sys/sys/envsys.h,v
retrieving revision 1.10
diff -u -r1.10 envsys.h
--- sys/sys/envsys.h	15 Mar 2006 11:22:23 -0000	1.10
+++ sys/sys/envsys.h	21 Apr 2007 13:27:06 -0000
@@ -95,16 +95,34 @@
 	ENVSYS_SAMPHOUR,
 	ENVSYS_INDICATOR,	/* boolean indicator */
 	ENVSYS_INTEGER,		/* generic integer return */
+	ENVSYS_DRIVE,		/* disk status */
 	ENVSYS_NSENSORS
 };
 
+/* drive status */
+#define ENVSYS_DRIVE_EMPTY      1
+#define ENVSYS_DRIVE_READY      2
+#define ENVSYS_DRIVE_POWERUP    3
+#define ENVSYS_DRIVE_ONLINE     4
+#define ENVSYS_DRIVE_IDLE       5
+#define ENVSYS_DRIVE_ACTIVE     6
+#define ENVSYS_DRIVE_REBUILD    7
+#define ENVSYS_DRIVE_POWERDOWN  8
+#define ENVSYS_DRIVE_FAIL       9
+#define ENVSYS_DRIVE_PFAIL      10
+
 #ifdef ENVSYSUNITNAMES
 static const char * const envsysunitnames[] = {
     "degC", "RPM", "VAC", "V", "Ohms", "W",
-    "A", "Wh", "Ah", "bool", "integer", "Unk"
+    "A", "Wh", "Ah", "bool", "integer", "drive", "Unk"
+};
+static const char * const envsysdrivestatus[] = {
+    "unknown", "empty", "ready", "powering up", "online", "idle", "active",
+    "rebuilding", "powering down", "failed", "degraded"
 };
 #endif
 
+
 /* flags for validflags */
 #define ENVSYS_FVALID		0x00000001  /* sensor is valid */
 #define ENVSYS_FCURVALID	0x00000002  /* cur for this sens is valid */
Index: usr.sbin/envstat/envstat.c
===================================================================
RCS file: /cvsroot/src/usr.sbin/envstat/envstat.c,v
retrieving revision 1.23
diff -u -r1.23 envstat.c
--- usr.sbin/envstat/envstat.c	31 Aug 2006 10:25:04 -0000	1.23
+++ usr.sbin/envstat/envstat.c	21 Apr 2007 13:27:11 -0000
@@ -231,6 +231,9 @@
 		/* different units need some magic */
 		switch (ebis[i].units)
 		{
+		case ENVSYS_DRIVE:
+			printf(": drive %s",
+			    envsysdrivestatus[etds[i].cur.data_us]);
 		case ENVSYS_INDICATOR:
 			break;
 		case ENVSYS_INTEGER:
@@ -319,6 +322,10 @@
 			}
 
 			switch(etds[i].units) {
+			case ENVSYS_DRIVE:
+				printf(" %*.*s", (int)width, (int)width,
+				    envsysdrivestatus[etds[i].cur.data_us]);
+				break;
 			case ENVSYS_INDICATOR:
 				printf(" %*.*s", (int)width, (int)width,
 				    etds[i].cur.data_us ? "ON" : "OFF");
--- /dev/null	2007-04-21 15:19:30.000000000 +0200
+++ sbin/bioctl/Makefile	2007-04-08 20:01:44.000000000 +0200
@@ -0,0 +1,10 @@
+#	$OpenBSD: Makefile,v 1.8 2006/11/26 11:31:08 deraadt Exp $
+
+PROG=	bioctl
+SRCS=   bioctl.c strtonum.c
+MAN=	bioctl.8
+
+DPADD=  ${LIBUTIL}
+LDADD=  -lutil
+
+.include <bsd.prog.mk>
--- /dev/null	2007-04-21 15:19:30.000000000 +0200
+++ sbin/bioctl/bioctl.8	2007-04-08 23:46:55.000000000 +0200
@@ -0,0 +1,164 @@
+.\"	$NetBSD: $
+.\"	$OpenBSD: bioctl.8,v 1.43 2007/03/20 06:12:11 jmc Exp $
+.\"
+.\" Copyright (c) 2004, 2005 Marco Peereboom
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
+.\" ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd April 8, 2007
+.Dt BIOCTL 8
+.Os
+.Sh NAME
+.Nm bioctl
+.Nd RAID management interface
+.Sh SYNOPSIS
+.Nm bioctl
+.Bk -words
+.Op Fl Dhiv
+.Op Fl a Ar alarm-function
+.Op Fl b Ar channel:target[.lun]
+.Op Fl c Ar raidlevel
+.Op Fl H Ar channel:target[.lun]
+.Op Fl l Ar special[,special[,...]]
+.Op Fl u Ar channel:target[.lun]
+.Ar device
+.Ek
+.Sh DESCRIPTION
+RAID device drivers which support management functionality can
+register their services with the
+.Xr bio 4
+driver.
+.Nm bioctl
+then can be used to maintain RAID volumes.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl a Ar alarm-function
+Control the RAID card's alarm functionality, if supported.
+.Ar alarm-function
+may be one of:
+.Pp
+.Bl -tag -width disable -compact
+.It Ar disable
+Disable the alarm on the RAID controller.
+.It Ar enable
+Enable the alarm on the RAID controller.
+.It Ar get
+Retrieve the current alarm state (enabled or disabled).
+.It Ar silence | Ar quiet
+Silence the alarm if it is currently beeping.
+.El
+.Pp
+The
+.Ar alarm-function
+may be specified as given above,
+or by the first letter only
+(e.g. -a e).
+.It Fl b Ar channel:target[.lun]
+Instruct the device at
+.Ar channel:target[.lun]
+to start blinking, if there is
+.Xr ses 4
+or
+.Xr safte 4
+support in the enclosure.
+.It Fl c Ar raidlevel
+Create a
+.Xr softraid 4
+device of level
+.Ar raidlevel .
+The device must begin with
+.Dq softraid
+followed by a number.
+.It Fl D
+Enable debug output.
+.It Fl H Ar channel:target[.lun]
+If the device at
+.Ar channel:target[.lun]
+is currently marked
+.Dq Unused ,
+promote it to being a
+.Dq Hot Spare .
+.It Fl h
+Where necessary, produce "human-readable" output.
+Use unit suffixes: Byte, Kilobyte, Megabyte,
+Gigabyte, Terabyte, Petabyte, Exabyte in order to reduce the number of
+digits to four or less.
+.It Fl i
+Enumerate the selected RAID devices.
+.It Fl l Ar special[,special[,...]]
+Use
+.Ar special
+device list to create within the
+.Xr softraid 4
+framework.
+Requires
+.Fl c .
+.It Fl u Ar channel:target[.lun]
+Instruct the device at
+.Ar channel:target[.lun]
+to cease blinking, if there is
+.Xr ses 4
+or
+.Xr safte 4
+support in the enclosure.
+.It Fl v
+Be more verbose in output.
+.It Ar device
+Select a drive by name (e.g. sd0) or a RAID controller by name (e.g. ami0).
+For operations which will be performed against
+.Xr ses 4
+or
+.Xr safte 4
+enclosures, it is also possible to directly specify the enclosure name
+(e.g. safte0).
+.El
+.Sh EXAMPLES
+The following command, executed from the command line, shows the status of
+the logical drives on the RAID controller:
+.Bd -literal 
+# bioctl -h mfi0
+Volume  Status           Size Device  
+ mfi0 0 Online            74G mfi0    RAID1
+      0 Online            75G 1:0.0   noencl <ATA     WDC WD800JD-75MS1E04>
+      1 Online            75G 1:1.0   noencl <ATA     WDC WD800JD-75MS1E04>
+ mfi0 1 Online           697G mfi0    RAID5
+      0 Online           233G 1:3.0   noencl <ATA     WDC WD2500JS-75N2E04>
+      1 Online           233G 1:2.0   noencl <ATA     WDC WD2500JS-75N2E04>
+      2 Online           233G 1:4.0   noencl <ATA     WDC WD2500JS-75N2E04>
+      3 Online           233G 1:5.0   noencl <ATA     WDC WD2500JS-75N2E04>
+.Ed
+.Sh SEE ALSO
+.Xr bio 4 ,
+.Xr mfi 4 ,
+.Sh BUGS
+the softraid stuff available at this time.
+.Sh HISTORY
+The
+.Nm
+command first appeared in
+.Ox 3.8 .
+.Sh AUTHORS
+The
+.Nm
+interface was written by
+.An Marco Peereboom Aq marco@openbsd.org .
--- /dev/null	2007-04-21 15:19:30.000000000 +0200
+++ sbin/bioctl/bioctl.c	2007-04-08 20:34:31.000000000 +0200
@@ -0,0 +1,661 @@
+/* $NetBSD: $ */
+/* $OpenBSD: bioctl.c,v 1.52 2007/03/20 15:26:06 jmc Exp $       */
+
+/*
+ * Copyright (c) 2004, 2005 Marco Peereboom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#include <sys/cdefs.h>
+
+#ifndef lint
+__RCSID("$NetBSD: raidctl.c,v 1.38 2005/06/02 00:06:14 lukem Exp $");
+#endif
+
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+// #include <scsi/scsipi_disk.h>
+// #include <scsi/scsipi_all.h>
+#include <dev/biovar.h>
+
+#include <errno.h>
+#include <err.h>
+#include <fcntl.h>
+#include <util.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <util.h>
+#include "strtonum.h"
+
+struct locator {
+	int channel;
+	int target;
+	int lun;
+};
+
+void usage(void);
+const char *str2locator(const char *, struct locator *);
+void cleanup(void);
+
+void bio_inq(char *);
+void bio_alarm(char *);
+void bio_setstate(char *);
+void bio_setblink(char *, char *, int);
+void bio_blink(char *, int, int);
+void bio_createraid(u_int16_t, char *);
+
+int devh = -1;
+int debug;
+int human;
+int verbose;
+
+struct bio_locate bl;
+
+int
+main(int argc, char *argv[])
+{
+	extern char *optarg;
+	u_int64_t func = 0;
+	/* u_int64_t subfunc = 0; XXX */
+	char *bioc_dev = NULL, *sd_dev = NULL;
+	char /* *realname = NULL, XXX */ *al_arg = NULL;
+	char *bl_arg = NULL, *dev_list = NULL;
+	int ch, rv, blink = 0; /* XXX GCC */
+	u_int16_t cr_level = 0; /* XXX GCC */
+
+	if (argc < 2)
+		usage();
+
+	while ((ch = getopt(argc, argv, "b:c:l:u:H:ha:Div")) != -1) {
+		switch (ch) {
+		case 'a': /* alarm */
+			func |= BIOC_ALARM;
+			al_arg = optarg;
+			break;
+		case 'b': /* blink */
+			func |= BIOC_BLINK;
+			blink = BIOC_SBBLINK;
+			bl_arg = optarg;
+			break;
+		case 'c': /* create */
+			func |= BIOC_CREATERAID;
+			cr_level = atoi(optarg);
+			break;
+		case 'u': /* unblink */
+			func |= BIOC_BLINK;
+			blink = BIOC_SBUNBLINK;
+			bl_arg = optarg;
+			break;
+		case 'D': /* debug */
+			debug = 1;
+			break;
+		case 'H': /* set hotspare */
+			func |= BIOC_SETSTATE;
+			al_arg = optarg;
+			break;
+		case 'h':
+			human = 1;
+			break;
+		case 'i': /* inquiry */
+			func |= BIOC_INQ;
+			break;
+		case 'l': /* device list */
+			func |= BIOC_DEVLIST;
+			dev_list = optarg;
+			break;
+		case 'v':
+			verbose = 1;
+			break;
+		default:
+			usage();
+			/* NOTREACHED */
+		}
+	}
+	argc -= optind;
+	argv += optind;
+
+	if (argc != 1)
+		usage();
+
+	if (func == 0)
+		func |= BIOC_INQ;
+#if 0
+	/* if at least glob sd[0-9]*, it is a drive identifier */
+	if (strncmp(argv[0], "sd", 2) == 0 && strlen(argv[0]) > 2 &&
+	    isdigit((int)argv[0][2]))
+		sd_dev = argv[0];
+	else
+#endif
+		bioc_dev = argv[0];
+
+	if (bioc_dev) {
+		devh = open("/dev/bio", O_RDWR);
+		if (devh == -1)
+			err(1, "Can't open %s", "/dev/bio");
+
+		bl.bl_name = bioc_dev;
+		rv = ioctl(devh, BIOCLOCATE, &bl);
+		if (rv == -1)
+			errx(1, "Can't locate %s device via %s",
+			    bl.bl_name, "/dev/bio");
+	}
+#if 0
+	else if (sd_dev) {
+		devh = opendev(sd_dev, O_RDWR, OPENDEV_PART, &realname);
+		if (devh == -1)
+			err(1, "Can't open %s", sd_dev);
+	}
+#endif
+	else
+		errx(1, "need -d or -f parameter");
+
+	if (debug)
+		warnx("cookie = %p", bl.bl_cookie);
+
+	if (func & BIOC_INQ) {
+		bio_inq(sd_dev);
+	} else if (func == BIOC_ALARM) {
+		bio_alarm(al_arg);
+	} else if (func == BIOC_BLINK) {
+		bio_setblink(sd_dev, bl_arg, blink);
+	} else if (func == BIOC_SETSTATE) {
+		bio_setstate(al_arg);
+	} else if (func & BIOC_CREATERAID || func & BIOC_DEVLIST) {
+		if (!(func & BIOC_CREATERAID))
+			errx(1, "need -c parameter");
+		if (!(func & BIOC_DEVLIST))
+			errx(1, "need -l parameter");
+		if (sd_dev)
+			errx(1, "can't use sd device");
+		bio_createraid(cr_level, dev_list);
+	}
+
+	return (0);
+}
+
+void
+usage(void)
+{
+	extern char *__progname;
+
+	fprintf(stderr,
+		"usage: %s [-Dhiv] [-a alarm-function] "
+		"[-b channel:target[.lun]]\n"
+		"\t[-c raidlevel] [-H channel:target[.lun]]\n"
+		"\t[-l special[,special[,...]]] "
+		"[-u channel:target[.lun]] device\n", __progname);
+	
+	exit(1);
+}
+
+const char *
+str2locator(const char *string, struct locator *location)
+{
+	const char *errstr;
+	char parse[80], *targ, *lun;
+
+	strlcpy(parse, string, sizeof parse);
+	targ = strchr(parse, ':');
+	if (targ == NULL)
+		return ("target not specified");
+	*targ++ = '\0';
+
+	lun = strchr(targ, '.');
+	if (lun != NULL) {
+		*lun++ = '\0';
+		location->lun = strtonum(lun, 0, 256, &errstr);
+		if (errstr)
+			return (errstr);
+	} else
+		location->lun = 0;
+
+	location->target = strtonum(targ, 0, 256, &errstr);
+	if (errstr)
+		return (errstr);
+	location->channel = strtonum(parse, 0, 256, &errstr);
+	if (errstr)
+		return (errstr);
+	return (NULL);
+}
+
+void
+bio_inq(char *name)
+{
+	const char *status;
+	char size[64], scsiname[16], volname[32];
+	char percent[10], seconds[20];
+	int rv, i, d, volheader, hotspare, unused;
+	char encname[16], serial[32];
+	struct bioc_disk bd;
+	struct bioc_inq bi;
+	struct bioc_vol bv;
+
+	memset(&bi, 0, sizeof(bi));
+
+	if (debug)
+		printf("bio_inq\n");
+
+	bi.bi_cookie = bl.bl_cookie;
+
+	rv = ioctl(devh, BIOCINQ, &bi);
+	if (rv == -1) {
+		warn("BIOCINQ");
+		return;
+	}
+
+	if (debug)
+		printf("bio_inq { %p, %s, %d, %d }\n",
+		    bi.bi_cookie,
+		    bi.bi_dev,
+		    bi.bi_novol,
+		    bi.bi_nodisk);
+
+	volheader = 0;
+	for (i = 0; i < bi.bi_novol; i++) {
+		memset(&bv, 0, sizeof(bv));
+		bv.bv_cookie = bl.bl_cookie;
+		bv.bv_volid = i;
+		bv.bv_percent = -1;
+		bv.bv_seconds = 0;
+
+		rv = ioctl(devh, BIOCVOL, &bv);
+		if (rv == -1) {
+			warn("BIOCVOL");
+			return;
+		}
+
+		if (name && strcmp(name, bv.bv_dev) != 0)
+			continue;
+
+		if (!volheader) {
+			volheader = 1;
+			printf("%-7s %-10s %14s %-8s\n",
+			    "Volume", "Status", "Size", "Device");
+		}
+
+		percent[0] = '\0';
+		seconds[0] = '\0';
+		if (bv.bv_percent != -1)
+			snprintf(percent, sizeof percent,
+			    " %d%% done", bv.bv_percent);
+		if (bv.bv_seconds)
+			snprintf(seconds, sizeof seconds,
+			    " %u seconds", bv.bv_seconds);
+		switch (bv.bv_status) {
+		case BIOC_SVONLINE:
+			status = BIOC_SVONLINE_S;
+			break;
+		case BIOC_SVOFFLINE:
+			status = BIOC_SVOFFLINE_S;
+			break;
+		case BIOC_SVDEGRADED:
+			status = BIOC_SVDEGRADED_S;
+			break;
+		case BIOC_SVBUILDING:
+			status = BIOC_SVBUILDING_S;
+			break;
+		case BIOC_SVREBUILD:
+			status = BIOC_SVREBUILD_S;
+			break;
+		case BIOC_SVSCRUB:
+			status = BIOC_SVSCRUB_S;
+			break;
+		case BIOC_SVINVALID:
+		default:
+			status = BIOC_SVINVALID_S;
+		}
+
+		snprintf(volname, sizeof volname, "%s %u",
+		    bi.bi_dev, bv.bv_volid);
+
+		if (bv.bv_level == -1 && bv.bv_nodisk == 1) {
+			hotspare = 1;
+			unused = 0;
+		} else if (bv.bv_level == -2 && bv.bv_nodisk == 1) {
+			unused = 1;
+			hotspare = 0;
+		} else {
+			unused = 0;
+			hotspare = 0;
+
+			if (human)
+				humanize_number(size, 5,
+				    (int64_t)bv.bv_size, "", HN_AUTOSCALE,
+				    HN_B | HN_NOSPACE | HN_DECIMAL);
+			else
+				snprintf(size, sizeof size, "%14llu",
+				    (long long unsigned int)bv.bv_size);
+			printf("%7s %-10s %14s %-7s RAID%u%s%s\n",
+			    volname, status, size, bv.bv_dev,
+			    bv.bv_level, percent, seconds);
+		}
+
+		for (d = 0; d < bv.bv_nodisk; d++) {
+			memset(&bd, 0, sizeof(bd));
+			bd.bd_cookie = bl.bl_cookie;
+			bd.bd_diskid = d;
+			bd.bd_volid = i;
+
+			rv = ioctl(devh, BIOCDISK, &bd);
+			if (rv == -1) {
+				warn("BIOCDISK");
+				return;
+			}
+
+			switch (bd.bd_status) {
+			case BIOC_SDONLINE:
+				status = BIOC_SDONLINE_S;
+				break;
+			case BIOC_SDOFFLINE:
+				status = BIOC_SDOFFLINE_S;
+				break;
+			case BIOC_SDFAILED:
+				status = BIOC_SDFAILED_S;
+				break;
+			case BIOC_SDREBUILD:
+				status = BIOC_SDREBUILD_S;
+				break;
+			case BIOC_SDHOTSPARE:
+				status = BIOC_SDHOTSPARE_S;
+				break;
+			case BIOC_SDUNUSED:
+				status = BIOC_SDUNUSED_S;
+				break;
+			case BIOC_SDSCRUB:
+				status = BIOC_SDSCRUB_S;
+				break;
+			case BIOC_SDINVALID:
+			default:
+				status = BIOC_SDINVALID_S;
+			}
+
+			if (hotspare || unused)
+				;	/* use volname from parent volume */
+			else
+				snprintf(volname, sizeof volname, "    %3u",
+				    bd.bd_diskid);
+
+			if (human)
+				humanize_number(size, 5,
+				    bd.bd_size, "", HN_AUTOSCALE,
+				    HN_B | HN_NOSPACE | HN_DECIMAL);
+			else
+				snprintf(size, sizeof size, "%14llu",
+				    (long long unsigned int)bd.bd_size);
+			snprintf(scsiname, sizeof scsiname,
+			    "%u:%u.%u",
+			    bd.bd_channel, bd.bd_target, bd.bd_lun);
+			if (bd.bd_procdev[0])
+				strlcpy(encname, bd.bd_procdev, sizeof encname);
+			else
+				strlcpy(encname, "noencl", sizeof encname);
+			if (bd.bd_serial[0])
+				strlcpy(serial, bd.bd_serial, sizeof serial);
+			else
+				strlcpy(serial, "unknown serial", sizeof serial);
+
+			printf("%7s %-10s %14s %-7s %-6s <%s>\n",
+			    volname, status, size, scsiname, encname,
+			    bd.bd_vendor);
+			if (verbose)
+				printf("%7s %-10s %14s %-7s %-6s '%s'\n",
+				    "", "", "", "", "", serial);
+		}
+	}
+}
+
+void
+bio_alarm(char *arg)
+{
+	int rv;
+	struct bioc_alarm ba;
+
+	ba.ba_cookie = bl.bl_cookie;
+
+	switch (arg[0]) {
+	case 'q': /* silence alarm */
+		/* FALLTHROUGH */
+	case 's':
+		ba.ba_opcode = BIOC_SASILENCE;
+		break;
+
+	case 'e': /* enable alarm */
+		ba.ba_opcode = BIOC_SAENABLE;
+		break;
+
+	case 'd': /* disable alarm */
+		ba.ba_opcode = BIOC_SADISABLE;
+		break;
+
+	case 't': /* test alarm */
+		ba.ba_opcode = BIOC_SATEST;
+		break;
+
+	case 'g': /* get alarm state */
+		ba.ba_opcode = BIOC_GASTATUS;
+		break;
+
+	default:
+		warnx("invalid alarm function: %s", arg);
+		return;
+	}
+
+	rv = ioctl(devh, BIOCALARM, &ba);
+	if (rv == -1) {
+		warn("BIOCALARM");
+		return;
+	}
+
+	if (arg[0] == 'g') {
+		printf("alarm is currently %s\n",
+		    ba.ba_status ? "enabled" : "disabled");
+
+	}
+}
+
+void
+bio_setstate(char *arg)
+{
+	struct bioc_setstate	bs;
+	struct locator		location;
+	const char		*errstr;
+	int			rv;
+
+	errstr = str2locator(arg, &location);
+	if (errstr)
+		errx(1, "Target %s: %s", arg, errstr);
+
+	bs.bs_cookie = bl.bl_cookie;
+	bs.bs_status = BIOC_SSHOTSPARE;
+	bs.bs_channel = location.channel;
+	bs.bs_target = location.target;
+	bs.bs_lun = location.lun;
+
+	rv = ioctl(devh, BIOCSETSTATE, &bs);
+	if (rv == -1) {
+		warn("BIOCSETSTATE");
+		return;
+	}
+}
+
+void
+bio_setblink(char *name, char *arg, int blink)
+{
+	struct locator		location;
+	struct bioc_inq		bi;
+	struct bioc_vol		bv;
+	struct bioc_disk	bd;
+	struct bioc_blink	bb;
+	const char		*errstr;
+	int			v, d, rv;
+
+	errstr = str2locator(arg, &location);
+	if (errstr)
+		errx(1, "Target %s: %s", arg, errstr);
+
+	/* try setting blink on the device directly */
+	memset(&bb, 0, sizeof(bb));
+	bb.bb_cookie = bl.bl_cookie;
+	bb.bb_status = blink;
+	bb.bb_target = location.target;
+	bb.bb_channel = location.channel;
+	rv = ioctl(devh, BIOCBLINK, &bb);
+	if (rv == 0)
+		return;
+
+	/* if the blink didnt work, try to find something that will */
+
+	memset(&bi, 0, sizeof(bi));
+	bi.bi_cookie = bl.bl_cookie;
+	rv = ioctl(devh, BIOCINQ, &bi);
+	if (rv == -1) {
+		warn("BIOCINQ");
+		return;
+	}
+
+	for (v = 0; v < bi.bi_novol; v++) {
+		memset(&bv, 0, sizeof(bv));
+		bv.bv_cookie = bl.bl_cookie;
+		bv.bv_volid = v;
+		rv = ioctl(devh, BIOCVOL, &bv);
+		if (rv == -1) {
+			warn("BIOCVOL");
+			return;
+		}
+
+		if (name && strcmp(name, bv.bv_dev) != 0)
+			continue;
+
+		for (d = 0; d < bv.bv_nodisk; d++) {
+			memset(&bd, 0, sizeof(bd));
+			bd.bd_cookie = bl.bl_cookie;
+			bd.bd_volid = v;
+			bd.bd_diskid = d;
+
+			rv = ioctl(devh, BIOCDISK, &bd);
+			if (rv == -1) {
+				warn("BIOCDISK");
+				return;
+			}
+
+			if (bd.bd_channel == location.channel &&
+			    bd.bd_target == location.target &&
+			    bd.bd_lun == location.lun) {
+				if (bd.bd_procdev[0] != '\0') {
+					bio_blink(bd.bd_procdev,
+					    location.target, blink);
+				} else
+					warnx("Disk %s is not in an enclosure", arg);
+				return;
+			}
+		}
+	}
+
+	warnx("Disk %s does not exist", arg);
+	return;
+}
+
+void
+bio_blink(char *enclosure, int target, int blinktype)
+{
+	int			bioh;
+	struct bio_locate	bio;
+	struct bioc_blink	blink;
+	int			rv;
+
+	bioh = open("/dev/bio", O_RDWR);
+	if (bioh == -1)
+		err(1, "Can't open %s", "/dev/bio");
+
+	bio.bl_name = enclosure;
+	rv = ioctl(bioh, BIOCLOCATE, &bio);
+	if (rv == -1)
+		errx(1, "Can't locate %s device via %s", enclosure, "/dev/bio");
+
+	memset(&blink, 0, sizeof(blink));
+	blink.bb_cookie = bio.bl_cookie;
+	blink.bb_status = blinktype;
+	blink.bb_target = target;
+
+	rv = ioctl(bioh, BIOCBLINK, &blink);
+	if (rv == -1)
+		warn("BIOCBLINK");
+
+	close(bioh);
+}
+
+void
+bio_createraid(u_int16_t level, char *dev_list)
+{
+	struct bioc_createraid	create;
+	int			rv;
+	u_int16_t		min_disks = 0;
+
+	if (debug)
+		printf("bio_createraid\n");
+
+	if (!dev_list)
+		errx(1, "no devices specified");
+
+	switch (level) {
+	case 0:
+		min_disks = 1;
+		break;
+	case 1:
+		min_disks = 2;
+		break;
+	default:
+		errx(1, "unsuported raid level");
+	}
+
+	/* XXX validate device list for real */
+#if 0
+	if (strncmp(dev_list, "sd", 2) == 0 && strlen(dev_list) > 2 &&
+	    isdigit(dev_list[2])) {
+	    	if (strlen(dev_list) != 3)
+			errx(1, "only one device supported");
+
+		if (debug)
+			printf("bio_createraid: dev_list: %s\n", dev_list);
+	}
+	else
+		errx(1, "no sd device specified");
+#endif
+
+	memset(&create, 0, sizeof(create));
+	create.bc_cookie = bl.bl_cookie;
+	create.bc_level = level;
+	create.bc_dev_list_len = strlen(dev_list);
+	create.bc_dev_list = dev_list;
+
+	rv = ioctl(devh, BIOCCREATERAID, &create);
+	if (rv == -1) {
+		warn("BIOCCREATERAID");
+		return;
+	}
+}
--- /dev/null	2007-04-21 15:19:30.000000000 +0200
+++ sbin/bioctl/strtonum.c	2007-04-08 20:19:10.000000000 +0200
@@ -0,0 +1,72 @@
+/*	$NetBSD: $ */
+/*	$OpenBSD: strtonum.c,v 1.6 2004/08/03 19:38:01 millert Exp $	*/
+
+/*
+ * Copyright (c) 2004 Ted Unangst and Todd Miller
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <sys/cdefs.h>
+
+#ifndef lint
+__RCSID("$NetBSD: raidctl.c,v 1.38 2005/06/02 00:06:14 lukem Exp $");
+#endif
+
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include "strtonum.h"
+
+#define INVALID 	1
+#define TOOSMALL 	2
+#define TOOLARGE 	3
+
+long long
+strtonum(const char *numstr, long long minval, long long maxval,
+    const char **errstrp)
+{
+	long long ll = 0;
+	char *ep;
+	int error = 0;
+	struct errval {
+		const char *errstr;
+		int err;
+	} ev[4] = {
+		{ NULL,		0 },
+		{ "invalid",	EINVAL },
+		{ "too small",	ERANGE },
+		{ "too large",	ERANGE },
+	};
+
+	ev[0].err = errno;
+	errno = 0;
+	if (minval > maxval)
+		error = INVALID;
+	else {
+		ll = strtoll(numstr, &ep, 10);
+		if (numstr == ep || *ep != '\0')
+			error = INVALID;
+		else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval)
+			error = TOOSMALL;
+		else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval)
+			error = TOOLARGE;
+	}
+	if (errstrp != NULL)
+		*errstrp = ev[error].errstr;
+	errno = ev[error].err;
+	if (error)
+		ll = 0;
+
+	return (ll);
+}
--- /dev/null	2007-04-21 15:19:30.000000000 +0200
+++ sbin/bioctl/strtonum.h	2007-04-08 20:03:33.000000000 +0200
@@ -0,0 +1,4 @@
+/* $NetBSD: $ */
+
+long long strtonum(const char *, long long, long long, const char **);
+

--mP3DRpeJDSE+ciuQ--