ld.c revision 1.37.2.1
11.37.2.1Syamt/*	$NetBSD: ld.c,v 1.37.2.1 2006/06/21 15:02:12 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.1Syamt__KERNEL_RCSID(0, "$NetBSD: ld.c,v 1.37.2.1 2006/06/21 15:02:12 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.1Sad#if NRND > 0
671.1Sad#include <sys/rnd.h>
681.1Sad#endif
691.1Sad
701.1Sad#include <dev/ldvar.h>
711.1Sad
721.1Sadstatic void	ldgetdefaultlabel(struct ld_softc *, struct disklabel *);
731.1Sadstatic void	ldgetdisklabel(struct ld_softc *);
741.1Sadstatic void	ldminphys(struct buf *bp);
751.1Sadstatic void	ldshutdown(void *);
761.23Sthorpejstatic void	ldstart(struct ld_softc *);
771.1Sad
781.1Sadextern struct	cfdriver ld_cd;
791.1Sad
801.29Sthorpejstatic dev_type_open(ldopen);
811.29Sthorpejstatic dev_type_close(ldclose);
821.29Sthorpejstatic dev_type_read(ldread);
831.29Sthorpejstatic dev_type_write(ldwrite);
841.29Sthorpejstatic dev_type_ioctl(ldioctl);
851.29Sthorpejstatic dev_type_strategy(ldstrategy);
861.29Sthorpejstatic dev_type_dump(lddump);
871.29Sthorpejstatic dev_type_size(ldsize);
881.16Sgehenna
891.16Sgehennaconst struct bdevsw ld_bdevsw = {
901.16Sgehenna	ldopen, ldclose, ldstrategy, ldioctl, lddump, ldsize, D_DISK
911.16Sgehenna};
921.16Sgehenna
931.16Sgehennaconst struct cdevsw ld_cdevsw = {
941.16Sgehenna	ldopen, ldclose, ldread, ldwrite, ldioctl,
951.17Sjdolecek	nostop, notty, nopoll, nommap, nokqfilter, D_DISK
961.16Sgehenna};
971.16Sgehenna
981.30Sthorpejstatic struct	dkdriver lddkdriver = { ldstrategy, ldminphys };
991.1Sadstatic void	*ld_sdh;
1001.1Sad
1011.1Sadvoid
1021.1Sadldattach(struct ld_softc *sc)
1031.1Sad{
1041.37Schristos	char tbuf[9];
1051.1Sad
1061.7Sad	if ((sc->sc_flags & LDF_ENABLED) == 0) {
1071.34Sbriggs		aprint_normal("%s: disabled\n", sc->sc_dv.dv_xname);
1081.7Sad		return;
1091.7Sad	}
1101.7Sad
1111.1Sad	/* Initialise and attach the disk structure. */
1121.1Sad	sc->sc_dk.dk_driver = &lddkdriver;
1131.1Sad	sc->sc_dk.dk_name = sc->sc_dv.dv_xname;
1141.1Sad	disk_attach(&sc->sc_dk);
1151.1Sad
1161.1Sad	if (sc->sc_maxxfer > MAXPHYS)
1171.1Sad		sc->sc_maxxfer = MAXPHYS;
1181.9Sad
1191.19Sthorpej	/* Build synthetic geometry if necessary. */
1201.19Sthorpej	if (sc->sc_nheads == 0 || sc->sc_nsectors == 0 ||
1211.19Sthorpej	    sc->sc_ncylinders == 0) {
1221.28Sdbj		uint64_t ncyl;
1231.28Sdbj
1241.19Sthorpej		if (sc->sc_secperunit <= 528 * 2048)		/* 528MB */
1251.19Sthorpej			sc->sc_nheads = 16;
1261.19Sthorpej		else if (sc->sc_secperunit <= 1024 * 2048)	/* 1GB */
1271.19Sthorpej			sc->sc_nheads = 32;
1281.19Sthorpej		else if (sc->sc_secperunit <= 21504 * 2048)	/* 21GB */
1291.19Sthorpej			sc->sc_nheads = 64;
1301.19Sthorpej		else if (sc->sc_secperunit <= 43008 * 2048)	/* 42GB */
1311.19Sthorpej			sc->sc_nheads = 128;
1321.19Sthorpej		else
1331.19Sthorpej			sc->sc_nheads = 255;
1341.19Sthorpej
1351.19Sthorpej		sc->sc_nsectors = 63;
1361.28Sdbj		sc->sc_ncylinders = INT_MAX;
1371.35Sperry		ncyl = sc->sc_secperunit /
1381.19Sthorpej		    (sc->sc_nheads * sc->sc_nsectors);
1391.28Sdbj		if (ncyl < INT_MAX)
1401.28Sdbj			sc->sc_ncylinders = (int)ncyl;
1411.19Sthorpej	}
1421.1Sad
1431.37Schristos	format_bytes(tbuf, sizeof(tbuf), sc->sc_secperunit *
1441.1Sad	    sc->sc_secsize);
1451.34Sbriggs	aprint_normal("%s: %s, %d cyl, %d head, %d sec, %d bytes/sect x %"PRIu64" sectors\n",
1461.37Schristos	    sc->sc_dv.dv_xname, tbuf, sc->sc_ncylinders, sc->sc_nheads,
1471.1Sad	    sc->sc_nsectors, sc->sc_secsize, sc->sc_secperunit);
1481.1Sad
1491.1Sad#if NRND > 0
1501.1Sad	/* Attach the device into the rnd source list. */
1511.1Sad	rnd_attach_source(&sc->sc_rnd_source, sc->sc_dv.dv_xname,
1521.1Sad	    RND_TYPE_DISK, 0);
1531.1Sad#endif
1541.1Sad
1551.1Sad	/* Set the `shutdownhook'. */
1561.1Sad	if (ld_sdh == NULL)
1571.1Sad		ld_sdh = shutdownhook_establish(ldshutdown, NULL);
1581.37.2.1Syamt	bufq_alloc(&sc->sc_bufq, BUFQ_DISK_DEFAULT_STRAT, BUFQ_SORT_RAWBLOCK);
1591.30Sthorpej
1601.30Sthorpej	/* Discover wedges on this disk. */
1611.30Sthorpej	dkwedge_discover(&sc->sc_dk);
1621.1Sad}
1631.1Sad
1641.3Sadint
1651.37Schristosldadjqparam(struct ld_softc *sc, int xmax)
1661.3Sad{
1671.24Sthorpej	int s;
1681.7Sad
1691.7Sad	s = splbio();
1701.37Schristos	sc->sc_maxqueuecnt = xmax;
1711.7Sad	splx(s);
1721.7Sad
1731.24Sthorpej	return (0);
1741.7Sad}
1751.7Sad
1761.7Sadint
1771.7Sadldbegindetach(struct ld_softc *sc, int flags)
1781.7Sad{
1791.24Sthorpej	int s, rv = 0;
1801.7Sad
1811.7Sad	if ((sc->sc_flags & LDF_ENABLED) == 0)
1821.7Sad		return (0);
1831.3Sad
1841.3Sad	if ((flags & DETACH_FORCE) == 0 && sc->sc_dk.dk_openmask != 0)
1851.3Sad		return (EBUSY);
1861.3Sad
1871.3Sad	s = splbio();
1881.24Sthorpej	sc->sc_maxqueuecnt = 0;
1891.7Sad	sc->sc_flags |= LDF_DETACH;
1901.24Sthorpej	while (sc->sc_queuecnt > 0) {
1911.24Sthorpej		sc->sc_flags |= LDF_DRAIN;
1921.24Sthorpej		rv = tsleep(&sc->sc_queuecnt, PRIBIO, "lddrn", 0);
1931.24Sthorpej		if (rv)
1941.24Sthorpej			break;
1951.24Sthorpej	}
1961.3Sad	splx(s);
1971.7Sad
1981.7Sad	return (rv);
1991.3Sad}
2001.3Sad
2011.2Sadvoid
2021.7Sadldenddetach(struct ld_softc *sc)
2031.2Sad{
2041.13Sdrochner	int s, bmaj, cmaj, i, mn;
2051.2Sad
2061.7Sad	if ((sc->sc_flags & LDF_ENABLED) == 0)
2071.7Sad		return;
2081.7Sad
2091.2Sad	/* Wait for commands queued with the hardware to complete. */
2101.2Sad	if (sc->sc_queuecnt != 0)
2111.7Sad		if (tsleep(&sc->sc_queuecnt, PRIBIO, "lddtch", 30 * hz))
2121.7Sad			printf("%s: not drained\n", sc->sc_dv.dv_xname);
2131.2Sad
2141.2Sad	/* Locate the major numbers. */
2151.16Sgehenna	bmaj = bdevsw_lookup_major(&ld_bdevsw);
2161.16Sgehenna	cmaj = cdevsw_lookup_major(&ld_cdevsw);
2171.2Sad
2181.2Sad	/* Kill off any queued buffers. */
2191.2Sad	s = splbio();
2201.37.2.1Syamt	bufq_drain(sc->sc_bufq);
2211.36Syamt	splx(s);
2221.36Syamt
2231.37.2.1Syamt	bufq_free(sc->sc_bufq);
2241.2Sad
2251.2Sad	/* Nuke the vnodes for any open instances. */
2261.13Sdrochner	for (i = 0; i < MAXPARTITIONS; i++) {
2271.37.2.1Syamt		mn = DISKMINOR(device_unit(&sc->sc_dv), i);
2281.13Sdrochner		vdevgone(bmaj, mn, mn, VBLK);
2291.13Sdrochner		vdevgone(cmaj, mn, mn, VCHR);
2301.13Sdrochner	}
2311.13Sdrochner
2321.30Sthorpej	/* Delete all of our wedges. */
2331.30Sthorpej	dkwedge_delall(&sc->sc_dk);
2341.30Sthorpej
2351.2Sad	/* Detach from the disk list. */
2361.2Sad	disk_detach(&sc->sc_dk);
2371.2Sad
2381.2Sad#if NRND > 0
2391.2Sad	/* Unhook the entropy source. */
2401.2Sad	rnd_detach_source(&sc->sc_rnd_source);
2411.2Sad#endif
2421.2Sad
2431.24Sthorpej	/*
2441.24Sthorpej	 * XXX We can't really flush the cache here, beceause the
2451.24Sthorpej	 * XXX device may already be non-existent from the controller's
2461.24Sthorpej	 * XXX perspective.
2471.24Sthorpej	 */
2481.24Sthorpej#if 0
2491.2Sad	/* Flush the device's cache. */
2501.2Sad	if (sc->sc_flush != NULL)
2511.2Sad		if ((*sc->sc_flush)(sc) != 0)
2521.2Sad			printf("%s: unable to flush cache\n",
2531.2Sad			    sc->sc_dv.dv_xname);
2541.24Sthorpej#endif
2551.2Sad}
2561.2Sad
2571.8Slukem/* ARGSUSED */
2581.1Sadstatic void
2591.1Sadldshutdown(void *cookie)
2601.1Sad{
2611.1Sad	struct ld_softc *sc;
2621.1Sad	int i;
2631.1Sad
2641.1Sad	for (i = 0; i < ld_cd.cd_ndevs; i++) {
2651.1Sad		if ((sc = device_lookup(&ld_cd, i)) == NULL)
2661.1Sad			continue;
2671.1Sad		if (sc->sc_flush != NULL && (*sc->sc_flush)(sc) != 0)
2681.1Sad			printf("%s: unable to flush cache\n",
2691.1Sad			    sc->sc_dv.dv_xname);
2701.1Sad	}
2711.1Sad}
2721.1Sad
2731.8Slukem/* ARGSUSED */
2741.29Sthorpejstatic int
2751.37.2.1Syamtldopen(dev_t dev, int flags, int fmt, struct lwp *l)
2761.1Sad{
2771.1Sad	struct ld_softc *sc;
2781.30Sthorpej	int error, unit, part;
2791.1Sad
2801.1Sad	unit = DISKUNIT(dev);
2811.30Sthorpej	if ((sc = device_lookup(&ld_cd, unit)) == NULL)
2821.1Sad		return (ENXIO);
2831.1Sad	if ((sc->sc_flags & LDF_ENABLED) == 0)
2841.1Sad		return (ENODEV);
2851.1Sad	part = DISKPART(dev);
2861.30Sthorpej
2871.30Sthorpej	if ((error = lockmgr(&sc->sc_dk.dk_openlock, LK_EXCLUSIVE, NULL)) != 0)
2881.30Sthorpej		return (error);
2891.1Sad
2901.22Sthorpej	if (sc->sc_dk.dk_openmask == 0) {
2911.22Sthorpej		/* Load the partition info if not already loaded. */
2921.22Sthorpej		if ((sc->sc_flags & LDF_VLABEL) == 0)
2931.22Sthorpej			ldgetdisklabel(sc);
2941.22Sthorpej	}
2951.1Sad
2961.1Sad	/* Check that the partition exists. */
2971.1Sad	if (part != RAW_PART && (part >= sc->sc_dk.dk_label->d_npartitions ||
2981.1Sad	    sc->sc_dk.dk_label->d_partitions[part].p_fstype == FS_UNUSED)) {
2991.30Sthorpej		error = ENXIO;
3001.30Sthorpej		goto bad1;
3011.1Sad	}
3021.1Sad
3031.1Sad	/* Ensure only one open at a time. */
3041.1Sad	switch (fmt) {
3051.1Sad	case S_IFCHR:
3061.1Sad		sc->sc_dk.dk_copenmask |= (1 << part);
3071.1Sad		break;
3081.1Sad	case S_IFBLK:
3091.1Sad		sc->sc_dk.dk_bopenmask |= (1 << part);
3101.1Sad		break;
3111.1Sad	}
3121.1Sad	sc->sc_dk.dk_openmask =
3131.1Sad	    sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask;
3141.1Sad
3151.30Sthorpej	(void) lockmgr(&sc->sc_dk.dk_openlock, LK_RELEASE, NULL);
3161.1Sad	return (0);
3171.30Sthorpej
3181.30Sthorpej bad1:
3191.30Sthorpej	(void) lockmgr(&sc->sc_dk.dk_openlock, LK_RELEASE, NULL);
3201.30Sthorpej	return (error);
3211.1Sad}
3221.1Sad
3231.8Slukem/* ARGSUSED */
3241.29Sthorpejstatic int
3251.37.2.1Syamtldclose(dev_t dev, int flags, int fmt, struct lwp *l)
3261.1Sad{
3271.1Sad	struct ld_softc *sc;
3281.30Sthorpej	int error, part, unit;
3291.1Sad
3301.1Sad	unit = DISKUNIT(dev);
3311.1Sad	part = DISKPART(dev);
3321.1Sad	sc = device_lookup(&ld_cd, unit);
3331.30Sthorpej
3341.30Sthorpej	if ((error = lockmgr(&sc->sc_dk.dk_openlock, LK_EXCLUSIVE, NULL)) != 0)
3351.30Sthorpej		return (error);
3361.1Sad
3371.1Sad	switch (fmt) {
3381.1Sad	case S_IFCHR:
3391.1Sad		sc->sc_dk.dk_copenmask &= ~(1 << part);
3401.1Sad		break;
3411.1Sad	case S_IFBLK:
3421.1Sad		sc->sc_dk.dk_bopenmask &= ~(1 << part);
3431.1Sad		break;
3441.1Sad	}
3451.1Sad	sc->sc_dk.dk_openmask =
3461.1Sad	    sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask;
3471.1Sad
3481.22Sthorpej	if (sc->sc_dk.dk_openmask == 0) {
3491.22Sthorpej		if (sc->sc_flush != NULL && (*sc->sc_flush)(sc) != 0)
3501.1Sad			printf("%s: unable to flush cache\n",
3511.1Sad			    sc->sc_dv.dv_xname);
3521.22Sthorpej		if ((sc->sc_flags & LDF_KLABEL) == 0)
3531.22Sthorpej			sc->sc_flags &= ~LDF_VLABEL;
3541.22Sthorpej	}
3551.1Sad
3561.30Sthorpej	(void) lockmgr(&sc->sc_dk.dk_openlock, LK_RELEASE, NULL);
3571.1Sad	return (0);
3581.1Sad}
3591.1Sad
3601.8Slukem/* ARGSUSED */
3611.29Sthorpejstatic int
3621.1Sadldread(dev_t dev, struct uio *uio, int ioflag)
3631.1Sad{
3641.1Sad
3651.1Sad	return (physio(ldstrategy, NULL, dev, B_READ, ldminphys, uio));
3661.1Sad}
3671.1Sad
3681.8Slukem/* ARGSUSED */
3691.29Sthorpejstatic int
3701.1Sadldwrite(dev_t dev, struct uio *uio, int ioflag)
3711.1Sad{
3721.1Sad
3731.1Sad	return (physio(ldstrategy, NULL, dev, B_WRITE, ldminphys, uio));
3741.1Sad}
3751.1Sad
3761.8Slukem/* ARGSUSED */
3771.29Sthorpejstatic int
3781.37.2.1Syamtldioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct lwp *l)
3791.1Sad{
3801.1Sad	struct ld_softc *sc;
3811.1Sad	int part, unit, error;
3821.4Sfvdl#ifdef __HAVE_OLD_DISKLABEL
3831.6Sitojun	struct disklabel newlabel;
3841.4Sfvdl#endif
3851.6Sitojun	struct disklabel *lp;
3861.1Sad
3871.1Sad	unit = DISKUNIT(dev);
3881.1Sad	part = DISKPART(dev);
3891.1Sad	sc = device_lookup(&ld_cd, unit);
3901.1Sad	error = 0;
3911.1Sad
3921.1Sad	switch (cmd) {
3931.1Sad	case DIOCGDINFO:
3941.1Sad		memcpy(addr, sc->sc_dk.dk_label, sizeof(struct disklabel));
3951.1Sad		return (0);
3961.1Sad
3971.4Sfvdl#ifdef __HAVE_OLD_DISKLABEL
3981.4Sfvdl	case ODIOCGDINFO:
3991.4Sfvdl		newlabel = *(sc->sc_dk.dk_label);
4001.4Sfvdl		if (newlabel.d_npartitions > OLDMAXPARTITIONS)
4011.5Sfvdl			return ENOTTY;
4021.4Sfvdl		memcpy(addr, &newlabel, sizeof(struct olddisklabel));
4031.4Sfvdl		return (0);
4041.4Sfvdl#endif
4051.4Sfvdl
4061.1Sad	case DIOCGPART:
4071.1Sad		((struct partinfo *)addr)->disklab = sc->sc_dk.dk_label;
4081.1Sad		((struct partinfo *)addr)->part =
4091.1Sad		    &sc->sc_dk.dk_label->d_partitions[part];
4101.1Sad		break;
4111.1Sad
4121.1Sad	case DIOCWDINFO:
4131.1Sad	case DIOCSDINFO:
4141.4Sfvdl#ifdef __HAVE_OLD_DISKLABEL
4151.4Sfvdl	case ODIOCWDINFO:
4161.4Sfvdl	case ODIOCSDINFO:
4171.4Sfvdl
4181.4Sfvdl		if (cmd == ODIOCSDINFO || cmd == ODIOCWDINFO) {
4191.4Sfvdl			memset(&newlabel, 0, sizeof newlabel);
4201.4Sfvdl			memcpy(&newlabel, addr, sizeof (struct olddisklabel));
4211.4Sfvdl			lp = &newlabel;
4221.4Sfvdl		} else
4231.4Sfvdl#endif
4241.4Sfvdl		lp = (struct disklabel *)addr;
4251.4Sfvdl
4261.1Sad		if ((flag & FWRITE) == 0)
4271.1Sad			return (EBADF);
4281.1Sad
4291.30Sthorpej		if ((error = lockmgr(&sc->sc_dk.dk_openlock, LK_EXCLUSIVE,
4301.31Sjdolecek				     NULL)) != 0)
4311.1Sad			return (error);
4321.1Sad		sc->sc_flags |= LDF_LABELLING;
4331.1Sad
4341.1Sad		error = setdisklabel(sc->sc_dk.dk_label,
4351.4Sfvdl		    lp, /*sc->sc_dk.dk_openmask : */0,
4361.1Sad		    sc->sc_dk.dk_cpulabel);
4371.4Sfvdl		if (error == 0 && (cmd == DIOCWDINFO
4381.4Sfvdl#ifdef __HAVE_OLD_DISKLABEL
4391.4Sfvdl		    || cmd == ODIOCWDINFO
4401.4Sfvdl#endif
4411.4Sfvdl		    ))
4421.1Sad			error = writedisklabel(
4431.35Sperry			    MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART),
4441.35Sperry			    ldstrategy, sc->sc_dk.dk_label,
4451.1Sad			    sc->sc_dk.dk_cpulabel);
4461.1Sad
4471.1Sad		sc->sc_flags &= ~LDF_LABELLING;
4481.30Sthorpej		(void) lockmgr(&sc->sc_dk.dk_openlock, LK_RELEASE, NULL);
4491.1Sad		break;
4501.1Sad
4511.22Sthorpej	case DIOCKLABEL:
4521.22Sthorpej		if ((flag & FWRITE) == 0)
4531.22Sthorpej			return (EBADF);
4541.22Sthorpej		if (*(int *)addr)
4551.22Sthorpej			sc->sc_flags |= LDF_KLABEL;
4561.22Sthorpej		else
4571.22Sthorpej			sc->sc_flags &= ~LDF_KLABEL;
4581.22Sthorpej		break;
4591.22Sthorpej
4601.1Sad	case DIOCWLABEL:
4611.1Sad		if ((flag & FWRITE) == 0)
4621.1Sad			return (EBADF);
4631.1Sad		if (*(int *)addr)
4641.1Sad			sc->sc_flags |= LDF_WLABEL;
4651.1Sad		else
4661.1Sad			sc->sc_flags &= ~LDF_WLABEL;
4671.1Sad		break;
4681.1Sad
4691.1Sad	case DIOCGDEFLABEL:
4701.1Sad		ldgetdefaultlabel(sc, (struct disklabel *)addr);
4711.1Sad		break;
4721.4Sfvdl
4731.4Sfvdl#ifdef __HAVE_OLD_DISKLABEL
4741.4Sfvdl	case ODIOCGDEFLABEL:
4751.4Sfvdl		ldgetdefaultlabel(sc, &newlabel);
4761.4Sfvdl		if (newlabel.d_npartitions > OLDMAXPARTITIONS)
4771.5Sfvdl			return ENOTTY;
4781.4Sfvdl		memcpy(addr, &newlabel, sizeof (struct olddisklabel));
4791.4Sfvdl		break;
4801.4Sfvdl#endif
4811.1Sad
4821.32Sthorpej	case DIOCCACHESYNC:
4831.32Sthorpej		/*
4841.32Sthorpej		 * XXX Do we really need to care about having a writable
4851.32Sthorpej		 * file descriptor here?
4861.32Sthorpej		 */
4871.32Sthorpej		if ((flag & FWRITE) == 0)
4881.32Sthorpej			error = EBADF;
4891.32Sthorpej		else if (sc->sc_flush)
4901.32Sthorpej			error = (*sc->sc_flush)(sc);
4911.32Sthorpej		else
4921.32Sthorpej			error = 0;	/* XXX Error out instead? */
4931.32Sthorpej		break;
4941.32Sthorpej
4951.30Sthorpej	case DIOCAWEDGE:
4961.30Sthorpej	    {
4971.30Sthorpej	    	struct dkwedge_info *dkw = (void *) addr;
4981.30Sthorpej
4991.30Sthorpej		if ((flag & FWRITE) == 0)
5001.30Sthorpej			return (EBADF);
5011.30Sthorpej
5021.30Sthorpej		/* If the ioctl happens here, the parent is us. */
5031.30Sthorpej		strcpy(dkw->dkw_parent, sc->sc_dv.dv_xname);
5041.30Sthorpej		return (dkwedge_add(dkw));
5051.30Sthorpej	    }
5061.35Sperry
5071.30Sthorpej	case DIOCDWEDGE:
5081.30Sthorpej	    {
5091.30Sthorpej	    	struct dkwedge_info *dkw = (void *) addr;
5101.30Sthorpej
5111.30Sthorpej		if ((flag & FWRITE) == 0)
5121.30Sthorpej			return (EBADF);
5131.30Sthorpej
5141.30Sthorpej		/* If the ioctl happens here, the parent is us. */
5151.30Sthorpej		strcpy(dkw->dkw_parent, sc->sc_dv.dv_xname);
5161.30Sthorpej		return (dkwedge_del(dkw));
5171.30Sthorpej	    }
5181.35Sperry
5191.30Sthorpej	case DIOCLWEDGES:
5201.30Sthorpej	    {
5211.30Sthorpej	    	struct dkwedge_list *dkwl = (void *) addr;
5221.30Sthorpej
5231.37.2.1Syamt		return (dkwedge_list(&sc->sc_dk, dkwl, l));
5241.30Sthorpej	    }
5251.30Sthorpej
5261.1Sad	default:
5271.1Sad		error = ENOTTY;
5281.1Sad		break;
5291.1Sad	}
5301.1Sad
5311.1Sad	return (error);
5321.1Sad}
5331.1Sad
5341.29Sthorpejstatic void
5351.1Sadldstrategy(struct buf *bp)
5361.1Sad{
5371.1Sad	struct ld_softc *sc;
5381.23Sthorpej	struct disklabel *lp;
5391.23Sthorpej	daddr_t blkno;
5401.23Sthorpej	int s, part;
5411.1Sad
5421.1Sad	sc = device_lookup(&ld_cd, DISKUNIT(bp->b_dev));
5431.23Sthorpej	part = DISKPART(bp->b_dev);
5441.1Sad
5451.7Sad	if ((sc->sc_flags & LDF_DETACH) != 0) {
5461.2Sad		bp->b_error = EIO;
5471.23Sthorpej		goto bad;
5481.2Sad	}
5491.2Sad
5501.1Sad	lp = sc->sc_dk.dk_label;
5511.1Sad
5521.1Sad	/*
5531.1Sad	 * The transfer must be a whole number of blocks and the offset must
5541.1Sad	 * not be negative.
5551.1Sad	 */
5561.1Sad	if ((bp->b_bcount % lp->d_secsize) != 0 || bp->b_blkno < 0) {
5571.23Sthorpej		bp->b_error = EINVAL;
5581.23Sthorpej		goto bad;
5591.1Sad	}
5601.1Sad
5611.23Sthorpej	/* If it's a null transfer, return immediately. */
5621.23Sthorpej	if (bp->b_bcount == 0)
5631.23Sthorpej		goto done;
5641.1Sad
5651.1Sad	/*
5661.1Sad	 * Do bounds checking and adjust the transfer.  If error, process.
5671.1Sad	 * If past the end of partition, just return.
5681.1Sad	 */
5691.1Sad	if (part != RAW_PART &&
5701.20Sthorpej	    bounds_check_with_label(&sc->sc_dk, bp,
5711.1Sad	    (sc->sc_flags & (LDF_WLABEL | LDF_LABELLING)) != 0) <= 0) {
5721.23Sthorpej		goto done;
5731.1Sad	}
5741.1Sad
5751.1Sad	/*
5761.23Sthorpej	 * Convert the block number to absolute and put it in terms
5771.23Sthorpej	 * of the device's logical block size.
5781.1Sad	 */
5791.23Sthorpej	if (lp->d_secsize == DEV_BSIZE)
5801.23Sthorpej		blkno = bp->b_blkno;
5811.23Sthorpej	else if (lp->d_secsize > DEV_BSIZE)
5821.23Sthorpej		blkno = bp->b_blkno / (lp->d_secsize / DEV_BSIZE);
5831.1Sad	else
5841.23Sthorpej		blkno = bp->b_blkno * (DEV_BSIZE / lp->d_secsize);
5851.1Sad
5861.11Sthorpej	if (part != RAW_PART)
5871.23Sthorpej		blkno += lp->d_partitions[part].p_offset;
5881.23Sthorpej
5891.23Sthorpej	bp->b_rawblkno = blkno;
5901.1Sad
5911.1Sad	s = splbio();
5921.37.2.1Syamt	BUFQ_PUT(sc->sc_bufq, bp);
5931.23Sthorpej	ldstart(sc);
5941.1Sad	splx(s);
5951.23Sthorpej	return;
5961.23Sthorpej
5971.23Sthorpej bad:
5981.23Sthorpej	bp->b_flags |= B_ERROR;
5991.23Sthorpej done:
6001.23Sthorpej	bp->b_resid = bp->b_bcount;
6011.23Sthorpej	biodone(bp);
6021.23Sthorpej}
6031.23Sthorpej
6041.23Sthorpejstatic void
6051.23Sthorpejldstart(struct ld_softc *sc)
6061.23Sthorpej{
6071.23Sthorpej	struct buf *bp;
6081.23Sthorpej	int error;
6091.1Sad
6101.23Sthorpej	while (sc->sc_queuecnt < sc->sc_maxqueuecnt) {
6111.23Sthorpej		/* See if there is work to do. */
6121.37.2.1Syamt		if ((bp = BUFQ_PEEK(sc->sc_bufq)) == NULL)
6131.23Sthorpej			break;
6141.23Sthorpej
6151.23Sthorpej		disk_busy(&sc->sc_dk);
6161.23Sthorpej		sc->sc_queuecnt++;
6171.23Sthorpej
6181.23Sthorpej		if (__predict_true((error = (*sc->sc_start)(sc, bp)) == 0)) {
6191.23Sthorpej			/*
6201.23Sthorpej			 * The back-end is running the job; remove it from
6211.23Sthorpej			 * the queue.
6221.23Sthorpej			 */
6231.37.2.1Syamt			(void) BUFQ_GET(sc->sc_bufq);
6241.23Sthorpej		} else  {
6251.23Sthorpej			disk_unbusy(&sc->sc_dk, 0, (bp->b_flags & B_READ));
6261.23Sthorpej			sc->sc_queuecnt--;
6271.23Sthorpej			if (error == EAGAIN) {
6281.23Sthorpej				/*
6291.23Sthorpej				 * Temporary resource shortage in the
6301.23Sthorpej				 * back-end; just defer the job until
6311.23Sthorpej				 * later.
6321.23Sthorpej				 *
6331.23Sthorpej				 * XXX We might consider a watchdog timer
6341.23Sthorpej				 * XXX to make sure we are kicked into action.
6351.23Sthorpej				 */
6361.23Sthorpej				break;
6371.23Sthorpej			} else {
6381.37.2.1Syamt				(void) BUFQ_GET(sc->sc_bufq);
6391.23Sthorpej				bp->b_error = error;
6401.23Sthorpej				bp->b_flags |= B_ERROR;
6411.23Sthorpej				bp->b_resid = bp->b_bcount;
6421.23Sthorpej				biodone(bp);
6431.23Sthorpej			}
6441.23Sthorpej		}
6451.1Sad	}
6461.1Sad}
6471.1Sad
6481.1Sadvoid
6491.1Sadlddone(struct ld_softc *sc, struct buf *bp)
6501.1Sad{
6511.1Sad
6521.1Sad	if ((bp->b_flags & B_ERROR) != 0) {
6531.1Sad		diskerr(bp, "ld", "error", LOG_PRINTF, 0, sc->sc_dk.dk_label);
6541.1Sad		printf("\n");
6551.1Sad	}
6561.1Sad
6571.18Smrg	disk_unbusy(&sc->sc_dk, bp->b_bcount - bp->b_resid,
6581.18Smrg	    (bp->b_flags & B_READ));
6591.1Sad#if NRND > 0
6601.1Sad	rnd_add_uint32(&sc->sc_rnd_source, bp->b_rawblkno);
6611.1Sad#endif
6621.1Sad	biodone(bp);
6631.1Sad
6641.7Sad	if (--sc->sc_queuecnt <= sc->sc_maxqueuecnt) {
6651.24Sthorpej		if ((sc->sc_flags & LDF_DRAIN) != 0) {
6661.24Sthorpej			sc->sc_flags &= ~LDF_DRAIN;
6671.7Sad			wakeup(&sc->sc_queuecnt);
6681.24Sthorpej		}
6691.23Sthorpej		ldstart(sc);
6701.1Sad	}
6711.1Sad}
6721.1Sad
6731.29Sthorpejstatic int
6741.1Sadldsize(dev_t dev)
6751.1Sad{
6761.1Sad	struct ld_softc *sc;
6771.1Sad	int part, unit, omask, size;
6781.1Sad
6791.1Sad	unit = DISKUNIT(dev);
6801.1Sad	if ((sc = device_lookup(&ld_cd, unit)) == NULL)
6811.1Sad		return (ENODEV);
6821.1Sad	if ((sc->sc_flags & LDF_ENABLED) == 0)
6831.1Sad		return (ENODEV);
6841.1Sad	part = DISKPART(dev);
6851.1Sad
6861.1Sad	omask = sc->sc_dk.dk_openmask & (1 << part);
6871.1Sad
6881.1Sad	if (omask == 0 && ldopen(dev, 0, S_IFBLK, NULL) != 0)
6891.1Sad		return (-1);
6901.1Sad	else if (sc->sc_dk.dk_label->d_partitions[part].p_fstype != FS_SWAP)
6911.1Sad		size = -1;
6921.1Sad	else
6931.1Sad		size = sc->sc_dk.dk_label->d_partitions[part].p_size *
6941.1Sad		    (sc->sc_dk.dk_label->d_secsize / DEV_BSIZE);
6951.1Sad	if (omask == 0 && ldclose(dev, 0, S_IFBLK, NULL) != 0)
6961.1Sad		return (-1);
6971.1Sad
6981.1Sad	return (size);
6991.1Sad}
7001.1Sad
7011.1Sad/*
7021.1Sad * Load the label information from the specified device.
7031.1Sad */
7041.1Sadstatic void
7051.1Sadldgetdisklabel(struct ld_softc *sc)
7061.1Sad{
7071.1Sad	const char *errstring;
7081.1Sad
7091.1Sad	ldgetdefaultlabel(sc, sc->sc_dk.dk_label);
7101.1Sad
7111.1Sad	/* Call the generic disklabel extraction routine. */
7121.37.2.1Syamt	errstring = readdisklabel(MAKEDISKDEV(0, device_unit(&sc->sc_dv),
7131.37.2.1Syamt	    RAW_PART), ldstrategy, sc->sc_dk.dk_label, sc->sc_dk.dk_cpulabel);
7141.1Sad	if (errstring != NULL)
7151.1Sad		printf("%s: %s\n", sc->sc_dv.dv_xname, errstring);
7161.22Sthorpej
7171.22Sthorpej	/* In-core label now valid. */
7181.22Sthorpej	sc->sc_flags |= LDF_VLABEL;
7191.1Sad}
7201.1Sad
7211.1Sad/*
7221.1Sad * Construct a ficticious label.
7231.1Sad */
7241.1Sadstatic void
7251.1Sadldgetdefaultlabel(struct ld_softc *sc, struct disklabel *lp)
7261.1Sad{
7271.1Sad
7281.1Sad	memset(lp, 0, sizeof(struct disklabel));
7291.1Sad
7301.1Sad	lp->d_secsize = sc->sc_secsize;
7311.1Sad	lp->d_ntracks = sc->sc_nheads;
7321.1Sad	lp->d_nsectors = sc->sc_nsectors;
7331.1Sad	lp->d_ncylinders = sc->sc_ncylinders;
7341.1Sad	lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors;
7351.1Sad	lp->d_type = DTYPE_LD;
7361.21Sitojun	strlcpy(lp->d_typename, "unknown", sizeof(lp->d_typename));
7371.21Sitojun	strlcpy(lp->d_packname, "fictitious", sizeof(lp->d_packname));
7381.1Sad	lp->d_secperunit = sc->sc_secperunit;
7391.1Sad	lp->d_rpm = 7200;
7401.1Sad	lp->d_interleave = 1;
7411.1Sad	lp->d_flags = 0;
7421.1Sad
7431.1Sad	lp->d_partitions[RAW_PART].p_offset = 0;
7441.1Sad	lp->d_partitions[RAW_PART].p_size =
7451.1Sad	    lp->d_secperunit * (lp->d_secsize / DEV_BSIZE);
7461.1Sad	lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED;
7471.1Sad	lp->d_npartitions = RAW_PART + 1;
7481.1Sad
7491.1Sad	lp->d_magic = DISKMAGIC;
7501.1Sad	lp->d_magic2 = DISKMAGIC;
7511.1Sad	lp->d_checksum = dkcksum(lp);
7521.1Sad}
7531.1Sad
7541.1Sad/*
7551.1Sad * Take a dump.
7561.1Sad */
7571.29Sthorpejstatic int
7581.1Sadlddump(dev_t dev, daddr_t blkno, caddr_t va, size_t size)
7591.1Sad{
7601.1Sad	struct ld_softc *sc;
7611.1Sad	struct disklabel *lp;
7621.1Sad	int unit, part, nsects, sectoff, towrt, nblk, maxblkcnt, rv;
7631.1Sad	static int dumping;
7641.1Sad
7651.1Sad	unit = DISKUNIT(dev);
7661.1Sad	if ((sc = device_lookup(&ld_cd, unit)) == NULL)
7671.1Sad		return (ENXIO);
7681.1Sad	if ((sc->sc_flags & LDF_ENABLED) == 0)
7691.1Sad		return (ENODEV);
7701.3Sad	if (sc->sc_dump == NULL)
7711.3Sad		return (ENXIO);
7721.3Sad
7731.3Sad	/* Check if recursive dump; if so, punt. */
7741.3Sad	if (dumping)
7751.3Sad		return (EFAULT);
7761.3Sad	dumping = 1;
7771.1Sad
7781.1Sad	/* Convert to disk sectors.  Request must be a multiple of size. */
7791.3Sad	part = DISKPART(dev);
7801.1Sad	lp = sc->sc_dk.dk_label;
7811.1Sad	if ((size % lp->d_secsize) != 0)
7821.1Sad		return (EFAULT);
7831.1Sad	towrt = size / lp->d_secsize;
7841.1Sad	blkno = dbtob(blkno) / lp->d_secsize;	/* blkno in DEV_BSIZE units */
7851.1Sad
7861.1Sad	nsects = lp->d_partitions[part].p_size;
7871.1Sad	sectoff = lp->d_partitions[part].p_offset;
7881.1Sad
7891.1Sad	/* Check transfer bounds against partition size. */
7901.1Sad	if ((blkno < 0) || ((blkno + towrt) > nsects))
7911.1Sad		return (EINVAL);
7921.1Sad
7931.1Sad	/* Offset block number to start of partition. */
7941.1Sad	blkno += sectoff;
7951.1Sad
7961.1Sad	/* Start dumping and return when done. */
7971.3Sad	maxblkcnt = sc->sc_maxxfer / sc->sc_secsize - 1;
7981.1Sad	while (towrt > 0) {
7991.3Sad		nblk = min(maxblkcnt, towrt);
8001.1Sad
8011.1Sad		if ((rv = (*sc->sc_dump)(sc, va, blkno, nblk)) != 0)
8021.1Sad			return (rv);
8031.1Sad
8041.1Sad		towrt -= nblk;
8051.1Sad		blkno += nblk;
8061.1Sad		va += nblk * sc->sc_secsize;
8071.1Sad	}
8081.1Sad
8091.1Sad	dumping = 0;
8101.1Sad	return (0);
8111.1Sad}
8121.1Sad
8131.1Sad/*
8141.1Sad * Adjust the size of a transfer.
8151.1Sad */
8161.1Sadstatic void
8171.1Sadldminphys(struct buf *bp)
8181.1Sad{
8191.1Sad	struct ld_softc *sc;
8201.1Sad
8211.1Sad	sc = device_lookup(&ld_cd, DISKUNIT(bp->b_dev));
8221.1Sad
8231.1Sad	if (bp->b_bcount > sc->sc_maxxfer)
8241.1Sad		bp->b_bcount = sc->sc_maxxfer;
8251.1Sad	minphys(bp);
8261.1Sad}
827