ld.c revision 1.37.2.5
11.37.2.5Syamt/* $NetBSD: ld.c,v 1.37.2.5 2007/11/15 11:44:01 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.5Syamt__KERNEL_RCSID(0, "$NetBSD: ld.c,v 1.37.2.5 2007/11/15 11:44:01 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.37.2.4Syamt disk_init(&sc->sc_dk, sc->sc_dv.dv_xname, &lddkdriver); 1201.1Sad disk_attach(&sc->sc_dk); 1211.1Sad 1221.1Sad if (sc->sc_maxxfer > MAXPHYS) 1231.1Sad sc->sc_maxxfer = MAXPHYS; 1241.9Sad 1251.19Sthorpej /* Build synthetic geometry if necessary. */ 1261.19Sthorpej if (sc->sc_nheads == 0 || sc->sc_nsectors == 0 || 1271.19Sthorpej sc->sc_ncylinders == 0) { 1281.28Sdbj uint64_t ncyl; 1291.28Sdbj 1301.19Sthorpej if (sc->sc_secperunit <= 528 * 2048) /* 528MB */ 1311.19Sthorpej sc->sc_nheads = 16; 1321.19Sthorpej else if (sc->sc_secperunit <= 1024 * 2048) /* 1GB */ 1331.19Sthorpej sc->sc_nheads = 32; 1341.19Sthorpej else if (sc->sc_secperunit <= 21504 * 2048) /* 21GB */ 1351.19Sthorpej sc->sc_nheads = 64; 1361.19Sthorpej else if (sc->sc_secperunit <= 43008 * 2048) /* 42GB */ 1371.19Sthorpej sc->sc_nheads = 128; 1381.19Sthorpej else 1391.19Sthorpej sc->sc_nheads = 255; 1401.19Sthorpej 1411.19Sthorpej sc->sc_nsectors = 63; 1421.28Sdbj sc->sc_ncylinders = INT_MAX; 1431.35Sperry ncyl = sc->sc_secperunit / 1441.19Sthorpej (sc->sc_nheads * sc->sc_nsectors); 1451.28Sdbj if (ncyl < INT_MAX) 1461.28Sdbj sc->sc_ncylinders = (int)ncyl; 1471.19Sthorpej } 1481.1Sad 1491.37Schristos format_bytes(tbuf, sizeof(tbuf), sc->sc_secperunit * 1501.1Sad sc->sc_secsize); 1511.34Sbriggs aprint_normal("%s: %s, %d cyl, %d head, %d sec, %d bytes/sect x %"PRIu64" sectors\n", 1521.37Schristos sc->sc_dv.dv_xname, tbuf, sc->sc_ncylinders, sc->sc_nheads, 1531.1Sad sc->sc_nsectors, sc->sc_secsize, sc->sc_secperunit); 1541.1Sad 1551.37.2.2Syamt ld_set_properties(sc); 1561.37.2.2Syamt 1571.1Sad#if NRND > 0 1581.1Sad /* Attach the device into the rnd source list. */ 1591.1Sad rnd_attach_source(&sc->sc_rnd_source, sc->sc_dv.dv_xname, 1601.1Sad RND_TYPE_DISK, 0); 1611.1Sad#endif 1621.1Sad 1631.1Sad /* Set the `shutdownhook'. */ 1641.1Sad if (ld_sdh == NULL) 1651.1Sad ld_sdh = shutdownhook_establish(ldshutdown, NULL); 1661.37.2.1Syamt bufq_alloc(&sc->sc_bufq, BUFQ_DISK_DEFAULT_STRAT, BUFQ_SORT_RAWBLOCK); 1671.30Sthorpej 1681.30Sthorpej /* Discover wedges on this disk. */ 1691.37.2.2Syamt config_interrupts(&sc->sc_dv, ld_config_interrupts); 1701.1Sad} 1711.1Sad 1721.3Sadint 1731.37Schristosldadjqparam(struct ld_softc *sc, int xmax) 1741.3Sad{ 1751.24Sthorpej int s; 1761.7Sad 1771.7Sad s = splbio(); 1781.37Schristos sc->sc_maxqueuecnt = xmax; 1791.7Sad splx(s); 1801.7Sad 1811.24Sthorpej return (0); 1821.7Sad} 1831.7Sad 1841.7Sadint 1851.7Sadldbegindetach(struct ld_softc *sc, int flags) 1861.7Sad{ 1871.24Sthorpej int s, rv = 0; 1881.7Sad 1891.7Sad if ((sc->sc_flags & LDF_ENABLED) == 0) 1901.7Sad return (0); 1911.3Sad 1921.3Sad if ((flags & DETACH_FORCE) == 0 && sc->sc_dk.dk_openmask != 0) 1931.3Sad return (EBUSY); 1941.3Sad 1951.3Sad s = splbio(); 1961.24Sthorpej sc->sc_maxqueuecnt = 0; 1971.7Sad sc->sc_flags |= LDF_DETACH; 1981.24Sthorpej while (sc->sc_queuecnt > 0) { 1991.24Sthorpej sc->sc_flags |= LDF_DRAIN; 2001.24Sthorpej rv = tsleep(&sc->sc_queuecnt, PRIBIO, "lddrn", 0); 2011.24Sthorpej if (rv) 2021.24Sthorpej break; 2031.24Sthorpej } 2041.3Sad splx(s); 2051.7Sad 2061.7Sad return (rv); 2071.3Sad} 2081.3Sad 2091.2Sadvoid 2101.7Sadldenddetach(struct ld_softc *sc) 2111.2Sad{ 2121.13Sdrochner int s, bmaj, cmaj, i, mn; 2131.2Sad 2141.7Sad if ((sc->sc_flags & LDF_ENABLED) == 0) 2151.7Sad return; 2161.7Sad 2171.2Sad /* Wait for commands queued with the hardware to complete. */ 2181.2Sad if (sc->sc_queuecnt != 0) 2191.7Sad if (tsleep(&sc->sc_queuecnt, PRIBIO, "lddtch", 30 * hz)) 2201.7Sad printf("%s: not drained\n", sc->sc_dv.dv_xname); 2211.2Sad 2221.2Sad /* Locate the major numbers. */ 2231.16Sgehenna bmaj = bdevsw_lookup_major(&ld_bdevsw); 2241.16Sgehenna cmaj = cdevsw_lookup_major(&ld_cdevsw); 2251.2Sad 2261.2Sad /* Kill off any queued buffers. */ 2271.2Sad s = splbio(); 2281.37.2.1Syamt bufq_drain(sc->sc_bufq); 2291.36Syamt splx(s); 2301.36Syamt 2311.37.2.1Syamt bufq_free(sc->sc_bufq); 2321.2Sad 2331.2Sad /* Nuke the vnodes for any open instances. */ 2341.13Sdrochner for (i = 0; i < MAXPARTITIONS; i++) { 2351.37.2.1Syamt mn = DISKMINOR(device_unit(&sc->sc_dv), i); 2361.13Sdrochner vdevgone(bmaj, mn, mn, VBLK); 2371.13Sdrochner vdevgone(cmaj, mn, mn, VCHR); 2381.13Sdrochner } 2391.13Sdrochner 2401.30Sthorpej /* Delete all of our wedges. */ 2411.30Sthorpej dkwedge_delall(&sc->sc_dk); 2421.30Sthorpej 2431.2Sad /* Detach from the disk list. */ 2441.2Sad disk_detach(&sc->sc_dk); 2451.37.2.4Syamt disk_destroy(&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.37.2.3Syamt mutex_enter(&sc->sc_dk.dk_openlock); 2971.1Sad 2981.22Sthorpej if (sc->sc_dk.dk_openmask == 0) { 2991.22Sthorpej /* Load the partition info if not already loaded. */ 3001.22Sthorpej if ((sc->sc_flags & LDF_VLABEL) == 0) 3011.22Sthorpej ldgetdisklabel(sc); 3021.22Sthorpej } 3031.1Sad 3041.1Sad /* Check that the partition exists. */ 3051.1Sad if (part != RAW_PART && (part >= sc->sc_dk.dk_label->d_npartitions || 3061.1Sad sc->sc_dk.dk_label->d_partitions[part].p_fstype == FS_UNUSED)) { 3071.30Sthorpej error = ENXIO; 3081.30Sthorpej goto bad1; 3091.1Sad } 3101.1Sad 3111.1Sad /* Ensure only one open at a time. */ 3121.1Sad switch (fmt) { 3131.1Sad case S_IFCHR: 3141.1Sad sc->sc_dk.dk_copenmask |= (1 << part); 3151.1Sad break; 3161.1Sad case S_IFBLK: 3171.1Sad sc->sc_dk.dk_bopenmask |= (1 << part); 3181.1Sad break; 3191.1Sad } 3201.1Sad sc->sc_dk.dk_openmask = 3211.1Sad sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask; 3221.1Sad 3231.37.2.3Syamt error = 0; 3241.30Sthorpej bad1: 3251.37.2.3Syamt mutex_exit(&sc->sc_dk.dk_openlock); 3261.30Sthorpej return (error); 3271.1Sad} 3281.1Sad 3291.8Slukem/* ARGSUSED */ 3301.29Sthorpejstatic int 3311.37.2.1Syamtldclose(dev_t dev, int flags, int fmt, struct lwp *l) 3321.1Sad{ 3331.1Sad struct ld_softc *sc; 3341.37.2.3Syamt int part, unit; 3351.1Sad 3361.1Sad unit = DISKUNIT(dev); 3371.1Sad part = DISKPART(dev); 3381.1Sad sc = device_lookup(&ld_cd, unit); 3391.30Sthorpej 3401.37.2.3Syamt mutex_enter(&sc->sc_dk.dk_openlock); 3411.1Sad 3421.1Sad switch (fmt) { 3431.1Sad case S_IFCHR: 3441.1Sad sc->sc_dk.dk_copenmask &= ~(1 << part); 3451.1Sad break; 3461.1Sad case S_IFBLK: 3471.1Sad sc->sc_dk.dk_bopenmask &= ~(1 << part); 3481.1Sad break; 3491.1Sad } 3501.1Sad sc->sc_dk.dk_openmask = 3511.1Sad sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask; 3521.1Sad 3531.22Sthorpej if (sc->sc_dk.dk_openmask == 0) { 3541.22Sthorpej if (sc->sc_flush != NULL && (*sc->sc_flush)(sc) != 0) 3551.1Sad printf("%s: unable to flush cache\n", 3561.1Sad sc->sc_dv.dv_xname); 3571.22Sthorpej if ((sc->sc_flags & LDF_KLABEL) == 0) 3581.22Sthorpej sc->sc_flags &= ~LDF_VLABEL; 3591.22Sthorpej } 3601.1Sad 3611.37.2.3Syamt mutex_exit(&sc->sc_dk.dk_openlock); 3621.1Sad return (0); 3631.1Sad} 3641.1Sad 3651.8Slukem/* ARGSUSED */ 3661.29Sthorpejstatic int 3671.1Sadldread(dev_t dev, struct uio *uio, int ioflag) 3681.1Sad{ 3691.1Sad 3701.1Sad return (physio(ldstrategy, NULL, dev, B_READ, ldminphys, uio)); 3711.1Sad} 3721.1Sad 3731.8Slukem/* ARGSUSED */ 3741.29Sthorpejstatic int 3751.1Sadldwrite(dev_t dev, struct uio *uio, int ioflag) 3761.1Sad{ 3771.1Sad 3781.1Sad return (physio(ldstrategy, NULL, dev, B_WRITE, ldminphys, uio)); 3791.1Sad} 3801.1Sad 3811.8Slukem/* ARGSUSED */ 3821.29Sthorpejstatic int 3831.37.2.3Syamtldioctl(dev_t dev, u_long cmd, void *addr, int32_t flag, struct lwp *l) 3841.1Sad{ 3851.1Sad struct ld_softc *sc; 3861.1Sad int part, unit, error; 3871.4Sfvdl#ifdef __HAVE_OLD_DISKLABEL 3881.6Sitojun struct disklabel newlabel; 3891.4Sfvdl#endif 3901.6Sitojun struct disklabel *lp; 3911.1Sad 3921.1Sad unit = DISKUNIT(dev); 3931.1Sad part = DISKPART(dev); 3941.1Sad sc = device_lookup(&ld_cd, unit); 3951.1Sad 3961.37.2.2Syamt error = disk_ioctl(&sc->sc_dk, cmd, addr, flag, l); 3971.37.2.2Syamt if (error != EPASSTHROUGH) 3981.37.2.2Syamt return (error); 3991.37.2.2Syamt 4001.37.2.3Syamt error = 0; 4011.1Sad switch (cmd) { 4021.1Sad case DIOCGDINFO: 4031.1Sad memcpy(addr, sc->sc_dk.dk_label, sizeof(struct disklabel)); 4041.1Sad return (0); 4051.1Sad 4061.4Sfvdl#ifdef __HAVE_OLD_DISKLABEL 4071.4Sfvdl case ODIOCGDINFO: 4081.4Sfvdl newlabel = *(sc->sc_dk.dk_label); 4091.4Sfvdl if (newlabel.d_npartitions > OLDMAXPARTITIONS) 4101.5Sfvdl return ENOTTY; 4111.4Sfvdl memcpy(addr, &newlabel, sizeof(struct olddisklabel)); 4121.4Sfvdl return (0); 4131.4Sfvdl#endif 4141.4Sfvdl 4151.1Sad case DIOCGPART: 4161.1Sad ((struct partinfo *)addr)->disklab = sc->sc_dk.dk_label; 4171.1Sad ((struct partinfo *)addr)->part = 4181.1Sad &sc->sc_dk.dk_label->d_partitions[part]; 4191.1Sad break; 4201.1Sad 4211.1Sad case DIOCWDINFO: 4221.1Sad case DIOCSDINFO: 4231.4Sfvdl#ifdef __HAVE_OLD_DISKLABEL 4241.4Sfvdl case ODIOCWDINFO: 4251.4Sfvdl case ODIOCSDINFO: 4261.4Sfvdl 4271.4Sfvdl if (cmd == ODIOCSDINFO || cmd == ODIOCWDINFO) { 4281.4Sfvdl memset(&newlabel, 0, sizeof newlabel); 4291.4Sfvdl memcpy(&newlabel, addr, sizeof (struct olddisklabel)); 4301.4Sfvdl lp = &newlabel; 4311.4Sfvdl } else 4321.4Sfvdl#endif 4331.4Sfvdl lp = (struct disklabel *)addr; 4341.4Sfvdl 4351.1Sad if ((flag & FWRITE) == 0) 4361.1Sad return (EBADF); 4371.1Sad 4381.37.2.3Syamt mutex_enter(&sc->sc_dk.dk_openlock); 4391.1Sad sc->sc_flags |= LDF_LABELLING; 4401.1Sad 4411.1Sad error = setdisklabel(sc->sc_dk.dk_label, 4421.4Sfvdl lp, /*sc->sc_dk.dk_openmask : */0, 4431.1Sad sc->sc_dk.dk_cpulabel); 4441.4Sfvdl if (error == 0 && (cmd == DIOCWDINFO 4451.4Sfvdl#ifdef __HAVE_OLD_DISKLABEL 4461.4Sfvdl || cmd == ODIOCWDINFO 4471.4Sfvdl#endif 4481.4Sfvdl )) 4491.1Sad error = writedisklabel( 4501.35Sperry MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART), 4511.35Sperry ldstrategy, sc->sc_dk.dk_label, 4521.1Sad sc->sc_dk.dk_cpulabel); 4531.1Sad 4541.1Sad sc->sc_flags &= ~LDF_LABELLING; 4551.37.2.3Syamt mutex_exit(&sc->sc_dk.dk_openlock); 4561.1Sad break; 4571.1Sad 4581.22Sthorpej case DIOCKLABEL: 4591.22Sthorpej if ((flag & FWRITE) == 0) 4601.22Sthorpej return (EBADF); 4611.22Sthorpej if (*(int *)addr) 4621.22Sthorpej sc->sc_flags |= LDF_KLABEL; 4631.22Sthorpej else 4641.22Sthorpej sc->sc_flags &= ~LDF_KLABEL; 4651.22Sthorpej break; 4661.22Sthorpej 4671.1Sad case DIOCWLABEL: 4681.1Sad if ((flag & FWRITE) == 0) 4691.1Sad return (EBADF); 4701.1Sad if (*(int *)addr) 4711.1Sad sc->sc_flags |= LDF_WLABEL; 4721.1Sad else 4731.1Sad sc->sc_flags &= ~LDF_WLABEL; 4741.1Sad break; 4751.1Sad 4761.1Sad case DIOCGDEFLABEL: 4771.1Sad ldgetdefaultlabel(sc, (struct disklabel *)addr); 4781.1Sad break; 4791.4Sfvdl 4801.4Sfvdl#ifdef __HAVE_OLD_DISKLABEL 4811.4Sfvdl case ODIOCGDEFLABEL: 4821.4Sfvdl ldgetdefaultlabel(sc, &newlabel); 4831.4Sfvdl if (newlabel.d_npartitions > OLDMAXPARTITIONS) 4841.5Sfvdl return ENOTTY; 4851.4Sfvdl memcpy(addr, &newlabel, sizeof (struct olddisklabel)); 4861.4Sfvdl break; 4871.4Sfvdl#endif 4881.1Sad 4891.32Sthorpej case DIOCCACHESYNC: 4901.32Sthorpej /* 4911.32Sthorpej * XXX Do we really need to care about having a writable 4921.32Sthorpej * file descriptor here? 4931.32Sthorpej */ 4941.32Sthorpej if ((flag & FWRITE) == 0) 4951.32Sthorpej error = EBADF; 4961.32Sthorpej else if (sc->sc_flush) 4971.32Sthorpej error = (*sc->sc_flush)(sc); 4981.32Sthorpej else 4991.32Sthorpej error = 0; /* XXX Error out instead? */ 5001.32Sthorpej break; 5011.32Sthorpej 5021.30Sthorpej case DIOCAWEDGE: 5031.30Sthorpej { 5041.30Sthorpej struct dkwedge_info *dkw = (void *) addr; 5051.30Sthorpej 5061.30Sthorpej if ((flag & FWRITE) == 0) 5071.30Sthorpej return (EBADF); 5081.30Sthorpej 5091.30Sthorpej /* If the ioctl happens here, the parent is us. */ 5101.30Sthorpej strcpy(dkw->dkw_parent, sc->sc_dv.dv_xname); 5111.30Sthorpej return (dkwedge_add(dkw)); 5121.30Sthorpej } 5131.35Sperry 5141.30Sthorpej case DIOCDWEDGE: 5151.30Sthorpej { 5161.30Sthorpej struct dkwedge_info *dkw = (void *) addr; 5171.30Sthorpej 5181.30Sthorpej if ((flag & FWRITE) == 0) 5191.30Sthorpej return (EBADF); 5201.30Sthorpej 5211.30Sthorpej /* If the ioctl happens here, the parent is us. */ 5221.30Sthorpej strcpy(dkw->dkw_parent, sc->sc_dv.dv_xname); 5231.30Sthorpej return (dkwedge_del(dkw)); 5241.30Sthorpej } 5251.35Sperry 5261.30Sthorpej case DIOCLWEDGES: 5271.30Sthorpej { 5281.30Sthorpej struct dkwedge_list *dkwl = (void *) addr; 5291.30Sthorpej 5301.37.2.1Syamt return (dkwedge_list(&sc->sc_dk, dkwl, l)); 5311.30Sthorpej } 5321.37.2.5Syamt case DIOCGSTRATEGY: 5331.37.2.5Syamt { 5341.37.2.5Syamt struct disk_strategy *dks = (void *)addr; 5351.37.2.5Syamt 5361.37.2.5Syamt mutex_enter(&sc->sc_mutex); 5371.37.2.5Syamt strlcpy(dks->dks_name, bufq_getstrategyname(sc->sc_bufq), 5381.37.2.5Syamt sizeof(dks->dks_name)); 5391.37.2.5Syamt mutex_exit(&sc->sc_mutex); 5401.37.2.5Syamt dks->dks_paramlen = 0; 5411.37.2.5Syamt 5421.37.2.5Syamt return 0; 5431.37.2.5Syamt } 5441.37.2.5Syamt case DIOCSSTRATEGY: 5451.37.2.5Syamt { 5461.37.2.5Syamt struct disk_strategy *dks = (void *)addr; 5471.37.2.5Syamt struct bufq_state *new, *old; 5481.30Sthorpej 5491.37.2.5Syamt if ((flag & FWRITE) == 0) 5501.37.2.5Syamt return EPERM; 5511.37.2.5Syamt 5521.37.2.5Syamt if (dks->dks_param != NULL) 5531.37.2.5Syamt return EINVAL; 5541.37.2.5Syamt 5551.37.2.5Syamt dks->dks_name[sizeof(dks->dks_name) - 1] = 0; /* ensure term */ 5561.37.2.5Syamt error = bufq_alloc(&new, dks->dks_name, 5571.37.2.5Syamt BUFQ_EXACT|BUFQ_SORT_RAWBLOCK); 5581.37.2.5Syamt if (error) 5591.37.2.5Syamt return error; 5601.37.2.5Syamt 5611.37.2.5Syamt mutex_enter(&sc->sc_mutex); 5621.37.2.5Syamt old = sc->sc_bufq; 5631.37.2.5Syamt bufq_move(new, old); 5641.37.2.5Syamt sc->sc_bufq = new; 5651.37.2.5Syamt mutex_exit(&sc->sc_mutex); 5661.37.2.5Syamt bufq_free(old); 5671.37.2.5Syamt 5681.37.2.5Syamt return 0; 5691.37.2.5Syamt } 5701.1Sad default: 5711.1Sad error = ENOTTY; 5721.1Sad break; 5731.1Sad } 5741.1Sad 5751.1Sad return (error); 5761.1Sad} 5771.1Sad 5781.29Sthorpejstatic void 5791.1Sadldstrategy(struct buf *bp) 5801.1Sad{ 5811.1Sad struct ld_softc *sc; 5821.23Sthorpej struct disklabel *lp; 5831.23Sthorpej daddr_t blkno; 5841.23Sthorpej int s, part; 5851.1Sad 5861.1Sad sc = device_lookup(&ld_cd, DISKUNIT(bp->b_dev)); 5871.23Sthorpej part = DISKPART(bp->b_dev); 5881.1Sad 5891.7Sad if ((sc->sc_flags & LDF_DETACH) != 0) { 5901.2Sad bp->b_error = EIO; 5911.37.2.3Syamt goto done; 5921.2Sad } 5931.2Sad 5941.1Sad lp = sc->sc_dk.dk_label; 5951.1Sad 5961.1Sad /* 5971.1Sad * The transfer must be a whole number of blocks and the offset must 5981.1Sad * not be negative. 5991.1Sad */ 6001.1Sad if ((bp->b_bcount % lp->d_secsize) != 0 || bp->b_blkno < 0) { 6011.23Sthorpej bp->b_error = EINVAL; 6021.37.2.3Syamt goto done; 6031.1Sad } 6041.1Sad 6051.23Sthorpej /* If it's a null transfer, return immediately. */ 6061.23Sthorpej if (bp->b_bcount == 0) 6071.23Sthorpej goto done; 6081.1Sad 6091.1Sad /* 6101.1Sad * Do bounds checking and adjust the transfer. If error, process. 6111.1Sad * If past the end of partition, just return. 6121.1Sad */ 6131.1Sad if (part != RAW_PART && 6141.20Sthorpej bounds_check_with_label(&sc->sc_dk, bp, 6151.1Sad (sc->sc_flags & (LDF_WLABEL | LDF_LABELLING)) != 0) <= 0) { 6161.23Sthorpej goto done; 6171.1Sad } 6181.1Sad 6191.1Sad /* 6201.23Sthorpej * Convert the block number to absolute and put it in terms 6211.23Sthorpej * of the device's logical block size. 6221.1Sad */ 6231.23Sthorpej if (lp->d_secsize == DEV_BSIZE) 6241.23Sthorpej blkno = bp->b_blkno; 6251.23Sthorpej else if (lp->d_secsize > DEV_BSIZE) 6261.23Sthorpej blkno = bp->b_blkno / (lp->d_secsize / DEV_BSIZE); 6271.1Sad else 6281.23Sthorpej blkno = bp->b_blkno * (DEV_BSIZE / lp->d_secsize); 6291.1Sad 6301.11Sthorpej if (part != RAW_PART) 6311.23Sthorpej blkno += lp->d_partitions[part].p_offset; 6321.23Sthorpej 6331.23Sthorpej bp->b_rawblkno = blkno; 6341.1Sad 6351.1Sad s = splbio(); 6361.37.2.2Syamt ldstart(sc, bp); 6371.1Sad splx(s); 6381.23Sthorpej return; 6391.23Sthorpej 6401.23Sthorpej done: 6411.23Sthorpej bp->b_resid = bp->b_bcount; 6421.23Sthorpej biodone(bp); 6431.23Sthorpej} 6441.23Sthorpej 6451.23Sthorpejstatic void 6461.37.2.2Syamtldstart(struct ld_softc *sc, struct buf *bp) 6471.23Sthorpej{ 6481.23Sthorpej int error; 6491.1Sad 6501.37.2.2Syamt mutex_enter(&sc->sc_mutex); 6511.37.2.2Syamt 6521.37.2.2Syamt if (bp != NULL) 6531.37.2.2Syamt BUFQ_PUT(sc->sc_bufq, bp); 6541.37.2.2Syamt 6551.23Sthorpej while (sc->sc_queuecnt < sc->sc_maxqueuecnt) { 6561.23Sthorpej /* See if there is work to do. */ 6571.37.2.1Syamt if ((bp = BUFQ_PEEK(sc->sc_bufq)) == NULL) 6581.23Sthorpej break; 6591.23Sthorpej 6601.23Sthorpej disk_busy(&sc->sc_dk); 6611.23Sthorpej sc->sc_queuecnt++; 6621.23Sthorpej 6631.23Sthorpej if (__predict_true((error = (*sc->sc_start)(sc, bp)) == 0)) { 6641.23Sthorpej /* 6651.23Sthorpej * The back-end is running the job; remove it from 6661.23Sthorpej * the queue. 6671.23Sthorpej */ 6681.37.2.1Syamt (void) BUFQ_GET(sc->sc_bufq); 6691.23Sthorpej } else { 6701.23Sthorpej disk_unbusy(&sc->sc_dk, 0, (bp->b_flags & B_READ)); 6711.23Sthorpej sc->sc_queuecnt--; 6721.23Sthorpej if (error == EAGAIN) { 6731.23Sthorpej /* 6741.23Sthorpej * Temporary resource shortage in the 6751.23Sthorpej * back-end; just defer the job until 6761.23Sthorpej * later. 6771.23Sthorpej * 6781.23Sthorpej * XXX We might consider a watchdog timer 6791.23Sthorpej * XXX to make sure we are kicked into action. 6801.23Sthorpej */ 6811.23Sthorpej break; 6821.23Sthorpej } else { 6831.37.2.1Syamt (void) BUFQ_GET(sc->sc_bufq); 6841.23Sthorpej bp->b_error = error; 6851.23Sthorpej bp->b_resid = bp->b_bcount; 6861.37.2.2Syamt mutex_exit(&sc->sc_mutex); 6871.23Sthorpej biodone(bp); 6881.37.2.2Syamt mutex_enter(&sc->sc_mutex); 6891.23Sthorpej } 6901.23Sthorpej } 6911.1Sad } 6921.37.2.2Syamt 6931.37.2.2Syamt mutex_exit(&sc->sc_mutex); 6941.1Sad} 6951.1Sad 6961.1Sadvoid 6971.1Sadlddone(struct ld_softc *sc, struct buf *bp) 6981.1Sad{ 6991.1Sad 7001.37.2.3Syamt if (bp->b_error != 0) { 7011.1Sad diskerr(bp, "ld", "error", LOG_PRINTF, 0, sc->sc_dk.dk_label); 7021.1Sad printf("\n"); 7031.1Sad } 7041.1Sad 7051.18Smrg disk_unbusy(&sc->sc_dk, bp->b_bcount - bp->b_resid, 7061.18Smrg (bp->b_flags & B_READ)); 7071.1Sad#if NRND > 0 7081.1Sad rnd_add_uint32(&sc->sc_rnd_source, bp->b_rawblkno); 7091.1Sad#endif 7101.1Sad biodone(bp); 7111.1Sad 7121.37.2.2Syamt mutex_enter(&sc->sc_mutex); 7131.7Sad if (--sc->sc_queuecnt <= sc->sc_maxqueuecnt) { 7141.24Sthorpej if ((sc->sc_flags & LDF_DRAIN) != 0) { 7151.24Sthorpej sc->sc_flags &= ~LDF_DRAIN; 7161.7Sad wakeup(&sc->sc_queuecnt); 7171.24Sthorpej } 7181.37.2.2Syamt mutex_exit(&sc->sc_mutex); 7191.37.2.2Syamt ldstart(sc, NULL); 7201.37.2.2Syamt } else 7211.37.2.2Syamt mutex_exit(&sc->sc_mutex); 7221.1Sad} 7231.1Sad 7241.29Sthorpejstatic int 7251.1Sadldsize(dev_t dev) 7261.1Sad{ 7271.1Sad struct ld_softc *sc; 7281.1Sad int part, unit, omask, size; 7291.1Sad 7301.1Sad unit = DISKUNIT(dev); 7311.1Sad if ((sc = device_lookup(&ld_cd, unit)) == NULL) 7321.1Sad return (ENODEV); 7331.1Sad if ((sc->sc_flags & LDF_ENABLED) == 0) 7341.1Sad return (ENODEV); 7351.1Sad part = DISKPART(dev); 7361.1Sad 7371.1Sad omask = sc->sc_dk.dk_openmask & (1 << part); 7381.1Sad 7391.1Sad if (omask == 0 && ldopen(dev, 0, S_IFBLK, NULL) != 0) 7401.1Sad return (-1); 7411.1Sad else if (sc->sc_dk.dk_label->d_partitions[part].p_fstype != FS_SWAP) 7421.1Sad size = -1; 7431.1Sad else 7441.1Sad size = sc->sc_dk.dk_label->d_partitions[part].p_size * 7451.1Sad (sc->sc_dk.dk_label->d_secsize / DEV_BSIZE); 7461.1Sad if (omask == 0 && ldclose(dev, 0, S_IFBLK, NULL) != 0) 7471.1Sad return (-1); 7481.1Sad 7491.1Sad return (size); 7501.1Sad} 7511.1Sad 7521.1Sad/* 7531.1Sad * Load the label information from the specified device. 7541.1Sad */ 7551.1Sadstatic void 7561.1Sadldgetdisklabel(struct ld_softc *sc) 7571.1Sad{ 7581.1Sad const char *errstring; 7591.1Sad 7601.1Sad ldgetdefaultlabel(sc, sc->sc_dk.dk_label); 7611.1Sad 7621.1Sad /* Call the generic disklabel extraction routine. */ 7631.37.2.1Syamt errstring = readdisklabel(MAKEDISKDEV(0, device_unit(&sc->sc_dv), 7641.37.2.1Syamt RAW_PART), ldstrategy, sc->sc_dk.dk_label, sc->sc_dk.dk_cpulabel); 7651.1Sad if (errstring != NULL) 7661.1Sad printf("%s: %s\n", sc->sc_dv.dv_xname, errstring); 7671.22Sthorpej 7681.22Sthorpej /* In-core label now valid. */ 7691.22Sthorpej sc->sc_flags |= LDF_VLABEL; 7701.1Sad} 7711.1Sad 7721.1Sad/* 7731.1Sad * Construct a ficticious label. 7741.1Sad */ 7751.1Sadstatic void 7761.1Sadldgetdefaultlabel(struct ld_softc *sc, struct disklabel *lp) 7771.1Sad{ 7781.1Sad 7791.1Sad memset(lp, 0, sizeof(struct disklabel)); 7801.1Sad 7811.1Sad lp->d_secsize = sc->sc_secsize; 7821.1Sad lp->d_ntracks = sc->sc_nheads; 7831.1Sad lp->d_nsectors = sc->sc_nsectors; 7841.1Sad lp->d_ncylinders = sc->sc_ncylinders; 7851.1Sad lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors; 7861.1Sad lp->d_type = DTYPE_LD; 7871.21Sitojun strlcpy(lp->d_typename, "unknown", sizeof(lp->d_typename)); 7881.21Sitojun strlcpy(lp->d_packname, "fictitious", sizeof(lp->d_packname)); 7891.1Sad lp->d_secperunit = sc->sc_secperunit; 7901.1Sad lp->d_rpm = 7200; 7911.1Sad lp->d_interleave = 1; 7921.1Sad lp->d_flags = 0; 7931.1Sad 7941.1Sad lp->d_partitions[RAW_PART].p_offset = 0; 7951.1Sad lp->d_partitions[RAW_PART].p_size = 7961.1Sad lp->d_secperunit * (lp->d_secsize / DEV_BSIZE); 7971.1Sad lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED; 7981.1Sad lp->d_npartitions = RAW_PART + 1; 7991.1Sad 8001.1Sad lp->d_magic = DISKMAGIC; 8011.1Sad lp->d_magic2 = DISKMAGIC; 8021.1Sad lp->d_checksum = dkcksum(lp); 8031.1Sad} 8041.1Sad 8051.1Sad/* 8061.1Sad * Take a dump. 8071.1Sad */ 8081.29Sthorpejstatic int 8091.37.2.3Syamtlddump(dev_t dev, daddr_t blkno, void *vav, size_t size) 8101.1Sad{ 8111.37.2.3Syamt char *va = vav; 8121.1Sad struct ld_softc *sc; 8131.1Sad struct disklabel *lp; 8141.1Sad int unit, part, nsects, sectoff, towrt, nblk, maxblkcnt, rv; 8151.1Sad static int dumping; 8161.1Sad 8171.1Sad unit = DISKUNIT(dev); 8181.1Sad if ((sc = device_lookup(&ld_cd, unit)) == NULL) 8191.1Sad return (ENXIO); 8201.1Sad if ((sc->sc_flags & LDF_ENABLED) == 0) 8211.1Sad return (ENODEV); 8221.3Sad if (sc->sc_dump == NULL) 8231.3Sad return (ENXIO); 8241.3Sad 8251.3Sad /* Check if recursive dump; if so, punt. */ 8261.3Sad if (dumping) 8271.3Sad return (EFAULT); 8281.3Sad dumping = 1; 8291.1Sad 8301.1Sad /* Convert to disk sectors. Request must be a multiple of size. */ 8311.3Sad part = DISKPART(dev); 8321.1Sad lp = sc->sc_dk.dk_label; 8331.1Sad if ((size % lp->d_secsize) != 0) 8341.1Sad return (EFAULT); 8351.1Sad towrt = size / lp->d_secsize; 8361.1Sad blkno = dbtob(blkno) / lp->d_secsize; /* blkno in DEV_BSIZE units */ 8371.1Sad 8381.1Sad nsects = lp->d_partitions[part].p_size; 8391.1Sad sectoff = lp->d_partitions[part].p_offset; 8401.1Sad 8411.1Sad /* Check transfer bounds against partition size. */ 8421.1Sad if ((blkno < 0) || ((blkno + towrt) > nsects)) 8431.1Sad return (EINVAL); 8441.1Sad 8451.1Sad /* Offset block number to start of partition. */ 8461.1Sad blkno += sectoff; 8471.1Sad 8481.1Sad /* Start dumping and return when done. */ 8491.3Sad maxblkcnt = sc->sc_maxxfer / sc->sc_secsize - 1; 8501.1Sad while (towrt > 0) { 8511.3Sad nblk = min(maxblkcnt, towrt); 8521.1Sad 8531.1Sad if ((rv = (*sc->sc_dump)(sc, va, blkno, nblk)) != 0) 8541.1Sad return (rv); 8551.1Sad 8561.1Sad towrt -= nblk; 8571.1Sad blkno += nblk; 8581.1Sad va += nblk * sc->sc_secsize; 8591.1Sad } 8601.1Sad 8611.1Sad dumping = 0; 8621.1Sad return (0); 8631.1Sad} 8641.1Sad 8651.1Sad/* 8661.1Sad * Adjust the size of a transfer. 8671.1Sad */ 8681.1Sadstatic void 8691.1Sadldminphys(struct buf *bp) 8701.1Sad{ 8711.1Sad struct ld_softc *sc; 8721.1Sad 8731.1Sad sc = device_lookup(&ld_cd, DISKUNIT(bp->b_dev)); 8741.1Sad 8751.1Sad if (bp->b_bcount > sc->sc_maxxfer) 8761.1Sad bp->b_bcount = sc->sc_maxxfer; 8771.1Sad minphys(bp); 8781.1Sad} 8791.37.2.2Syamt 8801.37.2.2Syamtstatic void 8811.37.2.2Syamtld_set_properties(struct ld_softc *ld) 8821.37.2.2Syamt{ 8831.37.2.2Syamt prop_dictionary_t disk_info, odisk_info, geom; 8841.37.2.2Syamt 8851.37.2.2Syamt disk_info = prop_dictionary_create(); 8861.37.2.2Syamt 8871.37.2.2Syamt geom = prop_dictionary_create(); 8881.37.2.2Syamt 8891.37.2.2Syamt prop_dictionary_set_uint64(geom, "sectors-per-unit", 8901.37.2.2Syamt ld->sc_secperunit); 8911.37.2.2Syamt 8921.37.2.2Syamt prop_dictionary_set_uint32(geom, "sector-size", 8931.37.2.2Syamt ld->sc_secsize); 8941.37.2.2Syamt 8951.37.2.2Syamt prop_dictionary_set_uint16(geom, "sectors-per-track", 8961.37.2.2Syamt ld->sc_nsectors); 8971.37.2.2Syamt 8981.37.2.2Syamt prop_dictionary_set_uint16(geom, "tracks-per-cylinder", 8991.37.2.2Syamt ld->sc_nheads); 9001.37.2.2Syamt 9011.37.2.2Syamt prop_dictionary_set_uint64(geom, "cylinders-per-unit", 9021.37.2.2Syamt ld->sc_ncylinders); 9031.37.2.2Syamt 9041.37.2.2Syamt prop_dictionary_set(disk_info, "geometry", geom); 9051.37.2.2Syamt prop_object_release(geom); 9061.37.2.2Syamt 9071.37.2.2Syamt prop_dictionary_set(device_properties(&ld->sc_dv), 9081.37.2.2Syamt "disk-info", disk_info); 9091.37.2.2Syamt 9101.37.2.2Syamt /* 9111.37.2.2Syamt * Don't release disk_info here; we keep a reference to it. 9121.37.2.2Syamt * disk_detach() will release it when we go away. 9131.37.2.2Syamt */ 9141.37.2.2Syamt 9151.37.2.2Syamt odisk_info = ld->sc_dk.dk_info; 9161.37.2.2Syamt ld->sc_dk.dk_info = disk_info; 9171.37.2.2Syamt if (odisk_info) 9181.37.2.2Syamt prop_object_release(odisk_info); 9191.37.2.2Syamt} 9201.37.2.2Syamt 9211.37.2.2Syamtstatic void 9221.37.2.2Syamtld_config_interrupts (struct device *d) 9231.37.2.2Syamt{ 9241.37.2.2Syamt struct ld_softc *sc = (struct ld_softc *)d; 9251.37.2.2Syamt dkwedge_discover(&sc->sc_dk); 9261.37.2.2Syamt} 927