ld.c revision 1.37.2.2
11.37.2.2Syamt/*	$NetBSD: ld.c,v 1.37.2.2 2007/02/26 09:09:54 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.2Syamt__KERNEL_RCSID(0, "$NetBSD: ld.c,v 1.37.2.2 2007/02/26 09:09:54 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/lock.h>
621.1Sad#include <sys/conf.h>
631.1Sad#include <sys/fcntl.h>
641.2Sad#include <sys/vnode.h>
651.1Sad#include <sys/syslog.h>
661.37.2.2Syamt#include <sys/mutex.h>
671.1Sad#if NRND > 0
681.1Sad#include <sys/rnd.h>
691.1Sad#endif
701.1Sad
711.1Sad#include <dev/ldvar.h>
721.1Sad
731.37.2.2Syamt#include <prop/proplib.h>
741.37.2.2Syamt
751.1Sadstatic void	ldgetdefaultlabel(struct ld_softc *, struct disklabel *);
761.1Sadstatic void	ldgetdisklabel(struct ld_softc *);
771.1Sadstatic void	ldminphys(struct buf *bp);
781.1Sadstatic void	ldshutdown(void *);
791.37.2.2Syamtstatic void	ldstart(struct ld_softc *, struct buf *);
801.37.2.2Syamtstatic void	ld_set_properties(struct ld_softc *);
811.37.2.2Syamtstatic void	ld_config_interrupts (struct device *);
821.1Sad
831.1Sadextern struct	cfdriver ld_cd;
841.1Sad
851.29Sthorpejstatic dev_type_open(ldopen);
861.29Sthorpejstatic dev_type_close(ldclose);
871.29Sthorpejstatic dev_type_read(ldread);
881.29Sthorpejstatic dev_type_write(ldwrite);
891.29Sthorpejstatic dev_type_ioctl(ldioctl);
901.29Sthorpejstatic dev_type_strategy(ldstrategy);
911.29Sthorpejstatic dev_type_dump(lddump);
921.29Sthorpejstatic dev_type_size(ldsize);
931.16Sgehenna
941.16Sgehennaconst struct bdevsw ld_bdevsw = {
951.16Sgehenna	ldopen, ldclose, ldstrategy, ldioctl, lddump, ldsize, D_DISK
961.16Sgehenna};
971.16Sgehenna
981.16Sgehennaconst struct cdevsw ld_cdevsw = {
991.16Sgehenna	ldopen, ldclose, ldread, ldwrite, ldioctl,
1001.17Sjdolecek	nostop, notty, nopoll, nommap, nokqfilter, D_DISK
1011.16Sgehenna};
1021.16Sgehenna
1031.30Sthorpejstatic struct	dkdriver lddkdriver = { ldstrategy, ldminphys };
1041.1Sadstatic void	*ld_sdh;
1051.1Sad
1061.1Sadvoid
1071.1Sadldattach(struct ld_softc *sc)
1081.1Sad{
1091.37Schristos	char tbuf[9];
1101.1Sad
1111.37.2.2Syamt	mutex_init(&sc->sc_mutex, MUTEX_DRIVER, IPL_BIO);
1121.37.2.2Syamt
1131.7Sad	if ((sc->sc_flags & LDF_ENABLED) == 0) {
1141.34Sbriggs		aprint_normal("%s: disabled\n", sc->sc_dv.dv_xname);
1151.7Sad		return;
1161.7Sad	}
1171.7Sad
1181.1Sad	/* Initialise and attach the disk structure. */
1191.1Sad	sc->sc_dk.dk_driver = &lddkdriver;
1201.1Sad	sc->sc_dk.dk_name = sc->sc_dv.dv_xname;
1211.1Sad	disk_attach(&sc->sc_dk);
1221.1Sad
1231.1Sad	if (sc->sc_maxxfer > MAXPHYS)
1241.1Sad		sc->sc_maxxfer = MAXPHYS;
1251.9Sad
1261.19Sthorpej	/* Build synthetic geometry if necessary. */
1271.19Sthorpej	if (sc->sc_nheads == 0 || sc->sc_nsectors == 0 ||
1281.19Sthorpej	    sc->sc_ncylinders == 0) {
1291.28Sdbj		uint64_t ncyl;
1301.28Sdbj
1311.19Sthorpej		if (sc->sc_secperunit <= 528 * 2048)		/* 528MB */
1321.19Sthorpej			sc->sc_nheads = 16;
1331.19Sthorpej		else if (sc->sc_secperunit <= 1024 * 2048)	/* 1GB */
1341.19Sthorpej			sc->sc_nheads = 32;
1351.19Sthorpej		else if (sc->sc_secperunit <= 21504 * 2048)	/* 21GB */
1361.19Sthorpej			sc->sc_nheads = 64;
1371.19Sthorpej		else if (sc->sc_secperunit <= 43008 * 2048)	/* 42GB */
1381.19Sthorpej			sc->sc_nheads = 128;
1391.19Sthorpej		else
1401.19Sthorpej			sc->sc_nheads = 255;
1411.19Sthorpej
1421.19Sthorpej		sc->sc_nsectors = 63;
1431.28Sdbj		sc->sc_ncylinders = INT_MAX;
1441.35Sperry		ncyl = sc->sc_secperunit /
1451.19Sthorpej		    (sc->sc_nheads * sc->sc_nsectors);
1461.28Sdbj		if (ncyl < INT_MAX)
1471.28Sdbj			sc->sc_ncylinders = (int)ncyl;
1481.19Sthorpej	}
1491.1Sad
1501.37Schristos	format_bytes(tbuf, sizeof(tbuf), sc->sc_secperunit *
1511.1Sad	    sc->sc_secsize);
1521.34Sbriggs	aprint_normal("%s: %s, %d cyl, %d head, %d sec, %d bytes/sect x %"PRIu64" sectors\n",
1531.37Schristos	    sc->sc_dv.dv_xname, tbuf, sc->sc_ncylinders, sc->sc_nheads,
1541.1Sad	    sc->sc_nsectors, sc->sc_secsize, sc->sc_secperunit);
1551.1Sad
1561.37.2.2Syamt	ld_set_properties(sc);
1571.37.2.2Syamt
1581.1Sad#if NRND > 0
1591.1Sad	/* Attach the device into the rnd source list. */
1601.1Sad	rnd_attach_source(&sc->sc_rnd_source, sc->sc_dv.dv_xname,
1611.1Sad	    RND_TYPE_DISK, 0);
1621.1Sad#endif
1631.1Sad
1641.1Sad	/* Set the `shutdownhook'. */
1651.1Sad	if (ld_sdh == NULL)
1661.1Sad		ld_sdh = shutdownhook_establish(ldshutdown, NULL);
1671.37.2.1Syamt	bufq_alloc(&sc->sc_bufq, BUFQ_DISK_DEFAULT_STRAT, BUFQ_SORT_RAWBLOCK);
1681.30Sthorpej
1691.30Sthorpej	/* Discover wedges on this disk. */
1701.37.2.2Syamt	config_interrupts(&sc->sc_dv, ld_config_interrupts);
1711.1Sad}
1721.1Sad
1731.3Sadint
1741.37Schristosldadjqparam(struct ld_softc *sc, int xmax)
1751.3Sad{
1761.24Sthorpej	int s;
1771.7Sad
1781.7Sad	s = splbio();
1791.37Schristos	sc->sc_maxqueuecnt = xmax;
1801.7Sad	splx(s);
1811.7Sad
1821.24Sthorpej	return (0);
1831.7Sad}
1841.7Sad
1851.7Sadint
1861.7Sadldbegindetach(struct ld_softc *sc, int flags)
1871.7Sad{
1881.24Sthorpej	int s, rv = 0;
1891.7Sad
1901.7Sad	if ((sc->sc_flags & LDF_ENABLED) == 0)
1911.7Sad		return (0);
1921.3Sad
1931.3Sad	if ((flags & DETACH_FORCE) == 0 && sc->sc_dk.dk_openmask != 0)
1941.3Sad		return (EBUSY);
1951.3Sad
1961.3Sad	s = splbio();
1971.24Sthorpej	sc->sc_maxqueuecnt = 0;
1981.7Sad	sc->sc_flags |= LDF_DETACH;
1991.24Sthorpej	while (sc->sc_queuecnt > 0) {
2001.24Sthorpej		sc->sc_flags |= LDF_DRAIN;
2011.24Sthorpej		rv = tsleep(&sc->sc_queuecnt, PRIBIO, "lddrn", 0);
2021.24Sthorpej		if (rv)
2031.24Sthorpej			break;
2041.24Sthorpej	}
2051.3Sad	splx(s);
2061.7Sad
2071.7Sad	return (rv);
2081.3Sad}
2091.3Sad
2101.2Sadvoid
2111.7Sadldenddetach(struct ld_softc *sc)
2121.2Sad{
2131.13Sdrochner	int s, bmaj, cmaj, i, mn;
2141.2Sad
2151.7Sad	if ((sc->sc_flags & LDF_ENABLED) == 0)
2161.7Sad		return;
2171.7Sad
2181.2Sad	/* Wait for commands queued with the hardware to complete. */
2191.2Sad	if (sc->sc_queuecnt != 0)
2201.7Sad		if (tsleep(&sc->sc_queuecnt, PRIBIO, "lddtch", 30 * hz))
2211.7Sad			printf("%s: not drained\n", sc->sc_dv.dv_xname);
2221.2Sad
2231.2Sad	/* Locate the major numbers. */
2241.16Sgehenna	bmaj = bdevsw_lookup_major(&ld_bdevsw);
2251.16Sgehenna	cmaj = cdevsw_lookup_major(&ld_cdevsw);
2261.2Sad
2271.2Sad	/* Kill off any queued buffers. */
2281.2Sad	s = splbio();
2291.37.2.1Syamt	bufq_drain(sc->sc_bufq);
2301.36Syamt	splx(s);
2311.36Syamt
2321.37.2.1Syamt	bufq_free(sc->sc_bufq);
2331.2Sad
2341.2Sad	/* Nuke the vnodes for any open instances. */
2351.13Sdrochner	for (i = 0; i < MAXPARTITIONS; i++) {
2361.37.2.1Syamt		mn = DISKMINOR(device_unit(&sc->sc_dv), i);
2371.13Sdrochner		vdevgone(bmaj, mn, mn, VBLK);
2381.13Sdrochner		vdevgone(cmaj, mn, mn, VCHR);
2391.13Sdrochner	}
2401.13Sdrochner
2411.30Sthorpej	/* Delete all of our wedges. */
2421.30Sthorpej	dkwedge_delall(&sc->sc_dk);
2431.30Sthorpej
2441.2Sad	/* Detach from the disk list. */
2451.2Sad	disk_detach(&sc->sc_dk);
2461.2Sad
2471.2Sad#if NRND > 0
2481.2Sad	/* Unhook the entropy source. */
2491.2Sad	rnd_detach_source(&sc->sc_rnd_source);
2501.2Sad#endif
2511.2Sad
2521.24Sthorpej	/*
2531.24Sthorpej	 * XXX We can't really flush the cache here, beceause the
2541.24Sthorpej	 * XXX device may already be non-existent from the controller's
2551.24Sthorpej	 * XXX perspective.
2561.24Sthorpej	 */
2571.24Sthorpej#if 0
2581.2Sad	/* Flush the device's cache. */
2591.2Sad	if (sc->sc_flush != NULL)
2601.2Sad		if ((*sc->sc_flush)(sc) != 0)
2611.2Sad			printf("%s: unable to flush cache\n",
2621.2Sad			    sc->sc_dv.dv_xname);
2631.24Sthorpej#endif
2641.2Sad}
2651.2Sad
2661.8Slukem/* ARGSUSED */
2671.1Sadstatic void
2681.1Sadldshutdown(void *cookie)
2691.1Sad{
2701.1Sad	struct ld_softc *sc;
2711.1Sad	int i;
2721.1Sad
2731.1Sad	for (i = 0; i < ld_cd.cd_ndevs; i++) {
2741.1Sad		if ((sc = device_lookup(&ld_cd, i)) == NULL)
2751.1Sad			continue;
2761.1Sad		if (sc->sc_flush != NULL && (*sc->sc_flush)(sc) != 0)
2771.1Sad			printf("%s: unable to flush cache\n",
2781.1Sad			    sc->sc_dv.dv_xname);
2791.1Sad	}
2801.1Sad}
2811.1Sad
2821.8Slukem/* ARGSUSED */
2831.29Sthorpejstatic int
2841.37.2.1Syamtldopen(dev_t dev, int flags, int fmt, struct lwp *l)
2851.1Sad{
2861.1Sad	struct ld_softc *sc;
2871.30Sthorpej	int error, unit, part;
2881.1Sad
2891.1Sad	unit = DISKUNIT(dev);
2901.30Sthorpej	if ((sc = device_lookup(&ld_cd, unit)) == NULL)
2911.1Sad		return (ENXIO);
2921.1Sad	if ((sc->sc_flags & LDF_ENABLED) == 0)
2931.1Sad		return (ENODEV);
2941.1Sad	part = DISKPART(dev);
2951.30Sthorpej
2961.30Sthorpej	if ((error = lockmgr(&sc->sc_dk.dk_openlock, LK_EXCLUSIVE, NULL)) != 0)
2971.30Sthorpej		return (error);
2981.1Sad
2991.22Sthorpej	if (sc->sc_dk.dk_openmask == 0) {
3001.22Sthorpej		/* Load the partition info if not already loaded. */
3011.22Sthorpej		if ((sc->sc_flags & LDF_VLABEL) == 0)
3021.22Sthorpej			ldgetdisklabel(sc);
3031.22Sthorpej	}
3041.1Sad
3051.1Sad	/* Check that the partition exists. */
3061.1Sad	if (part != RAW_PART && (part >= sc->sc_dk.dk_label->d_npartitions ||
3071.1Sad	    sc->sc_dk.dk_label->d_partitions[part].p_fstype == FS_UNUSED)) {
3081.30Sthorpej		error = ENXIO;
3091.30Sthorpej		goto bad1;
3101.1Sad	}
3111.1Sad
3121.1Sad	/* Ensure only one open at a time. */
3131.1Sad	switch (fmt) {
3141.1Sad	case S_IFCHR:
3151.1Sad		sc->sc_dk.dk_copenmask |= (1 << part);
3161.1Sad		break;
3171.1Sad	case S_IFBLK:
3181.1Sad		sc->sc_dk.dk_bopenmask |= (1 << part);
3191.1Sad		break;
3201.1Sad	}
3211.1Sad	sc->sc_dk.dk_openmask =
3221.1Sad	    sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask;
3231.1Sad
3241.30Sthorpej	(void) lockmgr(&sc->sc_dk.dk_openlock, LK_RELEASE, NULL);
3251.1Sad	return (0);
3261.30Sthorpej
3271.30Sthorpej bad1:
3281.30Sthorpej	(void) lockmgr(&sc->sc_dk.dk_openlock, LK_RELEASE, NULL);
3291.30Sthorpej	return (error);
3301.1Sad}
3311.1Sad
3321.8Slukem/* ARGSUSED */
3331.29Sthorpejstatic int
3341.37.2.1Syamtldclose(dev_t dev, int flags, int fmt, struct lwp *l)
3351.1Sad{
3361.1Sad	struct ld_softc *sc;
3371.30Sthorpej	int error, part, unit;
3381.1Sad
3391.1Sad	unit = DISKUNIT(dev);
3401.1Sad	part = DISKPART(dev);
3411.1Sad	sc = device_lookup(&ld_cd, unit);
3421.30Sthorpej
3431.30Sthorpej	if ((error = lockmgr(&sc->sc_dk.dk_openlock, LK_EXCLUSIVE, NULL)) != 0)
3441.30Sthorpej		return (error);
3451.1Sad
3461.1Sad	switch (fmt) {
3471.1Sad	case S_IFCHR:
3481.1Sad		sc->sc_dk.dk_copenmask &= ~(1 << part);
3491.1Sad		break;
3501.1Sad	case S_IFBLK:
3511.1Sad		sc->sc_dk.dk_bopenmask &= ~(1 << part);
3521.1Sad		break;
3531.1Sad	}
3541.1Sad	sc->sc_dk.dk_openmask =
3551.1Sad	    sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask;
3561.1Sad
3571.22Sthorpej	if (sc->sc_dk.dk_openmask == 0) {
3581.22Sthorpej		if (sc->sc_flush != NULL && (*sc->sc_flush)(sc) != 0)
3591.1Sad			printf("%s: unable to flush cache\n",
3601.1Sad			    sc->sc_dv.dv_xname);
3611.22Sthorpej		if ((sc->sc_flags & LDF_KLABEL) == 0)
3621.22Sthorpej			sc->sc_flags &= ~LDF_VLABEL;
3631.22Sthorpej	}
3641.1Sad
3651.30Sthorpej	(void) lockmgr(&sc->sc_dk.dk_openlock, LK_RELEASE, NULL);
3661.1Sad	return (0);
3671.1Sad}
3681.1Sad
3691.8Slukem/* ARGSUSED */
3701.29Sthorpejstatic int
3711.1Sadldread(dev_t dev, struct uio *uio, int ioflag)
3721.1Sad{
3731.1Sad
3741.1Sad	return (physio(ldstrategy, NULL, dev, B_READ, ldminphys, uio));
3751.1Sad}
3761.1Sad
3771.8Slukem/* ARGSUSED */
3781.29Sthorpejstatic int
3791.1Sadldwrite(dev_t dev, struct uio *uio, int ioflag)
3801.1Sad{
3811.1Sad
3821.1Sad	return (physio(ldstrategy, NULL, dev, B_WRITE, ldminphys, uio));
3831.1Sad}
3841.1Sad
3851.8Slukem/* ARGSUSED */
3861.29Sthorpejstatic int
3871.37.2.1Syamtldioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct lwp *l)
3881.1Sad{
3891.1Sad	struct ld_softc *sc;
3901.1Sad	int part, unit, error;
3911.4Sfvdl#ifdef __HAVE_OLD_DISKLABEL
3921.6Sitojun	struct disklabel newlabel;
3931.4Sfvdl#endif
3941.6Sitojun	struct disklabel *lp;
3951.1Sad
3961.1Sad	unit = DISKUNIT(dev);
3971.1Sad	part = DISKPART(dev);
3981.1Sad	sc = device_lookup(&ld_cd, unit);
3991.1Sad	error = 0;
4001.1Sad
4011.37.2.2Syamt	error = disk_ioctl(&sc->sc_dk, cmd, addr, flag, l);
4021.37.2.2Syamt	if (error != EPASSTHROUGH)
4031.37.2.2Syamt		return (error);
4041.37.2.2Syamt
4051.1Sad	switch (cmd) {
4061.1Sad	case DIOCGDINFO:
4071.1Sad		memcpy(addr, sc->sc_dk.dk_label, sizeof(struct disklabel));
4081.1Sad		return (0);
4091.1Sad
4101.4Sfvdl#ifdef __HAVE_OLD_DISKLABEL
4111.4Sfvdl	case ODIOCGDINFO:
4121.4Sfvdl		newlabel = *(sc->sc_dk.dk_label);
4131.4Sfvdl		if (newlabel.d_npartitions > OLDMAXPARTITIONS)
4141.5Sfvdl			return ENOTTY;
4151.4Sfvdl		memcpy(addr, &newlabel, sizeof(struct olddisklabel));
4161.4Sfvdl		return (0);
4171.4Sfvdl#endif
4181.4Sfvdl
4191.1Sad	case DIOCGPART:
4201.1Sad		((struct partinfo *)addr)->disklab = sc->sc_dk.dk_label;
4211.1Sad		((struct partinfo *)addr)->part =
4221.1Sad		    &sc->sc_dk.dk_label->d_partitions[part];
4231.1Sad		break;
4241.1Sad
4251.1Sad	case DIOCWDINFO:
4261.1Sad	case DIOCSDINFO:
4271.4Sfvdl#ifdef __HAVE_OLD_DISKLABEL
4281.4Sfvdl	case ODIOCWDINFO:
4291.4Sfvdl	case ODIOCSDINFO:
4301.4Sfvdl
4311.4Sfvdl		if (cmd == ODIOCSDINFO || cmd == ODIOCWDINFO) {
4321.4Sfvdl			memset(&newlabel, 0, sizeof newlabel);
4331.4Sfvdl			memcpy(&newlabel, addr, sizeof (struct olddisklabel));
4341.4Sfvdl			lp = &newlabel;
4351.4Sfvdl		} else
4361.4Sfvdl#endif
4371.4Sfvdl		lp = (struct disklabel *)addr;
4381.4Sfvdl
4391.1Sad		if ((flag & FWRITE) == 0)
4401.1Sad			return (EBADF);
4411.1Sad
4421.30Sthorpej		if ((error = lockmgr(&sc->sc_dk.dk_openlock, LK_EXCLUSIVE,
4431.31Sjdolecek				     NULL)) != 0)
4441.1Sad			return (error);
4451.1Sad		sc->sc_flags |= LDF_LABELLING;
4461.1Sad
4471.1Sad		error = setdisklabel(sc->sc_dk.dk_label,
4481.4Sfvdl		    lp, /*sc->sc_dk.dk_openmask : */0,
4491.1Sad		    sc->sc_dk.dk_cpulabel);
4501.4Sfvdl		if (error == 0 && (cmd == DIOCWDINFO
4511.4Sfvdl#ifdef __HAVE_OLD_DISKLABEL
4521.4Sfvdl		    || cmd == ODIOCWDINFO
4531.4Sfvdl#endif
4541.4Sfvdl		    ))
4551.1Sad			error = writedisklabel(
4561.35Sperry			    MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART),
4571.35Sperry			    ldstrategy, sc->sc_dk.dk_label,
4581.1Sad			    sc->sc_dk.dk_cpulabel);
4591.1Sad
4601.1Sad		sc->sc_flags &= ~LDF_LABELLING;
4611.30Sthorpej		(void) lockmgr(&sc->sc_dk.dk_openlock, LK_RELEASE, NULL);
4621.1Sad		break;
4631.1Sad
4641.22Sthorpej	case DIOCKLABEL:
4651.22Sthorpej		if ((flag & FWRITE) == 0)
4661.22Sthorpej			return (EBADF);
4671.22Sthorpej		if (*(int *)addr)
4681.22Sthorpej			sc->sc_flags |= LDF_KLABEL;
4691.22Sthorpej		else
4701.22Sthorpej			sc->sc_flags &= ~LDF_KLABEL;
4711.22Sthorpej		break;
4721.22Sthorpej
4731.1Sad	case DIOCWLABEL:
4741.1Sad		if ((flag & FWRITE) == 0)
4751.1Sad			return (EBADF);
4761.1Sad		if (*(int *)addr)
4771.1Sad			sc->sc_flags |= LDF_WLABEL;
4781.1Sad		else
4791.1Sad			sc->sc_flags &= ~LDF_WLABEL;
4801.1Sad		break;
4811.1Sad
4821.1Sad	case DIOCGDEFLABEL:
4831.1Sad		ldgetdefaultlabel(sc, (struct disklabel *)addr);
4841.1Sad		break;
4851.4Sfvdl
4861.4Sfvdl#ifdef __HAVE_OLD_DISKLABEL
4871.4Sfvdl	case ODIOCGDEFLABEL:
4881.4Sfvdl		ldgetdefaultlabel(sc, &newlabel);
4891.4Sfvdl		if (newlabel.d_npartitions > OLDMAXPARTITIONS)
4901.5Sfvdl			return ENOTTY;
4911.4Sfvdl		memcpy(addr, &newlabel, sizeof (struct olddisklabel));
4921.4Sfvdl		break;
4931.4Sfvdl#endif
4941.1Sad
4951.32Sthorpej	case DIOCCACHESYNC:
4961.32Sthorpej		/*
4971.32Sthorpej		 * XXX Do we really need to care about having a writable
4981.32Sthorpej		 * file descriptor here?
4991.32Sthorpej		 */
5001.32Sthorpej		if ((flag & FWRITE) == 0)
5011.32Sthorpej			error = EBADF;
5021.32Sthorpej		else if (sc->sc_flush)
5031.32Sthorpej			error = (*sc->sc_flush)(sc);
5041.32Sthorpej		else
5051.32Sthorpej			error = 0;	/* XXX Error out instead? */
5061.32Sthorpej		break;
5071.32Sthorpej
5081.30Sthorpej	case DIOCAWEDGE:
5091.30Sthorpej	    {
5101.30Sthorpej	    	struct dkwedge_info *dkw = (void *) addr;
5111.30Sthorpej
5121.30Sthorpej		if ((flag & FWRITE) == 0)
5131.30Sthorpej			return (EBADF);
5141.30Sthorpej
5151.30Sthorpej		/* If the ioctl happens here, the parent is us. */
5161.30Sthorpej		strcpy(dkw->dkw_parent, sc->sc_dv.dv_xname);
5171.30Sthorpej		return (dkwedge_add(dkw));
5181.30Sthorpej	    }
5191.35Sperry
5201.30Sthorpej	case DIOCDWEDGE:
5211.30Sthorpej	    {
5221.30Sthorpej	    	struct dkwedge_info *dkw = (void *) addr;
5231.30Sthorpej
5241.30Sthorpej		if ((flag & FWRITE) == 0)
5251.30Sthorpej			return (EBADF);
5261.30Sthorpej
5271.30Sthorpej		/* If the ioctl happens here, the parent is us. */
5281.30Sthorpej		strcpy(dkw->dkw_parent, sc->sc_dv.dv_xname);
5291.30Sthorpej		return (dkwedge_del(dkw));
5301.30Sthorpej	    }
5311.35Sperry
5321.30Sthorpej	case DIOCLWEDGES:
5331.30Sthorpej	    {
5341.30Sthorpej	    	struct dkwedge_list *dkwl = (void *) addr;
5351.30Sthorpej
5361.37.2.1Syamt		return (dkwedge_list(&sc->sc_dk, dkwl, l));
5371.30Sthorpej	    }
5381.30Sthorpej
5391.1Sad	default:
5401.1Sad		error = ENOTTY;
5411.1Sad		break;
5421.1Sad	}
5431.1Sad
5441.1Sad	return (error);
5451.1Sad}
5461.1Sad
5471.29Sthorpejstatic void
5481.1Sadldstrategy(struct buf *bp)
5491.1Sad{
5501.1Sad	struct ld_softc *sc;
5511.23Sthorpej	struct disklabel *lp;
5521.23Sthorpej	daddr_t blkno;
5531.23Sthorpej	int s, part;
5541.1Sad
5551.1Sad	sc = device_lookup(&ld_cd, DISKUNIT(bp->b_dev));
5561.23Sthorpej	part = DISKPART(bp->b_dev);
5571.1Sad
5581.7Sad	if ((sc->sc_flags & LDF_DETACH) != 0) {
5591.2Sad		bp->b_error = EIO;
5601.23Sthorpej		goto bad;
5611.2Sad	}
5621.2Sad
5631.1Sad	lp = sc->sc_dk.dk_label;
5641.1Sad
5651.1Sad	/*
5661.1Sad	 * The transfer must be a whole number of blocks and the offset must
5671.1Sad	 * not be negative.
5681.1Sad	 */
5691.1Sad	if ((bp->b_bcount % lp->d_secsize) != 0 || bp->b_blkno < 0) {
5701.23Sthorpej		bp->b_error = EINVAL;
5711.23Sthorpej		goto bad;
5721.1Sad	}
5731.1Sad
5741.23Sthorpej	/* If it's a null transfer, return immediately. */
5751.23Sthorpej	if (bp->b_bcount == 0)
5761.23Sthorpej		goto done;
5771.1Sad
5781.1Sad	/*
5791.1Sad	 * Do bounds checking and adjust the transfer.  If error, process.
5801.1Sad	 * If past the end of partition, just return.
5811.1Sad	 */
5821.1Sad	if (part != RAW_PART &&
5831.20Sthorpej	    bounds_check_with_label(&sc->sc_dk, bp,
5841.1Sad	    (sc->sc_flags & (LDF_WLABEL | LDF_LABELLING)) != 0) <= 0) {
5851.23Sthorpej		goto done;
5861.1Sad	}
5871.1Sad
5881.1Sad	/*
5891.23Sthorpej	 * Convert the block number to absolute and put it in terms
5901.23Sthorpej	 * of the device's logical block size.
5911.1Sad	 */
5921.23Sthorpej	if (lp->d_secsize == DEV_BSIZE)
5931.23Sthorpej		blkno = bp->b_blkno;
5941.23Sthorpej	else if (lp->d_secsize > DEV_BSIZE)
5951.23Sthorpej		blkno = bp->b_blkno / (lp->d_secsize / DEV_BSIZE);
5961.1Sad	else
5971.23Sthorpej		blkno = bp->b_blkno * (DEV_BSIZE / lp->d_secsize);
5981.1Sad
5991.11Sthorpej	if (part != RAW_PART)
6001.23Sthorpej		blkno += lp->d_partitions[part].p_offset;
6011.23Sthorpej
6021.23Sthorpej	bp->b_rawblkno = blkno;
6031.1Sad
6041.1Sad	s = splbio();
6051.37.2.2Syamt	ldstart(sc, bp);
6061.1Sad	splx(s);
6071.23Sthorpej	return;
6081.23Sthorpej
6091.23Sthorpej bad:
6101.23Sthorpej	bp->b_flags |= B_ERROR;
6111.23Sthorpej done:
6121.23Sthorpej	bp->b_resid = bp->b_bcount;
6131.23Sthorpej	biodone(bp);
6141.23Sthorpej}
6151.23Sthorpej
6161.23Sthorpejstatic void
6171.37.2.2Syamtldstart(struct ld_softc *sc, struct buf *bp)
6181.23Sthorpej{
6191.23Sthorpej	int error;
6201.1Sad
6211.37.2.2Syamt	mutex_enter(&sc->sc_mutex);
6221.37.2.2Syamt
6231.37.2.2Syamt	if (bp != NULL)
6241.37.2.2Syamt		BUFQ_PUT(sc->sc_bufq, bp);
6251.37.2.2Syamt
6261.23Sthorpej	while (sc->sc_queuecnt < sc->sc_maxqueuecnt) {
6271.23Sthorpej		/* See if there is work to do. */
6281.37.2.1Syamt		if ((bp = BUFQ_PEEK(sc->sc_bufq)) == NULL)
6291.23Sthorpej			break;
6301.23Sthorpej
6311.23Sthorpej		disk_busy(&sc->sc_dk);
6321.23Sthorpej		sc->sc_queuecnt++;
6331.23Sthorpej
6341.23Sthorpej		if (__predict_true((error = (*sc->sc_start)(sc, bp)) == 0)) {
6351.23Sthorpej			/*
6361.23Sthorpej			 * The back-end is running the job; remove it from
6371.23Sthorpej			 * the queue.
6381.23Sthorpej			 */
6391.37.2.1Syamt			(void) BUFQ_GET(sc->sc_bufq);
6401.23Sthorpej		} else  {
6411.23Sthorpej			disk_unbusy(&sc->sc_dk, 0, (bp->b_flags & B_READ));
6421.23Sthorpej			sc->sc_queuecnt--;
6431.23Sthorpej			if (error == EAGAIN) {
6441.23Sthorpej				/*
6451.23Sthorpej				 * Temporary resource shortage in the
6461.23Sthorpej				 * back-end; just defer the job until
6471.23Sthorpej				 * later.
6481.23Sthorpej				 *
6491.23Sthorpej				 * XXX We might consider a watchdog timer
6501.23Sthorpej				 * XXX to make sure we are kicked into action.
6511.23Sthorpej				 */
6521.23Sthorpej				break;
6531.23Sthorpej			} else {
6541.37.2.1Syamt				(void) BUFQ_GET(sc->sc_bufq);
6551.23Sthorpej				bp->b_error = error;
6561.23Sthorpej				bp->b_flags |= B_ERROR;
6571.23Sthorpej				bp->b_resid = bp->b_bcount;
6581.37.2.2Syamt				mutex_exit(&sc->sc_mutex);
6591.23Sthorpej				biodone(bp);
6601.37.2.2Syamt				mutex_enter(&sc->sc_mutex);
6611.23Sthorpej			}
6621.23Sthorpej		}
6631.1Sad	}
6641.37.2.2Syamt
6651.37.2.2Syamt	mutex_exit(&sc->sc_mutex);
6661.1Sad}
6671.1Sad
6681.1Sadvoid
6691.1Sadlddone(struct ld_softc *sc, struct buf *bp)
6701.1Sad{
6711.1Sad
6721.1Sad	if ((bp->b_flags & B_ERROR) != 0) {
6731.1Sad		diskerr(bp, "ld", "error", LOG_PRINTF, 0, sc->sc_dk.dk_label);
6741.1Sad		printf("\n");
6751.1Sad	}
6761.1Sad
6771.18Smrg	disk_unbusy(&sc->sc_dk, bp->b_bcount - bp->b_resid,
6781.18Smrg	    (bp->b_flags & B_READ));
6791.1Sad#if NRND > 0
6801.1Sad	rnd_add_uint32(&sc->sc_rnd_source, bp->b_rawblkno);
6811.1Sad#endif
6821.1Sad	biodone(bp);
6831.1Sad
6841.37.2.2Syamt	mutex_enter(&sc->sc_mutex);
6851.7Sad	if (--sc->sc_queuecnt <= sc->sc_maxqueuecnt) {
6861.24Sthorpej		if ((sc->sc_flags & LDF_DRAIN) != 0) {
6871.24Sthorpej			sc->sc_flags &= ~LDF_DRAIN;
6881.7Sad			wakeup(&sc->sc_queuecnt);
6891.24Sthorpej		}
6901.37.2.2Syamt		mutex_exit(&sc->sc_mutex);
6911.37.2.2Syamt		ldstart(sc, NULL);
6921.37.2.2Syamt	} else
6931.37.2.2Syamt		mutex_exit(&sc->sc_mutex);
6941.1Sad}
6951.1Sad
6961.29Sthorpejstatic int
6971.1Sadldsize(dev_t dev)
6981.1Sad{
6991.1Sad	struct ld_softc *sc;
7001.1Sad	int part, unit, omask, size;
7011.1Sad
7021.1Sad	unit = DISKUNIT(dev);
7031.1Sad	if ((sc = device_lookup(&ld_cd, unit)) == NULL)
7041.1Sad		return (ENODEV);
7051.1Sad	if ((sc->sc_flags & LDF_ENABLED) == 0)
7061.1Sad		return (ENODEV);
7071.1Sad	part = DISKPART(dev);
7081.1Sad
7091.1Sad	omask = sc->sc_dk.dk_openmask & (1 << part);
7101.1Sad
7111.1Sad	if (omask == 0 && ldopen(dev, 0, S_IFBLK, NULL) != 0)
7121.1Sad		return (-1);
7131.1Sad	else if (sc->sc_dk.dk_label->d_partitions[part].p_fstype != FS_SWAP)
7141.1Sad		size = -1;
7151.1Sad	else
7161.1Sad		size = sc->sc_dk.dk_label->d_partitions[part].p_size *
7171.1Sad		    (sc->sc_dk.dk_label->d_secsize / DEV_BSIZE);
7181.1Sad	if (omask == 0 && ldclose(dev, 0, S_IFBLK, NULL) != 0)
7191.1Sad		return (-1);
7201.1Sad
7211.1Sad	return (size);
7221.1Sad}
7231.1Sad
7241.1Sad/*
7251.1Sad * Load the label information from the specified device.
7261.1Sad */
7271.1Sadstatic void
7281.1Sadldgetdisklabel(struct ld_softc *sc)
7291.1Sad{
7301.1Sad	const char *errstring;
7311.1Sad
7321.1Sad	ldgetdefaultlabel(sc, sc->sc_dk.dk_label);
7331.1Sad
7341.1Sad	/* Call the generic disklabel extraction routine. */
7351.37.2.1Syamt	errstring = readdisklabel(MAKEDISKDEV(0, device_unit(&sc->sc_dv),
7361.37.2.1Syamt	    RAW_PART), ldstrategy, sc->sc_dk.dk_label, sc->sc_dk.dk_cpulabel);
7371.1Sad	if (errstring != NULL)
7381.1Sad		printf("%s: %s\n", sc->sc_dv.dv_xname, errstring);
7391.22Sthorpej
7401.22Sthorpej	/* In-core label now valid. */
7411.22Sthorpej	sc->sc_flags |= LDF_VLABEL;
7421.1Sad}
7431.1Sad
7441.1Sad/*
7451.1Sad * Construct a ficticious label.
7461.1Sad */
7471.1Sadstatic void
7481.1Sadldgetdefaultlabel(struct ld_softc *sc, struct disklabel *lp)
7491.1Sad{
7501.1Sad
7511.1Sad	memset(lp, 0, sizeof(struct disklabel));
7521.1Sad
7531.1Sad	lp->d_secsize = sc->sc_secsize;
7541.1Sad	lp->d_ntracks = sc->sc_nheads;
7551.1Sad	lp->d_nsectors = sc->sc_nsectors;
7561.1Sad	lp->d_ncylinders = sc->sc_ncylinders;
7571.1Sad	lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
7581.1Sad	lp->d_type = DTYPE_LD;
7591.21Sitojun	strlcpy(lp->d_typename, "unknown", sizeof(lp->d_typename));
7601.21Sitojun	strlcpy(lp->d_packname, "fictitious", sizeof(lp->d_packname));
7611.1Sad	lp->d_secperunit = sc->sc_secperunit;
7621.1Sad	lp->d_rpm = 7200;
7631.1Sad	lp->d_interleave = 1;
7641.1Sad	lp->d_flags = 0;
7651.1Sad
7661.1Sad	lp->d_partitions[RAW_PART].p_offset = 0;
7671.1Sad	lp->d_partitions[RAW_PART].p_size =
7681.1Sad	    lp->d_secperunit * (lp->d_secsize / DEV_BSIZE);
7691.1Sad	lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED;
7701.1Sad	lp->d_npartitions = RAW_PART + 1;
7711.1Sad
7721.1Sad	lp->d_magic = DISKMAGIC;
7731.1Sad	lp->d_magic2 = DISKMAGIC;
7741.1Sad	lp->d_checksum = dkcksum(lp);
7751.1Sad}
7761.1Sad
7771.1Sad/*
7781.1Sad * Take a dump.
7791.1Sad */
7801.29Sthorpejstatic int
7811.1Sadlddump(dev_t dev, daddr_t blkno, caddr_t va, size_t size)
7821.1Sad{
7831.1Sad	struct ld_softc *sc;
7841.1Sad	struct disklabel *lp;
7851.1Sad	int unit, part, nsects, sectoff, towrt, nblk, maxblkcnt, rv;
7861.1Sad	static int dumping;
7871.1Sad
7881.1Sad	unit = DISKUNIT(dev);
7891.1Sad	if ((sc = device_lookup(&ld_cd, unit)) == NULL)
7901.1Sad		return (ENXIO);
7911.1Sad	if ((sc->sc_flags & LDF_ENABLED) == 0)
7921.1Sad		return (ENODEV);
7931.3Sad	if (sc->sc_dump == NULL)
7941.3Sad		return (ENXIO);
7951.3Sad
7961.3Sad	/* Check if recursive dump; if so, punt. */
7971.3Sad	if (dumping)
7981.3Sad		return (EFAULT);
7991.3Sad	dumping = 1;
8001.1Sad
8011.1Sad	/* Convert to disk sectors.  Request must be a multiple of size. */
8021.3Sad	part = DISKPART(dev);
8031.1Sad	lp = sc->sc_dk.dk_label;
8041.1Sad	if ((size % lp->d_secsize) != 0)
8051.1Sad		return (EFAULT);
8061.1Sad	towrt = size / lp->d_secsize;
8071.1Sad	blkno = dbtob(blkno) / lp->d_secsize;	/* blkno in DEV_BSIZE units */
8081.1Sad
8091.1Sad	nsects = lp->d_partitions[part].p_size;
8101.1Sad	sectoff = lp->d_partitions[part].p_offset;
8111.1Sad
8121.1Sad	/* Check transfer bounds against partition size. */
8131.1Sad	if ((blkno < 0) || ((blkno + towrt) > nsects))
8141.1Sad		return (EINVAL);
8151.1Sad
8161.1Sad	/* Offset block number to start of partition. */
8171.1Sad	blkno += sectoff;
8181.1Sad
8191.1Sad	/* Start dumping and return when done. */
8201.3Sad	maxblkcnt = sc->sc_maxxfer / sc->sc_secsize - 1;
8211.1Sad	while (towrt > 0) {
8221.3Sad		nblk = min(maxblkcnt, towrt);
8231.1Sad
8241.1Sad		if ((rv = (*sc->sc_dump)(sc, va, blkno, nblk)) != 0)
8251.1Sad			return (rv);
8261.1Sad
8271.1Sad		towrt -= nblk;
8281.1Sad		blkno += nblk;
8291.1Sad		va += nblk * sc->sc_secsize;
8301.1Sad	}
8311.1Sad
8321.1Sad	dumping = 0;
8331.1Sad	return (0);
8341.1Sad}
8351.1Sad
8361.1Sad/*
8371.1Sad * Adjust the size of a transfer.
8381.1Sad */
8391.1Sadstatic void
8401.1Sadldminphys(struct buf *bp)
8411.1Sad{
8421.1Sad	struct ld_softc *sc;
8431.1Sad
8441.1Sad	sc = device_lookup(&ld_cd, DISKUNIT(bp->b_dev));
8451.1Sad
8461.1Sad	if (bp->b_bcount > sc->sc_maxxfer)
8471.1Sad		bp->b_bcount = sc->sc_maxxfer;
8481.1Sad	minphys(bp);
8491.1Sad}
8501.37.2.2Syamt
8511.37.2.2Syamtstatic void
8521.37.2.2Syamtld_set_properties(struct ld_softc *ld)
8531.37.2.2Syamt{
8541.37.2.2Syamt	prop_dictionary_t disk_info, odisk_info, geom;
8551.37.2.2Syamt
8561.37.2.2Syamt	disk_info = prop_dictionary_create();
8571.37.2.2Syamt
8581.37.2.2Syamt	geom = prop_dictionary_create();
8591.37.2.2Syamt
8601.37.2.2Syamt	prop_dictionary_set_uint64(geom, "sectors-per-unit",
8611.37.2.2Syamt	    ld->sc_secperunit);
8621.37.2.2Syamt
8631.37.2.2Syamt	prop_dictionary_set_uint32(geom, "sector-size",
8641.37.2.2Syamt	    ld->sc_secsize);
8651.37.2.2Syamt
8661.37.2.2Syamt	prop_dictionary_set_uint16(geom, "sectors-per-track",
8671.37.2.2Syamt	    ld->sc_nsectors);
8681.37.2.2Syamt
8691.37.2.2Syamt	prop_dictionary_set_uint16(geom, "tracks-per-cylinder",
8701.37.2.2Syamt	    ld->sc_nheads);
8711.37.2.2Syamt
8721.37.2.2Syamt	prop_dictionary_set_uint64(geom, "cylinders-per-unit",
8731.37.2.2Syamt	    ld->sc_ncylinders);
8741.37.2.2Syamt
8751.37.2.2Syamt	prop_dictionary_set(disk_info, "geometry", geom);
8761.37.2.2Syamt	prop_object_release(geom);
8771.37.2.2Syamt
8781.37.2.2Syamt	prop_dictionary_set(device_properties(&ld->sc_dv),
8791.37.2.2Syamt	    "disk-info", disk_info);
8801.37.2.2Syamt
8811.37.2.2Syamt	/*
8821.37.2.2Syamt	 * Don't release disk_info here; we keep a reference to it.
8831.37.2.2Syamt	 * disk_detach() will release it when we go away.
8841.37.2.2Syamt	 */
8851.37.2.2Syamt
8861.37.2.2Syamt	odisk_info = ld->sc_dk.dk_info;
8871.37.2.2Syamt	ld->sc_dk.dk_info = disk_info;
8881.37.2.2Syamt	if (odisk_info)
8891.37.2.2Syamt		prop_object_release(odisk_info);
8901.37.2.2Syamt}
8911.37.2.2Syamt
8921.37.2.2Syamtstatic void
8931.37.2.2Syamtld_config_interrupts (struct device *d)
8941.37.2.2Syamt{
8951.37.2.2Syamt	struct ld_softc *sc = (struct ld_softc *)d;
8961.37.2.2Syamt	dkwedge_discover(&sc->sc_dk);
8971.37.2.2Syamt}
898