ld.c revision 1.37.2.7
11.37.2.7Syamt/*	$NetBSD: ld.c,v 1.37.2.7 2008/01/21 09:42:26 yamt Exp $	*/
21.1Sad
31.1Sad/*-
41.1Sad * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc.
51.1Sad * All rights reserved.
61.1Sad *
71.1Sad * This code is derived from software contributed to The NetBSD Foundation
81.1Sad * by Andrew Doran and Charles M. Hannum.
91.1Sad *
101.1Sad * Redistribution and use in source and binary forms, with or without
111.1Sad * modification, are permitted provided that the following conditions
121.1Sad * are met:
131.1Sad * 1. Redistributions of source code must retain the above copyright
141.1Sad *    notice, this list of conditions and the following disclaimer.
151.1Sad * 2. Redistributions in binary form must reproduce the above copyright
161.1Sad *    notice, this list of conditions and the following disclaimer in the
171.1Sad *    documentation and/or other materials provided with the distribution.
181.1Sad * 3. All advertising materials mentioning features or use of this software
191.1Sad *    must display the following acknowledgement:
201.1Sad *        This product includes software developed by the NetBSD
211.1Sad *        Foundation, Inc. and its contributors.
221.1Sad * 4. Neither the name of The NetBSD Foundation nor the names of its
231.1Sad *    contributors may be used to endorse or promote products derived
241.1Sad *    from this software without specific prior written permission.
251.1Sad *
261.1Sad * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
271.1Sad * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
281.1Sad * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
291.1Sad * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
301.1Sad * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
311.1Sad * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
321.1Sad * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
331.1Sad * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
341.1Sad * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
351.1Sad * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
361.1Sad * POSSIBILITY OF SUCH DAMAGE.
371.1Sad */
381.1Sad
391.1Sad/*
401.1Sad * Disk driver for use by RAID controllers.
411.1Sad */
421.12Slukem
431.12Slukem#include <sys/cdefs.h>
441.37.2.7Syamt__KERNEL_RCSID(0, "$NetBSD: ld.c,v 1.37.2.7 2008/01/21 09:42:26 yamt Exp $");
451.1Sad
461.1Sad#include "rnd.h"
471.1Sad
481.1Sad#include <sys/param.h>
491.1Sad#include <sys/systm.h>
501.1Sad#include <sys/kernel.h>
511.1Sad#include <sys/device.h>
521.1Sad#include <sys/queue.h>
531.1Sad#include <sys/proc.h>
541.1Sad#include <sys/buf.h>
551.33Syamt#include <sys/bufq.h>
561.1Sad#include <sys/endian.h>
571.1Sad#include <sys/disklabel.h>
581.1Sad#include <sys/disk.h>
591.1Sad#include <sys/dkio.h>
601.1Sad#include <sys/stat.h>
611.1Sad#include <sys/conf.h>
621.1Sad#include <sys/fcntl.h>
631.2Sad#include <sys/vnode.h>
641.1Sad#include <sys/syslog.h>
651.37.2.2Syamt#include <sys/mutex.h>
661.1Sad#if NRND > 0
671.1Sad#include <sys/rnd.h>
681.1Sad#endif
691.1Sad
701.1Sad#include <dev/ldvar.h>
711.1Sad
721.37.2.2Syamt#include <prop/proplib.h>
731.37.2.2Syamt
741.1Sadstatic void	ldgetdefaultlabel(struct ld_softc *, struct disklabel *);
751.1Sadstatic void	ldgetdisklabel(struct ld_softc *);
761.1Sadstatic void	ldminphys(struct buf *bp);
771.1Sadstatic void	ldshutdown(void *);
781.37.2.2Syamtstatic void	ldstart(struct ld_softc *, struct buf *);
791.37.2.2Syamtstatic void	ld_set_properties(struct ld_softc *);
801.37.2.2Syamtstatic void	ld_config_interrupts (struct device *);
811.1Sad
821.1Sadextern struct	cfdriver ld_cd;
831.1Sad
841.29Sthorpejstatic dev_type_open(ldopen);
851.29Sthorpejstatic dev_type_close(ldclose);
861.29Sthorpejstatic dev_type_read(ldread);
871.29Sthorpejstatic dev_type_write(ldwrite);
881.29Sthorpejstatic dev_type_ioctl(ldioctl);
891.29Sthorpejstatic dev_type_strategy(ldstrategy);
901.29Sthorpejstatic dev_type_dump(lddump);
911.29Sthorpejstatic dev_type_size(ldsize);
921.16Sgehenna
931.16Sgehennaconst struct bdevsw ld_bdevsw = {
941.16Sgehenna	ldopen, ldclose, ldstrategy, ldioctl, lddump, ldsize, D_DISK
951.16Sgehenna};
961.16Sgehenna
971.16Sgehennaconst struct cdevsw ld_cdevsw = {
981.16Sgehenna	ldopen, ldclose, ldread, ldwrite, ldioctl,
991.17Sjdolecek	nostop, notty, nopoll, nommap, nokqfilter, D_DISK
1001.16Sgehenna};
1011.16Sgehenna
1021.30Sthorpejstatic struct	dkdriver lddkdriver = { ldstrategy, ldminphys };
1031.1Sadstatic void	*ld_sdh;
1041.1Sad
1051.1Sadvoid
1061.1Sadldattach(struct ld_softc *sc)
1071.1Sad{
1081.37Schristos	char tbuf[9];
1091.1Sad
1101.37.2.6Syamt	mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_VM);
1111.37.2.2Syamt
1121.7Sad	if ((sc->sc_flags & LDF_ENABLED) == 0) {
1131.34Sbriggs		aprint_normal("%s: disabled\n", sc->sc_dv.dv_xname);
1141.7Sad		return;
1151.7Sad	}
1161.7Sad
1171.1Sad	/* Initialise and attach the disk structure. */
1181.37.2.4Syamt	disk_init(&sc->sc_dk, sc->sc_dv.dv_xname, &lddkdriver);
1191.1Sad	disk_attach(&sc->sc_dk);
1201.1Sad
1211.1Sad	if (sc->sc_maxxfer > MAXPHYS)
1221.1Sad		sc->sc_maxxfer = MAXPHYS;
1231.9Sad
1241.19Sthorpej	/* Build synthetic geometry if necessary. */
1251.19Sthorpej	if (sc->sc_nheads == 0 || sc->sc_nsectors == 0 ||
1261.19Sthorpej	    sc->sc_ncylinders == 0) {
1271.28Sdbj		uint64_t ncyl;
1281.28Sdbj
1291.19Sthorpej		if (sc->sc_secperunit <= 528 * 2048)		/* 528MB */
1301.19Sthorpej			sc->sc_nheads = 16;
1311.19Sthorpej		else if (sc->sc_secperunit <= 1024 * 2048)	/* 1GB */
1321.19Sthorpej			sc->sc_nheads = 32;
1331.19Sthorpej		else if (sc->sc_secperunit <= 21504 * 2048)	/* 21GB */
1341.19Sthorpej			sc->sc_nheads = 64;
1351.19Sthorpej		else if (sc->sc_secperunit <= 43008 * 2048)	/* 42GB */
1361.19Sthorpej			sc->sc_nheads = 128;
1371.19Sthorpej		else
1381.19Sthorpej			sc->sc_nheads = 255;
1391.19Sthorpej
1401.19Sthorpej		sc->sc_nsectors = 63;
1411.28Sdbj		sc->sc_ncylinders = INT_MAX;
1421.35Sperry		ncyl = sc->sc_secperunit /
1431.19Sthorpej		    (sc->sc_nheads * sc->sc_nsectors);
1441.28Sdbj		if (ncyl < INT_MAX)
1451.28Sdbj			sc->sc_ncylinders = (int)ncyl;
1461.19Sthorpej	}
1471.1Sad
1481.37Schristos	format_bytes(tbuf, sizeof(tbuf), sc->sc_secperunit *
1491.1Sad	    sc->sc_secsize);
1501.34Sbriggs	aprint_normal("%s: %s, %d cyl, %d head, %d sec, %d bytes/sect x %"PRIu64" sectors\n",
1511.37Schristos	    sc->sc_dv.dv_xname, tbuf, sc->sc_ncylinders, sc->sc_nheads,
1521.1Sad	    sc->sc_nsectors, sc->sc_secsize, sc->sc_secperunit);
1531.1Sad
1541.37.2.2Syamt	ld_set_properties(sc);
1551.37.2.2Syamt
1561.1Sad#if NRND > 0
1571.1Sad	/* Attach the device into the rnd source list. */
1581.1Sad	rnd_attach_source(&sc->sc_rnd_source, sc->sc_dv.dv_xname,
1591.1Sad	    RND_TYPE_DISK, 0);
1601.1Sad#endif
1611.1Sad
1621.1Sad	/* Set the `shutdownhook'. */
1631.1Sad	if (ld_sdh == NULL)
1641.1Sad		ld_sdh = shutdownhook_establish(ldshutdown, NULL);
1651.37.2.1Syamt	bufq_alloc(&sc->sc_bufq, BUFQ_DISK_DEFAULT_STRAT, BUFQ_SORT_RAWBLOCK);
1661.30Sthorpej
1671.30Sthorpej	/* Discover wedges on this disk. */
1681.37.2.2Syamt	config_interrupts(&sc->sc_dv, ld_config_interrupts);
1691.1Sad}
1701.1Sad
1711.3Sadint
1721.37Schristosldadjqparam(struct ld_softc *sc, int xmax)
1731.3Sad{
1741.24Sthorpej	int s;
1751.7Sad
1761.7Sad	s = splbio();
1771.37Schristos	sc->sc_maxqueuecnt = xmax;
1781.7Sad	splx(s);
1791.7Sad
1801.24Sthorpej	return (0);
1811.7Sad}
1821.7Sad
1831.7Sadint
1841.7Sadldbegindetach(struct ld_softc *sc, int flags)
1851.7Sad{
1861.24Sthorpej	int s, rv = 0;
1871.7Sad
1881.7Sad	if ((sc->sc_flags & LDF_ENABLED) == 0)
1891.7Sad		return (0);
1901.3Sad
1911.3Sad	if ((flags & DETACH_FORCE) == 0 && sc->sc_dk.dk_openmask != 0)
1921.3Sad		return (EBUSY);
1931.3Sad
1941.3Sad	s = splbio();
1951.24Sthorpej	sc->sc_maxqueuecnt = 0;
1961.7Sad	sc->sc_flags |= LDF_DETACH;
1971.24Sthorpej	while (sc->sc_queuecnt > 0) {
1981.24Sthorpej		sc->sc_flags |= LDF_DRAIN;
1991.24Sthorpej		rv = tsleep(&sc->sc_queuecnt, PRIBIO, "lddrn", 0);
2001.24Sthorpej		if (rv)
2011.24Sthorpej			break;
2021.24Sthorpej	}
2031.3Sad	splx(s);
2041.7Sad
2051.7Sad	return (rv);
2061.3Sad}
2071.3Sad
2081.2Sadvoid
2091.7Sadldenddetach(struct ld_softc *sc)
2101.2Sad{
2111.13Sdrochner	int s, bmaj, cmaj, i, mn;
2121.2Sad
2131.7Sad	if ((sc->sc_flags & LDF_ENABLED) == 0)
2141.7Sad		return;
2151.7Sad
2161.2Sad	/* Wait for commands queued with the hardware to complete. */
2171.2Sad	if (sc->sc_queuecnt != 0)
2181.7Sad		if (tsleep(&sc->sc_queuecnt, PRIBIO, "lddtch", 30 * hz))
2191.7Sad			printf("%s: not drained\n", sc->sc_dv.dv_xname);
2201.2Sad
2211.2Sad	/* Locate the major numbers. */
2221.16Sgehenna	bmaj = bdevsw_lookup_major(&ld_bdevsw);
2231.16Sgehenna	cmaj = cdevsw_lookup_major(&ld_cdevsw);
2241.2Sad
2251.2Sad	/* Kill off any queued buffers. */
2261.2Sad	s = splbio();
2271.37.2.1Syamt	bufq_drain(sc->sc_bufq);
2281.36Syamt	splx(s);
2291.36Syamt
2301.37.2.1Syamt	bufq_free(sc->sc_bufq);
2311.2Sad
2321.2Sad	/* Nuke the vnodes for any open instances. */
2331.13Sdrochner	for (i = 0; i < MAXPARTITIONS; i++) {
2341.37.2.1Syamt		mn = DISKMINOR(device_unit(&sc->sc_dv), i);
2351.13Sdrochner		vdevgone(bmaj, mn, mn, VBLK);
2361.13Sdrochner		vdevgone(cmaj, mn, mn, VCHR);
2371.13Sdrochner	}
2381.13Sdrochner
2391.30Sthorpej	/* Delete all of our wedges. */
2401.30Sthorpej	dkwedge_delall(&sc->sc_dk);
2411.30Sthorpej
2421.2Sad	/* Detach from the disk list. */
2431.2Sad	disk_detach(&sc->sc_dk);
2441.37.2.4Syamt	disk_destroy(&sc->sc_dk);
2451.2Sad
2461.2Sad#if NRND > 0
2471.2Sad	/* Unhook the entropy source. */
2481.2Sad	rnd_detach_source(&sc->sc_rnd_source);
2491.2Sad#endif
2501.2Sad
2511.24Sthorpej	/*
2521.24Sthorpej	 * XXX We can't really flush the cache here, beceause the
2531.24Sthorpej	 * XXX device may already be non-existent from the controller's
2541.24Sthorpej	 * XXX perspective.
2551.24Sthorpej	 */
2561.24Sthorpej#if 0
2571.2Sad	/* Flush the device's cache. */
2581.2Sad	if (sc->sc_flush != NULL)
2591.2Sad		if ((*sc->sc_flush)(sc) != 0)
2601.2Sad			printf("%s: unable to flush cache\n",
2611.2Sad			    sc->sc_dv.dv_xname);
2621.24Sthorpej#endif
2631.2Sad}
2641.2Sad
2651.8Slukem/* ARGSUSED */
2661.1Sadstatic void
2671.1Sadldshutdown(void *cookie)
2681.1Sad{
2691.1Sad	struct ld_softc *sc;
2701.1Sad	int i;
2711.1Sad
2721.1Sad	for (i = 0; i < ld_cd.cd_ndevs; i++) {
2731.1Sad		if ((sc = device_lookup(&ld_cd, i)) == NULL)
2741.1Sad			continue;
2751.1Sad		if (sc->sc_flush != NULL && (*sc->sc_flush)(sc) != 0)
2761.1Sad			printf("%s: unable to flush cache\n",
2771.1Sad			    sc->sc_dv.dv_xname);
2781.1Sad	}
2791.1Sad}
2801.1Sad
2811.8Slukem/* ARGSUSED */
2821.29Sthorpejstatic int
2831.37.2.1Syamtldopen(dev_t dev, int flags, int fmt, struct lwp *l)
2841.1Sad{
2851.1Sad	struct ld_softc *sc;
2861.30Sthorpej	int error, unit, part;
2871.1Sad
2881.1Sad	unit = DISKUNIT(dev);
2891.30Sthorpej	if ((sc = device_lookup(&ld_cd, unit)) == NULL)
2901.1Sad		return (ENXIO);
2911.1Sad	if ((sc->sc_flags & LDF_ENABLED) == 0)
2921.1Sad		return (ENODEV);
2931.1Sad	part = DISKPART(dev);
2941.30Sthorpej
2951.37.2.3Syamt	mutex_enter(&sc->sc_dk.dk_openlock);
2961.1Sad
2971.22Sthorpej	if (sc->sc_dk.dk_openmask == 0) {
2981.22Sthorpej		/* Load the partition info if not already loaded. */
2991.22Sthorpej		if ((sc->sc_flags & LDF_VLABEL) == 0)
3001.22Sthorpej			ldgetdisklabel(sc);
3011.22Sthorpej	}
3021.1Sad
3031.1Sad	/* Check that the partition exists. */
3041.1Sad	if (part != RAW_PART && (part >= sc->sc_dk.dk_label->d_npartitions ||
3051.1Sad	    sc->sc_dk.dk_label->d_partitions[part].p_fstype == FS_UNUSED)) {
3061.30Sthorpej		error = ENXIO;
3071.30Sthorpej		goto bad1;
3081.1Sad	}
3091.1Sad
3101.1Sad	/* Ensure only one open at a time. */
3111.1Sad	switch (fmt) {
3121.1Sad	case S_IFCHR:
3131.1Sad		sc->sc_dk.dk_copenmask |= (1 << part);
3141.1Sad		break;
3151.1Sad	case S_IFBLK:
3161.1Sad		sc->sc_dk.dk_bopenmask |= (1 << part);
3171.1Sad		break;
3181.1Sad	}
3191.1Sad	sc->sc_dk.dk_openmask =
3201.1Sad	    sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask;
3211.1Sad
3221.37.2.3Syamt	error = 0;
3231.30Sthorpej bad1:
3241.37.2.3Syamt	mutex_exit(&sc->sc_dk.dk_openlock);
3251.30Sthorpej	return (error);
3261.1Sad}
3271.1Sad
3281.8Slukem/* ARGSUSED */
3291.29Sthorpejstatic int
3301.37.2.1Syamtldclose(dev_t dev, int flags, int fmt, struct lwp *l)
3311.1Sad{
3321.1Sad	struct ld_softc *sc;
3331.37.2.3Syamt	int part, unit;
3341.1Sad
3351.1Sad	unit = DISKUNIT(dev);
3361.1Sad	part = DISKPART(dev);
3371.1Sad	sc = device_lookup(&ld_cd, unit);
3381.30Sthorpej
3391.37.2.3Syamt	mutex_enter(&sc->sc_dk.dk_openlock);
3401.1Sad
3411.1Sad	switch (fmt) {
3421.1Sad	case S_IFCHR:
3431.1Sad		sc->sc_dk.dk_copenmask &= ~(1 << part);
3441.1Sad		break;
3451.1Sad	case S_IFBLK:
3461.1Sad		sc->sc_dk.dk_bopenmask &= ~(1 << part);
3471.1Sad		break;
3481.1Sad	}
3491.1Sad	sc->sc_dk.dk_openmask =
3501.1Sad	    sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask;
3511.1Sad
3521.22Sthorpej	if (sc->sc_dk.dk_openmask == 0) {
3531.22Sthorpej		if (sc->sc_flush != NULL && (*sc->sc_flush)(sc) != 0)
3541.1Sad			printf("%s: unable to flush cache\n",
3551.1Sad			    sc->sc_dv.dv_xname);
3561.22Sthorpej		if ((sc->sc_flags & LDF_KLABEL) == 0)
3571.22Sthorpej			sc->sc_flags &= ~LDF_VLABEL;
3581.22Sthorpej	}
3591.1Sad
3601.37.2.3Syamt	mutex_exit(&sc->sc_dk.dk_openlock);
3611.1Sad	return (0);
3621.1Sad}
3631.1Sad
3641.8Slukem/* ARGSUSED */
3651.29Sthorpejstatic int
3661.1Sadldread(dev_t dev, struct uio *uio, int ioflag)
3671.1Sad{
3681.1Sad
3691.1Sad	return (physio(ldstrategy, NULL, dev, B_READ, ldminphys, uio));
3701.1Sad}
3711.1Sad
3721.8Slukem/* ARGSUSED */
3731.29Sthorpejstatic int
3741.1Sadldwrite(dev_t dev, struct uio *uio, int ioflag)
3751.1Sad{
3761.1Sad
3771.1Sad	return (physio(ldstrategy, NULL, dev, B_WRITE, ldminphys, uio));
3781.1Sad}
3791.1Sad
3801.8Slukem/* ARGSUSED */
3811.29Sthorpejstatic int
3821.37.2.3Syamtldioctl(dev_t dev, u_long cmd, void *addr, int32_t flag, struct lwp *l)
3831.1Sad{
3841.1Sad	struct ld_softc *sc;
3851.1Sad	int part, unit, error;
3861.4Sfvdl#ifdef __HAVE_OLD_DISKLABEL
3871.6Sitojun	struct disklabel newlabel;
3881.4Sfvdl#endif
3891.6Sitojun	struct disklabel *lp;
3901.1Sad
3911.1Sad	unit = DISKUNIT(dev);
3921.1Sad	part = DISKPART(dev);
3931.1Sad	sc = device_lookup(&ld_cd, unit);
3941.1Sad
3951.37.2.2Syamt	error = disk_ioctl(&sc->sc_dk, cmd, addr, flag, l);
3961.37.2.2Syamt	if (error != EPASSTHROUGH)
3971.37.2.2Syamt		return (error);
3981.37.2.2Syamt
3991.37.2.3Syamt	error = 0;
4001.1Sad	switch (cmd) {
4011.1Sad	case DIOCGDINFO:
4021.1Sad		memcpy(addr, sc->sc_dk.dk_label, sizeof(struct disklabel));
4031.1Sad		return (0);
4041.1Sad
4051.4Sfvdl#ifdef __HAVE_OLD_DISKLABEL
4061.4Sfvdl	case ODIOCGDINFO:
4071.4Sfvdl		newlabel = *(sc->sc_dk.dk_label);
4081.4Sfvdl		if (newlabel.d_npartitions > OLDMAXPARTITIONS)
4091.5Sfvdl			return ENOTTY;
4101.4Sfvdl		memcpy(addr, &newlabel, sizeof(struct olddisklabel));
4111.4Sfvdl		return (0);
4121.4Sfvdl#endif
4131.4Sfvdl
4141.1Sad	case DIOCGPART:
4151.1Sad		((struct partinfo *)addr)->disklab = sc->sc_dk.dk_label;
4161.1Sad		((struct partinfo *)addr)->part =
4171.1Sad		    &sc->sc_dk.dk_label->d_partitions[part];
4181.1Sad		break;
4191.1Sad
4201.1Sad	case DIOCWDINFO:
4211.1Sad	case DIOCSDINFO:
4221.4Sfvdl#ifdef __HAVE_OLD_DISKLABEL
4231.4Sfvdl	case ODIOCWDINFO:
4241.4Sfvdl	case ODIOCSDINFO:
4251.4Sfvdl
4261.4Sfvdl		if (cmd == ODIOCSDINFO || cmd == ODIOCWDINFO) {
4271.4Sfvdl			memset(&newlabel, 0, sizeof newlabel);
4281.4Sfvdl			memcpy(&newlabel, addr, sizeof (struct olddisklabel));
4291.4Sfvdl			lp = &newlabel;
4301.4Sfvdl		} else
4311.4Sfvdl#endif
4321.4Sfvdl		lp = (struct disklabel *)addr;
4331.4Sfvdl
4341.1Sad		if ((flag & FWRITE) == 0)
4351.1Sad			return (EBADF);
4361.1Sad
4371.37.2.3Syamt		mutex_enter(&sc->sc_dk.dk_openlock);
4381.1Sad		sc->sc_flags |= LDF_LABELLING;
4391.1Sad
4401.1Sad		error = setdisklabel(sc->sc_dk.dk_label,
4411.4Sfvdl		    lp, /*sc->sc_dk.dk_openmask : */0,
4421.1Sad		    sc->sc_dk.dk_cpulabel);
4431.4Sfvdl		if (error == 0 && (cmd == DIOCWDINFO
4441.4Sfvdl#ifdef __HAVE_OLD_DISKLABEL
4451.4Sfvdl		    || cmd == ODIOCWDINFO
4461.4Sfvdl#endif
4471.4Sfvdl		    ))
4481.1Sad			error = writedisklabel(
4491.35Sperry			    MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART),
4501.35Sperry			    ldstrategy, sc->sc_dk.dk_label,
4511.1Sad			    sc->sc_dk.dk_cpulabel);
4521.1Sad
4531.1Sad		sc->sc_flags &= ~LDF_LABELLING;
4541.37.2.3Syamt		mutex_exit(&sc->sc_dk.dk_openlock);
4551.1Sad		break;
4561.1Sad
4571.22Sthorpej	case DIOCKLABEL:
4581.22Sthorpej		if ((flag & FWRITE) == 0)
4591.22Sthorpej			return (EBADF);
4601.22Sthorpej		if (*(int *)addr)
4611.22Sthorpej			sc->sc_flags |= LDF_KLABEL;
4621.22Sthorpej		else
4631.22Sthorpej			sc->sc_flags &= ~LDF_KLABEL;
4641.22Sthorpej		break;
4651.22Sthorpej
4661.1Sad	case DIOCWLABEL:
4671.1Sad		if ((flag & FWRITE) == 0)
4681.1Sad			return (EBADF);
4691.1Sad		if (*(int *)addr)
4701.1Sad			sc->sc_flags |= LDF_WLABEL;
4711.1Sad		else
4721.1Sad			sc->sc_flags &= ~LDF_WLABEL;
4731.1Sad		break;
4741.1Sad
4751.1Sad	case DIOCGDEFLABEL:
4761.1Sad		ldgetdefaultlabel(sc, (struct disklabel *)addr);
4771.1Sad		break;
4781.4Sfvdl
4791.4Sfvdl#ifdef __HAVE_OLD_DISKLABEL
4801.4Sfvdl	case ODIOCGDEFLABEL:
4811.4Sfvdl		ldgetdefaultlabel(sc, &newlabel);
4821.4Sfvdl		if (newlabel.d_npartitions > OLDMAXPARTITIONS)
4831.5Sfvdl			return ENOTTY;
4841.4Sfvdl		memcpy(addr, &newlabel, sizeof (struct olddisklabel));
4851.4Sfvdl		break;
4861.4Sfvdl#endif
4871.1Sad
4881.32Sthorpej	case DIOCCACHESYNC:
4891.32Sthorpej		/*
4901.32Sthorpej		 * XXX Do we really need to care about having a writable
4911.32Sthorpej		 * file descriptor here?
4921.32Sthorpej		 */
4931.32Sthorpej		if ((flag & FWRITE) == 0)
4941.32Sthorpej			error = EBADF;
4951.32Sthorpej		else if (sc->sc_flush)
4961.32Sthorpej			error = (*sc->sc_flush)(sc);
4971.32Sthorpej		else
4981.32Sthorpej			error = 0;	/* XXX Error out instead? */
4991.32Sthorpej		break;
5001.32Sthorpej
5011.30Sthorpej	case DIOCAWEDGE:
5021.30Sthorpej	    {
5031.30Sthorpej	    	struct dkwedge_info *dkw = (void *) addr;
5041.30Sthorpej
5051.30Sthorpej		if ((flag & FWRITE) == 0)
5061.30Sthorpej			return (EBADF);
5071.30Sthorpej
5081.30Sthorpej		/* If the ioctl happens here, the parent is us. */
5091.30Sthorpej		strcpy(dkw->dkw_parent, sc->sc_dv.dv_xname);
5101.30Sthorpej		return (dkwedge_add(dkw));
5111.30Sthorpej	    }
5121.35Sperry
5131.30Sthorpej	case DIOCDWEDGE:
5141.30Sthorpej	    {
5151.30Sthorpej	    	struct dkwedge_info *dkw = (void *) addr;
5161.30Sthorpej
5171.30Sthorpej		if ((flag & FWRITE) == 0)
5181.30Sthorpej			return (EBADF);
5191.30Sthorpej
5201.30Sthorpej		/* If the ioctl happens here, the parent is us. */
5211.30Sthorpej		strcpy(dkw->dkw_parent, sc->sc_dv.dv_xname);
5221.30Sthorpej		return (dkwedge_del(dkw));
5231.30Sthorpej	    }
5241.35Sperry
5251.30Sthorpej	case DIOCLWEDGES:
5261.30Sthorpej	    {
5271.30Sthorpej	    	struct dkwedge_list *dkwl = (void *) addr;
5281.30Sthorpej
5291.37.2.1Syamt		return (dkwedge_list(&sc->sc_dk, dkwl, l));
5301.30Sthorpej	    }
5311.37.2.5Syamt	case DIOCGSTRATEGY:
5321.37.2.5Syamt	    {
5331.37.2.5Syamt		struct disk_strategy *dks = (void *)addr;
5341.37.2.5Syamt
5351.37.2.5Syamt		mutex_enter(&sc->sc_mutex);
5361.37.2.5Syamt		strlcpy(dks->dks_name, bufq_getstrategyname(sc->sc_bufq),
5371.37.2.5Syamt		    sizeof(dks->dks_name));
5381.37.2.5Syamt		mutex_exit(&sc->sc_mutex);
5391.37.2.5Syamt		dks->dks_paramlen = 0;
5401.37.2.5Syamt
5411.37.2.5Syamt		return 0;
5421.37.2.5Syamt	    }
5431.37.2.5Syamt	case DIOCSSTRATEGY:
5441.37.2.5Syamt	    {
5451.37.2.5Syamt		struct disk_strategy *dks = (void *)addr;
5461.37.2.5Syamt		struct bufq_state *new, *old;
5471.30Sthorpej
5481.37.2.5Syamt		if ((flag & FWRITE) == 0)
5491.37.2.5Syamt			return EPERM;
5501.37.2.5Syamt
5511.37.2.5Syamt		if (dks->dks_param != NULL)
5521.37.2.5Syamt			return EINVAL;
5531.37.2.5Syamt
5541.37.2.5Syamt		dks->dks_name[sizeof(dks->dks_name) - 1] = 0; /* ensure term */
5551.37.2.5Syamt		error = bufq_alloc(&new, dks->dks_name,
5561.37.2.5Syamt		    BUFQ_EXACT|BUFQ_SORT_RAWBLOCK);
5571.37.2.5Syamt		if (error)
5581.37.2.5Syamt			return error;
5591.37.2.5Syamt
5601.37.2.5Syamt		mutex_enter(&sc->sc_mutex);
5611.37.2.5Syamt		old = sc->sc_bufq;
5621.37.2.5Syamt		bufq_move(new, old);
5631.37.2.5Syamt		sc->sc_bufq = new;
5641.37.2.5Syamt		mutex_exit(&sc->sc_mutex);
5651.37.2.5Syamt		bufq_free(old);
5661.37.2.5Syamt
5671.37.2.5Syamt		return 0;
5681.37.2.5Syamt	    }
5691.1Sad	default:
5701.1Sad		error = ENOTTY;
5711.1Sad		break;
5721.1Sad	}
5731.1Sad
5741.1Sad	return (error);
5751.1Sad}
5761.1Sad
5771.29Sthorpejstatic void
5781.1Sadldstrategy(struct buf *bp)
5791.1Sad{
5801.1Sad	struct ld_softc *sc;
5811.23Sthorpej	struct disklabel *lp;
5821.23Sthorpej	daddr_t blkno;
5831.23Sthorpej	int s, part;
5841.1Sad
5851.1Sad	sc = device_lookup(&ld_cd, DISKUNIT(bp->b_dev));
5861.23Sthorpej	part = DISKPART(bp->b_dev);
5871.1Sad
5881.7Sad	if ((sc->sc_flags & LDF_DETACH) != 0) {
5891.2Sad		bp->b_error = EIO;
5901.37.2.3Syamt		goto done;
5911.2Sad	}
5921.2Sad
5931.1Sad	lp = sc->sc_dk.dk_label;
5941.1Sad
5951.1Sad	/*
5961.1Sad	 * The transfer must be a whole number of blocks and the offset must
5971.1Sad	 * not be negative.
5981.1Sad	 */
5991.1Sad	if ((bp->b_bcount % lp->d_secsize) != 0 || bp->b_blkno < 0) {
6001.23Sthorpej		bp->b_error = EINVAL;
6011.37.2.3Syamt		goto done;
6021.1Sad	}
6031.1Sad
6041.23Sthorpej	/* If it's a null transfer, return immediately. */
6051.23Sthorpej	if (bp->b_bcount == 0)
6061.23Sthorpej		goto done;
6071.1Sad
6081.1Sad	/*
6091.1Sad	 * Do bounds checking and adjust the transfer.  If error, process.
6101.1Sad	 * If past the end of partition, just return.
6111.1Sad	 */
6121.1Sad	if (part != RAW_PART &&
6131.20Sthorpej	    bounds_check_with_label(&sc->sc_dk, bp,
6141.1Sad	    (sc->sc_flags & (LDF_WLABEL | LDF_LABELLING)) != 0) <= 0) {
6151.23Sthorpej		goto done;
6161.1Sad	}
6171.1Sad
6181.1Sad	/*
6191.23Sthorpej	 * Convert the block number to absolute and put it in terms
6201.23Sthorpej	 * of the device's logical block size.
6211.1Sad	 */
6221.23Sthorpej	if (lp->d_secsize == DEV_BSIZE)
6231.23Sthorpej		blkno = bp->b_blkno;
6241.23Sthorpej	else if (lp->d_secsize > DEV_BSIZE)
6251.23Sthorpej		blkno = bp->b_blkno / (lp->d_secsize / DEV_BSIZE);
6261.1Sad	else
6271.23Sthorpej		blkno = bp->b_blkno * (DEV_BSIZE / lp->d_secsize);
6281.1Sad
6291.11Sthorpej	if (part != RAW_PART)
6301.23Sthorpej		blkno += lp->d_partitions[part].p_offset;
6311.23Sthorpej
6321.23Sthorpej	bp->b_rawblkno = blkno;
6331.1Sad
6341.1Sad	s = splbio();
6351.37.2.2Syamt	ldstart(sc, bp);
6361.1Sad	splx(s);
6371.23Sthorpej	return;
6381.23Sthorpej
6391.23Sthorpej done:
6401.23Sthorpej	bp->b_resid = bp->b_bcount;
6411.23Sthorpej	biodone(bp);
6421.23Sthorpej}
6431.23Sthorpej
6441.23Sthorpejstatic void
6451.37.2.2Syamtldstart(struct ld_softc *sc, struct buf *bp)
6461.23Sthorpej{
6471.23Sthorpej	int error;
6481.1Sad
6491.37.2.2Syamt	mutex_enter(&sc->sc_mutex);
6501.37.2.2Syamt
6511.37.2.2Syamt	if (bp != NULL)
6521.37.2.2Syamt		BUFQ_PUT(sc->sc_bufq, bp);
6531.37.2.2Syamt
6541.23Sthorpej	while (sc->sc_queuecnt < sc->sc_maxqueuecnt) {
6551.23Sthorpej		/* See if there is work to do. */
6561.37.2.1Syamt		if ((bp = BUFQ_PEEK(sc->sc_bufq)) == NULL)
6571.23Sthorpej			break;
6581.23Sthorpej
6591.23Sthorpej		disk_busy(&sc->sc_dk);
6601.23Sthorpej		sc->sc_queuecnt++;
6611.23Sthorpej
6621.23Sthorpej		if (__predict_true((error = (*sc->sc_start)(sc, bp)) == 0)) {
6631.23Sthorpej			/*
6641.23Sthorpej			 * The back-end is running the job; remove it from
6651.23Sthorpej			 * the queue.
6661.23Sthorpej			 */
6671.37.2.1Syamt			(void) BUFQ_GET(sc->sc_bufq);
6681.23Sthorpej		} else  {
6691.23Sthorpej			disk_unbusy(&sc->sc_dk, 0, (bp->b_flags & B_READ));
6701.23Sthorpej			sc->sc_queuecnt--;
6711.23Sthorpej			if (error == EAGAIN) {
6721.23Sthorpej				/*
6731.23Sthorpej				 * Temporary resource shortage in the
6741.23Sthorpej				 * back-end; just defer the job until
6751.23Sthorpej				 * later.
6761.23Sthorpej				 *
6771.23Sthorpej				 * XXX We might consider a watchdog timer
6781.23Sthorpej				 * XXX to make sure we are kicked into action.
6791.23Sthorpej				 */
6801.23Sthorpej				break;
6811.23Sthorpej			} else {
6821.37.2.1Syamt				(void) BUFQ_GET(sc->sc_bufq);
6831.23Sthorpej				bp->b_error = error;
6841.23Sthorpej				bp->b_resid = bp->b_bcount;
6851.37.2.2Syamt				mutex_exit(&sc->sc_mutex);
6861.23Sthorpej				biodone(bp);
6871.37.2.2Syamt				mutex_enter(&sc->sc_mutex);
6881.23Sthorpej			}
6891.23Sthorpej		}
6901.1Sad	}
6911.37.2.2Syamt
6921.37.2.2Syamt	mutex_exit(&sc->sc_mutex);
6931.1Sad}
6941.1Sad
6951.1Sadvoid
6961.1Sadlddone(struct ld_softc *sc, struct buf *bp)
6971.1Sad{
6981.1Sad
6991.37.2.3Syamt	if (bp->b_error != 0) {
7001.1Sad		diskerr(bp, "ld", "error", LOG_PRINTF, 0, sc->sc_dk.dk_label);
7011.1Sad		printf("\n");
7021.1Sad	}
7031.1Sad
7041.18Smrg	disk_unbusy(&sc->sc_dk, bp->b_bcount - bp->b_resid,
7051.18Smrg	    (bp->b_flags & B_READ));
7061.1Sad#if NRND > 0
7071.1Sad	rnd_add_uint32(&sc->sc_rnd_source, bp->b_rawblkno);
7081.1Sad#endif
7091.1Sad	biodone(bp);
7101.1Sad
7111.37.2.2Syamt	mutex_enter(&sc->sc_mutex);
7121.7Sad	if (--sc->sc_queuecnt <= sc->sc_maxqueuecnt) {
7131.24Sthorpej		if ((sc->sc_flags & LDF_DRAIN) != 0) {
7141.24Sthorpej			sc->sc_flags &= ~LDF_DRAIN;
7151.7Sad			wakeup(&sc->sc_queuecnt);
7161.24Sthorpej		}
7171.37.2.2Syamt		mutex_exit(&sc->sc_mutex);
7181.37.2.2Syamt		ldstart(sc, NULL);
7191.37.2.2Syamt	} else
7201.37.2.2Syamt		mutex_exit(&sc->sc_mutex);
7211.1Sad}
7221.1Sad
7231.29Sthorpejstatic int
7241.1Sadldsize(dev_t dev)
7251.1Sad{
7261.1Sad	struct ld_softc *sc;
7271.1Sad	int part, unit, omask, size;
7281.1Sad
7291.1Sad	unit = DISKUNIT(dev);
7301.1Sad	if ((sc = device_lookup(&ld_cd, unit)) == NULL)
7311.1Sad		return (ENODEV);
7321.1Sad	if ((sc->sc_flags & LDF_ENABLED) == 0)
7331.1Sad		return (ENODEV);
7341.1Sad	part = DISKPART(dev);
7351.1Sad
7361.1Sad	omask = sc->sc_dk.dk_openmask & (1 << part);
7371.1Sad
7381.1Sad	if (omask == 0 && ldopen(dev, 0, S_IFBLK, NULL) != 0)
7391.1Sad		return (-1);
7401.1Sad	else if (sc->sc_dk.dk_label->d_partitions[part].p_fstype != FS_SWAP)
7411.1Sad		size = -1;
7421.1Sad	else
7431.1Sad		size = sc->sc_dk.dk_label->d_partitions[part].p_size *
7441.1Sad		    (sc->sc_dk.dk_label->d_secsize / DEV_BSIZE);
7451.1Sad	if (omask == 0 && ldclose(dev, 0, S_IFBLK, NULL) != 0)
7461.1Sad		return (-1);
7471.1Sad
7481.1Sad	return (size);
7491.1Sad}
7501.1Sad
7511.1Sad/*
7521.1Sad * Load the label information from the specified device.
7531.1Sad */
7541.1Sadstatic void
7551.1Sadldgetdisklabel(struct ld_softc *sc)
7561.1Sad{
7571.1Sad	const char *errstring;
7581.1Sad
7591.1Sad	ldgetdefaultlabel(sc, sc->sc_dk.dk_label);
7601.1Sad
7611.1Sad	/* Call the generic disklabel extraction routine. */
7621.37.2.1Syamt	errstring = readdisklabel(MAKEDISKDEV(0, device_unit(&sc->sc_dv),
7631.37.2.1Syamt	    RAW_PART), ldstrategy, sc->sc_dk.dk_label, sc->sc_dk.dk_cpulabel);
7641.1Sad	if (errstring != NULL)
7651.1Sad		printf("%s: %s\n", sc->sc_dv.dv_xname, errstring);
7661.22Sthorpej
7671.22Sthorpej	/* In-core label now valid. */
7681.22Sthorpej	sc->sc_flags |= LDF_VLABEL;
7691.1Sad}
7701.1Sad
7711.1Sad/*
7721.1Sad * Construct a ficticious label.
7731.1Sad */
7741.1Sadstatic void
7751.1Sadldgetdefaultlabel(struct ld_softc *sc, struct disklabel *lp)
7761.1Sad{
7771.1Sad
7781.1Sad	memset(lp, 0, sizeof(struct disklabel));
7791.1Sad
7801.1Sad	lp->d_secsize = sc->sc_secsize;
7811.1Sad	lp->d_ntracks = sc->sc_nheads;
7821.1Sad	lp->d_nsectors = sc->sc_nsectors;
7831.1Sad	lp->d_ncylinders = sc->sc_ncylinders;
7841.1Sad	lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
7851.1Sad	lp->d_type = DTYPE_LD;
7861.21Sitojun	strlcpy(lp->d_typename, "unknown", sizeof(lp->d_typename));
7871.21Sitojun	strlcpy(lp->d_packname, "fictitious", sizeof(lp->d_packname));
7881.1Sad	lp->d_secperunit = sc->sc_secperunit;
7891.1Sad	lp->d_rpm = 7200;
7901.1Sad	lp->d_interleave = 1;
7911.1Sad	lp->d_flags = 0;
7921.1Sad
7931.1Sad	lp->d_partitions[RAW_PART].p_offset = 0;
7941.1Sad	lp->d_partitions[RAW_PART].p_size =
7951.1Sad	    lp->d_secperunit * (lp->d_secsize / DEV_BSIZE);
7961.1Sad	lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED;
7971.1Sad	lp->d_npartitions = RAW_PART + 1;
7981.1Sad
7991.1Sad	lp->d_magic = DISKMAGIC;
8001.1Sad	lp->d_magic2 = DISKMAGIC;
8011.1Sad	lp->d_checksum = dkcksum(lp);
8021.1Sad}
8031.1Sad
8041.1Sad/*
8051.1Sad * Take a dump.
8061.1Sad */
8071.29Sthorpejstatic int
8081.37.2.3Syamtlddump(dev_t dev, daddr_t blkno, void *vav, size_t size)
8091.1Sad{
8101.37.2.3Syamt	char *va = vav;
8111.1Sad	struct ld_softc *sc;
8121.1Sad	struct disklabel *lp;
8131.1Sad	int unit, part, nsects, sectoff, towrt, nblk, maxblkcnt, rv;
8141.1Sad	static int dumping;
8151.1Sad
8161.1Sad	unit = DISKUNIT(dev);
8171.1Sad	if ((sc = device_lookup(&ld_cd, unit)) == NULL)
8181.1Sad		return (ENXIO);
8191.1Sad	if ((sc->sc_flags & LDF_ENABLED) == 0)
8201.1Sad		return (ENODEV);
8211.3Sad	if (sc->sc_dump == NULL)
8221.3Sad		return (ENXIO);
8231.3Sad
8241.3Sad	/* Check if recursive dump; if so, punt. */
8251.3Sad	if (dumping)
8261.3Sad		return (EFAULT);
8271.3Sad	dumping = 1;
8281.1Sad
8291.1Sad	/* Convert to disk sectors.  Request must be a multiple of size. */
8301.3Sad	part = DISKPART(dev);
8311.1Sad	lp = sc->sc_dk.dk_label;
8321.1Sad	if ((size % lp->d_secsize) != 0)
8331.1Sad		return (EFAULT);
8341.1Sad	towrt = size / lp->d_secsize;
8351.1Sad	blkno = dbtob(blkno) / lp->d_secsize;	/* blkno in DEV_BSIZE units */
8361.1Sad
8371.1Sad	nsects = lp->d_partitions[part].p_size;
8381.1Sad	sectoff = lp->d_partitions[part].p_offset;
8391.1Sad
8401.1Sad	/* Check transfer bounds against partition size. */
8411.1Sad	if ((blkno < 0) || ((blkno + towrt) > nsects))
8421.1Sad		return (EINVAL);
8431.1Sad
8441.1Sad	/* Offset block number to start of partition. */
8451.1Sad	blkno += sectoff;
8461.1Sad
8471.1Sad	/* Start dumping and return when done. */
8481.3Sad	maxblkcnt = sc->sc_maxxfer / sc->sc_secsize - 1;
8491.1Sad	while (towrt > 0) {
8501.3Sad		nblk = min(maxblkcnt, towrt);
8511.1Sad
8521.1Sad		if ((rv = (*sc->sc_dump)(sc, va, blkno, nblk)) != 0)
8531.1Sad			return (rv);
8541.1Sad
8551.1Sad		towrt -= nblk;
8561.1Sad		blkno += nblk;
8571.1Sad		va += nblk * sc->sc_secsize;
8581.1Sad	}
8591.1Sad
8601.1Sad	dumping = 0;
8611.1Sad	return (0);
8621.1Sad}
8631.1Sad
8641.1Sad/*
8651.1Sad * Adjust the size of a transfer.
8661.1Sad */
8671.1Sadstatic void
8681.1Sadldminphys(struct buf *bp)
8691.1Sad{
8701.1Sad	struct ld_softc *sc;
8711.1Sad
8721.1Sad	sc = device_lookup(&ld_cd, DISKUNIT(bp->b_dev));
8731.1Sad
8741.1Sad	if (bp->b_bcount > sc->sc_maxxfer)
8751.1Sad		bp->b_bcount = sc->sc_maxxfer;
8761.1Sad	minphys(bp);
8771.1Sad}
8781.37.2.2Syamt
8791.37.2.2Syamtstatic void
8801.37.2.2Syamtld_set_properties(struct ld_softc *ld)
8811.37.2.2Syamt{
8821.37.2.2Syamt	prop_dictionary_t disk_info, odisk_info, geom;
8831.37.2.2Syamt
8841.37.2.2Syamt	disk_info = prop_dictionary_create();
8851.37.2.2Syamt
8861.37.2.2Syamt	geom = prop_dictionary_create();
8871.37.2.2Syamt
8881.37.2.2Syamt	prop_dictionary_set_uint64(geom, "sectors-per-unit",
8891.37.2.2Syamt	    ld->sc_secperunit);
8901.37.2.2Syamt
8911.37.2.2Syamt	prop_dictionary_set_uint32(geom, "sector-size",
8921.37.2.2Syamt	    ld->sc_secsize);
8931.37.2.2Syamt
8941.37.2.2Syamt	prop_dictionary_set_uint16(geom, "sectors-per-track",
8951.37.2.2Syamt	    ld->sc_nsectors);
8961.37.2.2Syamt
8971.37.2.2Syamt	prop_dictionary_set_uint16(geom, "tracks-per-cylinder",
8981.37.2.2Syamt	    ld->sc_nheads);
8991.37.2.2Syamt
9001.37.2.2Syamt	prop_dictionary_set_uint64(geom, "cylinders-per-unit",
9011.37.2.2Syamt	    ld->sc_ncylinders);
9021.37.2.2Syamt
9031.37.2.2Syamt	prop_dictionary_set(disk_info, "geometry", geom);
9041.37.2.2Syamt	prop_object_release(geom);
9051.37.2.2Syamt
9061.37.2.2Syamt	prop_dictionary_set(device_properties(&ld->sc_dv),
9071.37.2.2Syamt	    "disk-info", disk_info);
9081.37.2.2Syamt
9091.37.2.2Syamt	/*
9101.37.2.2Syamt	 * Don't release disk_info here; we keep a reference to it.
9111.37.2.2Syamt	 * disk_detach() will release it when we go away.
9121.37.2.2Syamt	 */
9131.37.2.2Syamt
9141.37.2.2Syamt	odisk_info = ld->sc_dk.dk_info;
9151.37.2.2Syamt	ld->sc_dk.dk_info = disk_info;
9161.37.2.2Syamt	if (odisk_info)
9171.37.2.2Syamt		prop_object_release(odisk_info);
9181.37.2.2Syamt}
9191.37.2.2Syamt
9201.37.2.2Syamtstatic void
9211.37.2.2Syamtld_config_interrupts (struct device *d)
9221.37.2.2Syamt{
9231.37.2.2Syamt	struct ld_softc *sc = (struct ld_softc *)d;
9241.37.2.2Syamt	dkwedge_discover(&sc->sc_dk);
9251.37.2.2Syamt}
926